diff --git a/constants.asm b/constants.asm index ec45875d6..8a3d4d080 100644 --- a/constants.asm +++ b/constants.asm @@ -3417,15 +3417,20 @@ BATTLETYPE_SUICUNE EQU $0c ; joypad -NO_INPUT EQU %00000000 -BUTTON_A EQU %00000001 -BUTTON_B EQU %00000010 -SELECT EQU %00000100 -START EQU %00001000 -D_RIGHT EQU %00010000 -D_LEFT EQU %00100000 -D_UP EQU %01000000 -D_DOWN EQU %10000000 +BUTTONS EQU %00010000 +D_PAD EQU %00100000 + +AUTO_INPUT EQU $ff + +NO_INPUT EQU %00000000 +BUTTON_A EQU %00000001 +BUTTON_B EQU %00000010 +SELECT EQU %00000100 +START EQU %00001000 +D_RIGHT EQU %00010000 +D_LEFT EQU %00100000 +D_UP EQU %01000000 +D_DOWN EQU %10000000 ; mbc NUM_SRAM_BANKS EQU 4 diff --git a/joypad.asm b/joypad.asm new file mode 100644 index 000000000..2488ba1df --- /dev/null +++ b/joypad.asm @@ -0,0 +1,275 @@ +JoypadInt: ; 92e +; Replaced by Joypad, called from VBlank instead of the useless +; joypad interrupt. + +; This is a placeholder in case the interrupt is somehow enabled. + reti +; 92f + +ClearJoypadPublic: ; 92f + xor a +; Pressed this frame (delta) + ld [$ffa7], a +; Currently pressed + ld [$ffa8], a + ret +; 935 + +Joypad: ; 935 +; Read the joypad register and translate it to something more +; workable for use in-game. There are 8 buttons, so we can use +; one byte to contain all player input. + +; Updates: + +; $ffa2: released this frame (delta) +; $ffa3: pressed this frame (delta) +; $ffa4: currently pressed +; $ffa5: pressed so far + +; Any of these three bits can be used to disable input. + ld a, [$cfbe] + and %11010000 + ret nz + +; If we're saving, input is disabled. + ld a, [$c2cd] + and a + ret nz + +; We can only get four inputs at a time. +; We take d-pad first for no particular reason. + ld a, D_PAD + ld [$ff00], a +; Read twice to give the request time to take. + ld a, [$ff00] + ld a, [$ff00] + +; The Joypad register output is in the lo nybble (inversed). +; We make the hi nybble of our new container d-pad input. + cpl + and $f + swap a + +; We'll keep this in b for now. + ld b, a + +; Buttons make 8 total inputs (A, B, Select, Start). +; We can fit this into one byte. + ld a, BUTTONS + ld [$ff00], a +; Wait for input to stabilize. + ld a, [$ff00] + ld a, [$ff00] + ld a, [$ff00] + ld a, [$ff00] + ld a, [$ff00] + ld a, [$ff00] +; Buttons take the lo nybble. + cpl + and $f + or b + ld b, a + +; Reset the joypad register since we're done with it. + ld a, $30 + ld [$ff00], a + +; To get the delta we xor the last frame's input with the new one. + ld a, [$ffa4] ; last frame + ld e, a + xor b + ld d, a +; Released this frame: + and e + ld [$ffa2], a +; Pressed this frame: + ld a, d + and b + ld [$ffa3], a + +; Add any new presses to the list of collective presses: + ld c, a + ld a, [$ffa5] + or c + ld [$ffa5], a + +; Currently pressed: + ld a, b + ld [$ffa4], a + +; Now that we have the input, we can do stuff with it. + +; For example, soft reset: + and BUTTON_A | BUTTON_B | SELECT | START + cp BUTTON_A | BUTTON_B | SELECT | START + jp z, $0150 ; reset + + ret +; 984 + + +GetJoypadPublic: ; 984 +; Update mirror joypad input from $ffa4 (real input) + +; $ffa6: released this frame (delta) +; $ffa7: pressed this frame (delta) +; $ffa8: currently pressed + +; bit 0 A +; 1 B +; 2 SELECT +; 3 START +; 4 RIGHT +; 5 LEFT +; 6 UP +; 7 DOWN + + push af + push hl + push de + push bc + +; The player input can be automated using an input stream. +; See more below. + ld a, [InputType] + cp a, AUTO_INPUT + jr z, .auto + +; To get deltas, take this and last frame's input. + ld a, [$ffa4] ; real input + ld b, a + ld a, [$ffa8] ; last frame mirror + ld e, a + +; Released this frame: + xor b + ld d, a + and e + ld [$ffa6], a + +; Pressed this frame: + ld a, d + and b + ld [$ffa7], a + +; It looks like the collective presses got commented out here. + ld c, a + +; Currently pressed: + ld a, b + ld [$ffa8], a ; frame input + +.quit + pop bc + pop de + pop hl + pop af + ret + +.auto +; Use a predetermined input stream (used in the catching tutorial). + +; Stream format: [input][duration] +; A value of $ff will immediately end the stream. + +; Read from the input stream. + ld a, [$ff9d] + push af + ld a, [AutoInputBank] + rst Bankswitch + + ld hl, AutoInputAddress + ld a, [hli] + ld h, [hl] + ld l, a + +; We only update when the input duration has expired. + ld a, [AutoInputLength] + and a + jr z, .updateauto + +; Until then, don't change anything. + dec a + ld [AutoInputLength], a + pop af + rst Bankswitch + jr .quit + + +.updateauto +; An input of $ff will end the stream. + ld a, [hli] + cp a, $ff + jr z, .stopauto + ld b, a + +; A duration of $ff will end the stream indefinitely. + ld a, [hli] + ld [AutoInputLength], a + cp a, $ff + jr nz, .next + +; The current input is overwritten. + dec hl + dec hl + ld b, NO_INPUT + jr .finishauto + +.next +; On to the next input... + ld a, l + ld [AutoInputAddress], a + ld a, h + ld [AutoInputAddress+1], a + jr .finishauto + +.stopauto + call StopAutoInput + ld b, NO_INPUT + +.finishauto + pop af + rst Bankswitch + ld a, b + ld [$ffa7], a ; pressed + ld [$ffa8], a ; input + jr .quit +; 9ee + + +StartAutoInput: ; 9ee +; Start reading automated input stream at a:hl. + + ld [AutoInputBank], a + ld a, l + ld [AutoInputAddress], a + ld a, h + ld [AutoInputAddress+1], a +; Start reading the stream immediately. + xor a + ld [AutoInputLength], a +; Reset input mirrors. + xor a + ld [$ffa7], a ; pressed this frame + ld [$ffa6], a ; released this frame + ld [$ffa8], a ; currently pressed + + ld a, AUTO_INPUT + ld [InputType], a + ret +; a0a + + +StopAutoInput: ; a0a +; Clear variables related to automated input. + xor a + ld [AutoInputBank], a + ld [AutoInputAddress], a + ld [AutoInputAddress+1], a + ld [AutoInputLength], a +; Back to normal input. + ld [InputType], a + ret +; a1b + diff --git a/main.asm b/main.asm index 8c4cdd88a..fd2a1965f 100644 --- a/main.asm +++ b/main.asm @@ -47,7 +47,7 @@ SECTION "serial",HOME[$58] ; serial interrupt jp $06ef SECTION "joypad",HOME[$60] ; joypad interrupt - jp $092e + jp JoypadInt SECTION "romheader",HOME[$100] Start: @@ -425,252 +425,11 @@ SetClock: ; 691 ret ; 6c4 -INCBIN "baserom.gbc",$6c4,$935 - $6c4 - -Joypad: ; 935 -; update joypad state -; $ffa2: released -; $ffa3: pressed -; $ffa4: input -; $ffa5: total pressed - -; - ld a, [$cfbe] - and $d0 - ret nz - -; pause game update? - ld a, [$c2cd] - and a - ret nz - -; d-pad - ld a, $20 - ld [$ff00], a - ld a, [$ff00] - ld a, [$ff00] -; hi nybble - cpl - and $f - swap a - ld b, a - -; buttons - ld a, $10 - ld [$ff00], a -; wait to stabilize - ld a, [$ff00] - ld a, [$ff00] - ld a, [$ff00] - ld a, [$ff00] - ld a, [$ff00] - ld a, [$ff00] -; lo nybble - cpl - and $f - or b - ld b, a - -; reset joypad - ld a, $30 - ld [$ff00], a - -; get change in input - ld a, [$ffa4] ; last frame's input - ld e, a - xor b ; current frame input - ld d, a -; released - and e - ld [$ffa2], a -; pressed - ld a, d - and b - ld [$ffa3], a - -; total pressed - ld c, a - ld a, [$ffa5] - or c - ld [$ffa5], a - -; original input - ld a, b - ld [$ffa4], a - -; A+B+SELECT+START - and $f - cp $f - jp z, $0150 ; reset - - ret -; 984 +INCBIN "baserom.gbc",$6c4,$92e - $6c4 -GetJoypadPublic: ; 984 -; update mirror joypad input from $ffa4 (real input) +INCLUDE "joypad.asm" -; $ffa6: released -; $ffa7: pressed -; $ffa8: input - -; bit 0 A -; 1 B -; 2 SELECT -; 3 START -; 4 RIGHT -; 5 LEFT -; 6 UP -; 7 DOWN - - push af - push hl - push de - push bc - -; automated input? - ld a, [InputType] - cp a, $ff ; INPUT_AUTO - jr z, .auto - -; get input - ld a, [$ffa4] ; real input - ld b, a - ld a, [$ffa8] ; last frame mirror - ld e, a - -; released - xor b - ld d, a - and e - ld [$ffa6], a - -; pressed - ld a, d - and b - ld [$ffa7], a - -; leftover from pasted code - ld c, a - -; - ld a, b - ld [$ffa8], a ; frame input -.quit - pop bc - pop de - pop hl - pop af - ret - -.auto -; use predetermined input feed (used in catch tutorial) -; struct: [input][duration] - -; save bank - ld a, [$ff9d] - push af -; - ld a, [AutoInputBank] - rst Bankswitch -; - ld hl, AutoInputAddress ; AutoInputAddress-9 - ld a, [hli] - ld h, [hl] - ld l, a - -; update when frame count hits 0 - ld a, [AutoInputLength] - and a - jr z, .updateauto - -; until then, do nothing - dec a - ld [AutoInputLength], a -; restore bank - pop af - rst Bankswitch -; we're done - jr .quit - -.updateauto -; get input - ld a, [hli] -; stop? - cp a, $ff - jr z, .stopinput - ld b, a - -; duration - ld a, [hli] - ld [AutoInputLength], a -; duration $ff = end at input - cp a, $ff - jr nz, .next - -; no input - dec hl - dec hl - ld b, $00 ; no input - jr .finishauto - -.next -; output recorded - ld a, l - ld [AutoInputAddress], a - ld a, h - ld [AutoInputAddress+1], a - jr .finishauto - -.stopinput - call StopAutoInput - ld b, $00 ; no input - -.finishauto -; restore bank - pop af - rst Bankswitch -; update mirrors - ld a, b - ld [$ffa7], a ; pressed - ld [$ffa8], a ; input - jr .quit -; 9ee - -StartAutoInput: ; 9ee -; start auto input stream at a:hl -; bank - ld [AutoInputBank], a -; address - ld a, l - ld [AutoInputAddress], a - ld a, h - ld [AutoInputAddress+1], a -; don't wait to update - xor a - ld [AutoInputLength], a -; clear input mirrors - xor a - ld [$ffa7], a ; pressed - ld [$ffa6], a ; released - ld [$ffa8], a ; input -; start reading input stream instead of player input - ld a, $ff ; INPUT_AUTO - ld [InputType], a - ret -; a0a - -StopAutoInput: ; a0a -; clear autoinput ram - xor a - ld [AutoInputBank], a - ld [AutoInputAddress], a - ld [AutoInputAddress+1], a - ld [AutoInputLength], a -; normal input - ld [InputType], a - ret -; a1b INCBIN "baserom.gbc",$a1b,$b40 - $a1b