Controller reading code: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Removal of syntax highlighter)
(merge plz)
Line 1: Line 1:
{{merge|Gamepad code}}
To read the first player's [[standard controller]], write an odd value and then an even value to $4016, then read $4016 eight times in a row. For each read, if either of the low two bits are set, that means that the corresponding button is pressed. (On a Famicom, bit 0 returns hardwired controllers and bit 1 expansion controllers.) The buttons come in the order of A, B, Select, Start, Up, Down, Left, Right.
To read the first player's [[standard controller]], write an odd value and then an even value to $4016, then read $4016 eight times in a row. For each read, if either of the low two bits are set, that means that the corresponding button is pressed. (On a Famicom, bit 0 returns hardwired controllers and bit 1 expansion controllers.) The buttons come in the order of A, B, Select, Start, Up, Down, Left, Right.



Revision as of 16:05, 11 September 2014

To read the first player's standard controller, write an odd value and then an even value to $4016, then read $4016 eight times in a row. For each read, if either of the low two bits are set, that means that the corresponding button is pressed. (On a Famicom, bit 0 returns hardwired controllers and bit 1 expansion controllers.) The buttons come in the order of A, B, Select, Start, Up, Down, Left, Right.

; Reads controller
; Out: A=buttons pressed, where bit 0 is A button
read_joy:
       ; Strobe controller
       lda #1
       sta $4016
       lda #0
       sta $4016
       
       ; Read all 8 buttons
       ldx #8
loop:
       pha
       
       ; Read next button state and mask off low 2 bits.
       ; Compare with $01, which will set carry flag if
       ; either or both bits are set.
       lda $4016
       and #$03
       cmp #$01
       
       ; Now, rotate the carry flag into the top of A,
       ; land shift all the other buttons to the right
       pla
       ror a
       
       dex
       bne loop
       
       rts

If a DMC sample is playing, it can corrupt the above sequence by inserting an extra $4016 read somewhere between yours if it happens to try to read a sample just as you're reading $4016. The simplest way to compensate is to repeatedly read the controller twice, and see if the two readings match. If they don't, read the controller again and compare with the previous read. Repeat until they match.

; temp is a zero-page variable

; Reads controller. Reliable when DMC is playing.
; Out: A=buttons held, A button in bit 0
read_joy_safe:
       ; Get first reading
       jsr read_joy
       
mismatch:
       ; Save previous reading
       sta temp
       
       ; Read again and compare. If they differ,
       ; read again.
       jsr read_joy
       cmp temp
       bne mismatch
       
       rts

Note that if the player presses or releases a button between two reads, this will interpret that as a corrupt read and read again. Unfortunately, there's no way for it to tell the difference.

One problem with this approach is that it can read the controller more times than necessary, since for example after reading it three times (due to the first two not matching), it only compares the second and third reads; it doesn't compare the first and third, which might match. If the second read was the corrupt one, the second and third won't match, causing a fourth read.

A faster controller reading approach has been discussed, which assumes that the upper bits of $4016 will be the same each time it's read. This can fail on the Famicom, which has a microphone that toggles bit 2.