In this tutorial, you'll learn to read the joypad registers and react to player
input ;). We're going to need to store the player's input somewhere, so we'll
declare some variables in the system's RAM ($0000-$1FFF). If you make your own
variables later in this part of RAM, keep in mind that the stack is set to
$1FFF, so try not to get too close to that area (of course). We'll define
variables for both Controllers (aka joypads) 1 & 2.
Note: Up to 4 controllers can receive input through a multitap. You can expand
my code to do so yourself :P. There is a doc explaining this in the snesbase pack.
.ENUM $0000 Joy1Raw DW ; Holder of RAW joypad data from register (from last frame) Joy2Raw DW Joy1Press DW ; Contains only pressed buttons (not held down) Joy2Press DW Joy1Held DW ; Contains only buttons that are Held Joy2Held DW .ENDE
Alright, that's the variables. JoyRaw will act as a log of the raw data
received from reading the joypad registers. JoyPress will hold the buttons
that only been pressed for 1 frame. JoyHeld will hold data for the buttons
that have been pressed continously for > 1 frame. One example of the effects
you can do with button holds is running, such as in Super Mario World. Once Y
is pressed, Mario will accelerate as long Y is held until he reaches max speed.
Once Y is released, he slows down again. ta da
.ENUM is an easy way to map names to addresses. Joy1 is a word in size (DW),
so WLA just mapped Joy1 to $0000, and Joy2 to $0002 (leaving $0000 and $0001
for Joy1, a word). The other variables are mapped in the same way. If you
wanted to do it manually, you could .EQU (.DEFINE) xxxx $0000, but I personally
like this method because it automatically skips 1 or 2 addresses if it's a byte
or word..
Now, let's get on to reading the controllers, shall we? Onto some registers.
Register $4200: Counter Enable (1b/W)
n-vh---j n: NMI enable v: vertical counter enable
h: horizontal enable register j: joypad enable
vh-----j v: 0 = Not in VBlank state.
1 = In VBlank state.
h: 0 = Not in HBlank state.
1 = In HBlank state.
j: 0 = Joypad not ready.
1 = Joypad ready.
We just check bit 0 here to see if the joypad is ready to be read from. If the bit
is set, the joypads are NOT ready to be read from.
Register $4218: Joypad #1 status register (Low Byte) (1b/R)
axlriiii a: A button
x: X button
l: L
r: R
i: Identification code
The button bits are set when the buttons are pressed. Also, the first 4 bits (i)
identify the type of controller connected. 0000 is the ID for a standard snes controller.
If these bits output anything else, there is something else plugged in or corrupt data. I'm
not going to check these in the routine because it's kind of pointless for homebrew. You either
plug in the normal controller or the program won't work.. I mean DUH. More important for commercial
games to check that...
Register $4219: Joypad #1 status register (High Byte) (1b/R)
bystudlr
b: B button u: Up
y: Y button d: Down
s: Select l: Left
t: Start r: Right
Quick Note: $4218 can be read with a 16 bit A/X/Y and both $4218 and $4219 will be read
at the same time. I think you can read or write to an address like that. But remember that
some registers require writing twice, one byte at a time.
Register $421A-$421F - Same as $4218-$4219 but for joypads 2-4.
Registers $4016-$4017: Old-style joypad registers (2*1b/RW)
$4016 - Player 1 Joypad $4017 - Player 2 Joypad
Full source available in download.
; Input Cheat Sheet
; $4218
; $80 = A
; $40 = X
; $20 = L
; $10 = R
;
; $4219:
; $80 = B
; $40 = Y
; $20 = Select
; $10 = Start
; $08 = Up
; $04 = Down
; $02 = Left
; $01 = Right
; Let's define it to make it easier..
; $4218
.EQU Button_A $80
.EQU Button_X $40
.EQU Button_L $20
.EQU Button_R $10
; $4219
.EQU Button_B $80
.EQU Button_Y $40
.EQU Button_Select $20
.EQU Button_Start $10
.EQU Button_Up $08
.EQU Button_Down $04
.EQU Button_Left $02
.EQU Button_Right $01
; ....
; ..
; During initialization..
stz $4016 ; Write a byte of nothing to $4016. You know why I did that?
;.. almost ready to enter main loop..
lda #$81
sta $4200 ; Enable NMI and auto-joypad read
Gameloop:
WAI
;...
;.
;....
; react to input here..
jmp Gameloop
VBlank:
;...
;..
;..
;.
jsr Joypad
;.
;..
rti
; Time for the real code
Joypad:
lda $4212 ; auto-read joypad status
and #$01 ;
bne Joypad ; read is done when 0
rep #$30 ; A/X/Y - 16 bit
; Player 1
ldx Joy1Raw ; load log of last frame's RAW read of $4218
; the log will be 0 the first time read of course..
lda $4218 ; Read current frame's RAW joypad data
sta Joy1Raw ; save it for next frame.. (last frame log is still in X)
txa ; transfer last frame input from X -> A (it's still in X too)
eor Joy1Raw ; Xor last frame input with current frame input
; shows the changes in input
; buttons just pressed or just released become set.
; Held or unactive buttons are 0
and Joy1Raw ; AND changes to current frame's input.
; this ends up leaving you with the only the buttons that are pressed..
; It's MAGIC!
sta Joy1Press ; Store just pressed buttons
txa ; Transfer last frame input from X -> A again
and Joy1Raw ; Find buttons that are still pressed (held)
sta Joy1Held ; by storing only buttons that are pressed both frames
; Player 2 ; Repeat :)
ldx Joy2Raw
lda $421A ; Read Joypad2 Regs
sta Joy2Raw
txa
eor Joy2Raw ; Find just triggered buttons
and Joy2Raw
sta Joy2Press
txa
and Joy2Raw ; Find buttons that are still pressed (held)
sta Joy2Held
; Joypads standard (ie not a mouse or superscope..) and connected?
sep #$20
ldx #$0000 ; we'll clear recorded input if pad is invalid
lda $4016 ; Pad 1 - now we read this (after we stored a 0 to it earlier)
bne _check2 ; $4016 returns 0 if not connected, 1 if connected - branch if not 0
stx Joy1Raw ; otherwise clear all recorded input.. it's not valid..
stx Joy1Press
stx Joy1Held
_check2:
lda $4017 ; Pad 2
bne _done ; 0=not connected,
stx Joy2Raw
stx Joy2Press
stx Joy2Held
_done:
RTS
I'll upload source featuring input very soon..