From 96fbe066abbce1c927c0d3a216281b32a39a5d7c Mon Sep 17 00:00:00 2001 From: scawful Date: Wed, 24 Apr 2024 15:41:00 -0400 Subject: [PATCH] update cycle mgmt for vblank hblank, access time for routines --- src/app/emu/snes.cc | 191 ++++++++++++++++++++++++-------------------- src/app/emu/snes.h | 14 ++++ 2 files changed, 118 insertions(+), 87 deletions(-) diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index 90dbe245..bdd103d3 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -61,7 +61,7 @@ void SNES::Reset(bool hard) { h_irq_enabled_ = false; v_irq_enabled_ = false; nmi_enabled_ = false; - h_timer_ = 0x1ff; + h_timer_ = 0x1ff * 4; v_timer_ = 0x1ff; in_nmi_ = false; irq_condition_ = false; @@ -77,6 +77,8 @@ void SNES::Reset(bool hard) { divide_result_ = 0x101; fast_mem_ = false; memory_.set_open_bus(0); + nextHoriEvent = 16; + build_accesstime(false); } void SNES::RunFrame() { @@ -128,94 +130,103 @@ 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_ * 4 || !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; - - // handle positional stuff - if (memory_.h_pos() == 0) { - // end of hblank, do most v_pos_-tests - bool startingVblank = 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 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; - } - if (startingVblank) { - // if we are starting vblank - ppu_.HandleVblank(); - in_vblank_ = true; - in_nmi_ = true; - if (auto_joy_read_) { - // TODO: this starts a little after start of vblank - auto_joy_timer_ = 4224; - HandleInput(); - } - if (nmi_enabled_) { - cpu_.Nmi(); - } - } - } else if (memory_.h_pos() == 16) { - if (memory_.v_pos() == 0) memory_.init_hdma_request(); - } else if (memory_.h_pos() == 512) { - // render the line halfway of the screen for better compatibility - if (!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos()); - } else if (memory_.h_pos() == 1104) { - if (!in_vblank_) memory_.run_hdma_request(); - } - - // handle autoJoyRead-timer - if (auto_joy_timer_ > 0) auto_joy_timer_ -= 2; - - // increment position + // increment position; must come after irq checks! (hagane, cybernator) memory_.set_h_pos(memory_.h_pos() + 2); - 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_.even_frame && !ppu_.frame_interlace) || - memory_.h_pos() == 1364) { - memory_.set_h_pos(0); - memory_.set_v_pos(memory_.v_pos() + 1); - // even interlace frame is 263 lines - if ((memory_.v_pos() == 262 && - (!ppu_.frame_interlace || !ppu_.even_frame)) || - memory_.v_pos() == 263) { - memory_.set_v_pos(0); - frames_++; - } - } - } else { - // line 311 of odd frame with interlace is 4 cycles longer - if ((memory_.h_pos() == 1364 && - (memory_.v_pos() != 311 || ppu_.even_frame || - !ppu_.frame_interlace)) || - memory_.h_pos() == 1368) { - memory_.set_h_pos(0); - memory_.set_v_pos(memory_.v_pos() + 1); - // even interlace frame is 313 lines - if ((memory_.v_pos() == 312 && - (!ppu_.frame_interlace || !ppu_.even_frame)) || - memory_.v_pos() == 313) { - memory_.set_v_pos(0); - frames_++; - } + // handle positional stuff + if (memory_.h_pos() == nextHoriEvent) { + switch (memory_.h_pos()) { + case 16: { + nextHoriEvent = 512; + if(memory_.v_pos() == 0) memory_.init_hdma_request(); + } break; + case 512: { + nextHoriEvent = 1104; + // render the line halfway of the screen for better compatibility + 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()) { + // 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; + } 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; + } + } break; + case 1360: + case 1364: + case 1368: { // this is the end (of the h-line) + nextHoriEvent = 16; + + memory_.set_h_pos(0); + memory_.set_v_pos(memory_.v_pos() + 1); + 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) { + + memory_.set_v_pos(0); + frames_++; + } + } else { + // even interlace frame is 313 lines + 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) { + // 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; + } + if(startingVblank) { + // 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(); + // we are starting vblank + ppu_.HandleVblank(); + in_vblank_ = true; + in_nmi_ = true; + if(auto_joy_read_) { + // TODO: this starts a little after start of vblank + auto_joy_timer_ = 4224; + HandleInput(); + } + if(nmi_enabled_) { + cpu_.Nmi(); + } + } + } break; } } + // handle auto_joy_read_-timer + if(auto_joy_timer_ > 0) auto_joy_timer_ -= 2; } void SNES::RunCycles(int cycles) { @@ -386,7 +397,7 @@ void SNES::WriteReg(uint16_t adr, uint8_t val) { in_irq_ = false; cpu_.SetIrq(false); } - // if nmi is enabled while inNmi is still set, immediately generate nmi + // if nmi is enabled while in_nmi_ is still set, immediately generate nmi if (!nmi_enabled_ && (val & 0x80) && in_nmi_) { cpu_.Nmi(); } @@ -506,20 +517,26 @@ int SNES::GetAccessTime(uint32_t adr) { } uint8_t SNES::CpuRead(uint32_t adr) { - int cycles = GetAccessTime(adr); + cpu_.set_int_delay(false); + const int cycles = access_time[adr] - 4; memory::dma::HandleDma(this, &memory_, cycles); RunCycles(cycles); - return Read(adr); + uint8_t rv = Read(adr); + memory::dma::HandleDma(this, &memory_, 4); + RunCycles(4); + return rv; } void SNES::CpuWrite(uint32_t adr, uint8_t val) { - int cycles = GetAccessTime(adr); - memory::dma::HandleDma(this, &memory_, cycles); - RunCycles(cycles); + cpu_.set_int_delay(false); + const int cycles = access_time[adr]; // GetAccessTime(adr); + memory::dma::HandleDma(this, &memory_, cycles_); + RunCycles(cycles_); Write(adr, val); } void SNES::CpuIdle(bool waiting) { + cpu_.set_int_delay(false); memory::dma::HandleDma(this, &memory_, 6); RunCycles(6); } diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index c0728ded..aa0de97f 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -71,6 +71,19 @@ class SNES { 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_; } + + 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 @@ -103,6 +116,7 @@ class SNES { uint64_t cycles_ = 0; uint64_t sync_cycle_ = 0; double apu_catchup_cycles_; + uint32_t nextHoriEvent; // Nmi / Irq bool h_irq_enabled_ = false;