Spot bug in Mesen and Nintaco

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Spot bug in Mesen and Nintaco

Post by zeroone »

I got a chance to examine the controller code in Spot (see below), but I'm still confused. Nintaco updates the cached controller values once per frame, on the first dot of the pre-render scanline. The only unusual thing I found about the code below is that an interrupt often takes place between the second and third polls. Consequentially, the third polls may happen on a successive frame (after the cached values are updated). But, the cached values only become relevant when the controllers are strobed; it shouldn't matter at what point in the frame cycle that the cached values are updated. I'll study this further, but currently it doesn't look like an invariant value within frames is the culprit.

Code: Select all

                        ; This is a multitap game designed for NES Four Score / NES Satellite
                        ; and even for additional controllers connected to the Famicom expansion port.
                        ; Controllers #1 and #2 end up at [0] and [1], respectively.
                        ; Controllers #3 and #4 end up at [2] and [3], respectively.
                        ; 
                        ; Strobe the multitap to reset it for reading.
0F:FE43  LDX #$01       ; 
0F:FE45  STX $4016      ; [PORT] = 1;
0F:FE48  DEX            ; 
0F:FE49  STX $4016      ; [PORT] = 0;
0F:FE4C  INX            

                        ; Read NES / Famicom controllers #1 and #2 into [0] and [1], respectively.
                        ; 
loop2:                  ; for(X = 1; X >= 0; X--) { 
0F:FE4D  LDY #$08       
loop1:                  ;   for(Y = 8; Y > 0; Y--) {
0F:FE4F  LDA $4016,X    ;     
0F:FE52  AND #$03       ; 
0F:FE54  CMP #$01       ; 
0F:FE56  ROL $00,X      ;     [X] = ([X] << 1) | (([PORT + X] & 3) >= 1);
0F:FE58  DEY            ; 
0F:FE59  BNE $FE4F      ; 
0F:FE5B  DEX            ;   }
0F:FE5C  BEQ $FE4D      ; }
                        
                        ; Read NES multitap controllers #3 and #4 into [6] and [7], respectively.
                        ; Read Famicom controllers #3 and #4 into [4] and [5], respectively.                        
                        ;
0F:FE5E  LDX #$01       ; for(X = 1; X >= 0; X--) {
loop4:                  ;
0F:FE60  LDY #$08       ;   for(Y = 8; Y > 0; Y--) {
loop3:                  ;
0F:FE62  LDA $4016,X    ;     A = [PORT + X];
0F:FE65  LSR A          ;      
0F:FE66  ROL $06,X      ;     [6 + X] = ([6 + X] << 1) | (A & 1);
0F:FE68  LSR A          ;     
0F:FE69  ROL $04,X      ;     [4 + X] = ([4 + X] << 1) | ((A >> 1) & 1);
0F:FE6B  DEY            ; 
0F:FE6C  BNE $FE62      ;   }
0F:FE6E  DEX            ; 
0F:FE6F  BEQ $FE60      ; }

                        ; Read potential NES multitap signatures into [$A] and [$B], expecting $10 and $20 for 
                        ; the input ports #1 and #2, respectively.
                        ; Read potential Famicom expansion port signatures into [8] and [9], expecting $00 and
                        ; $10 for input ports #1 and #2, respectively.
0F:FE71  LDX #$01       ; for(X = 1; X >= 0; X--) {
loop6:
0F:FE73  LDY #$08       ;   for(Y = 8; Y > 0; Y--) {
loop5:
0F:FE75  LDA $4016,X    ;     A = [PORT + X];
0F:FE78  LSR A          ;     
0F:FE79  ROL $0A,X      ;     [$A + X] = ([$A + X] << 1) | (A & 1);
0F:FE7B  LSR A          ;     
0F:FE7C  ROL $08,X      ;     [8 + X] = ([8 + X] << 1) | ((A >> 1) & 1);
0F:FE7E  DEY            ;     
0F:FE7F  BNE $FE75      ;   }
0F:FE81  DEX            ; 
0F:FE82  BEQ $FE73      ; }

                        ; If the signatures are detected in [8], [9], [$A] or [$B], mark bits of [$C] accordingly.        
0F:FE84  LDA #$00       ; 
0F:FE86  STA $000C      ; [$C] = 0;
0F:FE88  LDA $0008      ; 
0F:FE8A  CMP #$20       ;
0F:FE8C  BNE $FE94      ; if ([8] == $20) {
0F:FE8E  LDA #$80       ;   [$C] |= $80;
0F:FE90  ORA $000C      ; }
0F:FE92  STA $000C      ; 
label7:
0F:FE94  LDA $0009      ; 
0F:FE96  CMP #$10       ;
0F:FE98  BNE $FEA0      ; if ([9] == $10) {
0F:FE9A  LDA #$40       ;   [$C] |= $40;
0F:FE9C  ORA $000C      ; }
0F:FE9E  STA $000C      ; 
label8:
0F:FEA0  LDA $000A      ; 
0F:FEA2  CMP #$10       ;
0F:FEA4  BNE $FEAC      ; if ([$A] == $10) {
0F:FEA6  LDA #$20       ;   [$C] |= $20;
0F:FEA8  ORA $000C      ; }
0F:FEAA  STA $000C      ; 
label9:
0F:FEAC  LDA $000B      ; 
0F:FEAE  CMP #$20       ;
0F:FEB0  BNE $FEB8      ; if ([$B] == $20) {
0F:FEB2  LDA #$10       ;   [$C] |= $10;
0F:FEB4  ORA $000C      ; }
0F:FEB6  STA $000C      ; 

                        ; If Famicom signatures are detected, then copy [4] and [5] (Famicom contollers #3 and #4) 
                        ; to [2] and [3], respectively.
label10:
0F:FEB8  LDA $000C      ; 
0F:FEBA  CMP #$C0       ;
0F:FEBC  BNE $FEC8      ; if ([$C] == $C0) {
0F:FEBE  LDX #$02       ;   for(X = 2; X > 0; X--) {
label11:
0F:FEC0  LDA $03,X      ;     
0F:FEC2  STA $01,X      ;     [1 + X] = [3 + X];
0F:FEC4  DEX            ;     
0F:FEC5  BNE $FEC0      ;   }
0F:FEC7  RTS            ;   
                        ; 

                        ; Otherwise, if NES signatures are detected, copy [6] and [7] (NES controllers #3 and #4)
                        ; to [2] and [3], respectively.
label12:
0F:FEC8  CMP #$30
0F:FECA  BNE $FED5      ; } else if ([$C] == $30) {
0F:FECC  LDX #$02       ;   for(X = 2; X > 0; X--) {
label13:
0F:FECE  LDA $05,X      ; 
0F:FED0  STA $01,X      ;     [1 + X] = [5 + X];
0F:FED2  DEX            ; 
0F:FED3  BNE $FECE      ;   }
                        ; }

label14:
0F:FED5  RTS            ; return;
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Spot bug in Mesen and Nintaco

Post by zeroone »

As a test, I modified Nintaco to update the cached button state at various scanlines. I found that the Spot menu works as long as the update doesn't happen around the pre-render scanline. There is a margin around it, though it's not that well-defined. Avoiding the range +/- ~16 scanlines about the pre-render scanline enables the menu to function properly. As mentioned prior in this thread, Nintaco does the cache update at the pre-render scanline. I also found that at or around the NMI scanline still enables it to work without issues. In fact, anywhere away from the pre-render scanline does the job. I'll have to investigate why this is the case and what other emulators are doing.
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Spot bug in Mesen and Nintaco

Post by zeroone »

Here's my final thoughts on this: Spot's options menu does suffer from exactly the same problem as Quattro Sports BMX Simulator: Around the pre-render scanline, it caches the button values. And, later within the frame, it polls the controllers 2 or 3 times, comparing against the cached values.

Emulators can get away with updating button values once per frame as long as the update doesn't occur around the pre-render scanline. Since the issue only affects the 2 aforementioned games, I'm considering actually marking them in Nintaco's cart DB, enabling input to be injected mid-frame, rather than top-of-frame. I'd rather not make a change to inputs that could affect games in general. Maybe the reverse issue will show up elsewhere if I do.

There are other ways to deal with this, but the button values are also used for Rewind Time and movies, which not only requires sampling exactly once-per-frame, it must always be updated at exactly the same point within each frame.
Sour
Posts: 890
Joined: Sun Feb 07, 2016 6:16 pm

Re: Spot bug in Mesen and Nintaco

Post by Sour »

Thanks for the research on this.

For a moment, I thought Mesen paused between frames on scanline 240, but that was a change I had done when I originally added support for Bizhawk & FCEUX's movie formats. Moving the pause from scanline -1 to 240 ended up causing issues with the overclocking logic, so I reverted it back to -1 - which explains why this particular game breaks.

Using a game-specific setting to solve this is not a bad idea - I might end up doing that too.
User avatar
Gilbert
Posts: 564
Joined: Sun Dec 12, 2010 10:27 pm
Location: Hong Kong
Contact:

Re: Spot bug in Mesen and Nintaco

Post by Gilbert »

Sorry for derailing this thread a bit, but every time I read its topic I interpret it as "find bug in Mesen and Nintaco" (though of course, Spot is the name of a game here). In some sense, this alternate interpretation is still correct though. :roll:
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Spot bug in Mesen and Nintaco

Post by tepples »

About 2 years later, I made a test ROM designed specially to spot bugs like this: Telling LYs?
Post Reply