Gamepad code: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(→‎Reading pressed buttons: unroll for 2 players and support FC expansion)
m (fix double redirect)
Tag: Redirect target changed
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
==For [[Nesasm]]==
#REDIRECT [[Controller reading]]
<pre>
; Uncomment the next line if you want to have all your button callbacks to
; execute upon "has gone from up to down" rather than "is currently down".
;EDGE_BUTTONS = 1
    .org 0x00
.ifdef EDGE_BUTTONS
buttons_down            .ds 8
.endifdef
current_button_sub      .ds 2
 
    .org 0x8000
; Need quite a bit of stuff here. See: [[Init code]]
 
get_buttons:
    lda #0x01 ; strobe
    sta 0x4016
    lda #0x00 ; MOAR strobe
    sta 0x4016
    ldx #0
.get_button:
    lda 0x4016
    and #0x01 ; Only want information about the first bit
; bne means branch on the zero register being false, which it
; will be if the number in the accumulator is non-zero.
    bne .pressed
    .ifdef EDGE_BUTTONS
    lda #0x00
    sta buttons_down, x
    .endifdef
    jmp .end
.pressed:
    .ifdef EDGE_BUTTONS
    lda buttons_down, x
    bne .end
    lda #0x01
    sta buttons_down, x
    .endifdef
    txa
    pha
    asl A
    tax
    lda button_subs + 0x00, x ; Equivalent to non-existent jsr (button_subs, x)
    sta current_button_sub + 0x00
    lda button_subs + 0x01, x
    sta current_button_sub + 0x01
    lda #HIGH(.after_button_sub + 0x01)
    pha
    lda #LOW(.after_button_sub + 0x01)
    pha
    jmp [current_button_sub]
.after_button_sub:
    pla
    tax
.end:
    txa
    inx
    cmp #8
    bmi .get_button
    jmp get_buttons
 
button_subs: ; Vector table
    .dw do_nothing ; a
    .dw do_nothing ; b
    .dw do_nothing ; select
    .dw do_nothing ; start
    .dw do_nothing ; up
    .dw do_nothing ; down
    .dw do_nothing ; left
    .dw do_nothing ; right
 
do_nothing:
    rts
</pre>
 
==For [[ca65]]==
I am a big fan of structured code, so this coding style will be a bit different from the nesasm version above. [http://www.cc65.org/doc/ca65-5.html#ss5.5 This] describes the nameless loops used in the code you see below.
 
=== Reading pressed buttons ===
If your code is intended to be used with [[APU DMC]] playback this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, ''Super Mario Bros. 3'' reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over.
 
The buttons byte ''buttons'' should be placed in zero page.
<pre>
; we reserve one byte for storing the data that is read from controller
.zeropage
buttons .res 1
</pre>
 
When reading from ''JOYPAD*'' what is read migh be different from $01/$00. See [[controller port registers|Controller Port]] for more information on that subject. In this code the only concern is bit 0 read from ''JOYPAD*.''.
<pre>
JOYPAD1 = $4016
JOYPAD2 = $4017
</pre>
 
This is the end result that will be stored in ''buttons''. '''1''' if the button was pressed, '''0''' otherwise.
<pre>
bit:  7    6    5    4    3    2    1    0
button: A    B  Select Start  Up  Down  Left  Right
</pre>
 
This subroutine takes 132 cycles to execute but ignores the Famicom expansion controller.
It uses a [[wikipedia:Ring counter|ring counter]] technique: $01 is loaded into the result first, and once eight bits are shifted in, the 1 bit will be shifted out, terminating the loop.
<pre>
; At the same time that we strobe bit 0, we initialize the ring counter
; so we're hitting two birds with one stone here
readjoy:
  lda #$01
  sta JOYPAD1
  sta buttons
  lsr a        ; now A is 0
  sta JOYPAD1
:
  lda JOYPAD1
  lsr a       ; bit0 -> Carry
  rol buttons  ; Carry -> bit0; bit 7 -> Carry
  bcc :-
  rts
</pre>
 
Adding support for controllers on the Famicom's DA15 expansion port and for player 2's controller is straightforward.
<pre>
.zeropage
buttons1: .res 1
buttons2: .res 1
 
.code
readjoy:
  lda #$01
  sta JOYPAD1
  sta buttons2  ; player 2's buttons double as a ring counter
  lsr a        ; now A is 0
  sta JOYPAD1
:
  lda JOYPAD1
  and #$03      ; ignore bits other than controller
  cmp #$01      ; Set carry if and only if nonzero
  rol buttons1  ; Carry -> bit0; bit 7 -> Carry
  lda JOYPAD2  ; Repeat
  and #$03
  cmp #$01
  rol buttons2  ; Carry -> bit0; bit 7 -> Carry
  bcc :-
  rts
</pre>

Latest revision as of 17:00, 24 January 2023

Redirect to: