Skip to content
Permalink
2c4c9c57f4
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
290 lines (258 sloc) 6.23 KB
;
; Controller reading for Game Boy and Super Game Boy
;
; Copyright 2018 Damian Yerrick
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software
; in a product, an acknowledgment in the product documentation would be
; appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
; misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.
;
include "src/gb.inc"
DAS_DELAY = 15
DAS_SPEED = 3
SECTION "hram_pads", HRAM
cur_keys:: ds 1
new_keys:: ds 1
SECTION "ram_pads", WRAM0
das_keys:: ds 1
das_timer:: ds 1
SECTION "rom_pads", ROM0
; Controller reading ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This controller reading routine is optimized for size.
; It stores currently pressed keys in cur_keys (1=pressed) and
; keys newly pressed since last read in new_keys, with the same
; nibble ordering as the Game Boy Advance.
; 76543210
; |||||||+- A
; ||||||+-- B
; |||||+--- Select
; ||||+---- Start
; |||+----- Right
; ||+------ Left
; |+------- Up
; +-------- Down
; R
; L (just kidding)
read_pad::
; Poll half the controller
ld a,P1F_BUTTONS
call .onenibble
ld b,a ; B7-4 = 1; B3-0 = unpressed buttons
; Poll the other half
ld a,P1F_DPAD
call .onenibble
swap a ; A3-0 = unpressed directions; A7-4 = 1
xor b ; A = pressed buttons + directions
ld b,a ; B = pressed buttons + directions
; And release the controller
ld a,P1F_NONE
ld [rP1],a
; Combine with previous cur_keys to make new_keys
ldh a,[cur_keys]
xor b ; A = keys that changed state
and b ; A = keys that changed to pressed
ldh [new_keys],a
ld a,b
ldh [cur_keys],a
ret
.onenibble:
ldh [rP1],a ; switch the key matrix
ldh a,[rP1] ; ignore value while waiting for the key matrix to settle
ldh a,[rP1]
ldh a,[rP1]
ldh a,[rP1]
ldh a,[rP1]
ldh a,[rP1] ; the actual read
or $F0 ; A7-4 = 1; A3-0 = unpressed keys
ret
autorepeat_60hz_if_sgb::
ldh a, [hw_capability]
rra
jr nc, autorepeat
autorepeat_60hz::
ld a,[das_timer]
cp 4
jr nc, autorepeat
ld a,1
ld [das_timer],a
; fall through
;;
; Adds held keys to new_keys, DAS_DELAY frames after press and
; every DAS_SPEED frames thereafter
; @param B which keys are eligible for autorepeat
autorepeat::
; If no eligible keys are held, skip all autorepeat processing
ldh a,[cur_keys]
and b
ret z
ld c,a ; C: Currently held
; If any keys were newly pressed, set the eligible keys among them
; as the autorepeating set. For example, changing from Up to
; Up+Right sets Right as the new autorepeating set.
ldh a,[new_keys]
ld d,a ; D: new_keys
or a
jr z,.no_restart_das
and b
ld [das_keys],a
ld a,DAS_DELAY
jr .have_das_timer
.no_restart_das:
; If time has expired, merge in the autorepeating set
ld a,[das_timer]
dec a
jr nz,.have_das_timer
ld a,[das_keys]
and c
or d
ldh [new_keys],a
ld a,DAS_SPEED
.have_das_timer:
ld [das_timer],a
ret
; Super Game Boy detection ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
; Sets hw_capability to $01 if on a Super Game Boy.
detect_sgb::
; Try to set the SGB to 2-player mode
di
ld b, 1
call sgb_set_bplus1_players
call sgb_wait
; When the key matrix is released, SGB returns player number in
; bits 1-0 (active low).
ldh a, [rP1]
and $03
xor $03
jr nz, .isSGB
; When the key matrix is asserted and released, such as when one
; controller is read, SGB advances to the next controller.
call read_pad
ldh a, [rP1]
and $03
xor $03
jr z, .notSGB ; a = 0
.isSGB:
ld a, $01
ldh [hw_capability],a
.notSGB:
; Now turn off 2-player mode
ld b, 0
; fall through
;;
; Set the number of controllers to read to B + 1, where B is
; 0, 1, or 3 for 1, 2, or 4 (multitap only) players.
sgb_set_bplus1_players::
ld a, ($11 << 3) + 1
; fall through
;;
; Send a 1-packet SGB command whose first two bytes are A and B
; and whose remainder is zero filled.
sgb_send_ab::
ld c, 0
; fall through
;;
; Send a 1-packet SGB command whose first three bytes are A, B, and C
; and whose remainder is zero filled.
sgb_send_abc::
ld hl, help_line_buffer
push hl
ld [hl+], a
ld a, b
ld [hl+], a
ld a, c
ld [hl+], a
xor a
ld c, 13
call memset_tiny
pop hl
jr sgb_send
SIZEOF_SGB_PACKET EQU 16
CHAR_BIT EQU 8
;;
; Clears the Super Game Boy attribute table to 0.
clear_sgb_attr::
ld hl, sgb_cmd_clear_attrs
sgb_send_if_sgb::
ldh a, [hw_capability]
rra
ret nc
;;
; Sends a Super Game Boy packet starting at HL.
; Assumes no IRQ handler does any sort of controller autoreading.
sgb_send::
; B: Number of remaining bytes in this packet
; C: Number of remaining packets
; D: Remaining bit data
; E: Number of remaining bits in this byte
ld a,$07
and [hl]
ret z
ld c,a
.packetloop:
; Start transfer by asserting both halves of the key matrix
; momentarily. (This is like strobing an NES controller.)
xor a
ldh [rP1],a
ld a,$30
ldh [rP1],a
push bc
ld b,SIZEOF_SGB_PACKET
.byteloop:
; Read a byte from the packet
ld e,CHAR_BIT
ld a,[hl+]
ld d,a
.bitloop:
; Send a 1 as $10 then $30, or a 0 as $20 then $30.
; This is constant time thanks to ISSOtm, unlike SGB BIOS
; which takes 1 mcycle longer to send a 0 then a 1.
ld a,$10
bit 0,d
jr nz,.bitIs1
add a,a ; ld a,$20
.bitIs1:
ldh [rP1],a
ld a,$30
ldh [rP1],a
; Advance D to next bit (this is like NES MMC1)
rr d
dec e
jr nz,.bitloop
dec b
jr nz,.byteloop
; Send $20 $30 as end of packet
ld a,$20
ld [rP1],a
ld a,$30
ld [rP1],a
call sgb_wait
pop bc
dec c
jr nz,.packetloop
ret
;;
; Waits about 4 frames for Super Game Boy to have processed a command
sgb_wait:
ld de, 7000
.loop:
nop
nop
nop
dec de
ld a, e
or d
jr nz, .loop
ret