diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index bdd103d3..97dc9492 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -19,22 +19,29 @@ namespace yaze { namespace app { namespace emu { -void SNES::Init(Rom& rom) { - // Initialize CPU +namespace { +void input_latch(Input* input, bool value) { + input->latch_line_ = value; + if (input->latch_line_) input->latched_state_ = input->current_state_; +} + +uint8_t input_read(Input* input) { + if (input->latch_line_) input->latched_state_ = input->current_state_; + uint8_t ret = input->latched_state_ & 1; + input->latched_state_ >>= 1; + input->latched_state_ |= 0x8000; + return ret; +} +} // namespace + +void SNES::Init(std::vector& rom_data) { + // Initialize the CPU, PPU, and APU cpu_.Init(); - - // Initialize PPU ppu_.Init(); - - // Initialize APU apu_.Init(); // Load the ROM into memory and set up the memory mapping - rom_data = rom.vector(); memory_.Initialize(rom_data); - - // Read the ROM header - // rom_info_ = memory_.ReadRomHeader(); Reset(true); running_ = true; @@ -45,11 +52,10 @@ void SNES::Reset(bool hard) { apu_.Reset(); ppu_.Reset(); memory::dma::Reset(&memory_); - input1.latchLine = false; - input2.latchLine = false; - input1.latchedState = 0; - input2.latchedState = 0; - // cart_reset(); + input1.latch_line_ = false; + input2.latch_line_ = false; + input1.latched_state_ = 0; + input2.latched_state_ = 0; if (hard) memset(ram, 0, sizeof(ram)); ram_adr_ = 0; memory_.set_h_pos(0); @@ -70,15 +76,15 @@ void SNES::Reset(bool hard) { memset(port_auto_read_, 0, sizeof(port_auto_read_)); auto_joy_read_ = false; auto_joy_timer_ = 0; - ppuLatch = false; + ppu_latch_ = false; multiply_a_ = 0xff; multiply_result_ = 0xFE01; divide_a_ = 0xffFF; divide_result_ = 0x101; fast_mem_ = false; memory_.set_open_bus(0); - nextHoriEvent = 16; - build_accesstime(false); + next_horiz_event = 16; + InitAccessTime(false); } void SNES::RunFrame() { @@ -91,25 +97,7 @@ void SNES::RunFrame() { } } -void SNES::CatchUpApu() { - apu_.RunCycles(cycles_); -} - -namespace { - -void input_latch(Input* input, bool value) { - input->latchLine = value; - if (input->latchLine) input->latchedState = input->currentState; -} - -uint8_t input_read(Input* input) { - if (input->latchLine) input->latchedState = input->currentState; - uint8_t ret = input->latchedState & 1; - input->latchedState >>= 1; - input->latchedState |= 0x8000; - return ret; -} -} // namespace +void SNES::CatchUpApu() { apu_.RunCycles(cycles_); } void SNES::HandleInput() { memset(port_auto_read_, 0, sizeof(port_auto_read_)); @@ -130,95 +118,109 @@ void SNES::HandleInput() { void SNES::RunCycle() { cycles_ += 2; + // check for h/v timer irq's - bool condition = ( - (v_irq_enabled_ || h_irq_enabled_) && - (memory_.v_pos() == v_timer_ || !v_irq_enabled_) && - (memory_.h_pos() == h_timer_ || !h_irq_enabled_) - ); - if(!irq_condition_ && condition) { + bool condition = ((v_irq_enabled_ || h_irq_enabled_) && + (memory_.v_pos() == v_timer_ || !v_irq_enabled_) && + (memory_.h_pos() == h_timer_ || !h_irq_enabled_)); + + if (!irq_condition_ && condition) { in_irq_ = true; cpu_.SetIrq(true); } irq_condition_ = condition; + // increment position; must come after irq checks! (hagane, cybernator) memory_.set_h_pos(memory_.h_pos() + 2); + // handle positional stuff - if (memory_.h_pos() == nextHoriEvent) { + if (memory_.h_pos() == next_horiz_event) { switch (memory_.h_pos()) { case 16: { - nextHoriEvent = 512; - if(memory_.v_pos() == 0) memory_.init_hdma_request(); + next_horiz_event = 512; + if (memory_.v_pos() == 0) memory_.init_hdma_request(); } break; case 512: { - nextHoriEvent = 1104; + next_horiz_event = 1104; // render the line halfway of the screen for better compatibility - if(!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos()); + if (!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos()); } break; case 1104: { - if(!in_vblank_) memory_.run_hdma_request(); - if(!memory_.pal_timing()) { + if (!in_vblank_) memory_.run_hdma_request(); + if (!memory_.pal_timing()) { // line 240 of odd frame with no interlace is 4 cycles shorter - // if((memory_.h_pos() == 1360 && memory_.v_pos() == 240 && !ppu_evenFrame() && !ppu_frameInterlace()) || memory_.h_pos() == 1364) { - nextHoriEvent = (memory_.v_pos() == 240 && !ppu_.even_frame && !ppu_.frame_interlace) ? 1360 : 1364; + next_horiz_event = (memory_.v_pos() == 240 && !ppu_.even_frame && + !ppu_.frame_interlace) + ? 1360 + : 1364; } else { // line 311 of odd frame with interlace is 4 cycles longer - // if((memory_.h_pos() == 1364 && (memory_.v_pos() != 311 || ppu_evenFrame() || !ppu_frameInterlace())) || memory_.h_pos() == 1368) - nextHoriEvent = (memory_.v_pos() != 311 || ppu_.even_frame || !ppu_.frame_interlace) ? 1364 : 1368; + next_horiz_event = (memory_.v_pos() != 311 || ppu_.even_frame || + !ppu_.frame_interlace) + ? 1364 + : 1368; } } break; case 1360: case 1364: - case 1368: { // this is the end (of the h-line) - nextHoriEvent = 16; + case 1368: { // this is the end (of the h-line) + next_horiz_event = 16; memory_.set_h_pos(0); memory_.set_v_pos(memory_.v_pos() + 1); - if(!memory_.pal_timing()) { + if (!memory_.pal_timing()) { // even interlace frame is 263 lines - if((memory_.v_pos() == 262 && (!ppu_.frame_interlace || !ppu_.even_frame)) || memory_.v_pos() == 263) { - + if ((memory_.v_pos() == 262 && + (!ppu_.frame_interlace || !ppu_.even_frame)) || + memory_.v_pos() == 263) { memory_.set_v_pos(0); frames_++; } - } else { + } else { // even interlace frame is 313 lines - if((memory_.v_pos() == 312 && (!ppu_.frame_interlace || !ppu_.even_frame)) || memory_.v_pos() == 313) { + if ((memory_.v_pos() == 312 && + (!ppu_.frame_interlace || !ppu_.even_frame)) || + memory_.v_pos() == 313) { memory_.set_v_pos(0); frames_++; } } // end of hblank, do most memory_.v_pos()-tests - bool startingVblank = false; - if(memory_.v_pos() == 0) { + bool starting_vblank = false; + if (memory_.v_pos() == 0) { // end of vblank in_vblank_ = false; in_nmi_ = false; ppu_.HandleFrameStart(); - } else if(memory_.v_pos() == 225) { - // ask the ppu if we start vblank now or at memory_.v_pos() 240 (overscan) - startingVblank = !ppu_.CheckOverscan(); - } else if(memory_.v_pos() == 240){ - // if we are not yet in vblank, we had an overscan frame, set startingVblank - if(!in_vblank_) startingVblank = true; + } else if (memory_.v_pos() == 225) { + // ask the ppu if we start vblank now or at memory_.v_pos() 240 + // (overscan) + starting_vblank = !ppu_.CheckOverscan(); + } else if (memory_.v_pos() == 240) { + // if we are not yet in vblank, we had an overscan frame, set + // starting_vblank + if (!in_vblank_) starting_vblank = true; } - if(startingVblank) { - // catch up the apu at end of emulated frame (we end frame @ start of vblank) + if (starting_vblank) { + // catch up the apu at end of emulated frame (we end frame @ start of + // vblank) CatchUpApu(); - // notify dsp of frame-end, because sometimes dma will extend much further past vblank (or even into the next frame) - // Megaman X2 (titlescreen animation), Tales of Phantasia (game demo), Actraiser 2 (fade-in @ bootup) - // dsp_.newFrame(); + // notify dsp of frame-end, because sometimes dma will extend much + // further past vblank (or even into the next frame) Megaman X2 + // (titlescreen animation), Tales of Phantasia (game demo), Actraiser + // 2 (fade-in @ bootup) + apu_.dsp().NewFrame(); // we are starting vblank ppu_.HandleVblank(); in_vblank_ = true; in_nmi_ = true; - if(auto_joy_read_) { + if (auto_joy_read_) { // TODO: this starts a little after start of vblank auto_joy_timer_ = 4224; HandleInput(); } - if(nmi_enabled_) { + if (nmi_enabled_) { cpu_.Nmi(); } } @@ -226,7 +228,7 @@ void SNES::RunCycle() { } } // handle auto_joy_read_-timer - if(auto_joy_timer_ > 0) auto_joy_timer_ -= 2; + if (auto_joy_timer_ > 0) auto_joy_timer_ -= 2; } void SNES::RunCycles(int cycles) { @@ -252,7 +254,7 @@ void SNES::SyncCycles(bool start, int sync_cycles) { uint8_t SNES::ReadBBus(uint8_t adr) { if (adr < 0x40) { - return ppu_.Read(adr); + return ppu_.Read(adr, ppu_latch_); } if (adr < 0x80) { CatchUpApu(); // catch up the apu before reading @@ -287,7 +289,7 @@ uint8_t SNES::ReadReg(uint16_t adr) { return val | (memory_.open_bus() & 0x3e); } case 0x4213: { - return ppuLatch << 7; // IO-port + return ppu_latch_ << 7; // IO-port } case 0x4214: { return divide_result_ & 0xff; @@ -405,11 +407,11 @@ void SNES::WriteReg(uint16_t adr, uint8_t val) { break; } case 0x4201: { - if (!(val & 0x80) && ppuLatch) { + if (!(val & 0x80) && ppu_latch_) { // latch the ppu - ppu_.Read(0x37); + ppu_.LatchHV(); } - ppuLatch = val & 0x80; + ppu_latch_ = val & 0x80; break; } case 0x4202: { @@ -529,7 +531,7 @@ uint8_t SNES::CpuRead(uint32_t adr) { void SNES::CpuWrite(uint32_t adr, uint8_t val) { cpu_.set_int_delay(false); - const int cycles = access_time[adr]; // GetAccessTime(adr); + const int cycles = access_time[adr]; memory::dma::HandleDma(this, &memory_, cycles_); RunCycles(cycles_); Write(adr, val); @@ -547,6 +549,31 @@ void SNES::SetSamples(int16_t* sample_data, int wanted_samples) { void SNES::SetPixels(uint8_t* pixel_data) { ppu_.PutPixels(pixel_data); } +void SNES::SetButtonState(int player, int button, bool pressed) { + // set key in controller + if (player == 1) { + if (pressed) { + input1.current_state_ |= 1 << button; + } else { + input1.current_state_ &= ~(1 << button); + } + } else { + if (pressed) { + input2.current_state_ |= 1 << button; + } else { + input2.current_state_ &= ~(1 << button); + } + } +} + +void SNES::InitAccessTime(bool recalc) { + int start = (recalc) ? 0x800000 : 0; // recalc only updates fast rom + access_time.resize(0x1000000); + for (int i = start; i < 0x1000000; i++) { + access_time[i] = GetAccessTime(i); + } +} + } // namespace emu } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index aa0de97f..912b3a5b 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -22,10 +22,10 @@ namespace emu { struct Input { uint8_t type; // latchline - bool latchLine; + bool latch_line_; // for controller - uint16_t currentState; // actual state - uint16_t latchedState; + uint16_t current_state_; // actual state + uint16_t latched_state_; }; class SNES { @@ -34,7 +34,7 @@ class SNES { ~SNES() = default; // Initialization - void Init(Rom& rom); + void Init(std::vector& rom_data); void Reset(bool hard = false); // Emulation @@ -65,26 +65,18 @@ class SNES { void SetSamples(int16_t* sample_data, int wanted_samples); void SetPixels(uint8_t* pixel_data); - + void SetButtonState(int player, int button, bool pressed); bool running() const { return running_; } auto cpu() -> Cpu& { return cpu_; } auto ppu() -> video::Ppu& { return ppu_; } auto apu() -> audio::Apu& { return apu_; } auto Memory() -> memory::MemoryImpl& { return memory_; } auto get_ram() -> uint8_t* { return ram; } - auto mutable_cycles() -> uint64_t& { return cycles_; } + void InitAccessTime(bool recalc); std::vector access_time; - void build_accesstime( bool recalc) { - int start = (recalc) ? 0x800000 : 0; // recalc only updates "fastMem" area - access_time.resize(0x1000000); - for (int i = start; i < 0x1000000; i++) { - access_time[i] = GetAccessTime(i); - } -} - private: // Components of the SNES ClockImpl clock_; @@ -116,7 +108,7 @@ class SNES { uint64_t cycles_ = 0; uint64_t sync_cycle_ = 0; double apu_catchup_cycles_; - uint32_t nextHoriEvent; + uint32_t next_horiz_event; // Nmi / Irq bool h_irq_enabled_ = false; @@ -141,7 +133,7 @@ class SNES { uint16_t port_auto_read_[4]; // as read by auto-joypad read bool auto_joy_read_ = false; uint16_t auto_joy_timer_ = 0; - bool ppuLatch; + bool ppu_latch_; bool fast_mem_ = false; };