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:
Tim Allen 2016-04-22 23:35:51 +10:00
parent 55e507d5df
commit e2ee6689a0
118 changed files with 1013 additions and 733 deletions

View File

@ -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/)

View File

@ -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));
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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/";

View File

@ -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

View File

@ -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();

View File

@ -1,4 +1,6 @@
struct APU : Thread {
shared_pointer<Emulator::Stream> stream;
APU();
static auto Enter() -> void;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {
}

View File

@ -5,6 +5,7 @@ struct PPU : Thread {
auto scanline() -> void;
auto frame() -> void;
auto refresh() -> void;
auto power() -> void;
auto reset() -> void;

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -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 {
}

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -30,7 +30,6 @@ struct System {
auto power() -> void;
//video.cpp
auto configureVideo() -> void;
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;

View File

@ -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);
}

View File

@ -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();

View File

@ -1,4 +1,6 @@
struct APU : Thread, MMIO {
shared_pointer<Emulator::Stream> stream;
#include "registers.hpp"
static auto Enter() -> void;

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -26,7 +26,6 @@ struct System {
auto runToSave() -> void;
//video.cpp
auto configureVideo() -> void;
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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});
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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++;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -103,7 +103,7 @@ auto DSP::echo27() -> void {
}
//output sample to DAC
audio.sample(outl, outr);
stream->sample(outl, outr);
}
auto DSP::echo28() -> void {

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -21,7 +21,6 @@ struct System {
auto reset() -> void;
//video.cpp
auto configureVideo() -> void;
auto configureVideoPalette() -> void;
auto configureVideoEffects() -> void;

View File

@ -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 {

View File

@ -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");

View File

@ -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());

View File

@ -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 {

View File

@ -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();

View File

@ -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