ASM: Key Input Routine with Re-defineable Keys

Keys !!!

Just found this hiding around in one of my random asm folders. Thought it may be useful to someone.  I seem to recall using it when I wrote Bozxle a couple of years back.

There may be quicker/shorter/better ways of doing this but I wasn’t aiming for anything other than *working*, and I was in a rush. Bozxle was written in a week (yes, yes, you can tell, :p )

Apologies for the formatting, seems this site tries to mangle it… (I’m working on it heh)

The .asm file is here if you don’t want to type/copy/paste this.


; Redefine Keys Routine
; written by John Young
; Started on 6th September 2011 @ 11:48am
; This routine will define keys for UP/DOWN/LEFT/RIGHT/FIRE/PAUSE/QUIT
; or... Check the defined keys and return which are pressed in A (see below);

;org 32768 ; or wherever...

; ---------------------- CONST -------------------

BORDCR:            EQU 23624
ATTR_P:            EQU 23693
ROM_CLS:        EQU 3435
CHAN_OPEN:        EQU 5633
CC_INK:            EQU 16
CC_PAPER:        EQU 17
CC_AT:            EQU 22
CC_OVER            EQU 21

; ---------------------- CODE --------------------

; ROUTINE: main
; This will either run the redefine routine *or* check for any of the defined keys being pressed
; On Entry: You will need to poke (main+1) with 0 to check the keys
;           or poke (main+1) with 1 to 255 to redefine keys
; On Exit: A and BC hold the bits sets if checking keys (see below)
;          or BC holds the address of the keyports/patterns array if it was the redefine routine you wanted. :)
; The bits in A after checking keys are as follows:
; A = 0udlrfpq - bit is set for that direction, ie, fire pressed would be %00000100
; Obviously %00000000 would be no key pressed, :p
; NOTE: I don't save/restore any registers on entry/exit, so don't forget to save what you wanna keep...
            ; ok, lets check the keys, :)
get_keys:        di
                        ld de,0            ; DE will hold the bits for the keys pressed
            ld hl,key_up_port    ; address of the ports/patterns
            ld b,7            ; 7 keys to ácheck
            res 0,d
            dec hl
gk_loop:        inc hl
            rl d
            ld a,(hl)        ; get the port row
            in a,(254)        ; check that row
            and 31
            inc hl            ; point to the key bit pattern
            cp (hl)            ; check if that key is pressed
            jr z,gk_key_is_pressed
            ;key is not pressed
            res 0,d            ; indicate key not pressed
            djnz gk_loop        ; go check next key
            jr gk_exit
gk_key_is_pressed:    set 0,d            ; indicate key is pressed
            djnz gk_loop        ; go check next key
            ; ok, all done, maybe, I hope, ahh pffffffffffffffffft
gk_exit:        ld a,d            ; return the bit thingy
            ld b,0
            ld c,a            ; stick it in BC also just in case anyone wants to use it from BASIC
            ld (keyretval),a
keyretval:              defb 0
; --------------------------------------------------
            ; border black, paper black, ink green
do_the_redefine:    xor a
            ld (BORDCR),a        ; set border sysvar to black
            out (254),a
            ld a,4            ; black paper, green ink
            ld (ATTR_P),a        ; set permanent attrs sysvar to green on black
            call ROM_CLS        ; do the clear screen
            ld hl,msg_define
            call print_message        

            ld hl,msg_up
            call print_message
            ld hl,key_up_port
            call get_defined_key

            ld hl,msg_down
            call print_message
            ld hl,key_down_port
            call get_defined_key

            ld hl,msg_left
            call print_message
            ld hl,key_left_port
            call get_defined_key

            ld hl,msg_right
            call print_message
            ld hl,key_right_port
            call get_defined_key

            ld hl,msg_fire
            call print_message
            ld hl,key_fire_port
            call get_defined_key

            ld hl,msg_pause
            call print_message
            ld hl,key_pause_port
            call get_defined_key

            ld hl,msg_quit
            call print_message
            ld hl,key_quit_port
            call get_defined_key

            ; ok, keys should have been saved, (/me hopes)
            ld bc,key_up_port            ; return the keys data to calling program

; ----------------------------------------------
; ROUTINE: print_message
; prints a string which is terminated with $ff
; On entry, HL = address of message
print_message:        push hl            ; save the message address
            ld a,2            ; upper screen
            call CHAN_OPEN        ; get the channel sorted
            pop hl            ; get the message address back
            ;call pm_do_it        ; print the message
pm_do_it:        ld a,(hl)        ; get the character to print
            inc hl            ; point to next character
            cp 255            ; end of the string
            ret z            ; so exit
            rst 16            ; print the character
            jr pm_do_it        ; and do it all over again
            ret            ; just for my head again, :p

; ----------------------------------------------
; ROUTINE: get_defined_key
; check for key being pressed
; On entry, HL = key_xxx_port where xxx is the direction, eg up/down/left/right/fire/pause/quit
; On exit, the key_xxx_port and key_xxx_pattern will hold the values for the key pressed
get_defined_key:    ld de,port_array    ; the array of rows
            ld b,8            ; number of rows to check
gdk_loop:        ld a,(de)        ; get row
            in a,(254)        ; check for keys on that row
            and 31            ; mask off the 5 bits I want
            jr z,gdk_none        ; nothing pressed? ok, go to next row
            inc hl            ; point to the key_xxx_pattern byte
            ld (hl),a        ; save the pattern byte
            dec hl            ; go back to the key_xxx_port byte
            ld a,(de)        ; get the port that the key was found on
            ld (hl),a        ; save it
            ; ok, details for the pressed key are saved, lets gtfo here
            call delay
gdk_none:        inc de            ; next row in array
            djnz gdk_loop        ; go check next row
            ; hmm, no key pressed on keyboard, sooooo do it all again
            jr get_defined_key
            ret            ; not executed, but it stops me from getting confused, :)

; -----------------------------------------------
delay:            ld bc,32768
            ld a,0
d_loop:            dec a
            out (254),a
            dec bc
            ld a,b
            or c
            jr nz,d_loop

; ---------------------- DATA -------------------

; I've set the keys below as Q,A,O,P,SPACE,H,Y
key_up_port:        defb $fb
key_up_pattern:        defb 1
key_down_port:        defb $fd
key_down_pattern:    defb 1
key_left_port:         defb $df
key_left_pattern:     defb 2
key_right_port:     defb $df
key_right_pattern:     defb 1
key_fire_port:         defb $7f
key_fire_pattern:     defb 1
key_pause_port:     defb $bf
key_pause_pattern:     defb 16
key_quit_port:         defb $df
key_quit_pattern:     defb 16
msg_up:         defm CC_AT,7,8,"Up ?",255
msg_down:         defm CC_AT,8,8,"Down ?",255
msg_left:         defm CC_AT,9,8,"Left ?",255
msg_right:         defm CC_AT,10,8,"Right ?",255
msg_fire:         defm CC_AT,11,8,"Fire ?",255
msg_pause:         defm CC_AT,12,8,"Pause ?",255
msg_quit:         defm CC_AT,13,8,"Quit ?",255
msg_define:        defm CC_PAPER,4,CC_INK,0,CC_AT,1,7,"Press key for ...",255

port_array:        defb $7f ; B-SPACE
            defb $bf ; H-ENTER
            defb $df ; Y-P
            defb $ef ; 6-0
            defb $f7 ; 1-5
            defb $fb ; Q-T
            defb $fd ; A-G
            defb $fe ; CAPS-V

pfft:            defm "John Young",255