- 
                Notifications
    You must be signed in to change notification settings 
- Fork 176
Improve auto-joypad polling timing #351
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
Improve auto-joypad polling timing #351
Conversation
auto-joypad test changes: fixes 01, 05, 10, 12, 14 still bad: 06, 08, 09 improved: 07, 13
broke in previous commit probably by random chance; this is probably more correct anyways
"clear-autojoy-after-autojoy-active" failed on the previous commit but worked before. Implementing this is clearly required to be accurate
fixes tests 06, 07, 08, 09, 13... mostly at least
        
          
                bsnes/sfc/cpu/serialization.cpp
              
                Outdated
          
        
      | s.integer(status.autoJoypadPort0); | ||
| s.integer(status.autoJoypadPort1); | ||
|  | ||
| s.boolean(status.cpuLatch); | ||
| s.boolean(status.autoJoypadLatch); | ||
|  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change what's being serialised, we should also bump the serialisation version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, and that's one thing I was contemplating while making the serialization and struct changes. But I looked at the last time serialization was changed (34af95d) and the version wasn't bumped after that either. Should I just bump it to something like 115.1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the previous idea was that the serialisation version only needed to be changed once per release, so if it had been changed since the previous release there was no need to change it again. Given releases are a lot further apart now, it seems prudent to change the number more regularly.
115.1 seems like a good choice, yeah.
| io.joy2 = 0; | ||
| io.joy3 = 0; | ||
| io.joy4 = 0; | ||
| if(status.autoJoypadCounter != 1 && !io.autoJoypadPoll) { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the status.autoJoypadCounter != 1 condition here? If the counter is 0 or 2-33, and auto-polling is disabled, we can skip the rest of the process, but if the counter is 1, we just wait until the counter becomes 2 and then disable auto-polling? If a game enables auto-polling after the counter ticks 1, and we've skipped the latching inputs and clearing the shift registers, does the SNES start shifting a new copy of the previously-latched inputs into the shift registers? Does this not happen if the game enables auto-polling on ticks 2 or later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right that this seems peculiar but it does appear to be accurate. Without this condition the tests 12-blip-autojoy-timing-test and 13-blip-autojoy-latches-joypad-test no longer match hardware results.
The implication of this condition is that disabling auto-joypad polling during this one cycle has no effect, so it could for example be toggled off and toggled on again without affecting the auto-joypad polling sequence at all during this specific timing window.
As far as I understand that's precisely what the blip-autojoy-timing-test intends to test: It waits until HVBJOY reports auto-joypad polling to be active, disables auto-joypad polling per NMITIMEN write, waits a certain amount of cpu cycles and then re-enables it. Of course this depends on HVBJOY and NMITIMEN behavior, but changing those as well causes other tests to break which leads me to believe that this combination is most accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fiskbit and I noticed that temporary disabling (NMITIMEN = 0) then quickly re-enabling auto-joy (NMITIMEN = 1) would not stop automatic joypad reading.
12-blip-autojoy-timing-test was used to determine if is a 128-clock or 256-clock window from when HVBJOY reports auto-joypad active.  My results indicate it is a 256-clock window.  If the enable auto-joy NMITIMEN write is > 256-clocks after auto-joypad starts then automatic joypad read stops.
Quoting my notes from that day:
It's matches what I expected for the 3chip. I didn't know if it was a 128 clock or 256 clock before autojoy could be re-enabled.
If my math is right, there's N + 6 fastROM cycles (48 mcycles) between HVBJOY reports auto-joy active and the NMITIMEN=1 write.
- 206 delay = 254 m-cycles
- 208 delay = 256 m-cycles.
For the 13-blip-autojoy-latches-joypad-test, Fiskbit asked for a test that checks if the S-CPU latches the controller twice if the NMITIMEN auto-joypad enable flag is blipped (disabled then quickly enabled).  Details about the test can be found in the test's source code.
| I'm sorry it's taken me so long to review this PR; it wasn't just a matter of finding time, but finding large blocks of time so that I could give it the attention it deserved. As best I can understand it, this patch is introducing these changes: Previously, "sixteen bits are shifted into joy{1-4}, one bit per 256 clocks", so every 256 clocks we'd read a bit and shift it in immediately. However, what seems to happen in hardware is that we read one bit, then 128 clocks later we shift it in, then 128 clocks later we read the next bit. So we introduce  I'm a little surprised that any software would notice the difference, but this seems much more like the sort of thing hardware would do. Previously, automatic joypad polling was controlled by the  That makes me wonder what happens if the CPU keeps the latch open while the auto-poller is trying to read all the buttons. Fullsnes says "Be sure that Out0 in Port 4016h is zero (otherwise the shift register gets stuck on the first bit, ie. all 16bit will be equal to the B-button state." and I think bsnes implements that. Again, this makes much more sense as a possible hardware implementation than what bsnes previously had. It looks like the  Previously, when the game wrote to $4200, it would set  Is that more or less correct? | 
| For completeness, checking the list of games in #322: 
 ...and the 01-auto-joy-timing-test gives: 
 ...which is extremely close to the real-hardware result. If my understanding in the previous comment is correct, I'm happy to merge this. | 
| 
 Yes, that seems like a good description of the changes. I realize I could have explained more in the PR description, but I'm glad it was understandable regardless. Just a couple notes: 
 It's possible this is coincidental due to the fact that the game spams  However there is a delay between checking the auto-joypad polling state and the actual read (TOCTOU problem). In rare cases, the last  Here's an excerpt of a printf-log I created showcasing the problematic sequence of events: 
 This is true, but this is also true for the very start of auto-joypad polling (status.autoJoypadCounter == 0). If the sequence was just started and io.autoJoypadPoll is false then the controller latch will be closed (if it isn't already) and  | 
| Thank you to undisbeliever for doing the research, and to Morilli for figuring out how to update bsnes to match! | 
| Looks like Zenkoku Koukou Soccer 2 has issues (ares-emulator/ares#970), but that's good progress. thanks for everyone who was involved! | 
| 
 That's the corresponding issue in ares, not bsnes. This problem is fixed in bsnes but someone would have to port the changes to ares for it to be fixed there as well. | 
| I've tested this using the latest bsnes nightly version, that's why I mentioned it. ;) It's buggy in bsnes now. Occasionally, it ignores the pause command and just skips the screen. | 
| 
 Interesting, I can reproduce that. I expected it to behave similar to Spellcraft where holding down the B button would eventually dismiss the menu, but here it seems that the game never unpauses from holding the start button if it entered the pause screen properly, but sometimes the pause screen will be immediately dismissed. While I can't reproduce this in snes9x, the same thing happens in mesen. I would definitely be interested in seeing someone test this on hardware to verify what's actually "correct" here. | 
| 
 I got a friend with a "real" 2/1/3 Super Famicom to test this game... and apparently both behaviours are correct. Sometimes when you press the Start button, it pauses and unpauses instantly, other times it pauses and stays paused. You can literally sit there tapping the Start button and sometimes it'll unpause and sometimes it won't - it's not just uninitialised memory or an accident of clock phase. I guess there's some more nuance to how often the auto-polling happens, or how fast it works, that we haven't yet captured. | 
| OK, I tested this game in the latest bsnes nightly myself, and once I figured out how to get in-game, I repeatedly held the Start button for about a second, and recorded how many times the game stayed paused ("solid"), and how many times it immediately unpaused ("flaky"): 
 My friend with the real SNES reports that it behaved very similarly. | 
Backport: bsnes-emu/bsnes#351 by Morilli This commit breaks state compatibility with standalone bsnes. Previous to this commit, the raw state data was still compatible, but would require some manipulation on the user's part as the standalone creates compressed states and encapsulates them in .bsz format. As standalone bsnes also broke the state format for this change, it made sense to use this opportunity to version the states differently in bsnes-jg. Effort has been taken to allow loading states created in previous versions of the emu, with the caveat that there may be some unexpected behaviour, though this is unlikely in nearly all cases.

This fixes #322.
In addition, these changes improve results in a number of test roms from #322 (comment).
Most of these test roms aren't "good or bad" and instead show trends and general behavior. In some cases it's clear when the emulator behavior is wrong and in other cases it's not so clear. The displayed values are not fixed and change frame-by-frame. I think it is most important to roughly match the hardware result. In some cases there may not even be one true correct result as even different hardware differs in results.
That being said, on current master the following tests are clearly bad:
With the changes in this PR, all of these tests seem "fixed", at least mostly.
Notable remaining differences with hardware:
FFFFin the column on the right. Should be 3-6 from the top, is 4-8.FFFFand does not flicker with occasional0000(it does on hardware).Bin test 08, the fourth row underJOY 1will never showFFFF, it showsBFFFat most.Bthe third row underJOY 1can never showC000, onlyE000andF000.Yin test 10, the left-most column underJOY 1may show 0s, but the video from hardware only shows 4s and 8s.These are mainly minor off-by-1 problems, and roughly half of these also show in Mesen. I think the current results are good enough for now.