Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sfc: Really fix automatic joypad polling, for sure. #171

Merged
merged 1 commit into from
Apr 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions higan/sfc/cpu/cpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,7 @@ struct CPU : WDC65816, Thread, PPUcounter {
bool hdmaPending = 0;
bool hdmaMode = 0; //0 = init, 1 = run

bool autoJoypadActive = 0;
bool autoJoypadLatch = 0;
uint autoJoypadCounter = 0;
uint autoJoypadCounter = 33; //state machine; 4224 / 128 = 33 (inactive)
} status;

struct IO {
Expand Down
2 changes: 1 addition & 1 deletion higan/sfc/cpu/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ auto CPU::writeCPU(uint24 address, uint8 data) -> void {

case 0x4200: //NMITIMEN
io.autoJoypadPoll = data.bit(0);
if(!io.autoJoypadPoll) status.autoJoypadLatch = 0;
if(!io.autoJoypadPoll) status.autoJoypadCounter = 33; // Disable auto-joypad read
nmitimenUpdate(data);
return;

Expand Down
2 changes: 0 additions & 2 deletions higan/sfc/cpu/serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(status.hdmaPending);
s.integer(status.hdmaMode);

s.integer(status.autoJoypadActive);
s.integer(status.autoJoypadLatch);
s.integer(status.autoJoypadCounter);

s.integer(io.wramAddress);
Expand Down
76 changes: 40 additions & 36 deletions higan/sfc/cpu/timing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ inline auto CPU::dmaCounter() const -> uint {

//joypad auto-poll clock divider
inline auto CPU::joypadCounter() const -> uint {
return counter.cpu & 255;
return counter.cpu & 127;
}

auto CPU::step(uint clocks) -> void {
Expand Down Expand Up @@ -61,8 +61,7 @@ auto CPU::scanline() -> void {
status.hdmaSetupPosition = (io.version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaSetupTriggered = 0;

status.autoJoypadLatch = 0;
status.autoJoypadCounter = 0;
status.autoJoypadCounter = 33; //33 = inactive
}

//DRAM refresh occurs once every scanline
Expand Down Expand Up @@ -137,7 +136,7 @@ alwaysinline auto CPU::dmaEdge() -> void {
}
}

//called every 256 clocks; see CPU::step()
//called every 128 clocks from inside the CPU::stepOnce() function
alwaysinline auto CPU::joypadEdge() -> void {
// Auto-Joypad-Read begins between dots 32.5 and 95.5 of the first V-Blank scanline,
// and ends 4224 master cycles later. (Anomie's timing doc)
Expand All @@ -148,43 +147,48 @@ alwaysinline auto CPU::joypadEdge() -> void {
// Todo: 4224 cycles would be 33*128 and not 16*256, hardware testing shows that higan ends auto-joypad read too early
// Todo: What happens to auto-joypad read when you read/write 4816/4817 while it is in progress?

if(vcounter() == ppu.vdisp() && hcounter() >= 130 && hcounter() <= 384) {
// start auto joypad read on the first scanline of vblank
if(!status.autoJoypadCounter) {
status.autoJoypadLatch = io.autoJoypadPoll;

if(status.autoJoypadLatch) {
//shift registers are cleared at start of auto joypad polling
io.joy1 = 0;
io.joy2 = 0;
io.joy3 = 0;
io.joy4 = 0;
}
}
if(!io.autoJoypadPoll) return;

if(status.autoJoypadLatch && !status.autoJoypadCounter) {
controllerPort1.latch(1);
controllerPort2.latch(1);
controllerPort1.latch(0);
controllerPort2.latch(0);
}
//polling can only be started once per frame on the first line of vblank between hdot 130 and 384
//Todo: 256 is used here because the function is called every 128 clocks, instead of 256
// this needs to be tested
//it cannot be restarted, once disabled, for the frame
if(vcounter() == ppu.vdisp() && hcounter() >= 130 && hcounter() <= 256) {
//begin new polling sequence
status.autoJoypadCounter = 0;
}

if(!status.autoJoypadLatch) {
status.autoJoypadActive = 0;
} else {
status.autoJoypadActive = status.autoJoypadCounter <= 15;
//stop after polling has been completed for this frame
if(status.autoJoypadCounter >= 33) return;

if(status.autoJoypadActive) {
uint2 port0 = controllerPort1.data();
uint2 port1 = controllerPort2.data();
if(status.autoJoypadCounter == 0) {
//latch controller states on the first polling cycle
controllerPort1.latch(1);
controllerPort2.latch(1);
}

io.joy1 = io.joy1 << 1 | port0.bit(0);
io.joy2 = io.joy2 << 1 | port1.bit(0);
io.joy3 = io.joy3 << 1 | port0.bit(1);
io.joy4 = io.joy4 << 1 | port1.bit(1);
}
if(status.autoJoypadCounter == 1) {
//release latch and begin reading on the second cycle
controllerPort1.latch(0);
controllerPort2.latch(0);

status.autoJoypadCounter++;
//shift registers are cleared to zero at start of auto-joypad polling
io.joy1 = 0;
io.joy2 = 0;
io.joy3 = 0;
io.joy4 = 0;
}

if(status.autoJoypadCounter >= 2 && !(status.autoJoypadCounter & 1)) {
//sixteen bits are shifted into joy{1-4}, one bit per 256 clocks
uint2 port0 = controllerPort1.data();
uint2 port1 = controllerPort2.data();

io.joy1 = io.joy1 << 1 | port0.bit(0);
io.joy2 = io.joy2 << 1 | port1.bit(0);
io.joy3 = io.joy3 << 1 | port0.bit(1);
io.joy4 = io.joy4 << 1 | port1.bit(1);
}

status.autoJoypadCounter++;
}