mirror of https://github.com/bsnes-emu/bsnes.git
Update to v098r06 release.
byuu says: Changelog: - emulation cores now refresh video from host thread instead of cothreads (fix AMD crash) - SFC: fixed another bug with leap year months in SharpRTC emulation - SFC: cleaned up camelCase on function names for armdsp,epsonrtc,hitachidsp,mcc,nss,sharprtc classes - GB: added MBC1M emulation (requires manually setting mapper=MBC1M in manifest.bml for now, sorry) - audio: implemented Emulator::Audio mixer and effects processor - audio: implemented Emulator::Stream interface - it is now possible to have more than two audio streams: eg SNES + SGB + MSU1 + Voicer-Kun (eventually) - audio: added reverb delay + reverb level settings; exposed balance configuration in UI - video: reworked palette generation to re-enable saturation, gamma, luminance adjustments - higan/emulator.cpp is gone since there was nothing left in it I know you guys are going to say the color adjust/balance/reverb stuff is pointless. And indeed it mostly is. But I like the idea of allowing some fun special effects and configurability that isn't system-wide. Note: there seems to be some kind of added audio lag in the SGB emulation now, and I don't really understand why. The code should be effectively identical to what I had before. The only main thing is that I'm sampling things to 48000hz instead of 32040hz before mixing. There's no point where I'm intentionally introducing added latency though. I'm kind of stumped, so if anyone wouldn't mind taking a look at it, it'd be much appreciated :/ I don't have an MSU1 test ROM, but the latency issue may affect MSU1 as well, and that would be very bad.
This commit is contained in:
parent
55e507d5df
commit
e2ee6689a0
|
@ -5,7 +5,7 @@ target := tomoko
|
|||
# console := true
|
||||
|
||||
flags += -I. -I.. -O3
|
||||
objects := libco emulator audio video
|
||||
objects := libco audio video
|
||||
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
|
@ -54,7 +54,6 @@ compile = \
|
|||
all: build;
|
||||
|
||||
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco/)
|
||||
obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/)
|
||||
obj/audio.o: audio/audio.cpp $(call rwildcard,audio/)
|
||||
obj/video.o: video/video.cpp $(call rwildcard,video/)
|
||||
|
||||
|
|
|
@ -1,5 +1,93 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
|
||||
namespace {
|
||||
namespace Emulator {
|
||||
|
||||
Audio audio;
|
||||
|
||||
Stream::Stream(double inputFrequency, double outputFrequency, double volume, double balance) {
|
||||
dsp.setChannels(2);
|
||||
dsp.setPrecision(16);
|
||||
dsp.setFrequency(inputFrequency);
|
||||
dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dsp.setResamplerFrequency(outputFrequency);
|
||||
dsp.setVolume(volume);
|
||||
dsp.setBalance(balance);
|
||||
}
|
||||
|
||||
auto Stream::sample(int16 left, int16 right) -> void {
|
||||
int samples[] = {left, right};
|
||||
dsp.sample(samples);
|
||||
audio.poll();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto Audio::reset() -> void {
|
||||
streams.reset();
|
||||
setReverbDelay(reverbDelay);
|
||||
}
|
||||
|
||||
auto Audio::setInterface(Interface* interface) -> void {
|
||||
this->interface = interface;
|
||||
}
|
||||
|
||||
auto Audio::setFrequency(double frequency) -> void {
|
||||
this->frequency = frequency;
|
||||
for(auto& stream : streams) stream->dsp.setResamplerFrequency(frequency);
|
||||
}
|
||||
|
||||
auto Audio::setVolume(double volume) -> void {
|
||||
this->volume = volume;
|
||||
for(auto& stream : streams) stream->dsp.setVolume(volume);
|
||||
}
|
||||
|
||||
auto Audio::setBalance(double balance) -> void {
|
||||
this->balance = balance;
|
||||
for(auto& stream : streams) stream->dsp.setBalance(balance);
|
||||
}
|
||||
|
||||
auto Audio::setReverbDelay(uint reverbDelay) -> void {
|
||||
this->reverbDelay = reverbDelay;
|
||||
reverbLeft.resize(frequency * reverbDelay / 1000.0);
|
||||
reverbRight.resize(frequency * reverbDelay / 1000.0);
|
||||
memory::fill(reverbLeft.data(), reverbLeft.size() * sizeof(int16));
|
||||
memory::fill(reverbRight.data(), reverbRight.size() * sizeof(int16));
|
||||
}
|
||||
|
||||
auto Audio::setReverbLevel(double reverbLevel) -> void {
|
||||
this->reverbLevel = reverbLevel;
|
||||
}
|
||||
|
||||
auto Audio::createStream(double frequency) -> shared_pointer<Stream> {
|
||||
shared_pointer<Stream> stream = new Stream{frequency, this->frequency, volume, balance};
|
||||
streams.append(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
//audio mixer
|
||||
auto Audio::poll() -> void {
|
||||
while(true) {
|
||||
for(auto& stream : streams) {
|
||||
if(!stream->dsp.pending()) return;
|
||||
}
|
||||
|
||||
int left = 0, right = 0;
|
||||
for(auto& stream : streams) {
|
||||
int samples[2];
|
||||
stream->dsp.read(samples);
|
||||
left += samples[0];
|
||||
right += samples[1];
|
||||
}
|
||||
|
||||
if(reverbDelay) {
|
||||
reverbLeft.append(left);
|
||||
reverbRight.append(right);
|
||||
left += reverbLeft.takeFirst() * reverbLevel;
|
||||
right += reverbRight.takeFirst() * reverbLevel;
|
||||
}
|
||||
|
||||
interface->audioSample(sclamp<16>(left), sclamp<16>(right));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,48 @@
|
|||
|
||||
#include "core.hpp"
|
||||
|
||||
namespace {
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface;
|
||||
|
||||
struct Stream {
|
||||
Stream(double inputFrequency, double outputFrequency, double volume, double balance);
|
||||
auto sample(int16 left, int16 right) -> void;
|
||||
|
||||
private:
|
||||
nall::DSP dsp;
|
||||
|
||||
friend class Audio;
|
||||
};
|
||||
|
||||
struct Audio {
|
||||
auto reset() -> void;
|
||||
auto setInterface(Interface*) -> void;
|
||||
|
||||
auto setFrequency(double frequency) -> void;
|
||||
auto setVolume(double volume) -> void;
|
||||
auto setBalance(double balance) -> void;
|
||||
auto setReverbDelay(uint milliseconds) -> void;
|
||||
auto setReverbLevel(double level) -> void;
|
||||
|
||||
auto createStream(double frequency) -> shared_pointer<Stream>;
|
||||
|
||||
auto poll() -> void;
|
||||
|
||||
private:
|
||||
Interface* interface = nullptr;
|
||||
vector<shared_pointer<Stream>> streams;
|
||||
double frequency = 0.0;
|
||||
double volume = 1.0;
|
||||
double balance = 0.0;
|
||||
uint reverbDelay = 0; //0 = disabled
|
||||
double reverbLevel = 0.0;
|
||||
vector<int16> reverbLeft;
|
||||
vector<int16> reverbRight;
|
||||
|
||||
friend class Stream;
|
||||
};
|
||||
|
||||
extern Audio audio;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
auto Interface::videoColor(uint16 r, uint16 g, uint16 b) -> uint32 {
|
||||
double saturation = 1.0;
|
||||
double gamma = 1.0;
|
||||
double luminance = 1.0;
|
||||
|
||||
if(saturation != 1.0) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double inverse = max(0.0, 1.0 - saturation);
|
||||
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(gamma != 1.0) {
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
|
||||
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
|
||||
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
|
||||
}
|
||||
|
||||
if(luminance != 1.0) {
|
||||
r = uclamp<16>(r * luminance);
|
||||
g = uclamp<16>(g * luminance);
|
||||
b = uclamp<16>(b * luminance);
|
||||
}
|
||||
|
||||
return 255 << 24 | (r >> 8) << 16 | (g >> 8) << 8 | (b >> 8) << 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "098.05";
|
||||
static const string Version = "098.06";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -75,7 +75,13 @@ struct Interface {
|
|||
//information
|
||||
virtual auto manifest() -> string = 0;
|
||||
virtual auto title() -> string = 0;
|
||||
|
||||
//video information
|
||||
virtual auto videoFrequency() -> double = 0;
|
||||
virtual auto videoColors() -> uint32 { return 1 << 19; }
|
||||
virtual auto videoColor(uint32 color) -> uint64 { return 0; }
|
||||
|
||||
//audio information
|
||||
virtual auto audioFrequency() -> double = 0;
|
||||
|
||||
//media interface
|
||||
|
|
|
@ -57,7 +57,7 @@ auto APU::main() -> void {
|
|||
//output = filter.run_lopass(output);
|
||||
output = sclamp<16>(output);
|
||||
|
||||
interface->audioSample(output, output);
|
||||
stream->sample(output, output);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
@ -89,6 +89,7 @@ auto APU::power() -> void {
|
|||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Enter, 21'477'272);
|
||||
stream = Emulator::audio.createStream(21'477'272.0 / 12.0);
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct APU : Thread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
APU();
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Famicom {
|
|||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,61 @@ auto Interface::videoFrequency() -> double {
|
|||
return 21477272.0 / (262.0 * 1364.0 - 4.0);
|
||||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return 1 << 9;
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 n) -> uint64 {
|
||||
double saturation = 2.0;
|
||||
double hue = 0.0;
|
||||
double contrast = 1.0;
|
||||
double brightness = 1.0;
|
||||
double gamma = settings.colorEmulation ? 1.8 : 2.2;
|
||||
|
||||
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
|
||||
|
||||
static const double black = 0.518, white = 1.962, attenuation = 0.746;
|
||||
static const double levels[8] = {
|
||||
0.350, 0.518, 0.962, 1.550,
|
||||
1.094, 1.506, 1.962, 1.962,
|
||||
};
|
||||
|
||||
double lo_and_hi[2] = {
|
||||
levels[level + 4 * (color == 0x0)],
|
||||
levels[level + 4 * (color < 0xd)],
|
||||
};
|
||||
|
||||
double y = 0.0, i = 0.0, q = 0.0;
|
||||
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
|
||||
for(int p : range(12)) {
|
||||
double spot = lo_and_hi[wave(p, color)];
|
||||
|
||||
if(((n & 0x040) && wave(p, 12))
|
||||
|| ((n & 0x080) && wave(p, 4))
|
||||
|| ((n & 0x100) && wave(p, 8))
|
||||
) spot *= attenuation;
|
||||
|
||||
double v = (spot - black) / (white - black);
|
||||
|
||||
v = (v - 0.5) * contrast + 0.5;
|
||||
v *= brightness / 12.0;
|
||||
|
||||
y += v;
|
||||
i += v * std::cos((3.141592653 / 6.0) * (p + hue));
|
||||
q += v * std::sin((3.141592653 / 6.0) * (p + hue));
|
||||
}
|
||||
|
||||
i *= saturation;
|
||||
q *= saturation;
|
||||
|
||||
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
|
||||
uint64 r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q));
|
||||
uint64 g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q));
|
||||
uint64 b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q));
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 21477272.0 / 12.0;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ struct Interface : Emulator::Interface {
|
|||
auto manifest() -> string;
|
||||
auto title() -> string;
|
||||
auto videoFrequency() -> double;
|
||||
auto videoColors() -> uint32;
|
||||
auto videoColor(uint32 color) -> uint64;
|
||||
auto audioFrequency() -> double;
|
||||
|
||||
auto loaded() -> bool;
|
||||
|
|
|
@ -42,10 +42,13 @@ auto PPU::scanline() -> void {
|
|||
|
||||
auto PPU::frame() -> void {
|
||||
status.field ^= 1;
|
||||
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
|
||||
auto PPU::refresh() -> void {
|
||||
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ struct PPU : Thread {
|
|||
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
|
|
@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
|
|||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ auto System::load() -> void {
|
|||
auto document = BML::unserialize(information.manifest);
|
||||
cartridge.load();
|
||||
serializeInit();
|
||||
configureVideo();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
|
@ -44,6 +43,14 @@ auto System::power() -> void {
|
|||
}
|
||||
|
||||
auto System::reset() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
|
|
|
@ -13,8 +13,8 @@ struct System {
|
|||
auto term() -> void;
|
||||
|
||||
//video.cpp
|
||||
auto configureVideo() -> void;
|
||||
auto configureVideoPalette() -> void;
|
||||
auto configureVideoEffects() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize() -> serializer;
|
||||
|
|
|
@ -1,57 +1,6 @@
|
|||
auto System::configureVideo() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoPalette() -> void {
|
||||
auto generateColor = [](uint n, double saturation, double hue, double contrast, double brightness, double gamma) -> uint64 {
|
||||
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
|
||||
|
||||
static const double black = 0.518, white = 1.962, attenuation = 0.746;
|
||||
static const double levels[8] = {
|
||||
0.350, 0.518, 0.962, 1.550,
|
||||
1.094, 1.506, 1.962, 1.962,
|
||||
};
|
||||
|
||||
double lo_and_hi[2] = {
|
||||
levels[level + 4 * (color == 0x0)],
|
||||
levels[level + 4 * (color < 0xd)],
|
||||
};
|
||||
|
||||
double y = 0.0, i = 0.0, q = 0.0;
|
||||
auto wave = [](int p, int color) { return (color + p + 8) % 12 < 6; };
|
||||
for(int p : range(12)) {
|
||||
double spot = lo_and_hi[wave(p, color)];
|
||||
|
||||
if(((n & 0x040) && wave(p, 12))
|
||||
|| ((n & 0x080) && wave(p, 4))
|
||||
|| ((n & 0x100) && wave(p, 8))
|
||||
) spot *= attenuation;
|
||||
|
||||
double v = (spot - black) / (white - black);
|
||||
|
||||
v = (v - 0.5) * contrast + 0.5;
|
||||
v *= brightness / 12.0;
|
||||
|
||||
y += v;
|
||||
i += v * std::cos((3.141592653 / 6.0) * (p + hue));
|
||||
q += v * std::sin((3.141592653 / 6.0) * (p + hue));
|
||||
}
|
||||
|
||||
i *= saturation;
|
||||
q *= saturation;
|
||||
|
||||
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
|
||||
uint64 r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q));
|
||||
uint64 g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q));
|
||||
uint64 b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q));
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
};
|
||||
|
||||
Emulator::video.setPalette(1 << 9, [&](uint32 color) -> uint64 {
|
||||
auto gamma = settings.colorEmulation ? 1.8 : 2.2;
|
||||
return generateColor(color, 2.0, 0.0, 1.0, 1.0, gamma);
|
||||
});
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoEffects() -> void {
|
||||
}
|
||||
|
|
|
@ -25,7 +25,11 @@ auto APU::main() -> void {
|
|||
hipass(sequencer.left, sequencer.leftBias);
|
||||
hipass(sequencer.right, sequencer.rightBias);
|
||||
|
||||
interface->audioSample(sequencer.left, sequencer.right);
|
||||
if(!system.sgb()) {
|
||||
stream->sample(sequencer.left, sequencer.right);
|
||||
} else {
|
||||
interface->audioSample(sequencer.left, sequencer.right);
|
||||
}
|
||||
|
||||
if(cycle == 0) { //512hz
|
||||
if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
|
||||
|
@ -58,6 +62,7 @@ auto APU::hipass(int16& sample, int64& bias) -> void {
|
|||
|
||||
auto APU::power() -> void {
|
||||
create(Enter, 2 * 1024 * 1024);
|
||||
if(!system.sgb()) stream = Emulator::audio.createStream(2 * 1024 * 1024);
|
||||
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
||||
square1.power();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct APU : Thread, MMIO {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto hipass(int16& sample, int64& bias) -> void;
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace GameBoy {
|
|||
|
||||
#include "mbc0/mbc0.cpp"
|
||||
#include "mbc1/mbc1.cpp"
|
||||
#include "mbc1m/mbc1m.cpp"
|
||||
#include "mbc2/mbc2.cpp"
|
||||
#include "mbc3/mbc3.cpp"
|
||||
#include "mbc5/mbc5.cpp"
|
||||
|
@ -40,6 +41,7 @@ auto Cartridge::load(System::Revision revision) -> void {
|
|||
auto mapperid = document["board/mapper"].text();
|
||||
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
|
||||
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
|
||||
if(mapperid == "MBC1M") information.mapper = Mapper::MBC1M;
|
||||
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
|
||||
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
|
||||
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
|
||||
|
@ -70,6 +72,7 @@ auto Cartridge::load(System::Revision revision) -> void {
|
|||
switch(information.mapper) { default:
|
||||
case Mapper::MBC0: mapper = &mbc0; break;
|
||||
case Mapper::MBC1: mapper = &mbc1; break;
|
||||
case Mapper::MBC1M: mapper = &mbc1m; break;
|
||||
case Mapper::MBC2: mapper = &mbc2; break;
|
||||
case Mapper::MBC3: mapper = &mbc3; break;
|
||||
case Mapper::MBC5: mapper = &mbc5; break;
|
||||
|
@ -139,6 +142,7 @@ auto Cartridge::power() -> void {
|
|||
|
||||
mbc0.power();
|
||||
mbc1.power();
|
||||
mbc1m.power();
|
||||
mbc2.power();
|
||||
mbc3.power();
|
||||
mbc5.power();
|
||||
|
|
|
@ -16,6 +16,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||
|
||||
#include "mbc0/mbc0.hpp"
|
||||
#include "mbc1/mbc1.hpp"
|
||||
#include "mbc1m/mbc1m.hpp"
|
||||
#include "mbc2/mbc2.hpp"
|
||||
#include "mbc3/mbc3.hpp"
|
||||
#include "mbc5/mbc5.hpp"
|
||||
|
@ -26,6 +27,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||
enum Mapper : uint {
|
||||
MBC0,
|
||||
MBC1,
|
||||
MBC1M,
|
||||
MBC2,
|
||||
MBC3,
|
||||
MBC5,
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
auto Cartridge::MBC1M::mmio_read(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom_read((romHi << 4) * 0x4000 + addr.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom_read((romHi << 4 | romLo) * 0x4000 + addr.bits(0,13));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1M::mmio_write(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
romLo = data.bits(0,3);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
romHi = data.bits(0,1);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1M::power() -> void {
|
||||
romHi = 0;
|
||||
romLo = 1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
struct MBC1M : MMIO {
|
||||
auto mmio_read(uint16 addr) -> uint8;
|
||||
auto mmio_write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
uint4 romLo;
|
||||
uint2 romHi;
|
||||
} mbc1m;
|
|
@ -7,6 +7,9 @@ auto Cartridge::serialize(serializer& s) -> void {
|
|||
s.integer(mbc1.ram_select);
|
||||
s.integer(mbc1.mode_select);
|
||||
|
||||
s.integer(mbc1m.romLo);
|
||||
s.integer(mbc1m.romHi);
|
||||
|
||||
s.integer(mbc2.ram_enable);
|
||||
s.integer(mbc2.rom_select);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace GameBoy {
|
|||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,67 @@ auto Interface::videoFrequency() -> double {
|
|||
return 4194304.0 / (154.0 * 456.0);
|
||||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return !system.cgb() ? 1 << 2 : 1 << 15;
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 color) -> uint64 {
|
||||
if(!system.cgb()) {
|
||||
if(!settings.colorEmulation) {
|
||||
uint64 L = image::normalize(3 - color, 2, 16);
|
||||
return L << 32 | L << 16 | L << 0;
|
||||
} else {
|
||||
#define DMG_PALETTE_GREEN
|
||||
//#define DMG_PALETTE_YELLOW
|
||||
//#define DMG_PALETTE_WHITE
|
||||
|
||||
const uint16 monochrome[4][3] = {
|
||||
#if defined(DMG_PALETTE_GREEN)
|
||||
{0xaeae, 0xd9d9, 0x2727},
|
||||
{0x5858, 0xa0a0, 0x2828},
|
||||
{0x2020, 0x6262, 0x2929},
|
||||
{0x1a1a, 0x4545, 0x2a2a},
|
||||
#elif defined(DMG_PALETTE_YELLOW)
|
||||
{0xffff, 0xf7f7, 0x7b7b},
|
||||
{0xb5b5, 0xaeae, 0x4a4a},
|
||||
{0x6b6b, 0x6969, 0x3131},
|
||||
{0x2121, 0x2020, 0x1010},
|
||||
#elif defined(DMG_PALETTE_WHITE)
|
||||
{0xffff, 0xffff, 0xffff},
|
||||
{0xaaaa, 0xaaaa, 0xaaaa},
|
||||
{0x5555, 0x5555, 0x5555},
|
||||
{0x0000, 0x0000, 0x0000},
|
||||
#endif
|
||||
};
|
||||
|
||||
uint64 R = monochrome[color][0];
|
||||
uint64 G = monochrome[color][1];
|
||||
uint64 B = monochrome[color][2];
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
}
|
||||
} else {
|
||||
uint r = color.bits( 0, 4);
|
||||
uint g = color.bits( 5, 9);
|
||||
uint b = color.bits(10,14);
|
||||
|
||||
uint64_t R = image::normalize(r, 5, 16);
|
||||
uint64_t G = image::normalize(g, 5, 16);
|
||||
uint64_t B = image::normalize(b, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
R = (r * 26 + g * 4 + b * 2);
|
||||
G = ( g * 24 + b * 8);
|
||||
B = (r * 6 + g * 4 + b * 22);
|
||||
R = image::normalize(min(960, R), 10, 16);
|
||||
G = image::normalize(min(960, G), 10, 16);
|
||||
B = image::normalize(min(960, B), 10, 16);
|
||||
}
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 4194304.0 / 2.0;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ struct Interface : Emulator::Interface {
|
|||
auto manifest() -> string;
|
||||
auto title() -> string;
|
||||
auto videoFrequency() -> double;
|
||||
auto videoColors() -> uint32;
|
||||
auto videoColor(uint32 color) -> uint64;
|
||||
auto audioFrequency() -> double;
|
||||
|
||||
auto loaded() -> bool;
|
||||
|
|
|
@ -60,11 +60,14 @@ auto PPU::main() -> void {
|
|||
|
||||
if(++status.ly == 154) {
|
||||
status.ly = 0;
|
||||
if(!system.sgb()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144);
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::refresh() -> void {
|
||||
if(!system.sgb()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144);
|
||||
}
|
||||
|
||||
auto PPU::add_clocks(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
if(status.dma_active) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct PPU : Thread, MMIO {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto refresh() -> void;
|
||||
auto add_clocks(uint clocks) -> void;
|
||||
|
||||
auto hflip(uint data) const -> uint;
|
||||
|
|
|
@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
|
|||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ auto System::load(Revision revision) -> void {
|
|||
|
||||
cartridge.load(revision);
|
||||
serializeInit();
|
||||
configureVideo();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
|
@ -61,6 +60,16 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
if(!system.sgb()) {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
}
|
||||
|
||||
bus.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
|
|
|
@ -30,7 +30,6 @@ struct System {
|
|||
auto power() -> void;
|
||||
|
||||
//video.cpp
|
||||
auto configureVideo() -> void;
|
||||
auto configureVideoPalette() -> void;
|
||||
auto configureVideoEffects() -> void;
|
||||
|
||||
|
|
|
@ -1,75 +1,9 @@
|
|||
auto System::configureVideo() -> void {
|
||||
if(sgb()) return;
|
||||
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
}
|
||||
|
||||
auto System::configureVideoPalette() -> void {
|
||||
if(sgb()) return;
|
||||
|
||||
if(dmg()) Emulator::video.setPalette(1 << 2, [&](uint32 color) -> uint64 {
|
||||
if(!settings.colorEmulation) {
|
||||
uint64 L = image::normalize(3 - color, 2, 16);
|
||||
return L << 32 | L << 16 | L << 0;
|
||||
} else {
|
||||
#define DMG_PALETTE_GREEN
|
||||
//#define DMG_PALETTE_YELLOW
|
||||
//#define DMG_PALETTE_WHITE
|
||||
|
||||
const uint16 monochrome[4][3] = {
|
||||
#if defined(DMG_PALETTE_GREEN)
|
||||
{0xaeae, 0xd9d9, 0x2727},
|
||||
{0x5858, 0xa0a0, 0x2828},
|
||||
{0x2020, 0x6262, 0x2929},
|
||||
{0x1a1a, 0x4545, 0x2a2a},
|
||||
#elif defined(DMG_PALETTE_YELLOW)
|
||||
{0xffff, 0xf7f7, 0x7b7b},
|
||||
{0xb5b5, 0xaeae, 0x4a4a},
|
||||
{0x6b6b, 0x6969, 0x3131},
|
||||
{0x2121, 0x2020, 0x1010},
|
||||
#elif defined(DMG_PALETTE_WHITE)
|
||||
{0xffff, 0xffff, 0xffff},
|
||||
{0xaaaa, 0xaaaa, 0xaaaa},
|
||||
{0x5555, 0x5555, 0x5555},
|
||||
{0x0000, 0x0000, 0x0000},
|
||||
#endif
|
||||
};
|
||||
|
||||
uint64 R = monochrome[color][0];
|
||||
uint64 G = monochrome[color][1];
|
||||
uint64 B = monochrome[color][2];
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
}
|
||||
});
|
||||
|
||||
if(cgb()) Emulator::video.setPalette(1 << 15, [&](uint32 color) -> uint64 {
|
||||
uint r = color.bits( 0, 4);
|
||||
uint g = color.bits( 5, 9);
|
||||
uint b = color.bits(10,14);
|
||||
|
||||
uint64_t R = image::normalize(r, 5, 16);
|
||||
uint64_t G = image::normalize(g, 5, 16);
|
||||
uint64_t B = image::normalize(b, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
R = (r * 26 + g * 4 + b * 2);
|
||||
G = ( g * 24 + b * 8);
|
||||
B = (r * 6 + g * 4 + b * 22);
|
||||
R = image::normalize(min(960, R), 10, 16);
|
||||
G = image::normalize(min(960, G), 10, 16);
|
||||
B = image::normalize(min(960, B), 10, 16);
|
||||
}
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
});
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoEffects() -> void {
|
||||
if(sgb()) return;
|
||||
|
||||
Emulator::video.setEffect(Emulator::Video::Effect::InterframeBlending, settings.blurEmulation);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ auto APU::main() -> void {
|
|||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||
|
||||
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
|
||||
interface->audioSample(sclamp<16>(lsample << 6), sclamp<16>(rsample << 6)); //should be <<5, use <<6 for added volume
|
||||
stream->sample(sclamp<16>(lsample << 6), sclamp<16>(rsample << 6)); //should be <<5; use <<6 for added volume
|
||||
step(512);
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ auto APU::step(uint clocks) -> void {
|
|||
|
||||
auto APU::power() -> void {
|
||||
create(APU::Enter, 16'777'216);
|
||||
stream = Emulator::audio.createStream(16'777'216.0 / 512.0);
|
||||
|
||||
square1.power();
|
||||
square2.power();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct APU : Thread, MMIO {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
#include "registers.hpp"
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
auto CPU::bus_idle() -> void {
|
||||
auto CPU::busIdle() -> void {
|
||||
prefetch_step(1);
|
||||
}
|
||||
|
||||
auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
unsigned word = pipeline.fetch.instruction;
|
||||
auto CPU::busRead(uint mode, uint32 addr) -> uint32 {
|
||||
uint wait = busWait(mode, addr);
|
||||
uint word = pipeline.fetch.instruction;
|
||||
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
|
@ -35,8 +35,8 @@ auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return word;
|
||||
}
|
||||
|
||||
auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
auto CPU::busWrite(uint mode, uint32 addr, uint32 word) -> void {
|
||||
uint wait = busWait(mode, addr);
|
||||
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
|
@ -57,7 +57,7 @@ auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
|
||||
auto CPU::busWait(uint mode, uint32 addr) -> uint {
|
||||
if(addr >= 0x1000'0000) return 1; //unmapped
|
||||
if(addr < 0x0200'0000) return 1;
|
||||
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
|
||||
|
@ -65,9 +65,9 @@ auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
|
|||
if(addr < 0x0700'0000) return mode & Word ? 2 : 1;
|
||||
if(addr < 0x0800'0000) return 1;
|
||||
|
||||
static unsigned timings[] = {5, 4, 3, 9};
|
||||
unsigned n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
|
||||
unsigned s = regs.wait.control.swait[addr >> 25 & 3];
|
||||
static uint timings[] = {5, 4, 3, 9};
|
||||
uint n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
|
||||
uint s = regs.wait.control.swait[addr >> 25 & 3];
|
||||
|
||||
switch(addr & 0x0e00'0000) {
|
||||
case 0x0800'0000: s = s ? 2 : 3; break;
|
||||
|
@ -79,7 +79,7 @@ auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
|
|||
bool sequential = (mode & Sequential);
|
||||
if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
|
||||
|
||||
unsigned clocks = sequential ? s : n;
|
||||
uint clocks = sequential ? s : n;
|
||||
if(mode & Word) clocks += s; //16-bit bus requires two transfers for words
|
||||
return clocks;
|
||||
}
|
||||
|
|
|
@ -39,10 +39,10 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
auto power() -> void;
|
||||
|
||||
//bus.cpp
|
||||
auto bus_idle() -> void override;
|
||||
auto bus_read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto bus_write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto bus_wait(uint mode, uint32 addr) -> uint;
|
||||
auto busIdle() -> void override;
|
||||
auto busRead(uint mode, uint32 addr) -> uint32 override;
|
||||
auto busWrite(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto busWait(uint mode, uint32 addr) -> uint;
|
||||
|
||||
//mmio.cpp
|
||||
auto read(uint32 addr) -> uint8;
|
||||
|
|
|
@ -39,7 +39,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
|||
uint32 addr = dma.run.source;
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & Half) addr &= ~1;
|
||||
dma.data = bus_read(mode, addr);
|
||||
dma.data = busRead(mode, addr);
|
||||
}
|
||||
|
||||
if(dma.run.target < 0x0200'0000) {
|
||||
|
@ -48,7 +48,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
|||
uint32 addr = dma.run.target;
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & Half) addr &= ~1;
|
||||
bus_write(mode, addr, dma.data);
|
||||
busWrite(mode, addr, dma.data);
|
||||
}
|
||||
|
||||
switch(dma.control.sourcemode) {
|
||||
|
|
|
@ -3,7 +3,7 @@ auto CPU::prefetch_sync(uint32 addr) -> void {
|
|||
|
||||
prefetch.addr = addr;
|
||||
prefetch.load = addr;
|
||||
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_step(uint clocks) -> void {
|
||||
|
@ -14,7 +14,7 @@ auto CPU::prefetch_step(uint clocks) -> void {
|
|||
if(--prefetch.wait) continue;
|
||||
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
|
||||
prefetch.load += 2;
|
||||
prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
|
||||
prefetch.wait = busWait(Half | Sequential, prefetch.load);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,14 +22,14 @@ auto CPU::prefetch_wait() -> void {
|
|||
if(!regs.wait.control.prefetch || active.dma || prefetch.full()) return;
|
||||
|
||||
prefetch_step(prefetch.wait);
|
||||
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_read() -> uint16 {
|
||||
if(prefetch.empty()) prefetch_step(prefetch.wait);
|
||||
else prefetch_step(1);
|
||||
|
||||
if(prefetch.full()) prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
|
||||
if(prefetch.full()) prefetch.wait = busWait(Half | Sequential, prefetch.load);
|
||||
|
||||
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||
prefetch.addr += 2;
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace GameBoyAdvance {
|
|||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,32 @@ auto Interface::videoFrequency() -> double {
|
|||
return 16777216.0 / (228.0 * 1232.0);
|
||||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return 1 << 15;
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 color) -> uint64 {
|
||||
uint R = color.bits( 0, 4);
|
||||
uint G = color.bits( 5, 9);
|
||||
uint B = color.bits(10,14);
|
||||
|
||||
uint64 r = image::normalize(R, 5, 16);
|
||||
uint64 g = image::normalize(G, 5, 16);
|
||||
uint64 b = image::normalize(B, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
double lcdGamma = 4.0, outGamma = 2.2;
|
||||
double lb = pow(B / 31.0, lcdGamma);
|
||||
double lg = pow(G / 31.0, lcdGamma);
|
||||
double lr = pow(R / 31.0, lcdGamma);
|
||||
r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
}
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 16777216.0 / 512.0;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ struct Interface : Emulator::Interface {
|
|||
auto manifest() -> string;
|
||||
auto title() -> string;
|
||||
auto videoFrequency() -> double;
|
||||
auto videoColors() -> uint32;
|
||||
auto videoColor(uint32 color) -> uint64;
|
||||
auto audioFrequency() -> double;
|
||||
|
||||
auto loaded() -> bool;
|
||||
|
|
|
@ -178,8 +178,11 @@ auto PPU::scanline() -> void {
|
|||
|
||||
auto PPU::frame() -> void {
|
||||
player.frame();
|
||||
Emulator::video.refresh(output, 240 * sizeof(uint32), 240, 160);
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
|
||||
auto PPU::refresh() -> void {
|
||||
Emulator::video.refresh(output, 240 * sizeof(uint32), 240, 160);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ struct PPU : Thread, MMIO {
|
|||
auto power() -> void;
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
|
|
|
@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
|
|||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,14 @@ auto System::term() -> void {
|
|||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
|
@ -36,7 +44,6 @@ auto System::load() -> void {
|
|||
|
||||
cartridge.load();
|
||||
serializeInit();
|
||||
configureVideo();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ struct System {
|
|||
auto runToSave() -> void;
|
||||
|
||||
//video.cpp
|
||||
auto configureVideo() -> void;
|
||||
auto configureVideoPalette() -> void;
|
||||
auto configureVideoEffects() -> void;
|
||||
|
||||
|
|
|
@ -1,32 +1,5 @@
|
|||
auto System::configureVideo() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
}
|
||||
|
||||
auto System::configureVideoPalette() -> void {
|
||||
Emulator::video.setPalette(1 << 15, [&](uint32 color) -> uint64 {
|
||||
uint R = color.bits( 0, 4);
|
||||
uint G = color.bits( 5, 9);
|
||||
uint B = color.bits(10,14);
|
||||
|
||||
uint64 r = image::normalize(R, 5, 16);
|
||||
uint64 g = image::normalize(G, 5, 16);
|
||||
uint64 b = image::normalize(B, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
double lcdGamma = 4.0, outGamma = 2.2;
|
||||
double lb = pow(B / 31.0, lcdGamma);
|
||||
double lg = pow(G / 31.0, lcdGamma);
|
||||
double lr = pow(R / 31.0, lcdGamma);
|
||||
r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
}
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
});
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoEffects() -> void {
|
||||
|
|
|
@ -31,16 +31,16 @@ auto ARM::exec() -> void {
|
|||
|
||||
auto ARM::idle() -> void {
|
||||
pipeline.nonsequential = true;
|
||||
return bus_idle();
|
||||
return busIdle();
|
||||
}
|
||||
|
||||
auto ARM::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
return bus_read(mode, addr);
|
||||
return busRead(mode, addr);
|
||||
}
|
||||
|
||||
auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
||||
pipeline.nonsequential = true;
|
||||
uint32 word = bus_read(Load | mode, addr);
|
||||
uint32 word = busRead(Load | mode, addr);
|
||||
|
||||
if(mode & Half) {
|
||||
addr &= 1;
|
||||
|
@ -64,7 +64,7 @@ auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
|||
|
||||
auto ARM::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
pipeline.nonsequential = true;
|
||||
return bus_write(mode, addr, word);
|
||||
return busWrite(mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
|
@ -73,7 +73,7 @@ auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
|
||||
return bus_write(Store | mode, addr, word);
|
||||
return busWrite(Store | mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
||||
|
|
|
@ -25,9 +25,9 @@ struct ARM {
|
|||
#include "disassembler.hpp"
|
||||
|
||||
virtual auto step(unsigned clocks) -> void = 0;
|
||||
virtual auto bus_idle() -> void = 0;
|
||||
virtual auto bus_read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
virtual auto busIdle() -> void = 0;
|
||||
virtual auto busRead(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto busWrite(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
|
||||
//arm.cpp
|
||||
auto power() -> void;
|
||||
|
|
|
@ -10,8 +10,8 @@ namespace Processor {
|
|||
auto HG51B::exec(uint24 addr) -> void {
|
||||
if(regs.halt) return;
|
||||
addr = addr + regs.pc * 2;
|
||||
opcode = bus_read(addr++) << 0;
|
||||
opcode |= bus_read(addr++) << 8;
|
||||
opcode = read(addr++) << 0;
|
||||
opcode |= read(addr++) << 8;
|
||||
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
|
||||
instruction();
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//Hitachi HG51B169 (HG51BS family/derivative?)
|
||||
|
||||
#pragma once
|
||||
|
||||
//Hitachi HG51B169 (HG51BS family/derivative?)
|
||||
|
||||
namespace Processor {
|
||||
|
||||
struct HG51B {
|
||||
auto exec(uint24 addr) -> void;
|
||||
virtual auto bus_read(uint24 addr) -> uint8 = 0;
|
||||
virtual auto bus_write(uint24 addr, uint8 data) -> void = 0;
|
||||
virtual auto read(uint24 addr) -> uint8 = 0;
|
||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
||||
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -25,8 +25,8 @@ protected:
|
|||
auto instruction() -> void;
|
||||
|
||||
//registers.cpp
|
||||
auto reg_read(uint8 addr) const -> uint24;
|
||||
auto reg_write(uint8 addr, uint24 data) -> void;
|
||||
auto regRead(uint8 addr) const -> uint24;
|
||||
auto regWrite(uint8 addr, uint24 data) -> void;
|
||||
|
||||
struct Registers {
|
||||
bool halt;
|
||||
|
|
|
@ -34,7 +34,7 @@ auto HG51B::sa() -> uint {
|
|||
//Register-or-Immediate: most opcodes can load from a register or immediate
|
||||
auto HG51B::ri() -> uint {
|
||||
if(opcode & 0x0400) return opcode & 0xff;
|
||||
return reg_read(opcode & 0xff);
|
||||
return regRead(opcode & 0xff);
|
||||
}
|
||||
|
||||
//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes)
|
||||
|
@ -115,7 +115,7 @@ auto HG51B::instruction() -> void {
|
|||
else if((opcode & 0xffff) == 0x4000) {
|
||||
//0100 0000 0000 0000
|
||||
//rdbus
|
||||
regs.busdata = bus_read(regs.busaddr++);
|
||||
regs.busdata = read(regs.busaddr++);
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x4800) {
|
||||
|
@ -306,7 +306,7 @@ auto HG51B::instruction() -> void {
|
|||
else if((opcode & 0xff00) == 0xe000) {
|
||||
//1110 0000 .... ....
|
||||
//st r,a
|
||||
reg_write(opcode & 0xff, regs.a);
|
||||
regWrite(opcode & 0xff, regs.a);
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xe800) {
|
||||
|
@ -333,10 +333,10 @@ auto HG51B::instruction() -> void {
|
|||
else if((opcode & 0xff00) == 0xf000) {
|
||||
//1111 0000 .... ....
|
||||
//swap a,r
|
||||
uint24 source = reg_read(opcode & 0xff);
|
||||
uint24 source = regRead(opcode & 0xff);
|
||||
uint24 target = regs.a;
|
||||
regs.a = source;
|
||||
reg_write(opcode & 0xff, target);
|
||||
regWrite(opcode & 0xff, target);
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0xfc00) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto HG51B::reg_read(uint8 addr) const -> uint24 {
|
||||
auto HG51B::regRead(uint8 addr) const -> uint24 {
|
||||
switch(addr) {
|
||||
case 0x00: return regs.a;
|
||||
case 0x01: return regs.acch;
|
||||
|
@ -44,7 +44,7 @@ auto HG51B::reg_read(uint8 addr) const -> uint24 {
|
|||
return 0x000000;
|
||||
}
|
||||
|
||||
auto HG51B::reg_write(uint8 addr, uint24 data) -> void {
|
||||
auto HG51B::regWrite(uint8 addr, uint24 data) -> void {
|
||||
switch(addr) {
|
||||
case 0x00: regs.a = data; return;
|
||||
case 0x01: regs.acch = data; return;
|
||||
|
|
|
@ -98,7 +98,7 @@ auto Cartridge::parseMarkupMCC(Markup::Node root) -> void {
|
|||
|
||||
for(auto node : root.find("map")) {
|
||||
if(node.text() == "mcu") {
|
||||
parseMarkupMap(node, {&MCC::mcu_read, &mcc}, {&MCC::mcu_write, &mcc});
|
||||
parseMarkupMap(node, {&MCC::mcuRead, &mcc}, {&MCC::mcuWrite, &mcc});
|
||||
} else {
|
||||
parseMarkupMap(node, {&MCC::read, &mcc}, {&MCC::write, &mcc});
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ auto Cartridge::parseMarkupARMDSP(Markup::Node root) -> void {
|
|||
memory.append({ID::ArmDSPRAM, root["dram"]["name"].text()});
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
parseMarkupMap(node, {&ArmDSP::mmio_read, &armdsp}, {&ArmDSP::mmio_write, &armdsp});
|
||||
parseMarkupMap(node, {&ArmDSP::read, &armdsp}, {&ArmDSP::write, &armdsp});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,19 +252,19 @@ auto Cartridge::parseMarkupHitachiDSP(Markup::Node root, uint roms) -> void {
|
|||
parseMarkupMemory(hitachidsp.ram, root["ram"], ID::HitachiDSPRAM, true);
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
parseMarkupMap(node, {&HitachiDSP::dsp_read, &hitachidsp}, {&HitachiDSP::dsp_write, &hitachidsp});
|
||||
parseMarkupMap(node, {&HitachiDSP::dspRead, &hitachidsp}, {&HitachiDSP::dspWrite, &hitachidsp});
|
||||
}
|
||||
|
||||
for(auto node : root["rom"].find("map")) {
|
||||
parseMarkupMap(node, {&HitachiDSP::rom_read, &hitachidsp}, {&HitachiDSP::rom_write, &hitachidsp});
|
||||
parseMarkupMap(node, {&HitachiDSP::romRead, &hitachidsp}, {&HitachiDSP::romWrite, &hitachidsp});
|
||||
}
|
||||
|
||||
for(auto node : root["ram"].find("map")) {
|
||||
parseMarkupMap(node, {&HitachiDSP::ram_read, &hitachidsp}, {&HitachiDSP::ram_write, &hitachidsp});
|
||||
parseMarkupMap(node, {&HitachiDSP::ramRead, &hitachidsp}, {&HitachiDSP::ramWrite, &hitachidsp});
|
||||
}
|
||||
|
||||
for(auto node : root["dram"].find("map")) {
|
||||
parseMarkupMap(node, {&HitachiDSP::dram_read, &hitachidsp}, {&HitachiDSP::dram_write, &hitachidsp});
|
||||
parseMarkupMap(node, {&HitachiDSP::dramRead, &hitachidsp}, {&HitachiDSP::dramWrite, &hitachidsp});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,6 +380,6 @@ auto Cartridge::parseMarkupMSU1(Markup::Node root) -> void {
|
|||
hasMSU1 = true;
|
||||
|
||||
for(auto node : root.find("map")) {
|
||||
parseMarkupMap(node, {&MSU1::mmioRead, &msu1}, {&MSU1::mmioWrite, &msu1});
|
||||
parseMarkupMap(node, {&MSU1::read, &msu1}, {&MSU1::write, &msu1});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ auto ArmDSP::boot() -> void {
|
|||
|
||||
//reset sequence delay
|
||||
if(bridge.ready == false) {
|
||||
step(65536);
|
||||
step(65'536);
|
||||
bridge.ready = true;
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
|||
//3800-3807 mirrored throughout
|
||||
//a0 ignored
|
||||
|
||||
auto ArmDSP::mmio_read(uint24 addr, uint8) -> uint8 {
|
||||
auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
uint8 data = 0x00;
|
||||
|
@ -82,7 +82,7 @@ auto ArmDSP::mmio_read(uint24 addr, uint8) -> uint8 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto ArmDSP::mmio_write(uint24 addr, uint8 data) -> void {
|
||||
auto ArmDSP::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
|
||||
addr &= 0xff06;
|
||||
|
|
|
@ -11,12 +11,12 @@ struct ArmDSP : Processor::ARM, Cothread {
|
|||
auto main() -> void;
|
||||
|
||||
auto step(uint clocks) -> void override;
|
||||
auto bus_idle() -> void override;
|
||||
auto bus_read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto bus_write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto busIdle() -> void override;
|
||||
auto busRead(uint mode, uint32 addr) -> uint32 override;
|
||||
auto busWrite(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
|
||||
auto mmio_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto mmio_write(uint24 addr, uint8 data) -> void;
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto init() -> void;
|
||||
auto load() -> void;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//note: timings are completely unverified
|
||||
//due to the ST018 chip design (on-die ROM), testing is nearly impossible
|
||||
|
||||
auto ArmDSP::bus_idle() -> void {
|
||||
auto ArmDSP::busIdle() -> void {
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto ArmDSP::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto ArmDSP::busRead(uint mode, uint32 addr) -> uint32 {
|
||||
step(1);
|
||||
|
||||
static auto memory = [&](const uint8* memory, uint mode, uint32 addr) -> uint32 {
|
||||
|
@ -19,34 +19,34 @@ auto ArmDSP::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
}
|
||||
};
|
||||
|
||||
switch(addr & 0xe0000000) {
|
||||
case 0x00000000: return memory(programROM, mode, addr & 0x1ffff);
|
||||
case 0x20000000: return pipeline.fetch.instruction;
|
||||
case 0x40000000: break;
|
||||
case 0x60000000: return 0x40404001;
|
||||
case 0x80000000: return pipeline.fetch.instruction;
|
||||
case 0xa0000000: return memory(dataROM, mode, addr & 0x7fff);
|
||||
case 0xc0000000: return pipeline.fetch.instruction;
|
||||
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff);
|
||||
switch(addr & 0xe000'0000) {
|
||||
case 0x0000'0000: return memory(programROM, mode, addr & 0x1ffff);
|
||||
case 0x2000'0000: return pipeline.fetch.instruction;
|
||||
case 0x4000'0000: break;
|
||||
case 0x6000'0000: return 0x40404001;
|
||||
case 0x8000'0000: return pipeline.fetch.instruction;
|
||||
case 0xa000'0000: return memory(dataROM, mode, addr & 0x7fff);
|
||||
case 0xc000'0000: return pipeline.fetch.instruction;
|
||||
case 0xe000'0000: return memory(programRAM, mode, addr & 0x3fff);
|
||||
}
|
||||
|
||||
addr &= 0xe000003f;
|
||||
addr &= 0xe000'003f;
|
||||
|
||||
if(addr == 0x40000010) {
|
||||
if(addr == 0x4000'0010) {
|
||||
if(bridge.cputoarm.ready) {
|
||||
bridge.cputoarm.ready = false;
|
||||
return bridge.cputoarm.data;
|
||||
}
|
||||
}
|
||||
|
||||
if(addr == 0x40000020) {
|
||||
if(addr == 0x4000'0020) {
|
||||
return bridge.status();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ArmDSP::bus_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto ArmDSP::busWrite(uint mode, uint32 addr, uint32 word) -> void {
|
||||
step(1);
|
||||
|
||||
static auto memory = [](uint8* memory, uint mode, uint32 addr, uint32 word) {
|
||||
|
@ -62,37 +62,30 @@ auto ArmDSP::bus_write(uint mode, uint32 addr, uint32 word) -> void {
|
|||
}
|
||||
};
|
||||
|
||||
switch(addr & 0xe0000000) {
|
||||
case 0x00000000: return;
|
||||
case 0x20000000: return;
|
||||
case 0x40000000: break;
|
||||
case 0x60000000: return;
|
||||
case 0x80000000: return;
|
||||
case 0xa0000000: return;
|
||||
case 0xc0000000: return;
|
||||
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff, word);
|
||||
switch(addr & 0xe000'0000) {
|
||||
case 0x0000'0000: return;
|
||||
case 0x2000'0000: return;
|
||||
case 0x4000'0000: break;
|
||||
case 0x6000'0000: return;
|
||||
case 0x8000'0000: return;
|
||||
case 0xa000'0000: return;
|
||||
case 0xc000'0000: return;
|
||||
case 0xe000'0000: return memory(programRAM, mode, addr & 0x3fff, word);
|
||||
}
|
||||
|
||||
addr &= 0xe000003f;
|
||||
word &= 0x000000ff;
|
||||
addr &= 0xe000'003f;
|
||||
word &= 0x0000'00ff;
|
||||
|
||||
if(addr == 0x40000000) {
|
||||
if(addr == 0x4000'0000) {
|
||||
bridge.armtocpu.ready = true;
|
||||
bridge.armtocpu.data = word;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0x40000010) {
|
||||
bridge.signal = true;
|
||||
return;
|
||||
}
|
||||
if(addr == 0x4000'0010) bridge.signal = true;
|
||||
|
||||
if(addr == 0x40000020) { bridge.timerlatch = (bridge.timerlatch & 0xffff00) | (word << 0); return; }
|
||||
if(addr == 0x40000024) { bridge.timerlatch = (bridge.timerlatch & 0xff00ff) | (word << 8); return; }
|
||||
if(addr == 0x40000028) { bridge.timerlatch = (bridge.timerlatch & 0x00ffff) | (word << 16); return; }
|
||||
if(addr == 0x4000'0020) bridge.timerlatch.byte(0) = word;
|
||||
if(addr == 0x4000'0024) bridge.timerlatch.byte(1) = word;
|
||||
if(addr == 0x4000'0028) bridge.timerlatch.byte(2) = word;
|
||||
|
||||
if(addr == 0x4000002c) {
|
||||
bridge.timer = bridge.timerlatch;
|
||||
return;
|
||||
}
|
||||
if(addr == 0x4000'002c) bridge.timer = bridge.timerlatch;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ struct Bridge {
|
|||
bool signal;
|
||||
|
||||
auto status() const -> uint8 {
|
||||
return (ready << 7) | (cputoarm.ready << 3) | (signal << 2) | (armtocpu.ready << 0);
|
||||
return (
|
||||
armtocpu.ready << 0
|
||||
| signal << 2
|
||||
| cputoarm.ready << 3
|
||||
| ready << 7
|
||||
);
|
||||
}
|
||||
} bridge;
|
||||
|
|
|
@ -15,7 +15,7 @@ auto EpsonRTC::main() -> void {
|
|||
if(wait) { if(--wait == 0) ready = 1; }
|
||||
|
||||
clocks++;
|
||||
if((clocks & ~0x00ff) == 0) round_seconds(); //125 microseconds
|
||||
if((clocks & ~0x00ff) == 0) roundSeconds(); //125 microseconds
|
||||
if((clocks & ~0x3fff) == 0) duty(); //1/128th second
|
||||
if((clocks & ~0x7fff) == 0) irq(0); //1/64th second
|
||||
if(clocks == 0) { //1 second
|
||||
|
@ -81,7 +81,7 @@ auto EpsonRTC::power() -> void {
|
|||
}
|
||||
|
||||
auto EpsonRTC::reset() -> void {
|
||||
create(EpsonRTC::Enter, 32768 * 64);
|
||||
create(EpsonRTC::Enter, 32'768 * 64);
|
||||
|
||||
clocks = 0;
|
||||
seconds = 0;
|
||||
|
@ -150,7 +150,7 @@ auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
|||
if(state != State::Read) return 0;
|
||||
ready = 0;
|
||||
wait = 8;
|
||||
return rtc_read(offset++);
|
||||
return rtcRead(offset++);
|
||||
}
|
||||
|
||||
if(addr == 2) {
|
||||
|
@ -166,7 +166,7 @@ auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
|||
|
||||
if(addr == 0) {
|
||||
chipselect = data;
|
||||
if(chipselect != 1) rtc_reset();
|
||||
if(chipselect != 1) rtcReset();
|
||||
ready = 1;
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
else if(state == State::Write) {
|
||||
rtc_write(offset++, data);
|
||||
rtcWrite(offset++, data);
|
||||
ready = 0;
|
||||
wait = 8;
|
||||
mdr = data;
|
||||
|
|
|
@ -67,9 +67,9 @@ struct EpsonRTC : Cothread {
|
|||
uint1 test;
|
||||
|
||||
//memory.cpp
|
||||
auto rtc_reset() -> void;
|
||||
auto rtc_read(uint4 addr) -> uint4;
|
||||
auto rtc_write(uint4 addr, uint4 data) -> void;
|
||||
auto rtcReset() -> void;
|
||||
auto rtcRead(uint4 addr) -> uint4;
|
||||
auto rtcWrite(uint4 addr, uint4 data) -> void;
|
||||
|
||||
auto load(const uint8* data) -> void;
|
||||
auto save(uint8* data) -> void;
|
||||
|
@ -77,15 +77,15 @@ struct EpsonRTC : Cothread {
|
|||
//time.cpp
|
||||
auto irq(uint2 period) -> void;
|
||||
auto duty() -> void;
|
||||
auto round_seconds() -> void;
|
||||
auto roundSeconds() -> void;
|
||||
auto tick() -> void;
|
||||
|
||||
auto tick_second() -> void;
|
||||
auto tick_minute() -> void;
|
||||
auto tick_hour() -> void;
|
||||
auto tick_day() -> void;
|
||||
auto tick_month() -> void;
|
||||
auto tick_year() -> void;
|
||||
auto tickSecond() -> void;
|
||||
auto tickMinute() -> void;
|
||||
auto tickHour() -> void;
|
||||
auto tickDay() -> void;
|
||||
auto tickMonth() -> void;
|
||||
auto tickYear() -> void;
|
||||
};
|
||||
|
||||
extern EpsonRTC epsonrtc;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto EpsonRTC::rtc_reset() -> void {
|
||||
auto EpsonRTC::rtcReset() -> void {
|
||||
state = State::Mode;
|
||||
offset = 0;
|
||||
|
||||
|
@ -7,7 +7,7 @@ auto EpsonRTC::rtc_reset() -> void {
|
|||
test = 0;
|
||||
}
|
||||
|
||||
auto EpsonRTC::rtc_read(uint4 addr) -> uint4 {
|
||||
auto EpsonRTC::rtcRead(uint4 addr) -> uint4 {
|
||||
switch(addr) { default:
|
||||
case 0: return secondlo;
|
||||
case 1: return secondhi | batteryfailure << 3;
|
||||
|
@ -32,7 +32,7 @@ auto EpsonRTC::rtc_read(uint4 addr) -> uint4 {
|
|||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::rtc_write(uint4 addr, uint4 data) -> void {
|
||||
auto EpsonRTC::rtcWrite(uint4 addr, uint4 data) -> void {
|
||||
switch(addr) {
|
||||
case 0:
|
||||
secondlo = data;
|
||||
|
@ -87,7 +87,7 @@ auto EpsonRTC::rtc_write(uint4 addr, uint4 data) -> void {
|
|||
if(held == 1 && hold == 0 && holdtick == 1) {
|
||||
//if a second has passed during hold, increment one second upon resuming
|
||||
holdtick = 0;
|
||||
tick_second();
|
||||
tickSecond();
|
||||
}
|
||||
} break;
|
||||
case 14:
|
||||
|
@ -156,10 +156,10 @@ auto EpsonRTC::load(const uint8* data) -> void {
|
|||
}
|
||||
|
||||
uint64 diff = (uint64)time(0) - timestamp;
|
||||
while(diff >= 60 * 60 * 24) { tick_day(); diff -= 60 * 60 * 24; }
|
||||
while(diff >= 60 * 60) { tick_hour(); diff -= 60 * 60; }
|
||||
while(diff >= 60) { tick_minute(); diff -= 60; }
|
||||
while(diff--) tick_second();
|
||||
while(diff >= 60 * 60 * 24) { tickDay(); diff -= 60 * 60 * 24; }
|
||||
while(diff >= 60 * 60) { tickHour(); diff -= 60 * 60; }
|
||||
while(diff >= 60) { tickMinute(); diff -= 60; }
|
||||
while(diff--) tickSecond();
|
||||
}
|
||||
|
||||
auto EpsonRTC::save(uint8* data) -> void {
|
||||
|
|
|
@ -8,11 +8,11 @@ auto EpsonRTC::duty() -> void {
|
|||
if(irqduty) irqflag = 0;
|
||||
}
|
||||
|
||||
auto EpsonRTC::round_seconds() -> void {
|
||||
auto EpsonRTC::roundSeconds() -> void {
|
||||
if(roundseconds == 0) return;
|
||||
roundseconds = 0;
|
||||
|
||||
if(secondhi >= 3) tick_minute();
|
||||
if(secondhi >= 3) tickMinute();
|
||||
secondlo = 0;
|
||||
secondhi = 0;
|
||||
}
|
||||
|
@ -26,13 +26,13 @@ auto EpsonRTC::tick() -> void {
|
|||
}
|
||||
|
||||
resync = 1;
|
||||
tick_second();
|
||||
tickSecond();
|
||||
}
|
||||
|
||||
//below code provides bit-perfect emulation of invalid BCD values on the RTC-4513
|
||||
//code makes extensive use of variable-length integers (see epsonrtc.hpp for sizes)
|
||||
|
||||
auto EpsonRTC::tick_second() -> void {
|
||||
auto EpsonRTC::tickSecond() -> void {
|
||||
if(secondlo <= 8 || secondlo == 12) {
|
||||
secondlo++;
|
||||
} else {
|
||||
|
@ -41,12 +41,12 @@ auto EpsonRTC::tick_second() -> void {
|
|||
secondhi++;
|
||||
} else {
|
||||
secondhi = 0;
|
||||
tick_minute();
|
||||
tickMinute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::tick_minute() -> void {
|
||||
auto EpsonRTC::tickMinute() -> void {
|
||||
if(minutelo <= 8 || minutelo == 12) {
|
||||
minutelo++;
|
||||
} else {
|
||||
|
@ -55,12 +55,12 @@ auto EpsonRTC::tick_minute() -> void {
|
|||
minutehi++;
|
||||
} else {
|
||||
minutehi = 0;
|
||||
tick_hour();
|
||||
tickHour();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::tick_hour() -> void {
|
||||
auto EpsonRTC::tickHour() -> void {
|
||||
if(atime) {
|
||||
if(hourhi < 2) {
|
||||
if(hourlo <= 8 || hourlo == 12) {
|
||||
|
@ -80,7 +80,7 @@ auto EpsonRTC::tick_hour() -> void {
|
|||
} else {
|
||||
hourlo = !(hourlo & 1);
|
||||
hourhi = 0;
|
||||
tick_day();
|
||||
tickDay();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -99,12 +99,12 @@ auto EpsonRTC::tick_hour() -> void {
|
|||
hourlo = !(hourlo & 1);
|
||||
hourhi ^= 1;
|
||||
}
|
||||
if(meridian == 0 && !(hourlo & 1)) tick_day();
|
||||
if(meridian == 0 && !(hourlo & 1)) tickDay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::tick_day() -> void {
|
||||
auto EpsonRTC::tickDay() -> void {
|
||||
if(calendar == 0) return;
|
||||
weekday = (weekday + 1) + (weekday == 6);
|
||||
|
||||
|
@ -124,25 +124,25 @@ auto EpsonRTC::tick_day() -> void {
|
|||
if(days == 28 && (dayhi == 3 || (dayhi == 2 && daylo >= 8))) {
|
||||
daylo = 1;
|
||||
dayhi = 0;
|
||||
return tick_month();
|
||||
return tickMonth();
|
||||
}
|
||||
|
||||
if(days == 29 && (dayhi == 3 || (dayhi == 2 && (daylo > 8 && daylo != 12)))) {
|
||||
daylo = 1;
|
||||
dayhi = 0;
|
||||
return tick_month();
|
||||
return tickMonth();
|
||||
}
|
||||
|
||||
if(days == 30 && (dayhi == 3 || (dayhi == 2 && (daylo == 10 || daylo == 14)))) {
|
||||
daylo = 1;
|
||||
dayhi = 0;
|
||||
return tick_month();
|
||||
return tickMonth();
|
||||
}
|
||||
|
||||
if(days == 31 && (dayhi == 3 && (daylo & 3))) {
|
||||
daylo = 1;
|
||||
dayhi = 0;
|
||||
return tick_month();
|
||||
return tickMonth();
|
||||
}
|
||||
|
||||
if(daylo <= 8 || daylo == 12) {
|
||||
|
@ -153,7 +153,7 @@ auto EpsonRTC::tick_day() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::tick_month() -> void {
|
||||
auto EpsonRTC::tickMonth() -> void {
|
||||
if(monthhi == 0 || !(monthlo & 2)) {
|
||||
if(monthlo <= 8 || monthlo == 12) {
|
||||
monthlo++;
|
||||
|
@ -164,11 +164,11 @@ auto EpsonRTC::tick_month() -> void {
|
|||
} else {
|
||||
monthlo = !(monthlo & 1);
|
||||
monthhi = 0;
|
||||
tick_year();
|
||||
tickYear();
|
||||
}
|
||||
}
|
||||
|
||||
auto EpsonRTC::tick_year() -> void {
|
||||
auto EpsonRTC::tickYear() -> void {
|
||||
if(yearlo <= 8 || yearlo == 12) {
|
||||
yearlo++;
|
||||
} else {
|
||||
|
|
|
@ -13,7 +13,7 @@ auto HitachiDSP::Enter() -> void {
|
|||
auto HitachiDSP::main() -> void {
|
||||
if(mmio.dma) {
|
||||
for(auto n : range(mmio.dma_length)) {
|
||||
bus_write(mmio.dma_target + n, bus_read(mmio.dma_source + n));
|
||||
write(mmio.dma_target + n, read(mmio.dma_source + n));
|
||||
step(2);
|
||||
}
|
||||
mmio.dma = false;
|
||||
|
|
|
@ -14,24 +14,24 @@ struct HitachiDSP : Processor::HG51B, Cothread {
|
|||
auto reset() -> void;
|
||||
|
||||
//HG51B read/write
|
||||
auto bus_read(uint24 addr) -> uint8;
|
||||
auto bus_write(uint24 addr, uint8 data) -> void;
|
||||
auto read(uint24 addr) -> uint8 override;
|
||||
auto write(uint24 addr, uint8 data) -> void override;
|
||||
|
||||
//CPU ROM read/write
|
||||
auto rom_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto rom_write(uint24 addr, uint8 data) -> void;
|
||||
auto romRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto romWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
//CPU RAM read/write
|
||||
auto ram_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto ram_write(uint24 addr, uint8 data) -> void;
|
||||
auto ramRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto ramWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
//HG51B data RAM read/write
|
||||
auto dram_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto dram_write(uint24 addr, uint8 data) -> void;
|
||||
auto dramRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto dramWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
//CPU MMIO read/write
|
||||
auto dsp_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto dsp_write(uint24 addr, uint8 data) -> void;
|
||||
auto dspRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto dspWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto firmware() const -> vector<uint8>;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
auto HitachiDSP::bus_read(uint24 addr) -> uint8 {
|
||||
auto HitachiDSP::read(uint24 addr) -> uint8 {
|
||||
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6cff,7c00-7cff
|
||||
return dsp_read(addr, 0x00);
|
||||
return dspRead(addr, 0x00);
|
||||
}
|
||||
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff
|
||||
return dram_read(addr, 0x00);
|
||||
return dramRead(addr, 0x00);
|
||||
}
|
||||
if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff
|
||||
if(rom.size() == 0) return 0x00;
|
||||
|
@ -20,12 +20,12 @@ auto HitachiDSP::bus_read(uint24 addr) -> uint8 {
|
|||
return 0x00;
|
||||
}
|
||||
|
||||
auto HitachiDSP::bus_write(uint24 addr, uint8 data) -> void {
|
||||
auto HitachiDSP::write(uint24 addr, uint8 data) -> void {
|
||||
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6fff,7c00-7fff
|
||||
return dsp_write(addr, data);
|
||||
return dspWrite(addr, data);
|
||||
}
|
||||
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff
|
||||
return dram_write(addr, data);
|
||||
return dramWrite(addr, data);
|
||||
}
|
||||
if((addr & 0xf88000) == 0x700000) { //$70-77:0000-7fff
|
||||
if(ram.size() == 0) return;
|
||||
|
@ -35,7 +35,7 @@ auto HitachiDSP::bus_write(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto HitachiDSP::rom_read(uint24 addr, uint8 data) -> uint8 {
|
||||
auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 {
|
||||
if(co_active() == hitachidsp.thread || regs.halt) {
|
||||
addr = Bus::mirror(addr, rom.size());
|
||||
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00;
|
||||
|
@ -45,32 +45,32 @@ auto HitachiDSP::rom_read(uint24 addr, uint8 data) -> uint8 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto HitachiDSP::rom_write(uint24 addr, uint8 data) -> void {
|
||||
auto HitachiDSP::romWrite(uint24 addr, uint8 data) -> void {
|
||||
}
|
||||
|
||||
auto HitachiDSP::ram_read(uint24 addr, uint8 data) -> uint8 {
|
||||
auto HitachiDSP::ramRead(uint24 addr, uint8 data) -> uint8 {
|
||||
if(ram.size() == 0) return 0x00; //not open bus
|
||||
return ram.read(Bus::mirror(addr, ram.size()), data);
|
||||
}
|
||||
|
||||
auto HitachiDSP::ram_write(uint24 addr, uint8 data) -> void {
|
||||
auto HitachiDSP::ramWrite(uint24 addr, uint8 data) -> void {
|
||||
if(ram.size() == 0) return;
|
||||
return ram.write(Bus::mirror(addr, ram.size()), data);
|
||||
}
|
||||
|
||||
auto HitachiDSP::dram_read(uint24 addr, uint8 data) -> uint8 {
|
||||
auto HitachiDSP::dramRead(uint24 addr, uint8 data) -> uint8 {
|
||||
addr &= 0xfff;
|
||||
if(addr >= 0xc00) return data;
|
||||
return dataRAM[addr];
|
||||
}
|
||||
|
||||
auto HitachiDSP::dram_write(uint24 addr, uint8 data) -> void {
|
||||
auto HitachiDSP::dramWrite(uint24 addr, uint8 data) -> void {
|
||||
addr &= 0xfff;
|
||||
if(addr >= 0xc00) return;
|
||||
dataRAM[addr] = data;
|
||||
}
|
||||
|
||||
auto HitachiDSP::dsp_read(uint24 addr, uint8) -> uint8 {
|
||||
auto HitachiDSP::dspRead(uint24 addr, uint8) -> uint8 {
|
||||
addr = 0x7c00 | (addr & 0x03ff);
|
||||
|
||||
//MMIO
|
||||
|
@ -115,7 +115,7 @@ auto HitachiDSP::dsp_read(uint24 addr, uint8) -> uint8 {
|
|||
return 0x00;
|
||||
}
|
||||
|
||||
auto HitachiDSP::dsp_write(uint24 addr, uint8 data) -> void {
|
||||
auto HitachiDSP::dspWrite(uint24 addr, uint8 data) -> void {
|
||||
addr = 0x7c00 | (addr & 0x03ff);
|
||||
|
||||
//MMIO
|
||||
|
|
|
@ -24,7 +24,7 @@ auto ICD2::main() -> void {
|
|||
step(GameBoy::system._clocksExecuted);
|
||||
GameBoy::system._clocksExecuted = 0;
|
||||
} else { //DMG halted
|
||||
audio.coprocessorSample(0, 0);
|
||||
stream->sample(0, 0);
|
||||
step(1);
|
||||
}
|
||||
synchronizeCPU();
|
||||
|
@ -50,12 +50,11 @@ auto ICD2::unload() -> void {
|
|||
}
|
||||
|
||||
auto ICD2::power() -> void {
|
||||
audio.coprocessorEnable(true);
|
||||
audio.coprocessorFrequency(2 * 1024 * 1024);
|
||||
}
|
||||
|
||||
auto ICD2::reset() -> void {
|
||||
auto ICD2::reset(bool soft) -> void {
|
||||
create(ICD2::Enter, cpu.frequency / 5);
|
||||
if(!soft) stream = Emulator::audio.createStream(4194304.0 / 2.0);
|
||||
|
||||
r6003 = 0x00;
|
||||
r6004 = 0xff;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#if defined(SFC_SUPERGAMEBOY)
|
||||
|
||||
struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
|
@ -8,7 +10,7 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread {
|
|||
auto load() -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
auto reset(bool soft = false) -> void;
|
||||
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
|
|
@ -120,7 +120,7 @@ auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height)
|
|||
}
|
||||
|
||||
auto ICD2::audioSample(int16 left, int16 right) -> void {
|
||||
audio.coprocessorSample(left, right);
|
||||
stream->sample(left, right);
|
||||
}
|
||||
|
||||
auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 {
|
||||
|
|
|
@ -54,7 +54,7 @@ auto ICD2::write(uint24 addr, uint8 data) -> void {
|
|||
//d1,d0: 0 = frequency divider (clock rate adjust)
|
||||
if(addr == 0x6003) {
|
||||
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
||||
reset();
|
||||
reset(true);
|
||||
}
|
||||
switch(data & 3) {
|
||||
case 0: frequency = cpu.frequency / 4; break; //fast (glitchy, even on real hardware)
|
||||
|
|
|
@ -26,7 +26,7 @@ auto MCC::reset() -> void {
|
|||
commit();
|
||||
}
|
||||
|
||||
auto MCC::memory_access(bool write, Memory& memory, uint24 addr, uint8 data) -> uint8 {
|
||||
auto MCC::memoryAccess(bool write, Memory& memory, uint24 addr, uint8 data) -> uint8 {
|
||||
addr = bus.mirror(addr, memory.size());
|
||||
if(!write) {
|
||||
return memory.read(addr, data);
|
||||
|
@ -37,7 +37,7 @@ auto MCC::memory_access(bool write, Memory& memory, uint24 addr, uint8 data) ->
|
|||
|
||||
//map address=00-3f,80-bf:8000-ffff mask=0x408000
|
||||
//map address=40-7d,c0-ff:0000-ffff
|
||||
auto MCC::mcu_access(bool write, uint24 addr, uint8 data) -> uint8 {
|
||||
auto MCC::mcuAccess(bool write, uint24 addr, uint8 data) -> uint8 {
|
||||
if(addr < 0x400000) {
|
||||
//note: manifest maps 00-3f,80-bf:8000-ffff mask=0x408000 => 00-3f:0000-ffff
|
||||
//the intention is consistency in pre-decoding as much as possible
|
||||
|
@ -50,31 +50,31 @@ auto MCC::mcu_access(bool write, uint24 addr, uint8 data) -> uint8 {
|
|||
if((addr & 0xe08000) == 0x008000) { //$00-1f:8000-ffff
|
||||
if(r07 == 1) {
|
||||
addr = ((addr & 0x1f0000) >> 1) | (addr & 0x7fff);
|
||||
return memory_access(write, rom, addr, data);
|
||||
return memoryAccess(write, rom, addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xe08000) == 0x808000) { //$80-9f:8000-ffff
|
||||
if(r08 == 1) {
|
||||
addr = ((addr & 0x1f0000) >> 1) | (addr & 0x7fff);
|
||||
return memory_access(write, rom, addr, data);
|
||||
return memoryAccess(write, rom, addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
if(r05 == 0) return memory_access(write, ram, addr & 0x0fffff, data);
|
||||
if(r05 == 0) return memoryAccess(write, ram, addr & 0x0fffff, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x500000) { //$50-5f:0000-ffff
|
||||
if(r06 == 0) return memory_access(write, ram, addr & 0x0fffff, data);
|
||||
if(r06 == 0) return memoryAccess(write, ram, addr & 0x0fffff, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||
if(r03 == 1) return memory_access(write, ram, addr & 0x0fffff, data);
|
||||
if(r03 == 1) return memoryAccess(write, ram, addr & 0x0fffff, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf80000) == 0x700000) { //$70-77:0000-ffff
|
||||
return memory_access(write, ram, addr & 0x07ffff, data);
|
||||
return memoryAccess(write, ram, addr & 0x07ffff, data);
|
||||
}
|
||||
|
||||
if(((addr & 0x408000) == 0x008000) //$00-3f,80-bf:8000-ffff
|
||||
|
@ -82,18 +82,18 @@ auto MCC::mcu_access(bool write, uint24 addr, uint8 data) -> uint8 {
|
|||
) {
|
||||
if(r02 == 0) addr = ((addr & 0x7f0000) >> 1) | (addr & 0x7fff);
|
||||
Memory& memory = (r01 == 0 ? (Memory&)bsmemory : (Memory&)ram);
|
||||
return memory_access(write, memory, addr & 0x7fffff, data);
|
||||
return memoryAccess(write, memory, addr & 0x7fffff, data);
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
auto MCC::mcu_read(uint24 addr, uint8 data) -> uint8 {
|
||||
return mcu_access(false, addr, data);
|
||||
auto MCC::mcuRead(uint24 addr, uint8 data) -> uint8 {
|
||||
return mcuAccess(false, addr, data);
|
||||
}
|
||||
|
||||
auto MCC::mcu_write(uint24 addr, uint8 data) -> void {
|
||||
mcu_access(true, addr, data);
|
||||
auto MCC::mcuWrite(uint24 addr, uint8 data) -> void {
|
||||
mcuAccess(true, addr, data);
|
||||
}
|
||||
|
||||
auto MCC::read(uint24 addr, uint8 data) -> uint8 {
|
||||
|
|
|
@ -10,11 +10,11 @@ struct MCC {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto memory_access(bool write, Memory& memory, uint24 addr, uint8 data) -> uint8;
|
||||
auto mcu_access(bool write, uint24 addr, uint8 data) -> uint8;
|
||||
auto memoryAccess(bool write, Memory& memory, uint24 addr, uint8 data) -> uint8;
|
||||
auto mcuAccess(bool write, uint24 addr, uint8 data) -> uint8;
|
||||
|
||||
auto mcu_read(uint24 addr, uint8 data) -> uint8;
|
||||
auto mcu_write(uint24 addr, uint8 data) -> void;
|
||||
auto mcuRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto mcuWrite(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
|
|
@ -38,7 +38,7 @@ auto MSU1::main() -> void {
|
|||
right = sclamp<16>(rchannel);
|
||||
if(dsp.mute()) left = 0, right = 0;
|
||||
|
||||
audio.coprocessorSample(left, right);
|
||||
stream->sample(left, right);
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
@ -55,12 +55,11 @@ auto MSU1::unload() -> void {
|
|||
}
|
||||
|
||||
auto MSU1::power() -> void {
|
||||
audio.coprocessorEnable(true);
|
||||
audio.coprocessorFrequency(44100.0);
|
||||
}
|
||||
|
||||
auto MSU1::reset() -> void {
|
||||
create(MSU1::Enter, 44100);
|
||||
stream = Emulator::audio.createStream(44100.0);
|
||||
|
||||
mmio.dataSeekOffset = 0;
|
||||
mmio.dataReadOffset = 0;
|
||||
|
@ -119,7 +118,7 @@ auto MSU1::audioOpen() -> void {
|
|||
mmio.audioError = true;
|
||||
}
|
||||
|
||||
auto MSU1::mmioRead(uint24 addr, uint8) -> uint8 {
|
||||
auto MSU1::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
addr = 0x2000 | (addr & 7);
|
||||
|
||||
|
@ -147,7 +146,7 @@ auto MSU1::mmioRead(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
}
|
||||
|
||||
auto MSU1::mmioWrite(uint24 addr, uint8 data) -> void {
|
||||
auto MSU1::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
addr = 0x2000 | (addr & 7);
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct MSU1 : Cothread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto init() -> void;
|
||||
|
@ -10,8 +12,8 @@ struct MSU1 : Cothread {
|
|||
auto dataOpen() -> void;
|
||||
auto audioOpen() -> void;
|
||||
|
||||
auto mmioRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto mmioWrite(uint24 addr, uint8 data) -> void;
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ auto NSS::power() -> void {
|
|||
auto NSS::reset() -> void {
|
||||
}
|
||||
|
||||
auto NSS::set_dip(uint16 dip) -> void {
|
||||
auto NSS::setDip(uint16 dip) -> void {
|
||||
this->dip = dip;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ struct NSS {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto set_dip(uint16 dip) -> void;
|
||||
auto setDip(uint16 dip) -> void;
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto SharpRTC::rtc_read(uint4 addr) -> uint4 {
|
||||
auto SharpRTC::rtcRead(uint4 addr) -> uint4 {
|
||||
switch(addr) {
|
||||
case 0: return second % 10;
|
||||
case 1: return second / 10;
|
||||
|
@ -17,7 +17,7 @@ auto SharpRTC::rtc_read(uint4 addr) -> uint4 {
|
|||
}
|
||||
}
|
||||
|
||||
auto SharpRTC::rtc_write(uint4 addr, uint4 data) -> void {
|
||||
auto SharpRTC::rtcWrite(uint4 addr, uint4 data) -> void {
|
||||
switch(addr) {
|
||||
case 0: second = second / 10 * 10 + data; break;
|
||||
case 1: second = data * 10 + second % 10; break;
|
||||
|
@ -37,8 +37,8 @@ auto SharpRTC::rtc_write(uint4 addr, uint4 data) -> void {
|
|||
|
||||
auto SharpRTC::load(const uint8* data) -> void {
|
||||
for(auto byte : range(8)) {
|
||||
rtc_write(byte * 2 + 0, data[byte] >> 0);
|
||||
rtc_write(byte * 2 + 1, data[byte] >> 4);
|
||||
rtcWrite(byte * 2 + 0, data[byte] >> 0);
|
||||
rtcWrite(byte * 2 + 1, data[byte] >> 4);
|
||||
}
|
||||
|
||||
uint64 timestamp = 0;
|
||||
|
@ -47,16 +47,16 @@ auto SharpRTC::load(const uint8* data) -> void {
|
|||
}
|
||||
|
||||
uint64 diff = (uint64)time(0) - timestamp;
|
||||
while(diff >= 60 * 60 * 24) { tick_day(); diff -= 60 * 60 * 24; }
|
||||
while(diff >= 60 * 60) { tick_hour(); diff -= 60 * 60; }
|
||||
while(diff >= 60) { tick_minute(); diff -= 60; }
|
||||
while(diff--) tick_second();
|
||||
while(diff >= 60 * 60 * 24) { tickDay(); diff -= 60 * 60 * 24; }
|
||||
while(diff >= 60 * 60) { tickHour(); diff -= 60 * 60; }
|
||||
while(diff >= 60) { tickMinute(); diff -= 60; }
|
||||
while(diff--) tickSecond();
|
||||
}
|
||||
|
||||
auto SharpRTC::save(uint8* data) -> void {
|
||||
for(auto byte : range(8)) {
|
||||
data[byte] = rtc_read(byte * 2 + 0) << 0;
|
||||
data[byte] |= rtc_read(byte * 2 + 1) << 4;
|
||||
data[byte] = rtcRead(byte * 2 + 0) << 0;
|
||||
data[byte] |= rtcRead(byte * 2 + 1) << 4;
|
||||
}
|
||||
|
||||
uint64 timestamp = (uint64)time(nullptr);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
auto SharpRTC::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.integer((uint&)rtc_state);
|
||||
s.integer(rtc_index);
|
||||
s.integer((uint&)state);
|
||||
s.integer(index);
|
||||
|
||||
s.integer(second);
|
||||
s.integer(minute);
|
||||
|
|
|
@ -12,7 +12,7 @@ auto SharpRTC::Enter() -> void {
|
|||
}
|
||||
|
||||
auto SharpRTC::main() -> void {
|
||||
tick_second();
|
||||
tickSecond();
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
|
@ -42,8 +42,8 @@ auto SharpRTC::power() -> void {
|
|||
auto SharpRTC::reset() -> void {
|
||||
create(SharpRTC::Enter, 1);
|
||||
|
||||
rtc_state = State::Read;
|
||||
rtc_index = -1;
|
||||
state = State::Read;
|
||||
index = -1;
|
||||
}
|
||||
|
||||
auto SharpRTC::sync() -> void {
|
||||
|
@ -63,16 +63,16 @@ auto SharpRTC::read(uint24 addr, uint8 data) -> uint8 {
|
|||
addr &= 1;
|
||||
|
||||
if(addr == 0) {
|
||||
if(rtc_state != State::Read) return 0;
|
||||
if(state != State::Read) return 0;
|
||||
|
||||
if(rtc_index < 0) {
|
||||
rtc_index++;
|
||||
if(index < 0) {
|
||||
index++;
|
||||
return 15;
|
||||
} else if(rtc_index > 12) {
|
||||
rtc_index = -1;
|
||||
} else if(index > 12) {
|
||||
index = -1;
|
||||
return 15;
|
||||
} else {
|
||||
return rtc_read(rtc_index++);
|
||||
return rtcRead(index++);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,25 +84,25 @@ auto SharpRTC::write(uint24 addr, uint8 data) -> void {
|
|||
|
||||
if(addr == 1) {
|
||||
if(data == 0x0d) {
|
||||
rtc_state = State::Read;
|
||||
rtc_index = -1;
|
||||
state = State::Read;
|
||||
index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(data == 0x0e) {
|
||||
rtc_state = State::Command;
|
||||
state = State::Command;
|
||||
return;
|
||||
}
|
||||
|
||||
if(data == 0x0f) return; //unknown behavior
|
||||
|
||||
if(rtc_state == State::Command) {
|
||||
if(state == State::Command) {
|
||||
if(data == 0) {
|
||||
rtc_state = State::Write;
|
||||
rtc_index = 0;
|
||||
state = State::Write;
|
||||
index = 0;
|
||||
} else if(data == 4) {
|
||||
rtc_state = State::Ready;
|
||||
rtc_index = -1;
|
||||
state = State::Ready;
|
||||
index = -1;
|
||||
//reset time
|
||||
second = 0;
|
||||
minute = 0;
|
||||
|
@ -113,17 +113,17 @@ auto SharpRTC::write(uint24 addr, uint8 data) -> void {
|
|||
weekday = 0;
|
||||
} else {
|
||||
//unknown behavior
|
||||
rtc_state = State::Ready;
|
||||
state = State::Ready;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(rtc_state == State::Write) {
|
||||
if(rtc_index >= 0 && rtc_index < 12) {
|
||||
rtc_write(rtc_index++, data);
|
||||
if(rtc_index == 12) {
|
||||
if(state == State::Write) {
|
||||
if(index >= 0 && index < 12) {
|
||||
rtcWrite(index++, data);
|
||||
if(index == 12) {
|
||||
//day of week is automatically calculated and written
|
||||
weekday = calculate_weekday(1000 + year, month, day);
|
||||
weekday = calculateWeekday(1000 + year, month, day);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -14,8 +14,8 @@ struct SharpRTC : Cothread {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
enum class State : uint { Ready, Command, Read, Write } rtc_state;
|
||||
int rtc_index;
|
||||
enum class State : uint { Ready, Command, Read, Write } state;
|
||||
int index;
|
||||
|
||||
uint second;
|
||||
uint minute;
|
||||
|
@ -26,22 +26,22 @@ struct SharpRTC : Cothread {
|
|||
uint weekday;
|
||||
|
||||
//memory.cpp
|
||||
auto rtc_read(uint4 addr) -> uint4;
|
||||
auto rtc_write(uint4 addr, uint4 data) -> void;
|
||||
auto rtcRead(uint4 addr) -> uint4;
|
||||
auto rtcWrite(uint4 addr, uint4 data) -> void;
|
||||
|
||||
auto load(const uint8* data) -> void;
|
||||
auto save(uint8* data) -> void;
|
||||
|
||||
//time.cpp
|
||||
static const uint daysinmonth[12];
|
||||
auto tick_second() -> void;
|
||||
auto tick_minute() -> void;
|
||||
auto tick_hour() -> void;
|
||||
auto tick_day() -> void;
|
||||
auto tick_month() -> void;
|
||||
auto tick_year() -> void;
|
||||
static const uint daysInMonth[12];
|
||||
auto tickSecond() -> void;
|
||||
auto tickMinute() -> void;
|
||||
auto tickHour() -> void;
|
||||
auto tickDay() -> void;
|
||||
auto tickMonth() -> void;
|
||||
auto tickYear() -> void;
|
||||
|
||||
auto calculate_weekday(uint year, uint month, uint day) -> uint;
|
||||
auto calculateWeekday(uint year, uint month, uint day) -> uint;
|
||||
};
|
||||
|
||||
extern SharpRTC sharprtc;
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
const uint SharpRTC::daysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
const uint SharpRTC::daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
auto SharpRTC::tick_second() -> void {
|
||||
auto SharpRTC::tickSecond() -> void {
|
||||
if(++second < 60) return;
|
||||
second = 0;
|
||||
tick_minute();
|
||||
tickMinute();
|
||||
}
|
||||
|
||||
auto SharpRTC::tick_minute() -> void {
|
||||
auto SharpRTC::tickMinute() -> void {
|
||||
if(++minute < 60) return;
|
||||
minute = 0;
|
||||
tick_hour();
|
||||
tickHour();
|
||||
}
|
||||
|
||||
auto SharpRTC::tick_hour() -> void {
|
||||
auto SharpRTC::tickHour() -> void {
|
||||
if(++hour < 24) return;
|
||||
hour = 0;
|
||||
tick_day();
|
||||
tickDay();
|
||||
}
|
||||
|
||||
auto SharpRTC::tick_day() -> void {
|
||||
uint days = daysinmonth[month % 12];
|
||||
auto SharpRTC::tickDay() -> void {
|
||||
uint days = daysInMonth[(month - 1) % 12];
|
||||
|
||||
//add one day in February for leap years
|
||||
if(month == 1) {
|
||||
if(month == 2) {
|
||||
if(year % 400 == 0) days++;
|
||||
else if(year % 100 == 0);
|
||||
else if(year % 4 == 0) days++;
|
||||
|
@ -30,16 +30,16 @@ auto SharpRTC::tick_day() -> void {
|
|||
|
||||
if(day++ < days) return;
|
||||
day = 1;
|
||||
tick_month();
|
||||
tickMonth();
|
||||
}
|
||||
|
||||
auto SharpRTC::tick_month() -> void {
|
||||
auto SharpRTC::tickMonth() -> void {
|
||||
if(month++ < 12) return;
|
||||
month = 1;
|
||||
tick_year();
|
||||
tickYear();
|
||||
}
|
||||
|
||||
auto SharpRTC::tick_year() -> void {
|
||||
auto SharpRTC::tickYear() -> void {
|
||||
year++;
|
||||
year = (uint12)year;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ auto SharpRTC::tick_year() -> void {
|
|||
//returns day of week for specified date
|
||||
//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday
|
||||
//usage: calculate_weekday(2008, 1, 1) returns weekday of January 1st, 2008
|
||||
auto SharpRTC::calculate_weekday(uint year, uint month, uint day) -> uint {
|
||||
auto SharpRTC::calculateWeekday(uint year, uint month, uint day) -> uint {
|
||||
uint y = 1000, m = 1; //SharpRTC epoch is 1000-01-01
|
||||
uint sum = 0; //number of days passed since epoch
|
||||
|
||||
|
@ -66,7 +66,7 @@ auto SharpRTC::calculate_weekday(uint year, uint month, uint day) -> uint {
|
|||
}
|
||||
|
||||
while(m < month) {
|
||||
uint days = daysinmonth[m - 1];
|
||||
uint days = daysInMonth[(m - 1) % 12];
|
||||
bool leapyearmonth = false;
|
||||
if(days == 28) {
|
||||
if(y % 4 == 0) {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
Audio audio;
|
||||
|
||||
auto Audio::coprocessorEnable(bool enable) -> void {
|
||||
mixer.clear();
|
||||
mixerEnable = enable;
|
||||
dsp.read = dsp.write = 0;
|
||||
mix.read = mix.write = 0;
|
||||
}
|
||||
|
||||
auto Audio::coprocessorFrequency(double frequency) -> void {
|
||||
mixer.setFrequency(frequency);
|
||||
mixer.setResampler(nall::DSP::ResampleEngine::Sinc);
|
||||
mixer.setResamplerFrequency(system.apuFrequency() / 768.0);
|
||||
}
|
||||
|
||||
auto Audio::sample(int16 left, int16 right) -> void {
|
||||
if(!mixerEnable) return interface->audioSample(left, right);
|
||||
|
||||
dsp.left[dsp.write] = left;
|
||||
dsp.right[dsp.write] = right;
|
||||
dsp.write++;
|
||||
flush();
|
||||
}
|
||||
|
||||
auto Audio::coprocessorSample(int16 left, int16 right) -> void {
|
||||
int samples[] = {left, right};
|
||||
mixer.sample(samples);
|
||||
while(mixer.pending()) {
|
||||
mixer.read(samples);
|
||||
mix.left[mix.write] = samples[0];
|
||||
mix.right[mix.write] = samples[1];
|
||||
mix.write++;
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
auto Audio::flush() -> void {
|
||||
while(dsp.read != dsp.write && mix.read != mix.write) {
|
||||
interface->audioSample(
|
||||
sclamp<16>(dsp.left[dsp.read] + mix.left[mix.read]),
|
||||
sclamp<16>(dsp.right[dsp.read] + mix.right[mix.read])
|
||||
);
|
||||
dsp.read++;
|
||||
mix.read++;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
struct Audio {
|
||||
auto sample(int16 left, int16 right) -> void;
|
||||
|
||||
auto coprocessorEnable(bool enable) -> void;
|
||||
auto coprocessorFrequency(double frequency) -> void;
|
||||
auto coprocessorSample(int16 left, int16 right) -> void;
|
||||
auto flush() -> void;
|
||||
|
||||
private:
|
||||
nall::DSP mixer;
|
||||
bool mixerEnable = false;
|
||||
struct Buffer {
|
||||
int16 left[256];
|
||||
int16 right[256];
|
||||
uint8 read;
|
||||
uint8 write;
|
||||
} dsp, mix;
|
||||
};
|
||||
|
||||
extern Audio audio;
|
|
@ -3,7 +3,6 @@
|
|||
namespace SuperFamicom {
|
||||
|
||||
DSP dsp;
|
||||
#include "audio.cpp"
|
||||
|
||||
#define REG(n) state.regs[n]
|
||||
#define VREG(n) state.regs[v.vidx + n]
|
||||
|
@ -238,12 +237,11 @@ auto DSP::power() -> void {
|
|||
voice[n].vbit = 1 << n;
|
||||
voice[n].vidx = n * 0x10;
|
||||
}
|
||||
|
||||
audio.coprocessorEnable(false);
|
||||
}
|
||||
|
||||
auto DSP::reset() -> void {
|
||||
create(Enter, system.apuFrequency());
|
||||
stream = Emulator::audio.createStream(system.apuFrequency() / 768.0);
|
||||
|
||||
REG(FLG) = 0xe0;
|
||||
state.noise = 0x4000;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//Sony CXD1222Q-1
|
||||
|
||||
#include "audio.hpp"
|
||||
|
||||
struct DSP : Thread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
DSP();
|
||||
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
|
|
|
@ -103,7 +103,7 @@ auto DSP::echo27() -> void {
|
|||
}
|
||||
|
||||
//output sample to DAC
|
||||
audio.sample(outl, outr);
|
||||
stream->sample(outl, outr);
|
||||
}
|
||||
|
||||
auto DSP::echo28() -> void {
|
||||
|
|
|
@ -153,6 +153,36 @@ auto Interface::videoFrequency() -> double {
|
|||
}
|
||||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return 1 << 19;
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 color) -> uint64 {
|
||||
uint r = color.bits( 0, 4);
|
||||
uint g = color.bits( 5, 9);
|
||||
uint b = color.bits(10,14);
|
||||
uint l = color.bits(15,18);
|
||||
|
||||
double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5);
|
||||
uint64 R = L * image::normalize(r, 5, 16);
|
||||
uint64 G = L * image::normalize(g, 5, 16);
|
||||
uint64 B = L * image::normalize(b, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
static const uint8 gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
R = L * gammaRamp[r] * 0x0101;
|
||||
G = L * gammaRamp[g] * 0x0101;
|
||||
B = L * gammaRamp[b] * 0x0101;
|
||||
}
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return system.apuFrequency() / 768.0;
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ struct Interface : Emulator::Interface {
|
|||
auto manifest() -> string;
|
||||
auto title() -> string;
|
||||
auto videoFrequency() -> double;
|
||||
auto videoColors() -> uint32;
|
||||
auto videoColor(uint32 color) -> uint64;
|
||||
auto audioFrequency() -> double;
|
||||
|
||||
auto loaded() -> bool;
|
||||
|
|
|
@ -213,12 +213,6 @@ auto PPU::scanline() -> void {
|
|||
screen.scanline();
|
||||
|
||||
if(vcounter() == 241) {
|
||||
auto output = this->output;
|
||||
if(!overscan()) output -= 14 * 512;
|
||||
auto pitch = 1024 >> interlace();
|
||||
auto width = 512;
|
||||
auto height = !interlace() ? 240 : 480;
|
||||
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
@ -230,4 +224,13 @@ auto PPU::frame() -> void {
|
|||
display.overscan = regs.overscan;
|
||||
}
|
||||
|
||||
auto PPU::refresh() -> void {
|
||||
auto output = this->output;
|
||||
if(!overscan()) output -= 14 * 512;
|
||||
auto pitch = 1024 >> interlace();
|
||||
auto width = 512;
|
||||
auto height = !interlace() ? 240 : 480;
|
||||
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ privileged:
|
|||
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
struct Registers {
|
||||
uint8 ppu1_mdr;
|
||||
|
@ -152,7 +153,7 @@ privileged:
|
|||
friend class PPU::Sprite;
|
||||
friend class PPU::Window;
|
||||
friend class PPU::Screen;
|
||||
friend class Video;
|
||||
friend class Scheduler;
|
||||
|
||||
struct Debugger {
|
||||
hook<auto (uint16, uint8) -> void> vram_read;
|
||||
|
|
|
@ -13,6 +13,7 @@ auto Scheduler::enter(Mode mode_) -> Event {
|
|||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace SuperFamicom {
|
|||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(262'144 * sizeof(void*), entrypoint);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
}
|
||||
|
|
|
@ -91,7 +91,6 @@ auto System::load() -> void {
|
|||
if(cartridge.hasSufamiTurboSlots()) sufamiturboA.load(), sufamiturboB.load();
|
||||
|
||||
serializeInit();
|
||||
configureVideo();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
|
@ -152,6 +151,14 @@ auto System::power() -> void {
|
|||
}
|
||||
|
||||
auto System::reset() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
cpu.reset();
|
||||
smp.reset();
|
||||
dsp.reset();
|
||||
|
|
|
@ -21,7 +21,6 @@ struct System {
|
|||
auto reset() -> void;
|
||||
|
||||
//video.cpp
|
||||
auto configureVideo() -> void;
|
||||
auto configureVideoPalette() -> void;
|
||||
auto configureVideoEffects() -> void;
|
||||
|
||||
|
|
|
@ -1,36 +1,5 @@
|
|||
auto System::configureVideo() -> void {
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
configureVideoPalette();
|
||||
configureVideoEffects();
|
||||
}
|
||||
|
||||
auto System::configureVideoPalette() -> void {
|
||||
Emulator::video.setPalette(1 << 19, [&](uint32 color) -> uint64 {
|
||||
uint r = color.bits( 0, 4);
|
||||
uint g = color.bits( 5, 9);
|
||||
uint b = color.bits(10,14);
|
||||
uint l = color.bits(15,18);
|
||||
|
||||
double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5);
|
||||
uint64 R = L * image::normalize(r, 5, 16);
|
||||
uint64 G = L * image::normalize(g, 5, 16);
|
||||
uint64 B = L * image::normalize(b, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
static const uint8 gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
R = L * gammaRamp[r] * 0x0101;
|
||||
G = L * gammaRamp[g] * 0x0101;
|
||||
B = L * gammaRamp[b] * 0x0101;
|
||||
}
|
||||
|
||||
return R << 32 | G << 16 | B << 0;
|
||||
});
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto System::configureVideoEffects() -> void {
|
||||
|
|
|
@ -23,6 +23,10 @@ Settings::Settings() {
|
|||
set("Video/ColorEmulation", true);
|
||||
set("Video/ScanlineEmulation", false);
|
||||
|
||||
set("Video/Saturation", 100);
|
||||
set("Video/Gamma", 100);
|
||||
set("Video/Luminance", 100);
|
||||
|
||||
set("Video/Overscan/Mask", false);
|
||||
set("Video/Overscan/Horizontal", 8);
|
||||
set("Video/Overscan/Vertical", 8);
|
||||
|
@ -33,6 +37,9 @@ Settings::Settings() {
|
|||
set("Audio/Synchronize", true);
|
||||
set("Audio/Mute", false);
|
||||
set("Audio/Volume", 100);
|
||||
set("Audio/Balance", 50);
|
||||
set("Audio/Reverb/Delay", 0);
|
||||
set("Audio/Reverb/Level", 0);
|
||||
set("Audio/Latency", 60);
|
||||
set("Audio/Resampler", "Sinc");
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ Presentation::Presentation() {
|
|||
});
|
||||
muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
|
||||
settings["Audio/Mute"].setValue(muteAudio.checked());
|
||||
program->updateAudioVolume();
|
||||
program->updateAudioEffects();
|
||||
});
|
||||
showStatusBar.setText("Show Status Bar").setChecked(settings["UserInterface/ShowStatusBar"].boolean()).onToggle([&] {
|
||||
settings["UserInterface/ShowStatusBar"].setValue(showStatusBar.checked());
|
||||
|
|
|
@ -90,12 +90,13 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
|||
}
|
||||
|
||||
auto Program::audioSample(int16 lsample, int16 rsample) -> void {
|
||||
int samples[] = {lsample, rsample};
|
||||
dsp.sample(samples);
|
||||
while(dsp.pending()) {
|
||||
dsp.read(samples);
|
||||
audio->sample(samples[0], samples[1]);
|
||||
}
|
||||
audio->sample(lsample, rsample);
|
||||
//int samples[] = {lsample, rsample};
|
||||
//dsp.sample(samples);
|
||||
//while(dsp.pending()) {
|
||||
// dsp.read(samples);
|
||||
// audio->sample(samples[0], samples[1]);
|
||||
//}
|
||||
}
|
||||
|
||||
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
|
||||
|
|
|
@ -21,10 +21,13 @@ auto Program::loadMedia(Emulator::Interface& interface, Emulator::Interface::Med
|
|||
folderPaths.append(location);
|
||||
|
||||
//note: the order of operations in this block of code is critical
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setFrequency(audio->get(Audio::Frequency).get<uint>());
|
||||
emulator = &interface;
|
||||
connectDevices();
|
||||
emulator->load(media.id);
|
||||
updateAudio();
|
||||
updateAudioDriver();
|
||||
updateAudioEffects();
|
||||
emulator->power();
|
||||
|
||||
presentation->resizeViewport();
|
||||
|
|
|
@ -41,12 +41,6 @@ Program::Program(lstring args) {
|
|||
input->onChange({&InputManager::onChange, &inputManager()});
|
||||
if(!input->init()) input = Input::create("None");
|
||||
|
||||
dsp.setPrecision(16);
|
||||
dsp.setBalance(0.0);
|
||||
dsp.setFrequency(44100);
|
||||
dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dsp.setResamplerFrequency(audio->get(Audio::Frequency).get<uint>());
|
||||
|
||||
presentation->drawSplashScreen();
|
||||
|
||||
new InputManager;
|
||||
|
@ -55,7 +49,8 @@ Program::Program(lstring args) {
|
|||
new ToolsManager;
|
||||
|
||||
updateVideoShader();
|
||||
updateAudio();
|
||||
updateAudioDriver();
|
||||
updateAudioEffects();
|
||||
|
||||
args.takeFirst(); //ignore program location in argument parsing
|
||||
for(auto& argument : args) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue