update cycle mgmt for vblank hblank, access time for routines

This commit is contained in:
scawful
2024-04-24 15:41:00 -04:00
parent 5ce7ca9148
commit 96fbe066ab
2 changed files with 118 additions and 87 deletions

View File

@@ -61,7 +61,7 @@ void SNES::Reset(bool hard) {
h_irq_enabled_ = false; h_irq_enabled_ = false;
v_irq_enabled_ = false; v_irq_enabled_ = false;
nmi_enabled_ = false; nmi_enabled_ = false;
h_timer_ = 0x1ff; h_timer_ = 0x1ff * 4;
v_timer_ = 0x1ff; v_timer_ = 0x1ff;
in_nmi_ = false; in_nmi_ = false;
irq_condition_ = false; irq_condition_ = false;
@@ -77,6 +77,8 @@ void SNES::Reset(bool hard) {
divide_result_ = 0x101; divide_result_ = 0x101;
fast_mem_ = false; fast_mem_ = false;
memory_.set_open_bus(0); memory_.set_open_bus(0);
nextHoriEvent = 16;
build_accesstime(false);
} }
void SNES::RunFrame() { void SNES::RunFrame() {
@@ -128,94 +130,103 @@ void SNES::HandleInput() {
void SNES::RunCycle() { void SNES::RunCycle() {
cycles_ += 2; cycles_ += 2;
// check for h/v timer irq's // check for h/v timer irq's
bool condition = ((v_irq_enabled_ || h_irq_enabled_) && bool condition = (
(memory_.v_pos() == v_timer_ || !v_irq_enabled_) && (v_irq_enabled_ || h_irq_enabled_) &&
(memory_.h_pos() == h_timer_ * 4 || !h_irq_enabled_)); (memory_.v_pos() == v_timer_ || !v_irq_enabled_) &&
if (!irq_condition_ && condition) { (memory_.h_pos() == h_timer_ || !h_irq_enabled_)
);
if(!irq_condition_ && condition) {
in_irq_ = true; in_irq_ = true;
cpu_.SetIrq(true); cpu_.SetIrq(true);
} }
irq_condition_ = condition; irq_condition_ = condition;
// increment position; must come after irq checks! (hagane, cybernator)
// 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
memory_.set_h_pos(memory_.h_pos() + 2); memory_.set_h_pos(memory_.h_pos() + 2);
if (!memory_.pal_timing()) { // handle positional stuff
// line 240 of odd frame with no interlace is 4 cycles shorter if (memory_.h_pos() == nextHoriEvent) {
if ((memory_.h_pos() == 1360 && memory_.v_pos() == 240 && switch (memory_.h_pos()) {
!ppu_.even_frame && !ppu_.frame_interlace) || case 16: {
memory_.h_pos() == 1364) { nextHoriEvent = 512;
memory_.set_h_pos(0); if(memory_.v_pos() == 0) memory_.init_hdma_request();
memory_.set_v_pos(memory_.v_pos() + 1); } break;
// even interlace frame is 263 lines case 512: {
if ((memory_.v_pos() == 262 && nextHoriEvent = 1104;
(!ppu_.frame_interlace || !ppu_.even_frame)) || // render the line halfway of the screen for better compatibility
memory_.v_pos() == 263) { if(!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos());
memory_.set_v_pos(0); } break;
frames_++; case 1104: {
} if(!in_vblank_) memory_.run_hdma_request();
} if(!memory_.pal_timing()) {
} else { // line 240 of odd frame with no interlace is 4 cycles shorter
// line 311 of odd frame with interlace is 4 cycles longer // if((memory_.h_pos() == 1360 && memory_.v_pos() == 240 && !ppu_evenFrame() && !ppu_frameInterlace()) || memory_.h_pos() == 1364) {
if ((memory_.h_pos() == 1364 && nextHoriEvent = (memory_.v_pos() == 240 && !ppu_.even_frame && !ppu_.frame_interlace) ? 1360 : 1364;
(memory_.v_pos() != 311 || ppu_.even_frame || } else {
!ppu_.frame_interlace)) || // line 311 of odd frame with interlace is 4 cycles longer
memory_.h_pos() == 1368) { // if((memory_.h_pos() == 1364 && (memory_.v_pos() != 311 || ppu_evenFrame() || !ppu_frameInterlace())) || memory_.h_pos() == 1368)
memory_.set_h_pos(0); nextHoriEvent = (memory_.v_pos() != 311 || ppu_.even_frame || !ppu_.frame_interlace) ? 1364 : 1368;
memory_.set_v_pos(memory_.v_pos() + 1); }
// even interlace frame is 313 lines } break;
if ((memory_.v_pos() == 312 && case 1360:
(!ppu_.frame_interlace || !ppu_.even_frame)) || case 1364:
memory_.v_pos() == 313) { case 1368: { // this is the end (of the h-line)
memory_.set_v_pos(0); nextHoriEvent = 16;
frames_++;
} 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) { void SNES::RunCycles(int cycles) {
@@ -386,7 +397,7 @@ void SNES::WriteReg(uint16_t adr, uint8_t val) {
in_irq_ = false; in_irq_ = false;
cpu_.SetIrq(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_) { if (!nmi_enabled_ && (val & 0x80) && in_nmi_) {
cpu_.Nmi(); cpu_.Nmi();
} }
@@ -506,20 +517,26 @@ int SNES::GetAccessTime(uint32_t adr) {
} }
uint8_t SNES::CpuRead(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); memory::dma::HandleDma(this, &memory_, cycles);
RunCycles(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) { void SNES::CpuWrite(uint32_t adr, uint8_t val) {
int cycles = GetAccessTime(adr); cpu_.set_int_delay(false);
memory::dma::HandleDma(this, &memory_, cycles); const int cycles = access_time[adr]; // GetAccessTime(adr);
RunCycles(cycles); memory::dma::HandleDma(this, &memory_, cycles_);
RunCycles(cycles_);
Write(adr, val); Write(adr, val);
} }
void SNES::CpuIdle(bool waiting) { void SNES::CpuIdle(bool waiting) {
cpu_.set_int_delay(false);
memory::dma::HandleDma(this, &memory_, 6); memory::dma::HandleDma(this, &memory_, 6);
RunCycles(6); RunCycles(6);
} }

View File

@@ -71,6 +71,19 @@ class SNES {
auto ppu() -> video::Ppu& { return ppu_; } auto ppu() -> video::Ppu& { return ppu_; }
auto apu() -> audio::Apu& { return apu_; } auto apu() -> audio::Apu& { return apu_; }
auto Memory() -> memory::MemoryImpl& { return memory_; } auto Memory() -> memory::MemoryImpl& { return memory_; }
auto get_ram() -> uint8_t* { return ram; }
auto mutable_cycles() -> uint64_t& { return cycles_; }
std::vector<uint8_t> 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: private:
// Components of the SNES // Components of the SNES
@@ -103,6 +116,7 @@ class SNES {
uint64_t cycles_ = 0; uint64_t cycles_ = 0;
uint64_t sync_cycle_ = 0; uint64_t sync_cycle_ = 0;
double apu_catchup_cycles_; double apu_catchup_cycles_;
uint32_t nextHoriEvent;
// Nmi / Irq // Nmi / Irq
bool h_irq_enabled_ = false; bool h_irq_enabled_ = false;