From e2ee6689a0b913cdb05f63aaedcbe2436383629f Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 22 Apr 2016 23:35:51 +1000 Subject: [PATCH] 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. --- higan/GNUmakefile | 3 +- higan/audio/audio.cpp | 90 +++++++++++++- higan/audio/audio.hpp | 44 ++++++- higan/emulator/emulator.cpp | 34 ------ higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 6 + higan/fc/apu/apu.cpp | 3 +- higan/fc/apu/apu.hpp | 2 + higan/fc/fc.hpp | 2 +- higan/fc/interface/interface.cpp | 55 +++++++++ higan/fc/interface/interface.hpp | 2 + higan/fc/ppu/ppu.cpp | 5 +- higan/fc/ppu/ppu.hpp | 1 + higan/fc/scheduler/scheduler.cpp | 1 + higan/fc/system/system.cpp | 9 +- higan/fc/system/system.hpp | 2 +- higan/fc/system/video.cpp | 59 +-------- higan/gb/apu/apu.cpp | 7 +- higan/gb/apu/apu.hpp | 2 + higan/gb/cartridge/cartridge.cpp | 4 + higan/gb/cartridge/cartridge.hpp | 2 + higan/gb/cartridge/mbc1m/mbc1m.cpp | 26 ++++ higan/gb/cartridge/mbc1m/mbc1m.hpp | 8 ++ higan/gb/cartridge/serialization.cpp | 3 + higan/gb/gb.hpp | 2 +- higan/gb/interface/interface.cpp | 61 ++++++++++ higan/gb/interface/interface.hpp | 2 + higan/gb/ppu/ppu.cpp | 5 +- higan/gb/ppu/ppu.hpp | 1 + higan/gb/scheduler/scheduler.cpp | 1 + higan/gb/system/system.cpp | 11 +- higan/gb/system/system.hpp | 1 - higan/gb/system/video.cpp | 68 +---------- higan/gba/apu/apu.cpp | 3 +- higan/gba/apu/apu.hpp | 2 + higan/gba/cpu/bus.cpp | 22 ++-- higan/gba/cpu/cpu.hpp | 8 +- higan/gba/cpu/dma.cpp | 4 +- higan/gba/cpu/prefetch.cpp | 8 +- higan/gba/gba.hpp | 2 +- higan/gba/interface/interface.cpp | 26 ++++ higan/gba/interface/interface.hpp | 2 + higan/gba/ppu/ppu.cpp | 5 +- higan/gba/ppu/ppu.hpp | 1 + higan/gba/scheduler/scheduler.cpp | 1 + higan/gba/system/system.cpp | 9 +- higan/gba/system/system.hpp | 1 - higan/gba/system/video.cpp | 29 +---- higan/processor/arm/arm.cpp | 10 +- higan/processor/arm/arm.hpp | 6 +- higan/processor/hg51b/hg51b.cpp | 4 +- higan/processor/hg51b/hg51b.hpp | 12 +- higan/processor/hg51b/instructions.cpp | 10 +- higan/processor/hg51b/registers.cpp | 4 +- higan/sfc/cartridge/markup.cpp | 14 +-- higan/sfc/coprocessor/armdsp/armdsp.cpp | 6 +- higan/sfc/coprocessor/armdsp/armdsp.hpp | 10 +- higan/sfc/coprocessor/armdsp/memory.cpp | 71 +++++------ higan/sfc/coprocessor/armdsp/registers.hpp | 7 +- higan/sfc/coprocessor/epsonrtc/epsonrtc.cpp | 10 +- higan/sfc/coprocessor/epsonrtc/epsonrtc.hpp | 20 +-- higan/sfc/coprocessor/epsonrtc/memory.cpp | 16 +-- higan/sfc/coprocessor/epsonrtc/time.cpp | 36 +++--- .../sfc/coprocessor/hitachidsp/hitachidsp.cpp | 2 +- .../sfc/coprocessor/hitachidsp/hitachidsp.hpp | 20 +-- higan/sfc/coprocessor/hitachidsp/memory.cpp | 28 ++--- higan/sfc/coprocessor/icd2/icd2.cpp | 7 +- higan/sfc/coprocessor/icd2/icd2.hpp | 4 +- .../coprocessor/icd2/interface/interface.cpp | 2 +- higan/sfc/coprocessor/icd2/mmio/mmio.cpp | 2 +- higan/sfc/coprocessor/mcc/mcc.cpp | 26 ++-- higan/sfc/coprocessor/mcc/mcc.hpp | 8 +- higan/sfc/coprocessor/msu1/msu1.cpp | 9 +- higan/sfc/coprocessor/msu1/msu1.hpp | 6 +- higan/sfc/coprocessor/nss/nss.cpp | 2 +- higan/sfc/coprocessor/nss/nss.hpp | 2 +- higan/sfc/coprocessor/sharprtc/memory.cpp | 20 +-- .../coprocessor/sharprtc/serialization.cpp | 4 +- higan/sfc/coprocessor/sharprtc/sharprtc.cpp | 46 +++---- higan/sfc/coprocessor/sharprtc/sharprtc.hpp | 24 ++-- higan/sfc/coprocessor/sharprtc/time.cpp | 32 ++--- higan/sfc/dsp/audio.cpp | 46 ------- higan/sfc/dsp/audio.hpp | 20 --- higan/sfc/dsp/dsp.cpp | 4 +- higan/sfc/dsp/dsp.hpp | 4 +- higan/sfc/dsp/echo.cpp | 2 +- higan/sfc/interface/interface.cpp | 30 +++++ higan/sfc/interface/interface.hpp | 2 + higan/sfc/ppu/ppu.cpp | 15 ++- higan/sfc/ppu/ppu.hpp | 3 +- higan/sfc/scheduler/scheduler.cpp | 1 + higan/sfc/sfc.hpp | 2 +- higan/sfc/system/system.cpp | 9 +- higan/sfc/system/system.hpp | 1 - higan/sfc/system/video.cpp | 33 +---- .../configuration/configuration.cpp | 7 ++ .../presentation/presentation.cpp | 2 +- higan/target-tomoko/program/interface.cpp | 13 +- higan/target-tomoko/program/media.cpp | 5 +- higan/target-tomoko/program/program.cpp | 9 +- higan/target-tomoko/program/program.hpp | 8 +- higan/target-tomoko/program/utility.cpp | 41 ++++--- higan/target-tomoko/settings/audio.cpp | 73 ++++++----- higan/target-tomoko/settings/settings.hpp | 47 ++++++-- higan/target-tomoko/settings/video.cpp | 32 ++++- higan/video/video.cpp | 114 ++++++++++++------ higan/video/video.hpp | 15 ++- higan/ws/apu/apu.cpp | 3 +- higan/ws/apu/apu.hpp | 2 + higan/ws/interface/interface.cpp | 25 ++++ higan/ws/interface/interface.hpp | 2 + higan/ws/ppu/ppu.cpp | 7 +- higan/ws/ppu/ppu.hpp | 1 + higan/ws/scheduler/scheduler.cpp | 1 + higan/ws/system/system.cpp | 9 +- higan/ws/system/system.hpp | 1 - higan/ws/system/video.cpp | 28 +---- higan/ws/ws.hpp | 2 +- 118 files changed, 1013 insertions(+), 733 deletions(-) delete mode 100644 higan/emulator/emulator.cpp create mode 100644 higan/gb/cartridge/mbc1m/mbc1m.cpp create mode 100644 higan/gb/cartridge/mbc1m/mbc1m.hpp delete mode 100644 higan/sfc/dsp/audio.cpp delete mode 100644 higan/sfc/dsp/audio.hpp diff --git a/higan/GNUmakefile b/higan/GNUmakefile index 020c149c..f1d549e8 100644 --- a/higan/GNUmakefile +++ b/higan/GNUmakefile @@ -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/) diff --git a/higan/audio/audio.cpp b/higan/audio/audio.cpp index d0c691fb..d0a0cd92 100644 --- a/higan/audio/audio.cpp +++ b/higan/audio/audio.cpp @@ -1,5 +1,93 @@ #include -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 { + shared_pointer 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)); + } +} } diff --git a/higan/audio/audio.hpp b/higan/audio/audio.hpp index bb73278b..1915bb2c 100644 --- a/higan/audio/audio.hpp +++ b/higan/audio/audio.hpp @@ -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; + + auto poll() -> void; + +private: + Interface* interface = nullptr; + vector> streams; + double frequency = 0.0; + double volume = 1.0; + double balance = 0.0; + uint reverbDelay = 0; //0 = disabled + double reverbLevel = 0.0; + vector reverbLeft; + vector reverbRight; + + friend class Stream; +}; + +extern Audio audio; } diff --git a/higan/emulator/emulator.cpp b/higan/emulator/emulator.cpp deleted file mode 100644 index 321bbad7..00000000 --- a/higan/emulator/emulator.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -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; -} - -} diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 35517608..60b77028 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 11ea0417..0ec6f1e9 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -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 diff --git a/higan/fc/apu/apu.cpp b/higan/fc/apu/apu.cpp index 39d4e9be..e9418f23 100644 --- a/higan/fc/apu/apu.cpp +++ b/higan/fc/apu/apu.cpp @@ -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(); diff --git a/higan/fc/apu/apu.hpp b/higan/fc/apu/apu.hpp index 1e3824b8..e5f71162 100644 --- a/higan/fc/apu/apu.hpp +++ b/higan/fc/apu/apu.hpp @@ -1,4 +1,6 @@ struct APU : Thread { + shared_pointer stream; + APU(); static auto Enter() -> void; diff --git a/higan/fc/fc.hpp b/higan/fc/fc.hpp index 77cdf803..20d5a570 100644 --- a/higan/fc/fc.hpp +++ b/higan/fc/fc.hpp @@ -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; } diff --git a/higan/fc/interface/interface.cpp b/higan/fc/interface/interface.cpp index b9950858..4e6193eb 100644 --- a/higan/fc/interface/interface.cpp +++ b/higan/fc/interface/interface.cpp @@ -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; } diff --git a/higan/fc/interface/interface.hpp b/higan/fc/interface/interface.hpp index 580355f2..f561216a 100644 --- a/higan/fc/interface/interface.hpp +++ b/higan/fc/interface/interface.hpp @@ -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; diff --git a/higan/fc/ppu/ppu.cpp b/higan/fc/ppu/ppu.cpp index 26c4ed71..afadf92b 100644 --- a/higan/fc/ppu/ppu.cpp +++ b/higan/fc/ppu/ppu.cpp @@ -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 { } diff --git a/higan/fc/ppu/ppu.hpp b/higan/fc/ppu/ppu.hpp index 85b8f0dd..bfd0aba7 100644 --- a/higan/fc/ppu/ppu.hpp +++ b/higan/fc/ppu/ppu.hpp @@ -5,6 +5,7 @@ struct PPU : Thread { auto scanline() -> void; auto frame() -> void; + auto refresh() -> void; auto power() -> void; auto reset() -> void; diff --git a/higan/fc/scheduler/scheduler.cpp b/higan/fc/scheduler/scheduler.cpp index 47aae012..d8797956 100644 --- a/higan/fc/scheduler/scheduler.cpp +++ b/higan/fc/scheduler/scheduler.cpp @@ -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; } diff --git a/higan/fc/system/system.cpp b/higan/fc/system/system.cpp index fb6e4ed5..f19a61ce 100644 --- a/higan/fc/system/system.cpp +++ b/higan/fc/system/system.cpp @@ -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(); diff --git a/higan/fc/system/system.hpp b/higan/fc/system/system.hpp index ae210293..4d1a1d94 100644 --- a/higan/fc/system/system.hpp +++ b/higan/fc/system/system.hpp @@ -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; diff --git a/higan/fc/system/video.cpp b/higan/fc/system/video.cpp index a7ba6922..66d09298 100644 --- a/higan/fc/system/video.cpp +++ b/higan/fc/system/video.cpp @@ -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 { } diff --git a/higan/gb/apu/apu.cpp b/higan/gb/apu/apu.cpp index 9a1a629d..4c2f598c 100644 --- a/higan/gb/apu/apu.cpp +++ b/higan/gb/apu/apu.cpp @@ -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(); diff --git a/higan/gb/apu/apu.hpp b/higan/gb/apu/apu.hpp index e008d649..a198e3d7 100644 --- a/higan/gb/apu/apu.hpp +++ b/higan/gb/apu/apu.hpp @@ -1,4 +1,6 @@ struct APU : Thread, MMIO { + shared_pointer stream; + static auto Enter() -> void; auto main() -> void; auto hipass(int16& sample, int64& bias) -> void; diff --git a/higan/gb/cartridge/cartridge.cpp b/higan/gb/cartridge/cartridge.cpp index ab0e3116..39e544f2 100644 --- a/higan/gb/cartridge/cartridge.cpp +++ b/higan/gb/cartridge/cartridge.cpp @@ -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(); diff --git a/higan/gb/cartridge/cartridge.hpp b/higan/gb/cartridge/cartridge.hpp index ab13d649..1d8e5f97 100644 --- a/higan/gb/cartridge/cartridge.hpp +++ b/higan/gb/cartridge/cartridge.hpp @@ -16,6 +16,7 @@ struct Cartridge : MMIO, property { #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 { enum Mapper : uint { MBC0, MBC1, + MBC1M, MBC2, MBC3, MBC5, diff --git a/higan/gb/cartridge/mbc1m/mbc1m.cpp b/higan/gb/cartridge/mbc1m/mbc1m.cpp new file mode 100644 index 00000000..efd3cfe6 --- /dev/null +++ b/higan/gb/cartridge/mbc1m/mbc1m.cpp @@ -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; +} diff --git a/higan/gb/cartridge/mbc1m/mbc1m.hpp b/higan/gb/cartridge/mbc1m/mbc1m.hpp new file mode 100644 index 00000000..16d173a1 --- /dev/null +++ b/higan/gb/cartridge/mbc1m/mbc1m.hpp @@ -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; diff --git a/higan/gb/cartridge/serialization.cpp b/higan/gb/cartridge/serialization.cpp index a2465182..55181431 100644 --- a/higan/gb/cartridge/serialization.cpp +++ b/higan/gb/cartridge/serialization.cpp @@ -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); diff --git a/higan/gb/gb.hpp b/higan/gb/gb.hpp index 8b8e5bf9..0338cba6 100644 --- a/higan/gb/gb.hpp +++ b/higan/gb/gb.hpp @@ -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; } diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index fec86c66..735a3d57 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -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; } diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index 55723826..c0c6cc84 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -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; diff --git a/higan/gb/ppu/ppu.cpp b/higan/gb/ppu/ppu.cpp index 1f0472fb..a30336ca 100644 --- a/higan/gb/ppu/ppu.cpp +++ b/higan/gb/ppu/ppu.cpp @@ -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) { diff --git a/higan/gb/ppu/ppu.hpp b/higan/gb/ppu/ppu.hpp index 5d82381b..f75eb042 100644 --- a/higan/gb/ppu/ppu.hpp +++ b/higan/gb/ppu/ppu.hpp @@ -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; diff --git a/higan/gb/scheduler/scheduler.cpp b/higan/gb/scheduler/scheduler.cpp index aa6ce192..a1c88b2f 100644 --- a/higan/gb/scheduler/scheduler.cpp +++ b/higan/gb/scheduler/scheduler.cpp @@ -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; } diff --git a/higan/gb/system/system.cpp b/higan/gb/system/system.cpp index 747bd8b7..485e103d 100644 --- a/higan/gb/system/system.cpp +++ b/higan/gb/system/system.cpp @@ -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(); diff --git a/higan/gb/system/system.hpp b/higan/gb/system/system.hpp index b4d84d13..3acff4a9 100644 --- a/higan/gb/system/system.hpp +++ b/higan/gb/system/system.hpp @@ -30,7 +30,6 @@ struct System { auto power() -> void; //video.cpp - auto configureVideo() -> void; auto configureVideoPalette() -> void; auto configureVideoEffects() -> void; diff --git a/higan/gb/system/video.cpp b/higan/gb/system/video.cpp index 5c615110..6f27f6c8 100644 --- a/higan/gb/system/video.cpp +++ b/higan/gb/system/video.cpp @@ -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); } diff --git a/higan/gba/apu/apu.cpp b/higan/gba/apu/apu.cpp index e03f223f..0ed9a7ac 100644 --- a/higan/gba/apu/apu.cpp +++ b/higan/gba/apu/apu.cpp @@ -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(); diff --git a/higan/gba/apu/apu.hpp b/higan/gba/apu/apu.hpp index c78333f8..a043a1e4 100644 --- a/higan/gba/apu/apu.hpp +++ b/higan/gba/apu/apu.hpp @@ -1,4 +1,6 @@ struct APU : Thread, MMIO { + shared_pointer stream; + #include "registers.hpp" static auto Enter() -> void; diff --git a/higan/gba/cpu/bus.cpp b/higan/gba/cpu/bus.cpp index c79f01c5..2ffe24ef 100644 --- a/higan/gba/cpu/bus.cpp +++ b/higan/gba/cpu/bus.cpp @@ -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; } diff --git a/higan/gba/cpu/cpu.hpp b/higan/gba/cpu/cpu.hpp index 89129e8c..db535ab3 100644 --- a/higan/gba/cpu/cpu.hpp +++ b/higan/gba/cpu/cpu.hpp @@ -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; diff --git a/higan/gba/cpu/dma.cpp b/higan/gba/cpu/dma.cpp index a9a7a3af..8740de42 100644 --- a/higan/gba/cpu/dma.cpp +++ b/higan/gba/cpu/dma.cpp @@ -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) { diff --git a/higan/gba/cpu/prefetch.cpp b/higan/gba/cpu/prefetch.cpp index c829542f..ccc7d2ca 100644 --- a/higan/gba/cpu/prefetch.cpp +++ b/higan/gba/cpu/prefetch.cpp @@ -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; diff --git a/higan/gba/gba.hpp b/higan/gba/gba.hpp index 18a2055b..4d2a68a4 100644 --- a/higan/gba/gba.hpp +++ b/higan/gba/gba.hpp @@ -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; } diff --git a/higan/gba/interface/interface.cpp b/higan/gba/interface/interface.cpp index 5cd665fc..688332e4 100644 --- a/higan/gba/interface/interface.cpp +++ b/higan/gba/interface/interface.cpp @@ -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; } diff --git a/higan/gba/interface/interface.hpp b/higan/gba/interface/interface.hpp index c677b3c4..38a1e64e 100644 --- a/higan/gba/interface/interface.hpp +++ b/higan/gba/interface/interface.hpp @@ -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; diff --git a/higan/gba/ppu/ppu.cpp b/higan/gba/ppu/ppu.cpp index abbdccec..35190bd2 100644 --- a/higan/gba/ppu/ppu.cpp +++ b/higan/gba/ppu/ppu.cpp @@ -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); +} + } diff --git a/higan/gba/ppu/ppu.hpp b/higan/gba/ppu/ppu.hpp index 6e3061ab..4216f987 100644 --- a/higan/gba/ppu/ppu.hpp +++ b/higan/gba/ppu/ppu.hpp @@ -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; diff --git a/higan/gba/scheduler/scheduler.cpp b/higan/gba/scheduler/scheduler.cpp index 30bbf8c9..40c4d075 100644 --- a/higan/gba/scheduler/scheduler.cpp +++ b/higan/gba/scheduler/scheduler.cpp @@ -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; } diff --git a/higan/gba/system/system.cpp b/higan/gba/system/system.cpp index 1d69dba4..9e8432fa 100644 --- a/higan/gba/system/system.cpp +++ b/higan/gba/system/system.cpp @@ -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; } diff --git a/higan/gba/system/system.hpp b/higan/gba/system/system.hpp index 44eed38f..3cb319a7 100644 --- a/higan/gba/system/system.hpp +++ b/higan/gba/system/system.hpp @@ -26,7 +26,6 @@ struct System { auto runToSave() -> void; //video.cpp - auto configureVideo() -> void; auto configureVideoPalette() -> void; auto configureVideoEffects() -> void; diff --git a/higan/gba/system/video.cpp b/higan/gba/system/video.cpp index f775054a..f77e7ae9 100644 --- a/higan/gba/system/video.cpp +++ b/higan/gba/system/video.cpp @@ -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 { diff --git a/higan/processor/arm/arm.cpp b/higan/processor/arm/arm.cpp index 6c3090da..601b6b84 100644 --- a/higan/processor/arm/arm.cpp +++ b/higan/processor/arm/arm.cpp @@ -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 { diff --git a/higan/processor/arm/arm.hpp b/higan/processor/arm/arm.hpp index bea10663..608cf8f5 100644 --- a/higan/processor/arm/arm.hpp +++ b/higan/processor/arm/arm.hpp @@ -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; diff --git a/higan/processor/hg51b/hg51b.cpp b/higan/processor/hg51b/hg51b.cpp index 7fc40baa..15038909 100644 --- a/higan/processor/hg51b/hg51b.cpp +++ b/higan/processor/hg51b/hg51b.cpp @@ -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(); } diff --git a/higan/processor/hg51b/hg51b.hpp b/higan/processor/hg51b/hg51b.hpp index 3bf2150d..b7118878 100644 --- a/higan/processor/hg51b/hg51b.hpp +++ b/higan/processor/hg51b/hg51b.hpp @@ -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; diff --git a/higan/processor/hg51b/instructions.cpp b/higan/processor/hg51b/instructions.cpp index dc215f82..96ae8cc5 100644 --- a/higan/processor/hg51b/instructions.cpp +++ b/higan/processor/hg51b/instructions.cpp @@ -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) { diff --git a/higan/processor/hg51b/registers.cpp b/higan/processor/hg51b/registers.cpp index 82f9698e..4038efe5 100644 --- a/higan/processor/hg51b/registers.cpp +++ b/higan/processor/hg51b/registers.cpp @@ -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; diff --git a/higan/sfc/cartridge/markup.cpp b/higan/sfc/cartridge/markup.cpp index 7e28690b..28945113 100644 --- a/higan/sfc/cartridge/markup.cpp +++ b/higan/sfc/cartridge/markup.cpp @@ -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}); } } diff --git a/higan/sfc/coprocessor/armdsp/armdsp.cpp b/higan/sfc/coprocessor/armdsp/armdsp.cpp index f6b328f6..4cdf6234 100644 --- a/higan/sfc/coprocessor/armdsp/armdsp.cpp +++ b/higan/sfc/coprocessor/armdsp/armdsp.cpp @@ -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; diff --git a/higan/sfc/coprocessor/armdsp/armdsp.hpp b/higan/sfc/coprocessor/armdsp/armdsp.hpp index 13390f9b..db373a87 100644 --- a/higan/sfc/coprocessor/armdsp/armdsp.hpp +++ b/higan/sfc/coprocessor/armdsp/armdsp.hpp @@ -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; diff --git a/higan/sfc/coprocessor/armdsp/memory.cpp b/higan/sfc/coprocessor/armdsp/memory.cpp index c6d76a4b..05931a35 100644 --- a/higan/sfc/coprocessor/armdsp/memory.cpp +++ b/higan/sfc/coprocessor/armdsp/memory.cpp @@ -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; } diff --git a/higan/sfc/coprocessor/armdsp/registers.hpp b/higan/sfc/coprocessor/armdsp/registers.hpp index 813fdb47..c05a0975 100644 --- a/higan/sfc/coprocessor/armdsp/registers.hpp +++ b/higan/sfc/coprocessor/armdsp/registers.hpp @@ -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; diff --git a/higan/sfc/coprocessor/epsonrtc/epsonrtc.cpp b/higan/sfc/coprocessor/epsonrtc/epsonrtc.cpp index 5445b48d..b3e353d3 100644 --- a/higan/sfc/coprocessor/epsonrtc/epsonrtc.cpp +++ b/higan/sfc/coprocessor/epsonrtc/epsonrtc.cpp @@ -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; diff --git a/higan/sfc/coprocessor/epsonrtc/epsonrtc.hpp b/higan/sfc/coprocessor/epsonrtc/epsonrtc.hpp index 3cd36a4f..0e0b9c71 100644 --- a/higan/sfc/coprocessor/epsonrtc/epsonrtc.hpp +++ b/higan/sfc/coprocessor/epsonrtc/epsonrtc.hpp @@ -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; diff --git a/higan/sfc/coprocessor/epsonrtc/memory.cpp b/higan/sfc/coprocessor/epsonrtc/memory.cpp index be9da697..f3ab59ee 100644 --- a/higan/sfc/coprocessor/epsonrtc/memory.cpp +++ b/higan/sfc/coprocessor/epsonrtc/memory.cpp @@ -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 { diff --git a/higan/sfc/coprocessor/epsonrtc/time.cpp b/higan/sfc/coprocessor/epsonrtc/time.cpp index 2620627e..08c13a49 100644 --- a/higan/sfc/coprocessor/epsonrtc/time.cpp +++ b/higan/sfc/coprocessor/epsonrtc/time.cpp @@ -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 { diff --git a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp index 047f1fab..37770b00 100644 --- a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp +++ b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp @@ -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; diff --git a/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp b/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp index 18256f82..753797ce 100644 --- a/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp +++ b/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp @@ -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; auto serialize(serializer&) -> void; diff --git a/higan/sfc/coprocessor/hitachidsp/memory.cpp b/higan/sfc/coprocessor/hitachidsp/memory.cpp index 9537ee75..1c609889 100644 --- a/higan/sfc/coprocessor/hitachidsp/memory.cpp +++ b/higan/sfc/coprocessor/hitachidsp/memory.cpp @@ -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 diff --git a/higan/sfc/coprocessor/icd2/icd2.cpp b/higan/sfc/coprocessor/icd2/icd2.cpp index 7eb3fe93..f317f29d 100644 --- a/higan/sfc/coprocessor/icd2/icd2.cpp +++ b/higan/sfc/coprocessor/icd2/icd2.cpp @@ -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; diff --git a/higan/sfc/coprocessor/icd2/icd2.hpp b/higan/sfc/coprocessor/icd2/icd2.hpp index a73b2f72..9a619214 100644 --- a/higan/sfc/coprocessor/icd2/icd2.hpp +++ b/higan/sfc/coprocessor/icd2/icd2.hpp @@ -1,6 +1,8 @@ #if defined(SFC_SUPERGAMEBOY) struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread { + shared_pointer 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; diff --git a/higan/sfc/coprocessor/icd2/interface/interface.cpp b/higan/sfc/coprocessor/icd2/interface/interface.cpp index 0f93a78e..6440abef 100644 --- a/higan/sfc/coprocessor/icd2/interface/interface.cpp +++ b/higan/sfc/coprocessor/icd2/interface/interface.cpp @@ -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 { diff --git a/higan/sfc/coprocessor/icd2/mmio/mmio.cpp b/higan/sfc/coprocessor/icd2/mmio/mmio.cpp index e735ad53..ffbe4933 100644 --- a/higan/sfc/coprocessor/icd2/mmio/mmio.cpp +++ b/higan/sfc/coprocessor/icd2/mmio/mmio.cpp @@ -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) diff --git a/higan/sfc/coprocessor/mcc/mcc.cpp b/higan/sfc/coprocessor/mcc/mcc.cpp index 6f85da84..7daa64be 100644 --- a/higan/sfc/coprocessor/mcc/mcc.cpp +++ b/higan/sfc/coprocessor/mcc/mcc.cpp @@ -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 { diff --git a/higan/sfc/coprocessor/mcc/mcc.hpp b/higan/sfc/coprocessor/mcc/mcc.hpp index 442b8c30..0558b3cb 100644 --- a/higan/sfc/coprocessor/mcc/mcc.hpp +++ b/higan/sfc/coprocessor/mcc/mcc.hpp @@ -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; diff --git a/higan/sfc/coprocessor/msu1/msu1.cpp b/higan/sfc/coprocessor/msu1/msu1.cpp index 7b671924..05ebbaf5 100644 --- a/higan/sfc/coprocessor/msu1/msu1.cpp +++ b/higan/sfc/coprocessor/msu1/msu1.cpp @@ -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); diff --git a/higan/sfc/coprocessor/msu1/msu1.hpp b/higan/sfc/coprocessor/msu1/msu1.hpp index bd24cf7e..899a0309 100644 --- a/higan/sfc/coprocessor/msu1/msu1.hpp +++ b/higan/sfc/coprocessor/msu1/msu1.hpp @@ -1,4 +1,6 @@ struct MSU1 : Cothread { + shared_pointer 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; diff --git a/higan/sfc/coprocessor/nss/nss.cpp b/higan/sfc/coprocessor/nss/nss.cpp index 35db10ef..3274215e 100644 --- a/higan/sfc/coprocessor/nss/nss.cpp +++ b/higan/sfc/coprocessor/nss/nss.cpp @@ -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; } diff --git a/higan/sfc/coprocessor/nss/nss.hpp b/higan/sfc/coprocessor/nss/nss.hpp index 3a2c7255..cbdb0cd1 100644 --- a/higan/sfc/coprocessor/nss/nss.hpp +++ b/higan/sfc/coprocessor/nss/nss.hpp @@ -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; diff --git a/higan/sfc/coprocessor/sharprtc/memory.cpp b/higan/sfc/coprocessor/sharprtc/memory.cpp index f3e66ec8..3803f1ec 100644 --- a/higan/sfc/coprocessor/sharprtc/memory.cpp +++ b/higan/sfc/coprocessor/sharprtc/memory.cpp @@ -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); diff --git a/higan/sfc/coprocessor/sharprtc/serialization.cpp b/higan/sfc/coprocessor/sharprtc/serialization.cpp index 0a6d3179..d4666a19 100644 --- a/higan/sfc/coprocessor/sharprtc/serialization.cpp +++ b/higan/sfc/coprocessor/sharprtc/serialization.cpp @@ -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); diff --git a/higan/sfc/coprocessor/sharprtc/sharprtc.cpp b/higan/sfc/coprocessor/sharprtc/sharprtc.cpp index 3b6d202f..6dc86b42 100644 --- a/higan/sfc/coprocessor/sharprtc/sharprtc.cpp +++ b/higan/sfc/coprocessor/sharprtc/sharprtc.cpp @@ -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; diff --git a/higan/sfc/coprocessor/sharprtc/sharprtc.hpp b/higan/sfc/coprocessor/sharprtc/sharprtc.hpp index 50737363..20153b52 100644 --- a/higan/sfc/coprocessor/sharprtc/sharprtc.hpp +++ b/higan/sfc/coprocessor/sharprtc/sharprtc.hpp @@ -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; diff --git a/higan/sfc/coprocessor/sharprtc/time.cpp b/higan/sfc/coprocessor/sharprtc/time.cpp index 3fb05327..be7355df 100644 --- a/higan/sfc/coprocessor/sharprtc/time.cpp +++ b/higan/sfc/coprocessor/sharprtc/time.cpp @@ -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) { diff --git a/higan/sfc/dsp/audio.cpp b/higan/sfc/dsp/audio.cpp deleted file mode 100644 index b167bcb3..00000000 --- a/higan/sfc/dsp/audio.cpp +++ /dev/null @@ -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++; - } -} diff --git a/higan/sfc/dsp/audio.hpp b/higan/sfc/dsp/audio.hpp deleted file mode 100644 index 14188d7a..00000000 --- a/higan/sfc/dsp/audio.hpp +++ /dev/null @@ -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; diff --git a/higan/sfc/dsp/dsp.cpp b/higan/sfc/dsp/dsp.cpp index 99712c76..84b96bf8 100644 --- a/higan/sfc/dsp/dsp.cpp +++ b/higan/sfc/dsp/dsp.cpp @@ -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; diff --git a/higan/sfc/dsp/dsp.hpp b/higan/sfc/dsp/dsp.hpp index 665e36a5..747f0941 100644 --- a/higan/sfc/dsp/dsp.hpp +++ b/higan/sfc/dsp/dsp.hpp @@ -1,8 +1,8 @@ //Sony CXD1222Q-1 -#include "audio.hpp" - struct DSP : Thread { + shared_pointer stream; + DSP(); alwaysinline auto step(uint clocks) -> void; diff --git a/higan/sfc/dsp/echo.cpp b/higan/sfc/dsp/echo.cpp index c31fd295..b8f3e751 100644 --- a/higan/sfc/dsp/echo.cpp +++ b/higan/sfc/dsp/echo.cpp @@ -103,7 +103,7 @@ auto DSP::echo27() -> void { } //output sample to DAC - audio.sample(outl, outr); + stream->sample(outl, outr); } auto DSP::echo28() -> void { diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index ee3b626f..a8c0e9ae 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -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; } diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index 3d9995a4..f24663ec 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -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; diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index 1fa24d51..5ef771f7 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -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); +} + } diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index 393cd5fc..4bdaf20b 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -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 void> vram_read; diff --git a/higan/sfc/scheduler/scheduler.cpp b/higan/sfc/scheduler/scheduler.cpp index 7f9d327e..b02987c2 100644 --- a/higan/sfc/scheduler/scheduler.cpp +++ b/higan/sfc/scheduler/scheduler.cpp @@ -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; } diff --git a/higan/sfc/sfc.hpp b/higan/sfc/sfc.hpp index 87aecc83..0afc409b 100644 --- a/higan/sfc/sfc.hpp +++ b/higan/sfc/sfc.hpp @@ -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; } diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 67cb2c01..01467661 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -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(); diff --git a/higan/sfc/system/system.hpp b/higan/sfc/system/system.hpp index cc1bf49b..cbb79bdf 100644 --- a/higan/sfc/system/system.hpp +++ b/higan/sfc/system/system.hpp @@ -21,7 +21,6 @@ struct System { auto reset() -> void; //video.cpp - auto configureVideo() -> void; auto configureVideoPalette() -> void; auto configureVideoEffects() -> void; diff --git a/higan/sfc/system/video.cpp b/higan/sfc/system/video.cpp index e2c88c34..f537903b 100644 --- a/higan/sfc/system/video.cpp +++ b/higan/sfc/system/video.cpp @@ -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 { diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index 078067cc..887cffcb 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -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"); diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index 1cb6505f..2b580513 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -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()); diff --git a/higan/target-tomoko/program/interface.cpp b/higan/target-tomoko/program/interface.cpp index 04a8c247..b0c8ba03 100644 --- a/higan/target-tomoko/program/interface.cpp +++ b/higan/target-tomoko/program/interface.cpp @@ -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 { diff --git a/higan/target-tomoko/program/media.cpp b/higan/target-tomoko/program/media.cpp index 870ad26c..028ab820 100644 --- a/higan/target-tomoko/program/media.cpp +++ b/higan/target-tomoko/program/media.cpp @@ -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()); emulator = &interface; connectDevices(); emulator->load(media.id); - updateAudio(); + updateAudioDriver(); + updateAudioEffects(); emulator->power(); presentation->resizeViewport(); diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 2318ce86..68f247a4 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -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()); - 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) { diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index f7e2d5d8..f00bbfb5 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -33,13 +33,11 @@ struct Program : Emulator::Interface::Bind { auto connectDevices() -> void; auto showMessage(const string& text) -> void; auto updateStatusText() -> void; + auto updateVideoPalette() -> void; auto updateVideoShader() -> void; - auto updateAudio() -> void; - auto updateAudioMode() -> void; - auto updateAudioVolume() -> void; - auto updateDSP() -> void; + auto updateAudioDriver() -> void; + auto updateAudioEffects() -> void; - DSP dsp; bool pause = false; vector emulators; diff --git a/higan/target-tomoko/program/utility.cpp b/higan/target-tomoko/program/utility.cpp index e32fdef3..a258cb81 100644 --- a/higan/target-tomoko/program/utility.cpp +++ b/higan/target-tomoko/program/utility.cpp @@ -49,6 +49,16 @@ auto Program::updateStatusText() -> void { } } +auto Program::updateVideoPalette() -> void { + double saturation = settings["Video/Saturation"].natural() / 100.0; + double gamma = settings["Video/Gamma"].natural() / 100.0; + double luminance = settings["Video/Luminance"].natural() / 100.0; + Emulator::video.setSaturation(saturation); + Emulator::video.setGamma(gamma); + Emulator::video.setLuminance(luminance); + Emulator::video.setPalette(); +} + auto Program::updateVideoShader() -> void { if(settings["Video/Driver"].text() == "OpenGL" && settings["Video/Shader"].text() != "None" @@ -63,28 +73,23 @@ auto Program::updateVideoShader() -> void { } } -auto Program::updateAudio() -> void { +auto Program::updateAudioDriver() -> void { if(!audio) return; audio->clear(); - audio->set(Audio::Latency, (uint)settings["Audio/Latency"].natural()); - if(settings["Audio/Resampler"].text() == "Linear" ) dsp.setResampler(DSP::ResampleEngine::Linear); - if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite); - if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc); - dsp.setResamplerFrequency(audio->get(Audio::Frequency).get()); - updateAudioMode(); - updateAudioVolume(); - updateDSP(); -} - -auto Program::updateAudioMode() -> void { audio->set(Audio::Exclusive, settings["Audio/Exclusive"].boolean()); + audio->set(Audio::Latency, (uint)settings["Audio/Latency"].natural()); } -auto Program::updateAudioVolume() -> void { - dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01); -} +auto Program::updateAudioEffects() -> void { + auto volume = settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01; + Emulator::audio.setVolume(volume); -auto Program::updateDSP() -> void { - if(!emulator) return; - return dsp.setFrequency(emulator->audioFrequency()); + auto balance = max(-1.0, min(1.0, (settings["Audio/Balance"].integer() - 50) / 50.0)); + Emulator::audio.setBalance(balance); + + auto reverbDelay = settings["Audio/Reverb/Delay"].natural(); + Emulator::audio.setReverbDelay(reverbDelay); + + auto reverbLevel = settings["Audio/Reverb/Level"].natural() / 100.0; + Emulator::audio.setReverbLevel(reverbLevel); } diff --git a/higan/target-tomoko/settings/audio.cpp b/higan/target-tomoko/settings/audio.cpp index c2c0ea26..5067c5e3 100644 --- a/higan/target-tomoko/settings/audio.cpp +++ b/higan/target-tomoko/settings/audio.cpp @@ -4,10 +4,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { layout.setMargin(5); - frequencyLabel.setText("Frequency:"); - auto frequencyValue = audio->get(Audio::Frequency).get(); - frequencyCombo.append(ComboButtonItem().setText({frequencyValue, "hz"})); - frequencyCombo.setEnabled(false); //not user configurable + driverLabel.setFont(Font().setBold()).setText("Driver Settings"); latencyLabel.setText("Latency:"); latencyCombo.append(ComboButtonItem().setText("0ms")); @@ -24,27 +21,44 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { case 80: latencyCombo.item(4)->setSelected(); break; case 100: latencyCombo.item(5)->setSelected(); break; } - latencyCombo.onChange([&] { update(); }); + latencyCombo.onChange([&] { updateDriver(); }); + + frequencyLabel.setText("Frequency:"); + auto frequencyValue = audio->get(Audio::Frequency).get(); + frequencyCombo.append(ComboButtonItem().setText({frequencyValue, "hz"})); + frequencyCombo.setEnabled(false); resamplerLabel.setText("Resampler:"); - resamplerCombo.append(ComboButtonItem().setText("Linear")); - resamplerCombo.append(ComboButtonItem().setText("Hermite")); resamplerCombo.append(ComboButtonItem().setText("Sinc")); - if(settings["Audio/Resampler"].text() == "Linear" ) resamplerCombo.item(0)->setSelected(); - if(settings["Audio/Resampler"].text() == "Hermite") resamplerCombo.item(1)->setSelected(); - if(settings["Audio/Resampler"].text() == "Sinc" ) resamplerCombo.item(2)->setSelected(); - resamplerCombo.onChange([&] { update(); }); + resamplerCombo.setEnabled(false); + + exclusiveMode.setText("Exclusive Mode"); + exclusiveMode.setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] { updateDriver(); }); + if(!audio->cap(Audio::Exclusive)) exclusiveMode.remove(); + + effectsLabel.setFont(Font().setBold()).setText("Effects"); volumeLabel.setText("Volume:"); - volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); }); + volumeValue.setAlignment(0.5); + volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateEffects(); }); - exclusiveMode.setText("Exclusive Mode").setVisible(audio->cap(Audio::Exclusive)); - exclusiveMode.setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] { updateMode(); }); + balanceLabel.setText("Balance:"); + balanceValue.setAlignment(0.5); + balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] { updateEffects(); }); - update(); + reverbDelayLabel.setText("Reverb Delay:"); + reverbDelayValue.setAlignment(0.5); + reverbDelaySlider.setLength(201).setPosition(settings["Audio/Reverb/Delay"].natural()).onChange([&] { updateEffects(); }); + + reverbLevelLabel.setText("Reverb Level:"); + reverbLevelValue.setAlignment(0.5); + reverbLevelSlider.setLength(101).setPosition(settings["Audio/Reverb/Level"].natural()).onChange([&] { updateEffects(); }); + + updateDriver(); + updateEffects(); } -auto AudioSettings::update() -> void { +auto AudioSettings::updateDriver() -> void { if(auto item = latencyCombo.selected()) { uint latency = 60; if(item->offset() == 0) latency = 0; @@ -55,24 +69,23 @@ auto AudioSettings::update() -> void { if(item->offset() == 5) latency = 100; settings["Audio/Latency"].setValue(latency); } - if(auto item = resamplerCombo.selected()) { - string resampler = "Sinc"; - if(item->offset() == 0) resampler = "Linear"; - if(item->offset() == 1) resampler = "Hermite"; - if(item->offset() == 2) resampler = "Sinc"; - settings["Audio/Resampler"].setValue(resampler); - } - updateVolume(); - updateMode(); -} -auto AudioSettings::updateMode() -> void { settings["Audio/Exclusive"].setValue(exclusiveMode.checked()); - program->updateAudioMode(); + program->updateAudioDriver(); } -auto AudioSettings::updateVolume() -> void { +auto AudioSettings::updateEffects() -> void { settings["Audio/Volume"].setValue(volumeSlider.position()); volumeValue.setText({volumeSlider.position(), "%"}); - program->updateAudioVolume(); + + settings["Audio/Balance"].setValue(balanceSlider.position()); + balanceValue.setText({balanceSlider.position(), "%"}); + + settings["Audio/Reverb/Delay"].setValue(reverbDelaySlider.position()); + reverbDelayValue.setText({reverbDelaySlider.position(), "ms"}); + + settings["Audio/Reverb/Level"].setValue(reverbLevelSlider.position()); + reverbLevelValue.setText({reverbLevelSlider.position(), "%"}); + + program->updateAudioEffects(); } diff --git a/higan/target-tomoko/settings/settings.hpp b/higan/target-tomoko/settings/settings.hpp index cb6a64f9..ef690936 100644 --- a/higan/target-tomoko/settings/settings.hpp +++ b/higan/target-tomoko/settings/settings.hpp @@ -2,39 +2,66 @@ struct VideoSettings : TabFrameItem { VideoSettings(TabFrame*); VerticalLayout layout{this}; + Label colorAdjustmentLabel{&layout, Size{~0, 0}}; + HorizontalLayout saturationLayout{&layout, Size{~0, 0}}; + Label saturationLabel{&saturationLayout, Size{80, 0}}; + Label saturationValue{&saturationLayout, Size{50, 0}}; + HorizontalSlider saturationSlider{&saturationLayout, Size{~0, 0}}; + HorizontalLayout gammaLayout{&layout, Size{~0, 0}}; + Label gammaLabel{&gammaLayout, Size{80, 0}}; + Label gammaValue{&gammaLayout, Size{50, 0}}; + HorizontalSlider gammaSlider{&gammaLayout, Size{~0, 0}}; + HorizontalLayout luminanceLayout{&layout, Size{~0, 0}}; + Label luminanceLabel{&luminanceLayout, Size{80, 0}}; + Label luminanceValue{&luminanceLayout, Size{50, 0}}; + HorizontalSlider luminanceSlider{&luminanceLayout, Size{~0, 0}}; Label overscanMaskLabel{&layout, Size{~0, 0}}; HorizontalLayout horizontalMaskLayout{&layout, Size{~0, 0}}; Label horizontalMaskLabel{&horizontalMaskLayout, Size{80, 0}}; - Label horizontalMaskValue{&horizontalMaskLayout, Size{80, 0}}; + Label horizontalMaskValue{&horizontalMaskLayout, Size{50, 0}}; HorizontalSlider horizontalMaskSlider{&horizontalMaskLayout, Size{~0, 0}}; HorizontalLayout verticalMaskLayout{&layout, Size{~0, 0}}; Label verticalMaskLabel{&verticalMaskLayout, Size{80, 0}}; - Label verticalMaskValue{&verticalMaskLayout, Size{80, 0}}; + Label verticalMaskValue{&verticalMaskLayout, Size{50, 0}}; HorizontalSlider verticalMaskSlider{&verticalMaskLayout, Size{~0, 0}}; - auto update() -> void; + auto updateColor() -> void; + auto updateOverscan() -> void; }; struct AudioSettings : TabFrameItem { AudioSettings(TabFrame*); VerticalLayout layout{this}; + Label driverLabel{&layout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}}; - Label frequencyLabel{&controlLayout, Size{0, 0}}; - ComboButton frequencyCombo{&controlLayout, Size{~0, 0}}; Label latencyLabel{&controlLayout, Size{0, 0}}; ComboButton latencyCombo{&controlLayout, Size{~0, 0}}; + Label frequencyLabel{&controlLayout, Size{0, 0}}; + ComboButton frequencyCombo{&controlLayout, Size{~0, 0}}; Label resamplerLabel{&controlLayout, Size{0, 0}}; ComboButton resamplerCombo{&controlLayout, Size{~0, 0}}; + CheckLabel exclusiveMode{&layout, Size{~0, 0}}; + Label effectsLabel{&layout, Size{~0, 0}}; HorizontalLayout volumeLayout{&layout, Size{~0, 0}}; Label volumeLabel{&volumeLayout, Size{80, 0}}; - Label volumeValue{&volumeLayout, Size{80, 0}}; + Label volumeValue{&volumeLayout, Size{50, 0}}; HorizontalSlider volumeSlider{&volumeLayout, Size{~0, 0}}; - CheckLabel exclusiveMode{&layout, Size{~0, 0}}; + HorizontalLayout balanceLayout{&layout, Size{~0, 0}}; + Label balanceLabel{&balanceLayout, Size{80, 0}}; + Label balanceValue{&balanceLayout, Size{50, 0}}; + HorizontalSlider balanceSlider{&balanceLayout, Size{~0, 0}}; + HorizontalLayout reverbDelayLayout{&layout, Size{~0, 0}}; + Label reverbDelayLabel{&reverbDelayLayout, Size{80, 0}}; + Label reverbDelayValue{&reverbDelayLayout, Size{50, 0}}; + HorizontalSlider reverbDelaySlider{&reverbDelayLayout, Size{~0, 0}}; + HorizontalLayout reverbLevelLayout{&layout, Size{~0, 0}}; + Label reverbLevelLabel{&reverbLevelLayout, Size{80, 0}}; + Label reverbLevelValue{&reverbLevelLayout, Size{50, 0}}; + HorizontalSlider reverbLevelSlider{&reverbLevelLayout, Size{~0, 0}}; - auto update() -> void; - auto updateMode() -> void; - auto updateVolume() -> void; + auto updateDriver() -> void; + auto updateEffects() -> void; }; struct InputSettings : TabFrameItem { diff --git a/higan/target-tomoko/settings/video.cpp b/higan/target-tomoko/settings/video.cpp index fc3ce544..07cbaa8a 100644 --- a/higan/target-tomoko/settings/video.cpp +++ b/higan/target-tomoko/settings/video.cpp @@ -4,16 +4,40 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) { layout.setMargin(5); + colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment"); + saturationLabel.setText("Saturation:"); + saturationValue.setAlignment(0.5); + saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] { updateColor(); }); + gammaLabel.setText("Gamma:"); + gammaValue.setAlignment(0.5); + gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] { updateColor(); }); + luminanceLabel.setText("Luminance:"); + luminanceValue.setAlignment(0.5); + luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] { updateColor(); }); + overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask"); horizontalMaskLabel.setText("Horizontal:"); - horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { update(); }); + horizontalMaskValue.setAlignment(0.5); + horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { updateOverscan(); }); verticalMaskLabel.setText("Vertical:"); - verticalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { update(); }); + verticalMaskValue.setAlignment(0.5); + verticalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { updateOverscan(); }); - update(); + updateColor(); + updateOverscan(); } -auto VideoSettings::update() -> void { +auto VideoSettings::updateColor() -> void { + settings["Video/Saturation"].setValue(saturationSlider.position()); + settings["Video/Gamma"].setValue(100 + gammaSlider.position()); + settings["Video/Luminance"].setValue(luminanceSlider.position()); + saturationValue.setText({saturationSlider.position(), "%"}); + gammaValue.setText({100 + gammaSlider.position(), "%"}); + luminanceValue.setText({luminanceSlider.position(), "%"}); + program->updateVideoPalette(); +} + +auto VideoSettings::updateOverscan() -> void { settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position()); settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position()); horizontalMaskValue.setText({horizontalMaskSlider.position(), "px"}); diff --git a/higan/video/video.cpp b/higan/video/video.cpp index 4775593d..1d616e1a 100644 --- a/higan/video/video.cpp +++ b/higan/video/video.cpp @@ -8,6 +8,83 @@ Video::~Video() { reset(); } +auto Video::reset() -> void { + interface = nullptr; + delete output; + output = nullptr; + delete palette; + palette = nullptr; + width = 0; + height = 0; + effects.colorBleed = false; + effects.interframeBlending = false; +} + +auto Video::setInterface(Interface* interface) -> void { + this->interface = interface; +} + +auto Video::setPalette() -> void { + if(!interface) return; + + delete palette; + colors = interface->videoColors(); + palette = new uint32[colors]; + for(auto index : range(colors)) { + uint64 color = interface->videoColor(index); + uint16 b = color.bits( 0,15); + uint16 g = color.bits(16,31); + uint16 r = color.bits(32,47); + uint16 a = 0xffff; + + 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); + } + + //convert color from 16-bits/channel to 8-bits/channel; force alpha to 1.0 + palette[index] = a.byte(1) << 24 | r.byte(1) << 16 | g.byte(1) << 8 | b.byte(1) << 0; + } +} + +auto Video::setSaturation(double saturation) -> void { + this->saturation = saturation; +} + +auto Video::setGamma(double gamma) -> void { + this->gamma = gamma; +} + +auto Video::setLuminance(double luminance) -> void { + this->luminance = luminance; +} + +auto Video::setEffect(Effect effect, const any& value) -> void { + if(effect == Effect::ColorBleed && value.is()) { + effects.colorBleed = value.get(); + } + + if(effect == Effect::InterframeBlending && value.is()) { + effects.interframeBlending = value.get(); + } +} + auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void { if(this->width != width || this->height != height) { delete output; @@ -49,41 +126,4 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void interface->videoRefresh(output, width * sizeof(uint32), width, height); } -auto Video::reset() -> void { - interface = nullptr; - delete output; - output = nullptr; - delete palette; - palette = nullptr; - width = 0; - height = 0; - effects.colorBleed = false; - effects.interframeBlending = false; -} - -auto Video::setInterface(Interface* interface) -> void { - this->interface = interface; -} - -auto Video::setPalette(uint32 size, function callback) -> void { - delete palette; - palette = new uint32[size]; - for(auto index : range(size)) { - //convert color from 16-bits/channel to 8-bits/channel; force alpha to 1.0 - uint64 color = callback(index); - palette[index] = 255u << 24 | color.bits(40,47) << 16 | color.bits(24,31) << 8 | color.bits(8,15) << 0; - } - colors = size; -} - -auto Video::setEffect(Effect effect, const any& value) -> void { - if(effect == Effect::ColorBleed && value.is()) { - effects.colorBleed = value.get(); - } - - if(effect == Effect::InterframeBlending && value.is()) { - effects.interframeBlending = value.get(); - } -} - } diff --git a/higan/video/video.hpp b/higan/video/video.hpp index d630734d..acc2c3da 100644 --- a/higan/video/video.hpp +++ b/higan/video/video.hpp @@ -11,12 +11,19 @@ struct Video { }; ~Video(); - auto refresh(uint32* input, uint pitch, uint width, uint height) -> void; + auto reset() -> void; auto setInterface(Interface* interface) -> void; - auto setPalette(uint32 size, function callback) -> void; + + auto setPalette() -> void; + auto setSaturation(double saturation) -> void; + auto setGamma(double gamma) -> void; + auto setLuminance(double luminance) -> void; + auto setEffect(Effect effect, const any& value) -> void; + auto refresh(uint32* input, uint pitch, uint width, uint height) -> void; + private: Emulator::Interface* interface = nullptr; uint32* output = nullptr; @@ -25,6 +32,10 @@ private: uint height = 0; uint colors = 0; + double saturation = 1.0; + double gamma = 1.0; + double luminance = 1.0; + struct Effects { bool colorBleed = false; bool interframeBlending = false; diff --git a/higan/ws/apu/apu.cpp b/higan/ws/apu/apu.cpp index 30bba573..e148a878 100644 --- a/higan/ws/apu/apu.cpp +++ b/higan/ws/apu/apu.cpp @@ -56,7 +56,7 @@ auto APU::dacRun() -> void { right = 0; } - interface->audioSample(left, right); + stream->sample(left, right); } auto APU::step(uint clocks) -> void { @@ -66,6 +66,7 @@ auto APU::step(uint clocks) -> void { auto APU::power() -> void { create(APU::Enter, 3'072'000); + stream = Emulator::audio.createStream(3'072'000.0); bus.map(this, 0x004a, 0x004c); bus.map(this, 0x004e, 0x0050); diff --git a/higan/ws/apu/apu.hpp b/higan/ws/apu/apu.hpp index 02c286ba..940c96f9 100644 --- a/higan/ws/apu/apu.hpp +++ b/higan/ws/apu/apu.hpp @@ -1,4 +1,6 @@ struct APU : Thread, IO { + shared_pointer stream; + static auto Enter() -> void; auto main() -> void; auto sample(uint channel, uint5 index) -> uint4; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 1d1ae3b8..5b8ca805 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -72,6 +72,31 @@ auto Interface::videoFrequency() -> double { return 3072000.0 / (159.0 * 256.0); //~75.47hz } +auto Interface::videoColors() -> uint32 { + return 1 << 12; +} + +auto Interface::videoColor(uint32 color) -> uint64 { + uint b = color.bits(0, 3); + uint g = color.bits(4, 7); + uint r = color.bits(8,11); + + uint64_t R = image::normalize(r, 4, 16); + uint64_t G = image::normalize(g, 4, 16); + uint64_t B = image::normalize(b, 4, 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(480, R), 9, 16); + G = image::normalize(min(480, G), 9, 16); + B = image::normalize(min(480, B), 9, 16); + } + + return R << 32 | G << 16 | B << 0; +} + auto Interface::audioFrequency() -> double { return 3072000.0; } diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 2901c2c3..c9b85f0d 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -31,6 +31,8 @@ struct Interface : Emulator::Interface { auto manifest() -> string override; auto title() -> string override; auto videoFrequency() -> double override; + auto videoColors() -> uint32; + auto videoColor(uint32 color) -> uint64; auto audioFrequency() -> double override; auto loaded() -> bool override; diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 37d20ac1..38984658 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -75,12 +75,15 @@ auto PPU::scanline() -> void { auto PPU::frame() -> void { s.field = !s.field; s.vclk = 0; - Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 224); + scheduler.exit(Scheduler::Event::Frame); if(l.orientation != system.orientation()) { l.orientation = system.orientation(); memory::fill(output, 224 * 224 * sizeof(uint32)); } - scheduler.exit(Scheduler::Event::Frame); +} + +auto PPU::refresh() -> void { + Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 224); } auto PPU::step(uint clocks) -> void { diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 550137a9..4a2f94f1 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -3,6 +3,7 @@ struct PPU : Thread, IO { auto main() -> void; auto scanline() -> void; auto frame() -> void; + auto refresh() -> void; auto step(uint clocks) -> void; auto power() -> void; diff --git a/higan/ws/scheduler/scheduler.cpp b/higan/ws/scheduler/scheduler.cpp index 0a1d9d5f..a8ff677b 100644 --- a/higan/ws/scheduler/scheduler.cpp +++ b/higan/ws/scheduler/scheduler.cpp @@ -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; } diff --git a/higan/ws/system/system.cpp b/higan/ws/system/system.cpp index 63eaad1a..bd652887 100644 --- a/higan/ws/system/system.cpp +++ b/higan/ws/system/system.cpp @@ -41,7 +41,6 @@ auto System::load(Model model) -> void { cartridge.load(); serializeInit(); - configureVideo(); _orientation = cartridge.information.orientation; _loaded = true; } @@ -57,6 +56,14 @@ auto System::unload() -> void { } auto System::power() -> void { + Emulator::video.reset(); + Emulator::video.setInterface(interface); + configureVideoPalette(); + configureVideoEffects(); + + Emulator::audio.reset(); + Emulator::audio.setInterface(interface); + bus.power(); iram.power(); eeprom.power(); diff --git a/higan/ws/system/system.hpp b/higan/ws/system/system.hpp index e8eafcdf..c27ac3de 100644 --- a/higan/ws/system/system.hpp +++ b/higan/ws/system/system.hpp @@ -21,7 +21,6 @@ struct System : IO { auto portWrite(uint16 addr, uint8 data) -> void override; //video.cpp - auto configureVideo() -> void; auto configureVideoPalette() -> void; auto configureVideoEffects() -> void; diff --git a/higan/ws/system/video.cpp b/higan/ws/system/video.cpp index 6b65376d..f77e7ae9 100644 --- a/higan/ws/system/video.cpp +++ b/higan/ws/system/video.cpp @@ -1,31 +1,5 @@ -auto System::configureVideo() -> void { - Emulator::video.reset(); - Emulator::video.setInterface(interface); - configureVideoPalette(); - configureVideoEffects(); -} - auto System::configureVideoPalette() -> void { - Emulator::video.setPalette(1 << 12, [&](uint32 color) -> uint64 { - uint b = color.bits(0, 3); - uint g = color.bits(4, 7); - uint r = color.bits(8,11); - - uint64_t R = image::normalize(r, 4, 16); - uint64_t G = image::normalize(g, 4, 16); - uint64_t B = image::normalize(b, 4, 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(480, R), 9, 16); - G = image::normalize(min(480, G), 9, 16); - B = image::normalize(min(480, B), 9, 16); - } - - return R << 32 | G << 16 | B << 0; - }); + Emulator::video.setPalette(); } auto System::configureVideoEffects() -> void { diff --git a/higan/ws/ws.hpp b/higan/ws/ws.hpp index b9bf02bf..6c089fc2 100644 --- a/higan/ws/ws.hpp +++ b/higan/ws/ws.hpp @@ -30,7 +30,7 @@ namespace WonderSwan { 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; }