diff --git a/bsnes.exe b/bsnes.exe index b7297ff0..c3dcddb1 100644 Binary files a/bsnes.exe and b/bsnes.exe differ diff --git a/src/Makefile b/src/Makefile index 78a0b790..c3c1fe9d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ include nall/Makefile ui := ui_qt # compiler -c := $(compiler) --std=gnu99 +c := $(compiler) -std=gnu99 cpp := $(subst cc,++,$(compiler)) -std=gnu++0x flags := -O3 -fomit-frame-pointer -I. link := diff --git a/src/nall/function.hpp b/src/nall/function.hpp index 3f0f704e..036e4c9e 100644 --- a/src/nall/function.hpp +++ b/src/nall/function.hpp @@ -85,18 +85,6 @@ namespace nall { data.callback_global = (R (*)(P...))callback; } }; - - //bind functions to ease construction and assignment of function() with more than one argument - - template - function bind(R (C::*callback)(P...), C *object) { - return function(callback, object); - } - - template - function bind(R (C::*callback)(P...) const, C *object) { - return function(callback, object); - } } #endif diff --git a/src/snes/Makefile b/src/snes/Makefile index b8d19e9d..6a3fce44 100644 --- a/src/snes/Makefile +++ b/src/snes/Makefile @@ -1,4 +1,4 @@ -snes_core = sMemory sCPU sSMP sDSP sPPU +snes_core = sMemory sCPU sSMP aDSP bPPU snes_objects := libco snes_objects += snes-system diff --git a/src/snes/chip/superfx/superfx.cpp b/src/snes/chip/superfx/superfx.cpp index 4ffad73c..848c3087 100644 --- a/src/snes/chip/superfx/superfx.cpp +++ b/src/snes/chip/superfx/superfx.cpp @@ -37,8 +37,8 @@ void SuperFX::enter() { void SuperFX::init() { initialize_opcode_table(); - regs.r[14].on_modify = bind(&SuperFX::r14_modify, this); - regs.r[15].on_modify = bind(&SuperFX::r15_modify, this); + regs.r[14].on_modify = { &SuperFX::r14_modify, this }; + regs.r[15].on_modify = { &SuperFX::r15_modify, this }; } void SuperFX::enable() { diff --git a/src/snes/cpu/cpu.cpp b/src/snes/cpu/cpu.cpp index 4323a66b..a6a4074d 100644 --- a/src/snes/cpu/cpu.cpp +++ b/src/snes/cpu/cpu.cpp @@ -12,11 +12,11 @@ void CPU::power() { } void CPU::reset() { - PPUcounter::reset(); + PPUCounter::reset(); } void CPU::serialize(serializer &s) { - PPUcounter::serialize(s); + PPUCounter::serialize(s); s.integer(cpu_version); } diff --git a/src/snes/cpu/cpu.hpp b/src/snes/cpu/cpu.hpp index 654c8c6d..602121e8 100644 --- a/src/snes/cpu/cpu.hpp +++ b/src/snes/cpu/cpu.hpp @@ -2,7 +2,7 @@ #include "cpu-debugger.hpp" #endif -class CPU : public PPUcounter, public MMIO { +class CPU : public PPUCounter, public MMIO { public: virtual void enter() = 0; diff --git a/src/snes/cpu/scpu/dma/dma.cpp b/src/snes/cpu/scpu/dma/dma.cpp index ea6ee9cd..d48ebc25 100644 --- a/src/snes/cpu/scpu/dma/dma.cpp +++ b/src/snes/cpu/scpu/dma/dma.cpp @@ -161,7 +161,6 @@ void sCPU::dma_run() { } status.irq_lock = true; - event.enqueue(2, EventIrqLockRelease); } void sCPU::hdma_update(unsigned i) { @@ -223,7 +222,6 @@ void sCPU::hdma_run() { } status.irq_lock = true; - event.enqueue(2, EventIrqLockRelease); } void sCPU::hdma_init_reset() { @@ -247,7 +245,6 @@ void sCPU::hdma_init() { } status.irq_lock = true; - event.enqueue(2, EventIrqLockRelease); } //============== diff --git a/src/snes/cpu/scpu/scpu.cpp b/src/snes/cpu/scpu/scpu.cpp index 22d58477..cd1c7a25 100644 --- a/src/snes/cpu/scpu/scpu.cpp +++ b/src/snes/cpu/scpu/scpu.cpp @@ -103,8 +103,8 @@ void sCPU::reset() { apu_port[3] = 0x00; } -sCPU::sCPU() : event(512, bind(&sCPU::queue_event, this)) { - PPUcounter::scanline = bind(&sCPU::scanline, this); +sCPU::sCPU() { + PPUCounter::scanline = { &sCPU::scanline, this }; } sCPU::~sCPU() { diff --git a/src/snes/cpu/scpu/scpu.hpp b/src/snes/cpu/scpu/scpu.hpp index e6f1513d..361ad231 100644 --- a/src/snes/cpu/scpu/scpu.hpp +++ b/src/snes/cpu/scpu/scpu.hpp @@ -10,8 +10,6 @@ public: #include "mmio/mmio.hpp" #include "timing/timing.hpp" - priority_queue event; - struct Status { bool interrupt_pending; uint16 interrupt_vector; @@ -24,7 +22,15 @@ public: //====== bool irq_lock; + unsigned dram_refresh_position; + bool dram_refreshed; + + unsigned hdma_init_position; + bool hdma_init_triggered; + + unsigned hdma_position; + bool hdma_triggered; bool nmi_valid; bool nmi_line; diff --git a/src/snes/cpu/scpu/serialization.cpp b/src/snes/cpu/scpu/serialization.cpp index 3639be30..4d74c2d5 100644 --- a/src/snes/cpu/scpu/serialization.cpp +++ b/src/snes/cpu/scpu/serialization.cpp @@ -4,8 +4,6 @@ void sCPU::serialize(serializer &s) { CPU::serialize(s); CPUcore::core_serialize(s); - event.serialize(s); - s.integer(status.interrupt_pending); s.integer(status.interrupt_vector); @@ -13,7 +11,15 @@ void sCPU::serialize(serializer &s) { s.integer(status.line_clocks); s.integer(status.irq_lock); + s.integer(status.dram_refresh_position); + s.integer(status.dram_refreshed); + + s.integer(status.hdma_init_position); + s.integer(status.hdma_init_triggered); + + s.integer(status.hdma_position); + s.integer(status.hdma_triggered); s.integer(status.nmi_valid); s.integer(status.nmi_line); @@ -105,8 +111,6 @@ void sCPU::serialize(serializer &s) { s.integer(apu_port[1]); s.integer(apu_port[2]); s.integer(apu_port[3]); - - s.integer(cycle_edge_state); } #endif diff --git a/src/snes/cpu/scpu/timing/event.cpp b/src/snes/cpu/scpu/timing/event.cpp deleted file mode 100644 index dde115b4..00000000 --- a/src/snes/cpu/scpu/timing/event.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef SCPU_CPP - -void sCPU::queue_event(unsigned id) { - switch(id) { - //interrupts triggered during (H)DMA do not trigger immediately after - case EventIrqLockRelease: { - status.irq_lock = false; - } break; - - //S-CPU WRAM consists of two 64kbyte DRAM chips, which must be refreshed - //once per scanline to avoid memory decay. - case EventDramRefresh: { - add_clocks(40); - } break; - - //HDMA init routine; occurs once per frame - case EventHdmaInit: { - cycle_edge_state |= EventFlagHdmaInit; - } break; - - //HDMA run routine; occurs once per scanline - case EventHdmaRun: { - cycle_edge_state |= EventFlagHdmaRun; - } break; - } -} - -#endif diff --git a/src/snes/cpu/scpu/timing/irq.cpp b/src/snes/cpu/scpu/timing/irq.cpp index 78664f0c..c07c3a72 100644 --- a/src/snes/cpu/scpu/timing/irq.cpp +++ b/src/snes/cpu/scpu/timing/irq.cpp @@ -70,7 +70,6 @@ void sCPU::nmitimen_update(uint8 data) { } status.irq_lock = true; - event.enqueue(2, EventIrqLockRelease); } bool sCPU::rdnmi() { diff --git a/src/snes/cpu/scpu/timing/timing.cpp b/src/snes/cpu/scpu/timing/timing.cpp index 5dabc3b8..23f7d536 100644 --- a/src/snes/cpu/scpu/timing/timing.cpp +++ b/src/snes/cpu/scpu/timing/timing.cpp @@ -1,6 +1,5 @@ #ifdef SCPU_CPP -#include "event.cpp" #include "irq.cpp" #include "joypad.cpp" @@ -9,7 +8,6 @@ unsigned sCPU::dma_counter() { } void sCPU::add_clocks(unsigned clocks) { - event.tick(clocks); unsigned ticks = clocks >> 1; while(ticks--) { tick(); @@ -19,6 +17,11 @@ void sCPU::add_clocks(unsigned clocks) { } } scheduler.addclocks_cpu(clocks); + + if(status.dram_refreshed == false && hcounter() >= status.dram_refresh_position) { + status.dram_refreshed = true; + add_clocks(40); + } } //called by ppu.tick() when Hcounter=0 @@ -33,17 +36,19 @@ void sCPU::scanline() { system.scanline(); if(vcounter() == 0) { - //hdma init triggers once every frame - event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit); + //HDMA init triggers once every frame + status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); + status.hdma_init_triggered = false; } - //dram refresh occurs once every scanline + //DRAM refresh occurs once every scanline if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); - event.enqueue(status.dram_refresh_position, EventDramRefresh); + status.dram_refreshed = false; - //hdma triggers once every visible scanline + //HDMA triggers once every visible scanline if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { - event.enqueue(1104, EventHdmaRun); + status.hdma_position = 1104; + status.hdma_triggered = false; } if(status.auto_joypad_poll == true && vcounter() == (ppu.overscan() == false ? 227 : 242)) { @@ -106,25 +111,21 @@ void sCPU::dma_edge() { } } - while(cycle_edge_state) { - switch(bit::lowest(cycle_edge_state)) { - case EventFlagHdmaInit: { - hdma_init_reset(); - if(hdma_enabled_channels()) { - status.hdma_pending = true; - status.hdma_mode = 0; - } - } break; - - case EventFlagHdmaRun: { - if(hdma_active_channels()) { - status.hdma_pending = true; - status.hdma_mode = 1; - } - } break; + if(status.hdma_init_triggered == false && hcounter() >= status.hdma_init_position) { + status.hdma_init_triggered = true; + hdma_init_reset(); + if(hdma_enabled_channels()) { + status.hdma_pending = true; + status.hdma_mode = 0; } + } - cycle_edge_state = bit::clear_lowest(cycle_edge_state); + if(status.hdma_triggered == false && hcounter() >= status.hdma_position) { + status.hdma_triggered = true; + if(hdma_active_channels()) { + status.hdma_pending = true; + status.hdma_mode = 1; + } } if(status.dma_active == false) { @@ -141,7 +142,9 @@ void sCPU::dma_edge() { //status.irq_lock is used to simulate hardware delay before interrupts can //trigger during certain events (immediately after DMA, writes to $4200, etc) void sCPU::last_cycle() { - if(!status.irq_lock) { + if(status.irq_lock) { + status.irq_lock = false; + } else { status.nmi_pending |= nmi_test(); status.irq_pending |= irq_test(); @@ -153,14 +156,18 @@ void sCPU::timing_power() { } void sCPU::timing_reset() { - event.reset(); - status.clock_count = 0; status.line_clocks = lineclocks(); status.irq_lock = false; status.dram_refresh_position = (cpu_version == 1 ? 530 : 538); - event.enqueue(status.dram_refresh_position, EventDramRefresh); + status.dram_refreshed = false; + + status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); + status.hdma_init_triggered = false; + + status.hdma_position = 1104; + status.hdma_triggered = false; status.nmi_valid = false; status.nmi_line = false; @@ -184,8 +191,6 @@ void sCPU::timing_reset() { status.dma_pending = false; status.hdma_pending = false; status.hdma_mode = 0; - - cycle_edge_state = 0; } #endif diff --git a/src/snes/cpu/scpu/timing/timing.hpp b/src/snes/cpu/scpu/timing/timing.hpp index 91235861..c185ea4c 100644 --- a/src/snes/cpu/scpu/timing/timing.hpp +++ b/src/snes/cpu/scpu/timing/timing.hpp @@ -1,16 +1,3 @@ -enum { - EventNone, - EventIrqLockRelease, - EventDramRefresh, - EventHdmaInit, - EventHdmaRun, - - //cycle edge - EventFlagHdmaInit = 1 << 0, - EventFlagHdmaRun = 1 << 1, -}; -unsigned cycle_edge_state; - //timing.cpp unsigned dma_counter(); @@ -35,6 +22,3 @@ alwaysinline bool irq_test(); //joypad.cpp void run_auto_joypad_poll(); - -//event.cpp -void queue_event(unsigned); //priorityqueue callback function diff --git a/src/snes/dsp/adsp/adsp.cpp b/src/snes/dsp/adsp/adsp.cpp index 3567a068..c5c61f9d 100644 --- a/src/snes/dsp/adsp/adsp.cpp +++ b/src/snes/dsp/adsp/adsp.cpp @@ -3,15 +3,27 @@ #define ADSP_CPP namespace SNES { -aDSP dsp; +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + aDSPDebugger dsp; +#else + aDSP dsp; +#endif #include "tables.cpp" +#include "serialization.cpp" void aDSP::enter() { #if !defined(DSP_STATE_MACHINE) - while(true) + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + #endif + run(); + #if !defined(DSP_STATE_MACHINE) + } #endif - run(); } uint8 aDSP::readb(uint16 addr) { diff --git a/src/snes/dsp/adsp/adsp.hpp b/src/snes/dsp/adsp/adsp.hpp index 82f24aff..fa8bf84c 100644 --- a/src/snes/dsp/adsp/adsp.hpp +++ b/src/snes/dsp/adsp/adsp.hpp @@ -100,8 +100,8 @@ private: int16 envx; uint16 env_ctr, env_rate, env_sustain; - enum EnvelopeStates env_state; - enum EnvelopeModes env_mode; + uint32 env_state; + uint32 env_mode; int16 outx; @@ -167,8 +167,16 @@ public: void power(); void reset(); + void serialize(serializer&); aDSP(); ~aDSP(); + + friend class aDSPDebug; }; -extern aDSP dsp; +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern aDSPDebugger dsp; +#else + extern aDSP dsp; +#endif diff --git a/src/snes/dsp/adsp/debugger/debugger.cpp b/src/snes/dsp/adsp/debugger/debugger.cpp new file mode 100644 index 00000000..eb19c304 --- /dev/null +++ b/src/snes/dsp/adsp/debugger/debugger.cpp @@ -0,0 +1,3 @@ +#ifdef ADSP_CPP + +#endif diff --git a/src/snes/dsp/adsp/debugger/debugger.hpp b/src/snes/dsp/adsp/debugger/debugger.hpp new file mode 100644 index 00000000..845ae8bf --- /dev/null +++ b/src/snes/dsp/adsp/debugger/debugger.hpp @@ -0,0 +1,3 @@ +class aDSPDebugger : public aDSP, public DSPDebugger { +public: +}; diff --git a/src/snes/dsp/adsp/serialization.cpp b/src/snes/dsp/adsp/serialization.cpp new file mode 100644 index 00000000..38c645dd --- /dev/null +++ b/src/snes/dsp/adsp/serialization.cpp @@ -0,0 +1,71 @@ +#ifdef ADSP_CPP + +void aDSP::serialize(serializer &s) { + DSP::serialize(s); + + s.array(dspram); + s.integer(dsp_counter); + + s.integer(status.MVOLL); + s.integer(status.MVOLR); + s.integer(status.EVOLL); + s.integer(status.EVOLR); + s.integer(status.KON); + s.integer(status.KOFF); + s.integer(status.FLG); + s.integer(status.ENDX); + s.integer(status.EFB); + s.integer(status.PMON); + s.integer(status.NON); + s.integer(status.EON); + s.integer(status.DIR); + s.integer(status.ESA); + s.integer(status.EDL); + s.array(status.FIR); + + s.integer(status.kon); + s.integer(status.esa); + + s.integer(status.noise_ctr); + s.integer(status.noise_rate); + s.integer(status.noise_sample); + + s.integer(status.echo_index); + s.integer(status.echo_length); + s.array(status.fir_buffer[0]); + s.array(status.fir_buffer[1]); + s.integer(status.fir_buffer_index); + + for(unsigned i = 0; i < 8; i++) { + s.integer(voice[i].VOLL); + s.integer(voice[i].VOLR); + s.integer(voice[i].PITCH); + s.integer(voice[i].SRCN); + s.integer(voice[i].ADSR1); + s.integer(voice[i].ADSR2); + s.integer(voice[i].GAIN); + s.integer(voice[i].ENVX); + s.integer(voice[i].OUTX); + + s.integer(voice[i].pitch_ctr); + + s.integer(voice[i].brr_index); + s.integer(voice[i].brr_ptr); + s.integer(voice[i].brr_header); + s.integer(voice[i].brr_looped); + + s.array(voice[i].brr_data); + s.integer(voice[i].brr_data_index); + + s.integer(voice[i].envx); + s.integer(voice[i].env_ctr); + s.integer(voice[i].env_rate); + s.integer(voice[i].env_sustain); + s.integer(voice[i].env_state); + s.integer(voice[i].env_mode); + + s.integer(voice[i].outx); + } +} + +#endif diff --git a/src/snes/libsnes/libsnes.cpp b/src/snes/libsnes/libsnes.cpp index 554648c9..724032de 100644 --- a/src/snes/libsnes/libsnes.cpp +++ b/src/snes/libsnes/libsnes.cpp @@ -36,6 +36,22 @@ unsigned snes_library_revision() { return 1; } +void snes_set_video_refresh(snes_video_refresh_t video_refresh) { + interface.pvideo_refresh = video_refresh; +} + +void snes_set_audio_sample(snes_audio_sample_t audio_sample) { + interface.paudio_sample = audio_sample; +} + +void snes_set_input_poll(snes_input_poll_t input_poll) { + interface.pinput_poll = input_poll; +} + +void snes_set_input_state(snes_input_state_t input_state) { + interface.pinput_state = input_state; +} + void snes_init() { SNES::system.init(&interface); SNES::input.port_set_device(0, SNES::Input::Device::Joypad); @@ -54,24 +70,39 @@ void snes_run() { SNES::system.run(); } +void snes_runtosave() { + SNES::system.runtosave(); +} + void snes_set_controller_port_device(bool port, unsigned device) { SNES::input.port_set_device(port, (SNES::Input::Device)device); } -void snes_set_video_refresh(snes_video_refresh_t video_refresh) { - interface.pvideo_refresh = video_refresh; +unsigned snes_serialize_size() { + return SNES::system.serialize_size(); } -void snes_set_audio_sample(snes_audio_sample_t audio_sample) { - interface.paudio_sample = audio_sample; +bool snes_serialize(uint8_t *data, unsigned size) { + serializer s = SNES::system.serialize(); + if(s.size() > size) return false; + memcpy(data, s.data(), s.size()); + return true; } -void snes_set_input_poll(snes_input_poll_t input_poll) { - interface.pinput_poll = input_poll; +bool snes_unserialize(const uint8_t *data, unsigned size) { + serializer s(data, size); + return SNES::system.unserialize(s); } -void snes_set_input_state(snes_input_state_t input_state) { - interface.pinput_state = input_state; +void snes_cheat_reset() { + SNES::cheat.reset(); + SNES::cheat.synchronize(); +} + +void snes_cheat_set(unsigned index, bool enabled, const char *code) { + SNES::cheat[index] = code; + SNES::cheat[index].enabled = enabled; + SNES::cheat.synchronize(); } void snes_load_cartridge_normal( diff --git a/src/snes/libsnes/libsnes.hpp b/src/snes/libsnes/libsnes.hpp index 903f5db2..d17ed22e 100644 --- a/src/snes/libsnes/libsnes.hpp +++ b/src/snes/libsnes/libsnes.hpp @@ -5,11 +5,6 @@ extern "C" { #endif unsigned snes_library_revision(); -void snes_init(); -void snes_term(); -void snes_unload(); -void snes_run(); -void snes_set_controller_port_device(bool port, unsigned device); typedef void (*snes_video_refresh_t)(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); @@ -21,6 +16,21 @@ void snes_set_audio_sample(snes_audio_sample_t); void snes_set_input_poll(snes_input_poll_t); void snes_set_input_state(snes_input_state_t); +void snes_init(); +void snes_term(); +void snes_unload(); +void snes_run(); +void snes_runtosave(); + +void snes_set_controller_port_device(bool port, unsigned device); + +unsigned snes_serialize_size(); +bool snes_serialize(uint8_t *data, unsigned size); +bool snes_unserialize(const uint8_t *data, unsigned size); + +void snes_cheat_reset(); +void snes_cheat_set(unsigned index, bool enabled, const char *code); + void snes_load_cartridge_normal( const char *rom_xml, uint8_t *rom_data, unsigned rom_size ); diff --git a/src/snes/ppu/bppu/bppu.cpp b/src/snes/ppu/bppu/bppu.cpp index df876c81..9221507c 100644 --- a/src/snes/ppu/bppu/bppu.cpp +++ b/src/snes/ppu/bppu/bppu.cpp @@ -89,11 +89,6 @@ void bPPU::scanline() { } void bPPU::render_scanline() { - #ifdef FAST_FRAMESKIP - //note: this bypasses RTO status flag calculations, which is observable by software - if(status.render_output == false) return; - #endif - if(line >= 1 && line < (!overscan() ? 225 : 240)) { render_line_oam_rto(); render_line(); diff --git a/src/snes/ppu/ppu-inline.hpp b/src/snes/ppu/ppu-inline.hpp index 2c21b6d1..263d10a4 100644 --- a/src/snes/ppu/ppu-inline.hpp +++ b/src/snes/ppu/ppu-inline.hpp @@ -1,6 +1,6 @@ //this should only be called by CPU::PPUcounter::tick(); //keeps track of previous counter positions in history table -void PPUcounter::tick() { +void PPUCounter::tick() { status.hcounter += 2; //increment by smallest unit of time if(status.hcounter >= 1360 && status.hcounter == lineclocks()) { status.hcounter = 0; @@ -15,7 +15,7 @@ void PPUcounter::tick() { //this should only be called by PPU::PPUcounter::tick(n); //allows stepping by more than the smallest unit of time -void PPUcounter::tick(unsigned clocks) { +void PPUCounter::tick(unsigned clocks) { status.hcounter += clocks; if(status.hcounter >= lineclocks()) { status.hcounter -= lineclocks(); @@ -24,7 +24,7 @@ void PPUcounter::tick(unsigned clocks) { } //internal -void PPUcounter::vcounter_tick() { +void PPUCounter::vcounter_tick() { if(++status.vcounter == 128) status.interlace = ppu.interlace(); if((system.region() == System::Region::NTSC && status.interlace == false && status.vcounter == 262) @@ -40,13 +40,13 @@ void PPUcounter::vcounter_tick() { if(scanline) scanline(); } -bool PPUcounter::field () const { return status.field; } -uint16 PPUcounter::vcounter() const { return status.vcounter; } -uint16 PPUcounter::hcounter() const { return status.hcounter; } +bool PPUCounter::field () const { return status.field; } +uint16 PPUCounter::vcounter() const { return status.vcounter; } +uint16 PPUCounter::hcounter() const { return status.hcounter; } -bool PPUcounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } -uint16 PPUcounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } -uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } +bool PPUCounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } +uint16 PPUCounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } +uint16 PPUCounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } //one PPU dot = 4 CPU clocks // @@ -57,7 +57,7 @@ uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(hi //dot 323 range = { 1292, 1294, 1296 } //dot 327 range = { 1310, 1312, 1314 } -uint16 PPUcounter::hdot() const { +uint16 PPUCounter::hdot() const { if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) { return (hcounter() >> 2); } else { @@ -65,12 +65,12 @@ uint16 PPUcounter::hdot() const { } } -uint16 PPUcounter::lineclocks() const { +uint16 PPUCounter::lineclocks() const { if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) return 1360; return 1364; } -void PPUcounter::reset() { +void PPUCounter::reset() { status.interlace = false; status.field = 0; status.vcounter = 0; diff --git a/src/snes/ppu/ppu.cpp b/src/snes/ppu/ppu.cpp index 4fa9e6d5..4fe11b1d 100644 --- a/src/snes/ppu/ppu.cpp +++ b/src/snes/ppu/ppu.cpp @@ -9,25 +9,18 @@ namespace SNES { #include "serialization.cpp" -void PPU::enable_renderer(bool r) { status.render_output = r; } -bool PPU::renderer_enabled() { return status.render_output; } - void PPU::frame() { - status.frame_executed = true; - - static int32 fr = 0, fe = 0; + static signed framecount = 0; static time_t prev, curr; - fe++; - if(status.render_output)fr++; + framecount++; time(&curr); if(curr != prev) { - status.frames_updated = true; - status.frames_rendered = fr; - status.frames_executed = fe; - fr = fe = 0; + status.frames_updated = true; + status.frames_executed = framecount; + framecount = 0; + prev = curr; } - prev = curr; } void PPU::power() { @@ -36,16 +29,14 @@ void PPU::power() { } void PPU::reset() { - PPUcounter::reset(); + PPUCounter::reset(); memset(output, 0, 512 * 480 * sizeof(uint16)); } PPU::PPU() { output = new uint16[512 * 480]; - status.render_output = true; - status.frames_updated = false; - status.frames_rendered = 0; + status.frames_updated = false; status.frames_executed = 0; } diff --git a/src/snes/ppu/ppu.hpp b/src/snes/ppu/ppu.hpp index 978c6faf..a79efd5e 100644 --- a/src/snes/ppu/ppu.hpp +++ b/src/snes/ppu/ppu.hpp @@ -2,7 +2,7 @@ #include "ppu-debugger.hpp" #endif -//PPUcounter emulates the H/V latch counters of the S-PPU2. +//PPUCounter emulates the H/V latch counters of the S-PPU2. // //real hardware has the S-CPU maintain its own copy of these counters that are //updated based on the state of the S-PPU Vblank and Hblank pins. emulating this @@ -14,7 +14,7 @@ //point before this in the frame, which is handled internally by this class at //V=128. -class PPUcounter { +class PPUCounter { public: alwaysinline void tick(); alwaysinline void tick(unsigned clocks); @@ -52,17 +52,14 @@ private: } history; }; -class PPU : public PPUcounter, public MMIO { +class PPU : public PPUCounter, public MMIO { public: virtual void enter() = 0; uint16 *output; struct { - bool render_output; - bool frame_executed; bool frames_updated; - unsigned frames_rendered; unsigned frames_executed; } status; @@ -78,8 +75,6 @@ public: virtual void frame(); virtual void power(); virtual void reset(); - virtual void enable_renderer(bool r); - virtual bool renderer_enabled(); virtual void serialize(serializer&); PPU(); diff --git a/src/snes/ppu/serialization.cpp b/src/snes/ppu/serialization.cpp index 2a5ef315..a2bb6b19 100644 --- a/src/snes/ppu/serialization.cpp +++ b/src/snes/ppu/serialization.cpp @@ -1,6 +1,6 @@ #ifdef PPU_CPP -void PPUcounter::serialize(serializer &s) { +void PPUCounter::serialize(serializer &s) { s.integer(status.interlace); s.integer(status.field); s.integer(status.vcounter); @@ -13,12 +13,9 @@ void PPUcounter::serialize(serializer &s) { } void PPU::serialize(serializer &s) { - PPUcounter::serialize(s); + PPUCounter::serialize(s); - s.integer(status.render_output); - s.integer(status.frame_executed); s.integer(status.frames_updated); - s.integer(status.frames_rendered); s.integer(status.frames_executed); s.integer(ppu1_version); diff --git a/src/snes/ppu/sppu/background/background.hpp b/src/snes/ppu/sppu/background/background.hpp index 25249857..2c9d6a02 100644 --- a/src/snes/ppu/sppu/background/background.hpp +++ b/src/snes/ppu/sppu/background/background.hpp @@ -35,7 +35,7 @@ public: struct { struct { unsigned priority; //0 = none (transparent) - unsigned palette; //(direct_color_bits << 8) + index + unsigned palette; unsigned tile; } main, sub; } output; @@ -45,6 +45,8 @@ public: unsigned get_tile(unsigned x, unsigned y); unsigned get_color(unsigned x, unsigned y, uint16 offset); void reset(); + + void serialize(serializer&); Background(sPPU &self, unsigned id); private: diff --git a/src/snes/ppu/sppu/screen/screen.hpp b/src/snes/ppu/sppu/screen/screen.hpp index 98f92240..730277c0 100644 --- a/src/snes/ppu/sppu/screen/screen.hpp +++ b/src/snes/ppu/sppu/screen/screen.hpp @@ -25,6 +25,7 @@ public: void run(); void reset(); + void serialize(serializer&); Screen(sPPU &self); private: diff --git a/src/snes/ppu/sppu/serialization.cpp b/src/snes/ppu/sppu/serialization.cpp new file mode 100644 index 00000000..06d314a6 --- /dev/null +++ b/src/snes/ppu/sppu/serialization.cpp @@ -0,0 +1,239 @@ +#ifdef SPPU_CPP + +void sPPU::serialize(serializer &s) { + PPU::serialize(s); + + s.integer(display.interlace); + s.integer(display.overscan); + + s.integer(regs.ppu1_mdr); + s.integer(regs.ppu2_mdr); + + s.integer(regs.vram_readbuffer); + s.integer(regs.oam_latchdata); + s.integer(regs.cgram_latchdata); + s.integer(regs.bgofs_latchdata); + s.integer(regs.mode7_latchdata); + s.integer(regs.counters_latched); + s.integer(regs.latch_hcounter); + s.integer(regs.latch_vcounter); + + s.integer(regs.display_disabled); + s.integer(regs.display_brightness); + + s.integer(regs.oam_baseaddr); + s.integer(regs.oam_addr); + s.integer(regs.oam_priority); + + s.integer(regs.bg3_priority); + s.integer(regs.bgmode); + + s.integer(regs.mode7_hoffset); + s.integer(regs.mode7_voffset); + + s.integer(regs.vram_incmode); + s.integer(regs.vram_mapping); + s.integer(regs.vram_incsize); + + s.integer(regs.vram_addr); + + s.integer(regs.mode7_repeat); + s.integer(regs.mode7_vflip); + s.integer(regs.mode7_hflip); + + s.integer(regs.m7a); + s.integer(regs.m7b); + s.integer(regs.m7c); + s.integer(regs.m7d); + s.integer(regs.m7x); + s.integer(regs.m7y); + + s.integer(regs.cgram_addr); + + s.integer(regs.mode7_extbg); + s.integer(regs.pseudo_hires); + s.integer(regs.overscan); + s.integer(regs.interlace); + + s.integer(regs.hcounter); + s.integer(regs.vcounter); + + bg1.serialize(s); + bg2.serialize(s); + bg3.serialize(s); + bg4.serialize(s); + oam.serialize(s); + window.serialize(s); + screen.serialize(s); +} + +void sPPU::Background::serialize(serializer &s) { + s.integer(id); + + s.integer(t.x); + s.integer(t.mosaic_y); + s.integer(t.mosaic_countdown); + + s.integer(regs.tiledata_addr); + s.integer(regs.screen_addr); + s.integer(regs.screen_size); + s.integer(regs.mosaic); + s.integer(regs.tile_size); + + s.integer(regs.mode); + s.integer(regs.priority0); + s.integer(regs.priority1); + + s.integer(regs.main_enabled); + s.integer(regs.sub_enabled); + + s.integer(regs.hoffset); + s.integer(regs.voffset); + + s.integer(output.main.priority); + s.integer(output.main.palette); + s.integer(output.main.tile); + + s.integer(output.sub.priority); + s.integer(output.sub.palette); + s.integer(output.sub.tile); +} + +void sPPU::Sprite::serialize(serializer &s) { + for(unsigned i = 0; i < 128; i++) { + s.integer(list[i].width); + s.integer(list[i].height); + s.integer(list[i].x); + s.integer(list[i].y); + s.integer(list[i].character); + s.integer(list[i].nameselect); + s.integer(list[i].vflip); + s.integer(list[i].hflip); + s.integer(list[i].priority); + s.integer(list[i].palette); + } + + s.integer(t.x); + s.integer(t.y); + s.integer(t.item_count); + s.integer(t.tile_count); + s.array(t.output_palette); + s.array(t.output_priority); + s.array(t.item_list); + for(unsigned i = 0; i < 34; i++) { + s.integer(t.tile_list[i].x); + s.integer(t.tile_list[i].y); + s.integer(t.tile_list[i].priority); + s.integer(t.tile_list[i].palette); + s.integer(t.tile_list[i].tile); + s.integer(t.tile_list[i].hflip); + } + s.integer(t.active_sprite); + + s.integer(regs.main_enabled); + s.integer(regs.sub_enabled); + s.integer(regs.interlace); + + s.integer(regs.base_size); + s.integer(regs.nameselect); + s.integer(regs.tiledata_addr); + s.integer(regs.first_sprite); + + s.integer(regs.priority0); + s.integer(regs.priority1); + s.integer(regs.priority2); + s.integer(regs.priority3); + + s.integer(regs.time_over); + s.integer(regs.range_over); + + s.integer(output.main.priority); + s.integer(output.main.palette); + + s.integer(output.sub.priority); + s.integer(output.sub.palette); +} + +void sPPU::Window::serialize(serializer &s) { + s.integer(t.x); + + s.integer(regs.bg1_one_enable); + s.integer(regs.bg1_one_invert); + s.integer(regs.bg1_two_enable); + s.integer(regs.bg1_two_invert); + + s.integer(regs.bg2_one_enable); + s.integer(regs.bg2_one_invert); + s.integer(regs.bg2_two_enable); + s.integer(regs.bg2_two_invert); + + s.integer(regs.bg3_one_enable); + s.integer(regs.bg3_one_invert); + s.integer(regs.bg3_two_enable); + s.integer(regs.bg3_two_invert); + + s.integer(regs.bg4_one_enable); + s.integer(regs.bg4_one_invert); + s.integer(regs.bg4_two_enable); + s.integer(regs.bg4_two_invert); + + s.integer(regs.oam_one_enable); + s.integer(regs.oam_one_invert); + s.integer(regs.oam_two_enable); + s.integer(regs.oam_two_invert); + + s.integer(regs.col_one_enable); + s.integer(regs.col_one_invert); + s.integer(regs.col_two_enable); + s.integer(regs.col_two_invert); + + s.integer(regs.one_left); + s.integer(regs.one_right); + s.integer(regs.two_left); + s.integer(regs.two_right); + + s.integer(regs.bg1_mask); + s.integer(regs.bg2_mask); + s.integer(regs.bg3_mask); + s.integer(regs.bg4_mask); + s.integer(regs.oam_mask); + s.integer(regs.col_mask); + + s.integer(regs.bg1_main_enable); + s.integer(regs.bg1_sub_enable); + s.integer(regs.bg2_main_enable); + s.integer(regs.bg2_sub_enable); + s.integer(regs.bg3_main_enable); + s.integer(regs.bg3_sub_enable); + s.integer(regs.bg4_main_enable); + s.integer(regs.bg4_sub_enable); + s.integer(regs.oam_main_enable); + s.integer(regs.oam_sub_enable); + + s.integer(regs.col_main_mask); + s.integer(regs.col_sub_mask); + + s.integer(output.main.color_enable); + s.integer(output.sub.color_enable); + +} + +void sPPU::Screen::serialize(serializer &s) { + s.integer(regs.addsub_mode); + s.integer(regs.direct_color); + + s.integer(regs.color_mode); + s.integer(regs.color_halve); + s.integer(regs.bg1_color_enable); + s.integer(regs.bg2_color_enable); + s.integer(regs.bg3_color_enable); + s.integer(regs.bg4_color_enable); + s.integer(regs.oam_color_enable); + s.integer(regs.back_color_enable); + + s.integer(regs.color_b); + s.integer(regs.color_g); + s.integer(regs.color_r); +} + +#endif diff --git a/src/snes/ppu/sppu/sppu.cpp b/src/snes/ppu/sppu/sppu.cpp index 6085bad9..7f389528 100644 --- a/src/snes/ppu/sppu/sppu.cpp +++ b/src/snes/ppu/sppu/sppu.cpp @@ -3,12 +3,6 @@ #define SPPU_CPP namespace SNES { -#include "background/background.cpp" -#include "mmio/mmio.cpp" -#include "screen/screen.cpp" -#include "sprite/sprite.cpp" -#include "window/window.cpp" - #if defined(DEBUGGER) #include "debugger/debugger.cpp" sPPUDebugger ppu; @@ -16,10 +10,20 @@ namespace SNES { sPPU ppu; #endif +#include "background/background.cpp" +#include "mmio/mmio.cpp" +#include "screen/screen.cpp" +#include "sprite/sprite.cpp" +#include "window/window.cpp" +#include "serialization.cpp" + void sPPU::enter() { while(true) { - scanline(); + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + scanline(); add_clocks(88); if(vcounter() >= 1 && vcounter() <= (!regs.overscan ? 224 : 239)) { diff --git a/src/snes/ppu/sppu/sppu.hpp b/src/snes/ppu/sppu/sppu.hpp index 6d740c12..f7eba6f6 100644 --- a/src/snes/ppu/sppu/sppu.hpp +++ b/src/snes/ppu/sppu/sppu.hpp @@ -28,6 +28,7 @@ public: void scanline(); void frame(); + void serialize(serializer&); sPPU(); }; diff --git a/src/snes/ppu/sppu/sprite/sprite.hpp b/src/snes/ppu/sppu/sprite/sprite.hpp index a0a95c15..2896525a 100644 --- a/src/snes/ppu/sppu/sprite/sprite.hpp +++ b/src/snes/ppu/sppu/sprite/sprite.hpp @@ -20,8 +20,8 @@ public: bool nameselect; bool vflip; bool hflip; - uint8 palette; uint8 priority; + uint8 palette; } list[128]; struct State { @@ -59,7 +59,7 @@ public: struct { struct { unsigned priority; //0 = none (transparent) - unsigned palette; //index + unsigned palette; } main, sub; } output; @@ -69,6 +69,7 @@ public: void run(); void reset(); + void serialize(serializer&); Sprite(sPPU &self); private: diff --git a/src/snes/ppu/sppu/window/window.hpp b/src/snes/ppu/sppu/window/window.hpp index 828f3f26..48372df0 100644 --- a/src/snes/ppu/sppu/window/window.hpp +++ b/src/snes/ppu/sppu/window/window.hpp @@ -74,6 +74,7 @@ public: void run(); void reset(); + void serialize(serializer&); Window(sPPU &self); private: diff --git a/src/snes/snes.hpp b/src/snes/snes.hpp index 126c668a..8549a75e 100644 --- a/src/snes/snes.hpp +++ b/src/snes/snes.hpp @@ -1,12 +1,12 @@ -static const char bsnesVersion[] = "063.12"; +static const char bsnesVersion[] = "063.13"; static const char bsnesTitle[] = "bsnes"; -static const unsigned bsnesSerializerVersion = 9; +static const unsigned bsnesSerializerVersion = 10; #define CORE_SMEMORY #define CORE_SCPU #define CORE_SSMP -#define CORE_SDSP -#define CORE_SPPU +#define CORE_ADSP +#define CORE_BPPU //S-DSP can be encapsulated into a state machine using #define magic //this avoids ~2.048m co_switch() calls per second (~5% speedup) @@ -23,7 +23,6 @@ static const unsigned bsnesSerializerVersion = 9; #include #include #include -#include #include #include #include @@ -32,7 +31,6 @@ static const unsigned bsnesSerializerVersion = 9; #include #include #include -#include #include #include #include diff --git a/src/ui_qt/base/filebrowser.cpp b/src/ui_qt/base/filebrowser.cpp index 4a8303c8..0c1592f9 100644 --- a/src/ui_qt/base/filebrowser.cpp +++ b/src/ui_qt/base/filebrowser.cpp @@ -8,9 +8,9 @@ void FileBrowser::chooseFolder() { void FileBrowser::loadCartridge(CartridgeMode mode, signed filterIndex) { cartridgeMode = mode; - onChange = bind(&FileBrowser::onChangeCartridge, this); - onActivate = bind(&FileBrowser::onAcceptCartridge, this); - onAccept = bind(&FileBrowser::onAcceptCartridge, this); + onChange = { &FileBrowser::onChangeCartridge, this }; + onActivate = { &FileBrowser::onAcceptCartridge, this }; + onAccept = { &FileBrowser::onAcceptCartridge, this }; setPath(config().path.rom == "" ? config().path.current.cartridge : config().path.rom); setNameFilters(string() diff --git a/src/ui_qt/link/reader.cpp b/src/ui_qt/link/reader.cpp index e5f9d43f..d840983b 100644 --- a/src/ui_qt/link/reader.cpp +++ b/src/ui_qt/link/reader.cpp @@ -27,8 +27,8 @@ Reader::Reader() { } if(!supported || !load) { - supported = bind(&Reader::direct_supported, this); - load = bind(&Reader::direct_load, this); + supported = { &Reader::direct_supported, this }; + load = { &Reader::direct_load, this }; } compressionList = supported(); diff --git a/src/ui_qt/movie/movie.cpp b/src/ui_qt/movie/movie.cpp index 564c351e..95966d3e 100644 --- a/src/ui_qt/movie/movie.cpp +++ b/src/ui_qt/movie/movie.cpp @@ -4,8 +4,8 @@ Movie movie; void Movie::chooseFile() { fileBrowser->onChange.reset(); - fileBrowser->onActivate = bind(&Movie::play, this); - fileBrowser->onAccept = bind(&Movie::play, this); + fileBrowser->onActivate = { &Movie::play, this }; + fileBrowser->onAccept = { &Movie::play, this }; fileBrowser->setWindowTitle("Select Movie"); fileBrowser->setPath(config().path.current.movie); fileBrowser->setNameFilters("bsnes Movies (*.bsv)"); diff --git a/src/ui_qt/settings/paths.cpp b/src/ui_qt/settings/paths.cpp index 5b92d151..1796194e 100644 --- a/src/ui_qt/settings/paths.cpp +++ b/src/ui_qt/settings/paths.cpp @@ -52,7 +52,7 @@ void PathSettingWidget::updatePath() { void PathSettingWidget::selectPath() { fileBrowser->onChange.reset(); fileBrowser->onActivate.reset(); - fileBrowser->onAccept = bind(&PathSettingWidget::acceptPath, this); + fileBrowser->onAccept = { &PathSettingWidget::acceptPath, this }; fileBrowser->setWindowTitle(pathBrowseLabel); fileBrowser->setPath(config().path.current.folder); fileBrowser->chooseFolder(); diff --git a/src/ui_qt/settings/video.cpp b/src/ui_qt/settings/video.cpp index 8d83ec18..aa661cb9 100644 --- a/src/ui_qt/settings/video.cpp +++ b/src/ui_qt/settings/video.cpp @@ -303,8 +303,8 @@ void VideoSettingsWindow::cropBottomAdjust(int state) { void VideoSettingsWindow::selectFragmentShader() { fileBrowser->onChange.reset(); - fileBrowser->onActivate = bind(&VideoSettingsWindow::assignFragmentShader, this); - fileBrowser->onAccept = bind(&VideoSettingsWindow::assignFragmentShader, this); + fileBrowser->onActivate = { &VideoSettingsWindow::assignFragmentShader, this }; + fileBrowser->onAccept = { &VideoSettingsWindow::assignFragmentShader, this }; fileBrowser->setWindowTitle("Select Fragment Shader"); fileBrowser->setPath(config().path.current.shader); fileBrowser->setNameFilters("All files (*)"); @@ -313,8 +313,8 @@ void VideoSettingsWindow::selectFragmentShader() { void VideoSettingsWindow::selectVertexShader() { fileBrowser->onChange.reset(); - fileBrowser->onActivate = bind(&VideoSettingsWindow::assignVertexShader, this); - fileBrowser->onAccept = bind(&VideoSettingsWindow::assignVertexShader, this); + fileBrowser->onActivate = { &VideoSettingsWindow::assignVertexShader, this }; + fileBrowser->onAccept = { &VideoSettingsWindow::assignVertexShader, this }; fileBrowser->setWindowTitle("Select Vertex Shader"); fileBrowser->setPath(config().path.current.shader); fileBrowser->setNameFilters("All files (*)"); diff --git a/src/ui_sdl/main.cpp b/src/ui_sdl/main.cpp index a2707568..87be08ec 100644 --- a/src/ui_sdl/main.cpp +++ b/src/ui_sdl/main.cpp @@ -86,13 +86,32 @@ int main(int argc, char *argv[]) { snes_load_cartridge_normal(0, rom_data, rom_size); delete[] rom_data; + unsigned serial_size = snes_serialize_size(); + uint8_t *serial_data = new uint8_t[serial_size]; + snes_runtosave(); + snes_serialize(serial_data, serial_size); + + snes_cheat_reset(); +//snes_cheat_set(0, true, "DD32-6DAD"); + while(true) { SDL_Event event; SDL_PollEvent(&event); if(event.type == SDL_QUIT) break; + if(event.type == SDL_KEYDOWN) { + if(event.key.keysym.sym == SDLK_ESCAPE) { + break; + } else if(event.key.keysym.sym == SDLK_F2) { + snes_runtosave(); + snes_serialize(serial_data, serial_size); + } else if(event.key.keysym.sym == SDLK_F4) { + snes_unserialize(serial_data, serial_size); + } + } snes_run(); } + delete[] serial_data; snes_unload(); snes_term(); return 0;