bsnes/higan/sfc/cpu/timing.cpp

153 lines
4.0 KiB
C++
Raw Normal View History

auto CPU::dmaCounter() const -> uint {
return (status.dma_counter + hcounter()) & 7;
}
auto CPU::addClocks(uint clocks) -> void {
status.irq_lock = false;
uint ticks = clocks >> 1;
while(ticks--) {
tick();
if(hcounter() & 2) pollInterrupts();
}
step(clocks);
Update to v073r01 release. byuu says: While perhaps not perfect, pretty good is better than nothing ... I've added emulation of auto-joypad poll timing. Going off ikari_01's confirmation of what we suspected, that the strobe happens every 256 clocks, I've set up emulation as follows: Upon reset, our clock counter is reset to zero. At the start of each frame, our poll counter is reset to zero. Every 256 clocks, we call the step_auto_joypad_poll() function. If we are at V=225/240+ (based on overscan setting), we check the poll counter. At zero, we poll the actual controller and set the joypad polling flag in $4212.d0 to 1. From zero through fifteen, we read in one bit for each controller and shift it into the register. At sixteen, we turn off the joypad polling flag. The 256-clock divider allows the start point of polling for each frame to fluctuate wildly like real hardware. I count regardless of auto joypad enable, as per $4212.d0's behavior; but only poll when it's actually enabled. I do not consume any actual time from this polling. I honestly don't know if I even should, or if it manages to do it in the background. If it should consume time, then this most likely happens between opcode edges and we'll have to adjust the code a good bit. All commercial games should continue to work fine, but this will likely break some hacks/translations not tested on hardware. Without the timing emulation, reading $4218-421f before V=~228 would basically give you the valid input controller values of the previous frame. Now, like hardware, it should give you a state that is part previous frame, part current frame shifted into it. Button positions won't be reliable and will shift every 256 clocks. I've also removed the Qt GUI, and renamed ui-phoenix to just ui. This removes 400kb of source code (phoenix is a lean 130kb), and drops the archive size from 564KB to 475KB. Combined with the DSP HLE, and we've knocked off ~570KB of source cruft from the entire project. I am looking forward to not having to specify which GUI is included anymore.
2010-12-27 07:29:57 +00:00
status.auto_joypad_clock += clocks;
if(status.auto_joypad_clock >= 256) {
status.auto_joypad_clock -= 256;
stepAutoJoypadPoll();
Update to v073r01 release. byuu says: While perhaps not perfect, pretty good is better than nothing ... I've added emulation of auto-joypad poll timing. Going off ikari_01's confirmation of what we suspected, that the strobe happens every 256 clocks, I've set up emulation as follows: Upon reset, our clock counter is reset to zero. At the start of each frame, our poll counter is reset to zero. Every 256 clocks, we call the step_auto_joypad_poll() function. If we are at V=225/240+ (based on overscan setting), we check the poll counter. At zero, we poll the actual controller and set the joypad polling flag in $4212.d0 to 1. From zero through fifteen, we read in one bit for each controller and shift it into the register. At sixteen, we turn off the joypad polling flag. The 256-clock divider allows the start point of polling for each frame to fluctuate wildly like real hardware. I count regardless of auto joypad enable, as per $4212.d0's behavior; but only poll when it's actually enabled. I do not consume any actual time from this polling. I honestly don't know if I even should, or if it manages to do it in the background. If it should consume time, then this most likely happens between opcode edges and we'll have to adjust the code a good bit. All commercial games should continue to work fine, but this will likely break some hacks/translations not tested on hardware. Without the timing emulation, reading $4218-421f before V=~228 would basically give you the valid input controller values of the previous frame. Now, like hardware, it should give you a state that is part previous frame, part current frame shifted into it. Button positions won't be reliable and will shift every 256 clocks. I've also removed the Qt GUI, and renamed ui-phoenix to just ui. This removes 400kb of source code (phoenix is a lean 130kb), and drops the archive size from 564KB to 475KB. Combined with the DSP HLE, and we've knocked off ~570KB of source cruft from the entire project. I am looking forward to not having to specify which GUI is included anymore.
2010-12-27 07:29:57 +00:00
}
if(!status.dram_refreshed && hcounter() >= status.dram_refresh_position) {
status.dram_refreshed = true;
addClocks(40);
}
#if defined(DEBUGGER)
synchronizeSMP();
synchronizePPU();
synchronizeCoprocessors();
#endif
}
//called by ppu.tick() when Hcounter=0
auto CPU::scanline() -> void {
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
status.line_clocks = lineclocks();
//forcefully sync S-CPU to other processors, in case chips are not communicating
synchronizeSMP();
synchronizePPU();
synchronizeCoprocessors();
if(vcounter() == 0) {
//HDMA init triggers once every frame
status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdma_init_triggered = false;
Update to v073r01 release. byuu says: While perhaps not perfect, pretty good is better than nothing ... I've added emulation of auto-joypad poll timing. Going off ikari_01's confirmation of what we suspected, that the strobe happens every 256 clocks, I've set up emulation as follows: Upon reset, our clock counter is reset to zero. At the start of each frame, our poll counter is reset to zero. Every 256 clocks, we call the step_auto_joypad_poll() function. If we are at V=225/240+ (based on overscan setting), we check the poll counter. At zero, we poll the actual controller and set the joypad polling flag in $4212.d0 to 1. From zero through fifteen, we read in one bit for each controller and shift it into the register. At sixteen, we turn off the joypad polling flag. The 256-clock divider allows the start point of polling for each frame to fluctuate wildly like real hardware. I count regardless of auto joypad enable, as per $4212.d0's behavior; but only poll when it's actually enabled. I do not consume any actual time from this polling. I honestly don't know if I even should, or if it manages to do it in the background. If it should consume time, then this most likely happens between opcode edges and we'll have to adjust the code a good bit. All commercial games should continue to work fine, but this will likely break some hacks/translations not tested on hardware. Without the timing emulation, reading $4218-421f before V=~228 would basically give you the valid input controller values of the previous frame. Now, like hardware, it should give you a state that is part previous frame, part current frame shifted into it. Button positions won't be reliable and will shift every 256 clocks. I've also removed the Qt GUI, and renamed ui-phoenix to just ui. This removes 400kb of source code (phoenix is a lean 130kb), and drops the archive size from 564KB to 475KB. Combined with the DSP HLE, and we've knocked off ~570KB of source cruft from the entire project. I am looking forward to not having to specify which GUI is included anymore.
2010-12-27 07:29:57 +00:00
status.auto_joypad_counter = 0;
}
//DRAM refresh occurs once every scanline
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dmaCounter();
status.dram_refreshed = false;
//HDMA triggers once every visible scanline
if(vcounter() < ppu.vdisp()) {
status.hdma_position = 1104;
status.hdma_triggered = false;
}
}
auto CPU::aluEdge() -> void {
if(alu.mpyctr) {
alu.mpyctr--;
if(status.rddiv & 1) status.rdmpy += alu.shift;
status.rddiv >>= 1;
alu.shift <<= 1;
}
if(alu.divctr) {
alu.divctr--;
status.rddiv <<= 1;
alu.shift >>= 1;
if(status.rdmpy >= alu.shift) {
status.rdmpy -= alu.shift;
status.rddiv |= 1;
}
}
}
auto CPU::dmaEdge() -> void {
//H/DMA pending && DMA inactive?
//.. Run one full CPU cycle
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
//.. DMA pending && DMA enabled ? DMA sync + DMA run
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
//.. Run one bus CPU cycle
//.. CPU sync
if(status.dma_active == true) {
if(status.hdma_pending) {
status.hdma_pending = false;
if(hdmaEnabledChannels()) {
if(!dmaEnabledChannels()) {
dmaAddClocks(8 - dmaCounter());
}
status.hdma_mode == 0 ? hdmaInit() : hdmaRun();
if(!dmaEnabledChannels()) {
addClocks(status.clock_count - (status.dma_clocks % status.clock_count));
status.dma_active = false;
}
}
}
if(status.dma_pending) {
status.dma_pending = false;
if(dmaEnabledChannels()) {
dmaAddClocks(8 - dmaCounter());
dmaRun();
addClocks(status.clock_count - (status.dma_clocks % status.clock_count));
status.dma_active = false;
}
}
}
if(status.hdma_init_triggered == false && hcounter() >= status.hdma_init_position) {
status.hdma_init_triggered = true;
hdmaInitReset();
if(hdmaEnabledChannels()) {
status.hdma_pending = true;
status.hdma_mode = 0;
}
}
if(status.hdma_triggered == false && hcounter() >= status.hdma_position) {
status.hdma_triggered = true;
if(hdmaActiveChannels()) {
status.hdma_pending = true;
status.hdma_mode = 1;
}
}
if(status.dma_active == false) {
if(status.dma_pending || status.hdma_pending) {
status.dma_clocks = 0;
status.dma_active = true;
}
}
}
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
//test one cycle early to simulate two-stage pipeline of x816 CPU.
//
//status.irq_lock is used to simulate hardware delay before interrupts can
//trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void {
if(!status.irq_lock) {
status.nmi_pending |= nmiTest();
status.irq_pending |= irqTest();
status.interrupt_pending = (status.nmi_pending || status.irq_pending);
}
}