From f1ebef2ea8cb27de02726f25deeed6ca1561f087 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 23 Jan 2016 18:29:34 +1100 Subject: [PATCH] Update to v097r01 release. byuu says: A minor WIP to get us started. Changelog: - System::Video merged to PPU::Video - System::Audio merged to DSP::Audio - System::Configuration merged to Interface::Settings - created emulator/emulator.cpp and accompanying object file for shared code between all cores Currently, emulator.cpp just holds a videoColor() function that takes R16G16B16, performs gamma/saturation/luma adjust, and outputs (currently) A8R8G8B8. It's basically an internal function call for cores to use when generating palette entries. This code used to exist inside ui-tomoko/program/interface.cpp, but we have to move it internal for software display emulation. But in the future, we could add other useful cross-core functionality here. --- higan/emulator/GNUmakefile | 3 + higan/emulator/emulator.cpp | 34 ++++++ higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 3 + higan/processor/processor.hpp | 5 +- higan/sfc/coprocessor/icd2/icd2.cpp | 6 +- .../coprocessor/icd2/interface/interface.cpp | 2 +- higan/sfc/coprocessor/msu1/msu1.cpp | 6 +- higan/sfc/dsp/audio.cpp | 44 +++++++ higan/sfc/dsp/audio.hpp | 18 +++ higan/sfc/dsp/dsp.cpp | 5 +- higan/sfc/dsp/dsp.hpp | 107 +++++++++--------- higan/sfc/interface/interface.hpp | 5 + higan/sfc/ppu/ppu.cpp | 4 +- higan/sfc/ppu/ppu.hpp | 3 +- higan/sfc/{system => ppu}/video.cpp | 101 ++++++++--------- higan/sfc/{system => ppu}/video.hpp | 14 +-- higan/sfc/system/audio.cpp | 62 ---------- higan/sfc/system/audio.hpp | 19 ---- higan/sfc/system/device.cpp | 6 +- higan/sfc/system/random.cpp | 14 +++ higan/sfc/system/serialization.cpp | 10 +- higan/sfc/system/system.cpp | 56 ++++----- higan/sfc/system/system.hpp | 52 +++------ higan/target-tomoko/GNUmakefile | 1 + icarus/core/famicom.cpp | 4 + icarus/core/game-boy-advance.cpp | 4 + icarus/core/game-boy-color.cpp | 4 + icarus/core/game-boy.cpp | 4 + icarus/core/super-famicom.cpp | 3 + nall/run.hpp | 9 +- nall/string/platform.hpp | 16 +-- 32 files changed, 320 insertions(+), 306 deletions(-) create mode 100644 higan/emulator/GNUmakefile create mode 100644 higan/emulator/emulator.cpp create mode 100644 higan/sfc/dsp/audio.cpp create mode 100644 higan/sfc/dsp/audio.hpp rename higan/sfc/{system => ppu}/video.cpp (69%) rename higan/sfc/{system => ppu}/video.hpp (70%) delete mode 100644 higan/sfc/system/audio.cpp delete mode 100644 higan/sfc/system/audio.hpp create mode 100644 higan/sfc/system/random.cpp diff --git a/higan/emulator/GNUmakefile b/higan/emulator/GNUmakefile new file mode 100644 index 00000000..8b8147bd --- /dev/null +++ b/higan/emulator/GNUmakefile @@ -0,0 +1,3 @@ +objects += emulator + +obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator/) diff --git a/higan/emulator/emulator.cpp b/higan/emulator/emulator.cpp new file mode 100644 index 00000000..ce4b4d3f --- /dev/null +++ b/higan/emulator/emulator.cpp @@ -0,0 +1,34 @@ +#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 : 32767 * pow(r * reciprocal, gamma); + g = g > 32767 ? g : 32767 * pow(g * reciprocal, gamma); + b = b > 32767 ? b : 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 3664b3d9..60883c6f 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "097"; + static const string Version = "097.01"; 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 decd72c3..f532a721 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -108,6 +108,9 @@ struct Interface { virtual auto cap(const string& name) -> bool { return false; } virtual auto get(const string& name) -> any { return {}; } virtual auto set(const string& name, const any& value) -> bool { return false; } + + //shared functions + auto videoColor(uint16 r, uint16 g, uint16 b) -> uint32; }; } diff --git a/higan/processor/processor.hpp b/higan/processor/processor.hpp index 1d6f7209..b549982c 100644 --- a/higan/processor/processor.hpp +++ b/higan/processor/processor.hpp @@ -1,6 +1,3 @@ -#ifndef PROCESSOR_HPP -#define PROCESSOR_HPP +#pragma once #include - -#endif diff --git a/higan/sfc/coprocessor/icd2/icd2.cpp b/higan/sfc/coprocessor/icd2/icd2.cpp index c816a7ed..950d65e0 100644 --- a/higan/sfc/coprocessor/icd2/icd2.cpp +++ b/higan/sfc/coprocessor/icd2/icd2.cpp @@ -21,7 +21,7 @@ auto ICD2::enter() -> void { step(GameBoy::system.clocks_executed); GameBoy::system.clocks_executed = 0; } else { //DMG halted - audio.coprocessor_sample(0x0000, 0x0000); + dsp.audio.coprocessorSample(0, 0); step(1); } synchronizeCPU(); @@ -44,8 +44,8 @@ auto ICD2::unload() -> void { } auto ICD2::power() -> void { - audio.coprocessor_enable(true); - audio.coprocessor_frequency(2 * 1024 * 1024); + dsp.audio.coprocessorEnable(true); + dsp.audio.coprocessorFrequency(2 * 1024 * 1024); } auto ICD2::reset() -> void { diff --git a/higan/sfc/coprocessor/icd2/interface/interface.cpp b/higan/sfc/coprocessor/icd2/interface/interface.cpp index 47796f20..985b96bb 100644 --- a/higan/sfc/coprocessor/icd2/interface/interface.cpp +++ b/higan/sfc/coprocessor/icd2/interface/interface.cpp @@ -93,7 +93,7 @@ auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height) } auto ICD2::audioSample(int16 left, int16 right) -> void { - audio.coprocessor_sample(left, right); + dsp.audio.coprocessorSample(left, right); } auto ICD2::inputPoll(uint port, uint device, uint id) -> int16 { diff --git a/higan/sfc/coprocessor/msu1/msu1.cpp b/higan/sfc/coprocessor/msu1/msu1.cpp index 96864205..2e634db2 100644 --- a/higan/sfc/coprocessor/msu1/msu1.cpp +++ b/higan/sfc/coprocessor/msu1/msu1.cpp @@ -41,7 +41,7 @@ auto MSU1::enter() -> void { right = sclamp<16>(rchannel); if(dsp.mute()) left = 0, right = 0; - audio.coprocessor_sample(left, right); + dsp.audio.coprocessorSample(left, right); step(1); synchronizeCPU(); } @@ -59,8 +59,8 @@ auto MSU1::unload() -> void { } auto MSU1::power() -> void { - audio.coprocessor_enable(true); - audio.coprocessor_frequency(44100.0); + dsp.audio.coprocessorEnable(true); + dsp.audio.coprocessorFrequency(44100.0); } auto MSU1::reset() -> void { diff --git a/higan/sfc/dsp/audio.cpp b/higan/sfc/dsp/audio.cpp new file mode 100644 index 00000000..5ab2c345 --- /dev/null +++ b/higan/sfc/dsp/audio.cpp @@ -0,0 +1,44 @@ +auto DSP::Audio::coprocessorEnable(bool enable) -> void { + mixer.clear(); + mixerEnable = enable; + dsp.read = dsp.write = 0; + mix.read = mix.write = 0; +} + +auto DSP::Audio::coprocessorFrequency(double frequency) -> void { + mixer.setFrequency(frequency); + mixer.setResampler(nall::DSP::ResampleEngine::Sinc); + mixer.setResamplerFrequency(system.apuFrequency() / 768.0); +} + +auto DSP::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 DSP::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 DSP::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 new file mode 100644 index 00000000..56e172f6 --- /dev/null +++ b/higan/sfc/dsp/audio.hpp @@ -0,0 +1,18 @@ +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; +}; diff --git a/higan/sfc/dsp/dsp.cpp b/higan/sfc/dsp/dsp.cpp index 305dc408..035d78ad 100644 --- a/higan/sfc/dsp/dsp.cpp +++ b/higan/sfc/dsp/dsp.cpp @@ -7,7 +7,6 @@ DSP dsp; #define REG(n) state.regs[n] #define VREG(n) state.regs[v.vidx + n] -#include "serialization.cpp" #include "gaussian.cpp" #include "counter.cpp" #include "envelope.cpp" @@ -15,6 +14,8 @@ DSP dsp; #include "misc.cpp" #include "voice.cpp" #include "echo.cpp" +#include "serialization.cpp" +#include "audio.cpp" DSP::DSP() { static_assert(sizeof(signed) >= 32 / 8, "signed >= 32-bits"); @@ -283,6 +284,8 @@ auto DSP::power() -> void { voice[n].hiddenEnvelope = 0; voice[n]._envxOut = 0; } + + audio.coprocessorEnable(false); } auto DSP::reset() -> void { diff --git a/higan/sfc/dsp/dsp.hpp b/higan/sfc/dsp/dsp.hpp index a1e52126..e349c69a 100644 --- a/higan/sfc/dsp/dsp.hpp +++ b/higan/sfc/dsp/dsp.hpp @@ -5,7 +5,7 @@ struct DSP : Thread { DSP(); - alwaysinline auto step(unsigned clocks) -> void; + alwaysinline auto step(uint clocks) -> void; alwaysinline auto synchronizeSMP() -> void; auto mute() const -> bool; @@ -18,10 +18,13 @@ struct DSP : Thread { auto serialize(serializer&) -> void; + #include "audio.hpp" + Audio audio; + privileged: #include "modulo-array.hpp" - enum GlobalRegister : unsigned { + enum GlobalRegister : uint { MVOLL = 0x0c, MVOLR = 0x1c, EVOLL = 0x2c, EVOLR = 0x3c, KON = 0x4c, KOFF = 0x5c, @@ -32,7 +35,7 @@ privileged: EDL = 0x7d, FIR = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f }; - enum VoiceRegister : unsigned { + enum VoiceRegister : uint { VOLL = 0x00, VOLR = 0x01, PITCHL = 0x02, PITCHH = 0x03, SRCN = 0x04, ADSR0 = 0x05, @@ -40,14 +43,14 @@ privileged: ENVX = 0x08, OUTX = 0x09, }; - enum EnvelopeMode : unsigned { + enum EnvelopeMode : uint { EnvelopeRelease, EnvelopeAttack, EnvelopeDecay, EnvelopeSustain, }; - enum : unsigned { + enum : uint { EchoHistorySize = 8, BrrBufferSize = 12, BrrBlockSize = 9, @@ -57,77 +60,77 @@ privileged: struct State { uint8 regs[128]; - ModuloArray echoHistory[2]; //echo history keeps most recent 8 samples - signed echoHistoryOffset; + ModuloArray echoHistory[2]; //echo history keeps most recent 8 samples + int echoHistoryOffset; bool everyOtherSample; //toggles every sample - signed kon; //KON value when last checked - signed noise; - signed counter; - signed echoOffset; //offset from ESA in echo buffer - signed echoLength; //number of bytes that echo_offset will stop at + int kon; //KON value when last checked + int noise; + int counter; + int echoOffset; //offset from ESA in echo buffer + int echoLength; //number of bytes that echo_offset will stop at //hidden registers also written to when main register is written to - signed konBuffer; - signed endxBuffer; - signed envxBuffer; - signed outxBuffer; + int konBuffer; + int endxBuffer; + int envxBuffer; + int outxBuffer; //temporary state between clocks (prefixed with _) //read once per sample - signed _pmon; - signed _non; - signed _eon; - signed _dir; - signed _koff; + int _pmon; + int _non; + int _eon; + int _dir; + int _koff; //read a few clocks ahead before used - signed _brrNextAddress; - signed _adsr0; - signed _brrHeader; - signed _brrByte; - signed _srcn; - signed _esa; - signed _echoDisabled; + int _brrNextAddress; + int _adsr0; + int _brrHeader; + int _brrByte; + int _srcn; + int _esa; + int _echoDisabled; //internal state that is recalculated every sample - signed _dirAddress; - signed _pitch; - signed _output; - signed _looped; - signed _echoPointer; + int _dirAddress; + int _pitch; + int _output; + int _looped; + int _echoPointer; //left/right sums - signed _mainOut[2]; - signed _echoOut[2]; - signed _echoIn [2]; + int _mainOut[2]; + int _echoOut[2]; + int _echoIn [2]; } state; struct Voice { - ModuloArray buffer; //decoded samples - signed bufferOffset; //place in buffer where next samples will be decoded - signed gaussianOffset; //relative fractional position in sample (0x1000 = 1.0) - signed brrAddress; //address of current BRR block - signed brrOffset; //current decoding offset in BRR block - signed vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc - signed vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc - signed konDelay; //KON delay/current setup phase - signed envelopeMode; - signed envelope; //current envelope level - signed hiddenEnvelope; //used by GAIN mode 7, very obscure quirk - signed _envxOut; + ModuloArray buffer; //decoded samples + int bufferOffset; //place in buffer where next samples will be decoded + int gaussianOffset; //relative fractional position in sample (0x1000 = 1.0) + int brrAddress; //address of current BRR block + int brrOffset; //current decoding offset in BRR block + int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc + int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc + int konDelay; //KON delay/current setup phase + int envelopeMode; + int envelope; //current envelope level + int hiddenEnvelope; //used by GAIN mode 7, very obscure quirk + int _envxOut; } voice[8]; //gaussian static const int16 GaussianTable[512]; - auto gaussianInterpolate(const Voice& v) -> signed; + auto gaussianInterpolate(const Voice& v) -> int; //counter static const uint16 CounterRate[32]; static const uint16 CounterOffset[32]; auto counterTick() -> void; - auto counterPoll(unsigned rate) -> bool; + auto counterPoll(uint rate) -> bool; //envelope auto envelopeRun(Voice& v) -> void; @@ -157,8 +160,8 @@ privileged: auto voice9 (Voice& v) -> void; //echo - auto calculateFIR(signed i, bool channel) -> signed; - auto echoOutput(bool channel) -> signed; + auto calculateFIR(int i, bool channel) -> int; + auto echoOutput(bool channel) -> int; auto echoRead(bool channel) -> void; auto echoWrite(bool channel) -> void; auto echo22() -> void; diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index c804101d..f7a46b63 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -128,6 +128,11 @@ struct Settings { bool blurEmulation = true; bool colorEmulation = true; bool scanlineEmulation = true; + + uint controllerPort1 = 0; + uint controllerPort2 = 0; + uint expansionPort = 0; + bool random = true; }; extern Interface* interface; diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index f3ff6af8..6826591a 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -10,6 +10,7 @@ PPU ppu; #include "sprite/sprite.cpp" #include "window/window.cpp" #include "serialization.cpp" +#include "video.cpp" PPU::PPU() : bg1(*this, Background::ID::BG1), @@ -119,6 +120,7 @@ auto PPU::reset() -> void { sprite.reset(); window.reset(); screen.reset(); + video.reset(); frame(); } @@ -141,7 +143,7 @@ auto PPU::scanline() -> void { screen.scanline(); if(vcounter() == 241) { - scheduler.exit(Scheduler::ExitReason::FrameEvent); + video.refresh(); } } diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index ff1a3d3f..55ff494c 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -38,6 +38,7 @@ privileged: #include "screen/screen.hpp" #include "sprite/sprite.hpp" #include "window/window.hpp" + #include "video.hpp" Background bg1; Background bg2; @@ -46,6 +47,7 @@ privileged: Sprite sprite; Window window; Screen screen; + Video video; static auto Enter() -> void; alwaysinline auto add_clocks(uint) -> void; @@ -57,7 +59,6 @@ privileged: friend class PPU::Sprite; friend class PPU::Window; friend class PPU::Screen; - friend class Video; struct Debugger { hook vram_read; diff --git a/higan/sfc/system/video.cpp b/higan/sfc/ppu/video.cpp similarity index 69% rename from higan/sfc/system/video.cpp rename to higan/sfc/ppu/video.cpp index e1d568d6..efaedcd0 100644 --- a/higan/sfc/system/video.cpp +++ b/higan/sfc/ppu/video.cpp @@ -1,22 +1,23 @@ -Video video; - -Video::Video() { +PPU::Video::Video() { output = new uint32[512 * 512](); + output += 16 * 512; //overscan padding + + paletteLiteral = new uint32[1 << 19]; paletteStandard = new uint32[1 << 19]; paletteEmulation = new uint32[1 << 19]; - - output += 16 * 512; //overscan padding } -Video::~Video() { +PPU::Video::~Video() { output -= 16 * 512; delete[] output; + + delete[] paletteLiteral; delete[] paletteStandard; delete[] paletteEmulation; } -auto Video::reset() -> void { - memory::fill(output, 512 * 480 * sizeof(uint32)); //padding area already cleared +auto PPU::Video::reset() -> void { + memory::fill(output, 512 * 480 * sizeof(uint32)); for(auto color : range(1 << 19)) { uint l = (uint4)(color >> 15); @@ -26,28 +27,31 @@ auto Video::reset() -> void { double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5); - { uint R = L * image::normalize(r, 5, 8); - uint G = L * image::normalize(g, 5, 8); - uint B = L * image::normalize(b, 5, 8); - paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); + { paletteLiteral[color] = color; } - { uint R = L * gammaRamp[r]; - uint G = L * gammaRamp[g]; - uint B = L * gammaRamp[b]; - paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); + { uint R = L * image::normalize(r, 5, 16); + uint G = L * image::normalize(g, 5, 16); + uint B = L * image::normalize(b, 5, 16); + paletteStandard[color] = interface->videoColor(R, G, B); } - } - for(auto color : range(1 << 19)) { - uint l = (uint4)(color >> 15); - uint b = (uint5)(color >> 10); - uint g = (uint5)(color >> 5); - uint r = (uint5)(color >> 0); + { 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, + }; + + uint R = L * gammaRamp[r] * 0x0101; + uint G = L * gammaRamp[g] * 0x0101; + uint B = L * gammaRamp[b] * 0x0101; + paletteEmulation[color] = interface->videoColor(R, G, B); + } } } -auto Video::refresh() -> void { +auto PPU::Video::refresh() -> void { auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard; if(settings.scanlineEmulation) { @@ -109,13 +113,29 @@ auto Video::refresh() -> void { } drawCursors(); - interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480); + scheduler.exit(Scheduler::ExitReason::FrameEvent); } -//internal +auto PPU::Video::drawCursor(uint32 color, int x, int y) -> void { + static const uint8 cursor[15 * 15] = { + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 1,2,2,1,1,2,2,2,2,2,1,1,2,2,1, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, + }; -auto Video::drawCursor(uint32 color, int x, int y) -> void { auto data = (uint32*)output; if(ppu.interlace() && ppu.field()) data += 512; @@ -138,8 +158,8 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void { } } -auto Video::drawCursors() -> void { - switch(configuration.controllerPort2) { +auto PPU::Video::drawCursors() -> void { + switch((Device::ID)settings.controllerPort2) { case Device::ID::SuperScope: if(dynamic_cast(device.controllerPort2)) { auto& controller = (SuperScope&)*device.controllerPort2; @@ -157,28 +177,3 @@ auto Video::drawCursors() -> void { break; } } - -const uint8 Video::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, -}; - -const uint8 Video::cursor[15 * 15] = { - 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, - 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, - 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, - 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, - 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, - 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, - 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, - 1,2,2,1,1,2,2,2,2,2,1,1,2,2,1, - 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, - 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, - 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, - 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, - 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, - 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, - 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, -}; diff --git a/higan/sfc/system/video.hpp b/higan/sfc/ppu/video.hpp similarity index 70% rename from higan/sfc/system/video.hpp rename to higan/sfc/ppu/video.hpp index 840f29f6..ce521f5d 100644 --- a/higan/sfc/system/video.hpp +++ b/higan/sfc/ppu/video.hpp @@ -5,18 +5,12 @@ struct Video { auto reset() -> void; auto refresh() -> void; - uint32* output = nullptr; - uint32* paletteStandard = nullptr; - uint32* paletteEmulation = nullptr; - private: auto drawCursor(uint32 color, int x, int y) -> void; auto drawCursors() -> void; - static const uint8 gammaRamp[32]; - static const uint8 cursor[15 * 15]; - - friend class System; + uint32* output = nullptr; + uint32* paletteLiteral = nullptr; + uint32* paletteStandard = nullptr; + uint32* paletteEmulation = nullptr; }; - -extern Video video; diff --git a/higan/sfc/system/audio.cpp b/higan/sfc/system/audio.cpp deleted file mode 100644 index 556e80fe..00000000 --- a/higan/sfc/system/audio.cpp +++ /dev/null @@ -1,62 +0,0 @@ -Audio audio; - -auto Audio::coprocessor_enable(bool state) -> void { - coprocessor = state; - dspaudio.clear(); - - dsp_rdoffset = cop_rdoffset = 0; - dsp_wroffset = cop_wroffset = 0; - dsp_length = cop_length = 0; -} - -auto Audio::coprocessor_frequency(double input_frequency) -> void { - dspaudio.setFrequency(input_frequency); - dspaudio.setResampler(nall::DSP::ResampleEngine::Sinc); - dspaudio.setResamplerFrequency(system.apuFrequency() / 768.0); -} - -auto Audio::sample(int16 lsample, int16 rsample) -> void { - if(coprocessor == false) return interface->audioSample(lsample, rsample); - - dsp_buffer[dsp_wroffset] = ((uint16)lsample << 0) + ((uint16)rsample << 16); - dsp_wroffset = (dsp_wroffset + 1) & buffer_mask; - dsp_length = (dsp_length + 1) & buffer_mask; - flush(); -} - -auto Audio::coprocessor_sample(int16 lsample, int16 rsample) -> void { - int samples[] = {lsample, rsample}; - dspaudio.sample(samples); - while(dspaudio.pending()) { - dspaudio.read(samples); - - cop_buffer[cop_wroffset] = ((uint16)samples[0] << 0) + ((uint16)samples[1] << 16); - cop_wroffset = (cop_wroffset + 1) & buffer_mask; - cop_length = (cop_length + 1) & buffer_mask; - flush(); - } -} - -auto Audio::flush() -> void { - while(dsp_length > 0 && cop_length > 0) { - uint32 dsp_sample = dsp_buffer[dsp_rdoffset]; - uint32 cop_sample = cop_buffer[cop_rdoffset]; - - dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask; - cop_rdoffset = (cop_rdoffset + 1) & buffer_mask; - - dsp_length--; - cop_length--; - - int dsp_left = (int16)(dsp_sample >> 0); - int dsp_right = (int16)(dsp_sample >> 16); - - int cop_left = (int16)(cop_sample >> 0); - int cop_right = (int16)(cop_sample >> 16); - - interface->audioSample( - sclamp<16>(dsp_left + cop_left ), - sclamp<16>(dsp_right + cop_right) - ); - } -} diff --git a/higan/sfc/system/audio.hpp b/higan/sfc/system/audio.hpp deleted file mode 100644 index ac438b1d..00000000 --- a/higan/sfc/system/audio.hpp +++ /dev/null @@ -1,19 +0,0 @@ -struct Audio { - auto coprocessor_enable(bool state) -> void; - auto coprocessor_frequency(double frequency) -> void; - auto sample(int16 lsample, int16 rsample) -> void; - auto coprocessor_sample(int16 lsample, int16 rsample) -> void; - -private: - auto flush() -> void; - - nall::DSP dspaudio; - bool coprocessor; - enum : uint { buffer_size = 256, buffer_mask = buffer_size - 1 }; - uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size]; - uint dsp_rdoffset, cop_rdoffset; - uint dsp_wroffset, cop_wroffset; - uint dsp_length, cop_length; -}; - -extern Audio audio; diff --git a/higan/sfc/system/device.cpp b/higan/sfc/system/device.cpp index 194725fb..5fdd8868 100644 --- a/higan/sfc/system/device.cpp +++ b/higan/sfc/system/device.cpp @@ -32,12 +32,12 @@ auto Device::connect(uint port, Device::ID id) -> void { } switch(port) { - case 0: configuration.controllerPort1 = id; break; - case 1: configuration.controllerPort2 = id; break; + case 0: settings.controllerPort1 = (uint)id; break; + case 1: settings.controllerPort2 = (uint)id; break; } } if(port == 2) { - configuration.expansionPort = id; + settings.expansionPort = (uint)id; } } diff --git a/higan/sfc/system/random.cpp b/higan/sfc/system/random.cpp new file mode 100644 index 00000000..5e68546c --- /dev/null +++ b/higan/sfc/system/random.cpp @@ -0,0 +1,14 @@ +Random random; + +auto Random::seed(uint seed) -> void { + iter = seed; +} + +auto Random::operator()(uint result) -> uint { + if(!settings.random) return result; + return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320); +} + +auto Random::serialize(serializer& s) -> void { + s.integer(iter); +} diff --git a/higan/sfc/system/serialization.cpp b/higan/sfc/system/serialization.cpp index a2c3bb0d..5b572417 100644 --- a/higan/sfc/system/serialization.cpp +++ b/higan/sfc/system/serialization.cpp @@ -1,5 +1,5 @@ auto System::serialize() -> serializer { - serializer s(serializeSize); + serializer s(_serializeSize); uint signature = 0x31545342, version = Info::SerializerVersion; char hash[64], description[512], profile[16]; @@ -37,13 +37,11 @@ auto System::unserialize(serializer& s) -> bool { return true; } -//======== //internal -//======== auto System::serialize(serializer& s) -> void { - s.integer((uint&)region); - s.integer((uint&)expansionPort); + s.integer((uint&)_region); + s.integer((uint&)_expansionPort); } auto System::serializeAll(serializer& s) -> void { @@ -89,5 +87,5 @@ auto System::serializeInit() -> void { s.array(description); serializeAll(s); - serializeSize = s.size(); + _serializeSize = s.size(); } diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 29a40c9b..60e00d35 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -3,53 +3,46 @@ namespace SuperFamicom { System system; -Configuration configuration; -Random random; -#include "video.cpp" -#include "audio.cpp" #include "device.cpp" +#include "random.cpp" #include "serialization.cpp" #include -System::System() { - region = Region::Autodetect; - expansionPort = Device::ID::eBoot; -} +auto System::region() const -> Region { return _region; } +auto System::expansionPort() const -> Device::ID { return _expansionPort; } +auto System::cpuFrequency() const -> uint { return _cpuFrequency; } +auto System::apuFrequency() const -> uint { return _apuFrequency; } auto System::run() -> void { scheduler.sync = Scheduler::SynchronizeMode::None; - scheduler.enter(); - if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - video.refresh(); - } } auto System::runToSave() -> void { - if(CPU::Threaded == true) { + if(CPU::Threaded) { scheduler.sync = Scheduler::SynchronizeMode::CPU; runThreadToSave(); } - if(SMP::Threaded == true) { + if(SMP::Threaded) { scheduler.thread = smp.thread; runThreadToSave(); } - if(PPU::Threaded == true) { + if(PPU::Threaded) { scheduler.thread = ppu.thread; runThreadToSave(); } - if(DSP::Threaded == true) { + if(DSP::Threaded) { scheduler.thread = dsp.thread; runThreadToSave(); } - for(unsigned i = 0; i < cpu.coprocessors.size(); i++) { - auto& chip = *cpu.coprocessors[i]; + for(uint n = 0; n < cpu.coprocessors.size(); n++) { + auto& chip = *cpu.coprocessors[n]; scheduler.thread = chip.thread; runThreadToSave(); } @@ -59,9 +52,6 @@ auto System::runThreadToSave() -> void { while(true) { scheduler.enter(); if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break; - if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - video.refresh(); - } } } @@ -89,8 +79,9 @@ auto System::init() -> void { bsmemory.init(); - device.connect(0, configuration.controllerPort1); - device.connect(1, configuration.controllerPort2); + device.connect(0, (Device::ID)settings.controllerPort1); + device.connect(1, (Device::ID)settings.controllerPort2); + device.connect(2, (Device::ID)settings.expansionPort); } auto System::term() -> void { @@ -104,15 +95,10 @@ auto System::load() -> void { interface->loadRequest(ID::IPLROM, iplrom, true); } - region = configuration.region; - if(region == Region::Autodetect) { - region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL); - } - expansionPort = configuration.expansionPort; - cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370; - apuFrequency = 24606720; - - audio.coprocessor_enable(false); + _region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL; + _expansionPort = (Device::ID)settings.expansionPort; + _cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370; + _apuFrequency = 24606720; bus.reset(); bus.map(); @@ -240,10 +226,10 @@ auto System::reset() -> void { if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110); if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1); - video.reset(); scheduler.init(); - device.connect(0, configuration.controllerPort1); - device.connect(1, configuration.controllerPort2); + device.connect(0, (Device::ID)settings.controllerPort1); + device.connect(1, (Device::ID)settings.controllerPort2); + device.connect(2, (Device::ID)settings.expansionPort); } } diff --git a/higan/sfc/system/system.hpp b/higan/sfc/system/system.hpp index 582cbba4..c4bb4e39 100644 --- a/higan/sfc/system/system.hpp +++ b/higan/sfc/system/system.hpp @@ -1,13 +1,14 @@ struct Interface; -#include "video.hpp" -#include "audio.hpp" #include "device.hpp" -struct System : property { - enum class Region : uint { NTSC = 0, PAL = 1, Autodetect = 2 }; +struct System { + enum class Region : bool { NTSC = 0, PAL = 1 }; - System(); + auto region() const -> Region; + auto expansionPort() const -> Device::ID; + auto cpuFrequency() const -> uint; + auto apuFrequency() const -> uint; auto run() -> void; auto runToSave() -> void; @@ -19,14 +20,6 @@ struct System : property { auto power() -> void; auto reset() -> void; - //return *active* system information (settings are cached upon power-on) - readonly region; - readonly expansionPort; - - readonly cpuFrequency; - readonly apuFrequency; - readonly serializeSize; - auto serialize() -> serializer; auto unserialize(serializer&) -> bool; @@ -41,9 +34,13 @@ private: auto serializeAll(serializer&) -> void; auto serializeInit() -> void; + Region _region = Region::NTSC; + Device::ID _expansionPort = Device::ID::None; + uint _cpuFrequency = 0; + uint _apuFrequency = 0; + uint _serializeSize = 0; + friend class Cartridge; - friend class Video; - friend class Audio; friend class Device; }; @@ -51,29 +48,10 @@ extern System system; #include -struct Configuration { - Device::ID controllerPort1 = Device::ID::None; - Device::ID controllerPort2 = Device::ID::None; - Device::ID expansionPort = Device::ID::None; - System::Region region = System::Region::Autodetect; - bool random = true; -}; - -extern Configuration configuration; - struct Random { - auto seed(uint seed) -> void { - iter = seed; - } - - auto operator()(uint result) -> uint { - if(configuration.random == false) return result; - return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320); - } - - auto serialize(serializer& s) -> void { - s.integer(iter); - } + auto seed(uint seed) -> void; + auto operator()(uint result) -> uint; + auto serialize(serializer& s) -> void; private: uint iter = 0; diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index d6c9ea77..7ca931df 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -3,6 +3,7 @@ name := higan processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050 include processor/GNUmakefile +include emulator/GNUmakefile include fc/GNUmakefile include sfc/GNUmakefile include gb/GNUmakefile diff --git a/icarus/core/famicom.cpp b/icarus/core/famicom.cpp index 80302d5e..d5292d31 100644 --- a/icarus/core/famicom.cpp +++ b/icarus/core/famicom.cpp @@ -47,7 +47,11 @@ auto Icarus::famicomImport(vector& buffer, string location) -> string { uint chrrom = 0; auto markup = famicomManifest(buffer, location, &prgrom, &chrrom); if(!markup) return failure("failed to parse ROM image"); + if(!directory::create(target)) return failure("library path unwritable"); + if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) { + file::copy({source, name, ".sav"}, {target, "save.ram"}); + } if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "ines.rom"}, buffer.data(), 16); diff --git a/icarus/core/game-boy-advance.cpp b/icarus/core/game-boy-advance.cpp index 833a29ab..e8e840fd 100644 --- a/icarus/core/game-boy-advance.cpp +++ b/icarus/core/game-boy-advance.cpp @@ -39,7 +39,11 @@ auto Icarus::gameBoyAdvanceImport(vector& buffer, string location) -> str auto markup = gameBoyAdvanceManifest(buffer, location); if(!markup) return failure("failed to parse ROM image"); + if(!directory::create(target)) return failure("library path unwritable"); + if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) { + file::copy({source, name, ".sav"}, {target, "save.ram"}); + } if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); diff --git a/icarus/core/game-boy-color.cpp b/icarus/core/game-boy-color.cpp index a6d4e6bb..4fa78587 100644 --- a/icarus/core/game-boy-color.cpp +++ b/icarus/core/game-boy-color.cpp @@ -39,7 +39,11 @@ auto Icarus::gameBoyColorImport(vector& buffer, string location) -> strin auto markup = gameBoyColorManifest(buffer, location); if(!markup) return failure("failed to parse ROM image"); + if(!directory::create(target)) return failure("library path unwritable"); + if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) { + file::copy({source, name, ".sav"}, {target, "save.ram"}); + } if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); diff --git a/icarus/core/game-boy.cpp b/icarus/core/game-boy.cpp index b8a1b18c..7ed040af 100644 --- a/icarus/core/game-boy.cpp +++ b/icarus/core/game-boy.cpp @@ -39,7 +39,11 @@ auto Icarus::gameBoyImport(vector& buffer, string location) -> string { auto markup = gameBoyManifest(buffer, location); if(!markup) return failure("failed to parse ROM image"); + if(!directory::create(target)) return failure("library path unwritable"); + if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) { + file::copy({source, name, ".sav"}, {target, "save.ram"}); + } if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); file::write({target, "program.rom"}, buffer); diff --git a/icarus/core/super-famicom.cpp b/icarus/core/super-famicom.cpp index 367d9020..abda6d09 100644 --- a/icarus/core/super-famicom.cpp +++ b/icarus/core/super-famicom.cpp @@ -66,6 +66,9 @@ auto Icarus::superFamicomImport(vector& buffer, string location) -> strin } if(!directory::create(target)) return failure("library path unwritable"); + if(file::exists({source, name, ".srm"}) && !file::exists({target, "save.ram"})) { + file::copy({source, name, ".srm"}, {target, "save.ram"}); + } if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup); uint offset = (buffer.size() & 0x7fff) == 512 ? 512 : 0; //skip header if present diff --git a/nall/run.hpp b/nall/run.hpp index d6d1ab75..eb4955e8 100644 --- a/nall/run.hpp +++ b/nall/run.hpp @@ -114,12 +114,9 @@ template inline auto execute(const string& name, P&&... p) -> str nullptr, nullptr, &si, &pi )) return ""; - while(true) { - DWORD exitCode; - GetExitCodeProcess(pi.hProcess, &exitCode); - Sleep(1); - if(exitCode != STILL_ACTIVE) break; - } + if(WaitForSingleObject(pi.hProcess, INFINITE)) return ""; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); string result; while(true) { diff --git a/nall/string/platform.hpp b/nall/string/platform.hpp index 6e30f761..89b59898 100644 --- a/nall/string/platform.hpp +++ b/nall/string/platform.hpp @@ -6,7 +6,7 @@ auto activepath() -> string { char path[PATH_MAX] = ""; auto unused = getcwd(path, PATH_MAX); string result = path; - if(result.empty()) result = "."; + if(!result) result = "."; result.transform("\\", "/"); if(result.endsWith("/") == false) result.append("/"); return result; @@ -16,7 +16,7 @@ auto realpath(rstring name) -> string { string result; char path[PATH_MAX] = ""; if(::realpath(name, path)) result = pathname(string{path}.transform("\\", "/")); - if(result.empty()) return activepath(); + if(!result) return activepath(); result.transform("\\", "/"); if(result.endsWith("/") == false) result.append("/"); return result; @@ -48,7 +48,7 @@ auto userpath() -> string { struct passwd* userinfo = getpwuid(getuid()); string result = userinfo->pw_dir; #endif - if(result.empty()) result = "."; + if(!result) result = "."; if(result.endsWith("/") == false) result.append("/"); return result; } @@ -66,12 +66,12 @@ auto configpath() -> string { #else string result = {userpath(), ".config/"}; #endif - if(result.empty()) result = "."; + if(!result) result = "."; if(result.endsWith("/") == false) result.append("/"); return result; } -// /home/username/.local/ +// /home/username/.local/share/ // c:/users/username/appdata/local/ auto localpath() -> string { #if defined(PLATFORM_WINDOWS) @@ -82,9 +82,9 @@ auto localpath() -> string { #elif defined(PLATFORM_MACOSX) string result = {userpath(), "Library/Application Support/"}; #else - string result = {userpath(), ".local/"}; + string result = {userpath(), ".local/share/"}; #endif - if(result.empty()) result = "."; + if(!result) result = "."; if(result.endsWith("/") == false) result.append("/"); return result; } @@ -103,7 +103,7 @@ auto sharedpath() -> string { #else string result = "/usr/share/"; #endif - if(result.empty()) result = "."; + if(!result) result = "."; if(result.endsWith("/") == false) result.append("/"); return result; }