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:
; 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;