Ale, JL, lo siento, más faena para ti.
Aquí van unos ejemplillos de lectura de teclas y joystick, que seguro que también son interesantes. Lo dejo todo en un LZH, con la Rom de ejemplo ensamblada. Abajo el código de la misma.
--------------------------------------------------
KEYBOARD / JOYSTICK
-------------------
Aquí van un ejemplo completito de como usar el teclado y el joystick. Este tema es un poco "duro", desde el punto de vista que existen muchas maneras de hacerlo, y además puede ser algo "no standard" cuando hablamos de algunas teclas.
Básicamente, existen muchos tipos de teclado en el ámbito del MSX. No todas las teclas están posicionadas en la misma fila/columna en todos los MSX. Pero cuando hablamos de las teclas comunmente utilizadas en los juegos, si tenemos una misma correspondencia.
Así pues el ejemplo nos demuestra como usar ambos joysticks, el teclado, formas de leer una tecla continuamente y esperar a que el player suelte, y además dos formas de leer el buffer del teclado, con y sin BIOS.
También podemos encontrar en MSX ASSEMBLY (
http://map.tni.nl/), un interesante artículo del problema de algunos teclados MSX, que "autopulsan" teclas con la combinación de otras.
Breve explicación del programa.
INITMAIN:
Inicializa la Rom, de una forma sobradamente conocida, posiciona Screen 0 con 40 columnas y unos colores para poder escribir texto y que sea visible. Elimina el keyclick, obliga a la rutina OUTDO a escribir en pantalla e inicializa los textos.
También hace una lectura forzada de teclado para inicializar los bufferes de teclado, y seguidamente espera a que se produzca una interrupción para rellenar adecuadamente el buffer de teclado antiguo.
MAIN:
El bucle principal no tiene mucho misterio. En cada interrupción revisa si hemos pulsado/soltado:
a) Cualquier tecla, y mantiene a 1 hasta que no se suelta
b) Cualquier tecla, y solo mantiene a 1 durante una interrupción. No se volvera a poner a uno hasta que no soltemos y pulsemos de nuevo
c) El boton 1 de Joystick y SPACE, se pondrá a 1 solo cuando pulsemos y soltemos. Mientras no soltemos no se pondrá a 0.
d) Idem con boton 2 Joystick y CTRL, solo que siempre estará pulsado hasta que no soltemos.
Por ultimo imprime los resultados de esa interrupción, espera a que se produzca una nueva interrupción y relee los buffers de teclado.
PANTALLITA:
Esta parte no creo que sea necesaria de explicar. Son unas rutinas rapiditas para imprimir en pantalla con la BIOS.
KEYBOARD AND JOY SUBR:
Aquí empieza la "chicha".
[] Presskey: Activará el flag Cy, si alguna tecla ha sido pulsada o está pulsada.
[] PressKeyTrigger: Activará el flag Cy si alguna tecla es pulsada, pero no lo activará de nuevo hasta que no sea soltada y vuelta a pulsar.
[] CheckFire: Aunque no se usa en los ejemplos, activa el flag Cy cuando el boton 1 del Joystick, Shift, Space, TAB y Return están pulsados. Una manera de tener más de un botón de fuego en nuestras aplicaciones, y evitar el "sobreuso" de SPACE.
[] CheckFireTrigger: Idem que el anterior, peeero, solo se activa el flag Cy, si se pulsa, se suelta y se vuelve a pulsar.
[] CheckFiretrigger2: Para botón 2 de Joystick, CTRL y GRAPH, con "trigger".
Aquí faltaría añadir la rutina Checkfire2, para saber si esta pulsado en cada interrupción sin soltar.
La dejo como ejercicio.
[] CheckfireTriggerall: Pues para saber si cualquier boton de fuego, 1 o 2, de cualquier joystick y teclado esta pulsado y se suelta y se vuelve a pulsar.
[] TECLADO: Esta rutina se ha de llamar al principio de cada interrupción. El hacerlo así es para poder aprovechar las teclas leídas de la BIOS, en caso de usar ese modo. En caso de usar el modo manual, pues lee el teclado. Básicamente lo que hace es pasar el buffer actual a viejo, leer el nuevo teclado y leer los joysticks.
[] JOYSTICK Y JOYSTICK2:
Una manera muy curiosa de leer los joysticks, y aprovecharlos para el mismo player, ya que el ejemplo sería funcionar para un juego de 1 jugador. Lo que hacen estas rutinas, además de leer ambos joysticks (PORT 1 Y 2), es aprovechar el buffer de teclado para marcar si ha sido pulsado alguno de los botones, y las direcciones. De esta manera nuestro bucle principal solo lee "teclas", y no necesita leer teclado por un lado y joysticks por otro lado.
[] READKEYBOARD: Esta rutina solo se ensambla si usamos el modo de lectura de teclado NO BIOS. Basicamente lee todas las filas de teclado y las almacena en keys.
[] TECLAOLD: La madre del cordero.
Le pasamos en A la fila que deseamos consultar con "trigger". Es decir que solo nos devolverá que una tecla de esa fila ha sido pulsada, en caso de que estuviese soltada. Si en el antiguo frame estaba pulsada la devuelve como no pulsada.
NOTAS FINALES:
Para finalizar, comentar que el programa tiene dos maneras de leer el teclado, aunque ya se ha explicado por encima en las descripciones de las rutinas. Normalmente si hacemos una Rom y no "jugueteamos" con pilas y punteros, la rutina de 38h nos lee automáticamente el buffer del teclado, cual rutina "readkeyboard". Si la BIOS lo hace, y ya que normalmente salta a 38h, ¿por qué hacerlo nosotros?
El modo "sin bios", es muy recomendable para de todo tipo que parcheen completamente la rutina de interrupción.
; *** CONSTANTES ***
NEWKEY: equ 0FBE5h
CHGMOD: equ 05Fh
PTRFLG: equ 0F416h
PTRFIL: equ 0F864h
OUTDO: equ 18h
FORCLR: equ 0F3E9h
BAKCLR: equ 0F3EAh
BDRCLR: equ 0F3EBh
LINL40: equ 0F3AEh
CLIKSW: equ 0F3DBh
CSRY: equ 0F3DCh
CSRX: equ 0F3DDh
; *** CONDICIONALES ***
SUPPORT_BIOS_KEYBOARD: equ 1
; La bios solo lee 11 filas en muchos MSX1
if (SUPPORT_BIOS_KEYBOARD)
KEYROWS: equ 11
else
KEYROWS: equ 12
endif
; *** PROGRAMA ***
org 04000h
db 041h,042h
dw initmain
ds 12
initmain:
di
im 1
ld sp,0F380h
; Screen 0 colores 15,4,4
ld hl,0404h
ld (BAKCLR),hl
ld a,15
ld (FORCLR),a
; 40 columnas
ld a,39
ld (LINL40),a
xor a
ld hl,0
ld (PTRFLG),a ; Escritura en pantalla
ld (PTRFIL),hl
ld (CLIKSW),a ; Fuera key click
call CHGMOD
call initscreen ; pintamos la pantalla
call teclado ; Teclado inicial
jr mainwait ; Ejecutamos 1 int para rellenar teclado
; y que funcionen las teclas "trigger"
main:
ld a,'0'
ld (keytrigger),a
ld (keynotrigger),a
ld (keybutton1),a
ld (keybutton2),a
; key trigger
call presskeytrigger
jr nc,main1
ld a,'1'
ld (keytrigger),a
main1: ; key no trigger
call presskey
jr nc,main2
ld a,'1'
ld (keynotrigger),a
main2:
; button 1 TRIGGER
ld a,8
call teclaold
and 1
jr nz,main3
ld a,'1'
ld (keybutton1),a
main3:
; button 2 NO TRIGGER
ld a,(keys+6)
and 2
jr nz,mainend
ld a,'1'
ld (keybutton2),a
mainend:
; print results
call printresult
mainwait:
ei
halt
call teclado
jr main
; *** PANTALLITA ***
; --------------------
; INISCREEN
; Inicializa los valores
; y la pantalla
; ---------------------
initscreen:
ld a,'0'
ld (keytrigger),a
ld (keynotrigger),a
ld (keybutton1),a
ld (keybutton2),a
; pintamos
call printlogo
call printkeys
call printresult
ret
printresult:
ld de,(locatetxtrtrigger)
ld a,(keytrigger)
call printchar
ld de,(locatetxtrnotrigger)
ld a,(keynotrigger)
call printchar
ld de,(locatetxtrbutton1)
ld a,(keybutton1)
call printchar
ld de,(locatetxtrbutton2)
ld a,(keybutton2)
jp printchar
printkeys:
ld de,(locatetxttrigger)
ld hl,txttrigger
call print
ld de,(locatetxtnotrigger)
ld hl,txtnotrigger
call print
ld de,(locatetxtbutton1)
ld hl,txtbutton1
call print
ld de,(locatetxtbutton2)
ld hl,txtbutton2
jp print
printlogo:
ld de,(locatetxtlogo)
push de
ld hl,txtlogo
call print
pop de
inc e
ld hl,txtlogo2
jp print
; ----------------
; PRINTCHAR
; CutrePrintChar
; DE: XY
; A : Char
; -----------------
printchar:
ld (CSRY),de
RST OUTDO
ret
; ------------------
; PRINT
; CutrePrint
; DE : XY
; HL : Puntero a texto
; ------------------
print:
ld (CSRY),de
print0:
ld a,(hl)
inc hl
or a
ret z
push hl
RST OUTDO
pop hl
jr print0
; *** KEYBOARD AND JOY SUBR ***
; -----------------
; PRESSKEY
; Press any key
; -----------------
presskey:
ld hl,keys
ld b,KEYROWS
presskey0:
ld a,(hl)
inc hl
inc a
jr nz,checkfirepush
djnz presskey0
or a
ret
; -----------------
; PRESSKEY
; Press any key trigger
; -----------------
presskeytrigger:
ld b,KEYROWS
ld c,0
presskeytrigger0:
push bc
ld a,c
call teclaold
pop bc
inc a
jr nz,checkfirepush
inc c
djnz presskeytrigger0
or a
ret
;----------------
; CHECKFIRE
; Chequea fuego
; Cy = 1 Si
; ----------------
checkfire:
ld a,(keys+8)
and 1
jr z,checkfirepush
ld a,(keys+7)
bit 3,a
jr z,checkfirepush
bit 7,a
jr z,checkfirepush
ld a,(keys+6)
and 1
jr z,checkfirepush
or a
ret
checkfirepush:
scf
ret
;----------------
; CHECKFIRETRIGGER
; Chequea fuego
; Cy = 1 Si
; ----------------
checkfiretrigger:
ld a,8
call teclaold
and 1
jr z,checkfirepush
ld a,7
call teclaold
bit 3,a
jr z,checkfirepush
bit 7,a
jr z,checkfirepush
ld a,6
call teclaold
and 1
jr z,checkfirepush
or a
ret
; -------------------------
; CHECKFIRETRIGGERALL
; Chequea AMBOS fires 1 y 2
; --------------------------
checkfiretriggerall:
call checkfiretrigger
ret c
; ----------------------
; CHECKFIRETRIGGER2
; Checkfiredos (CTRL / GRAPH)
; ----------------------
checkfiretrigger2:
ld a,6
call teclaold
bit 2,a
jr z,checkfirepush
and 2
jr z,checkfirepush
or a
ret
; ------------------
; TECLADO
; Lee el mapa del
; teclado
; ------------------
teclado:
; pasamos lo anterior a old
di
ld hl,keys
ld de,keysold
ld bc,KEYROWS
ldir
if (!SUPPORT_BIOS_KEYBOARD)
call readkeyboard
else
ld hl,NEWKEY
ld de,keys
ld bc,KEYROWS
ldir
endif
call tecladogen
ei
ret
tecladogen:
call joystick
joystick2:
ld a,0Fh
out (0A0h),a
in a,(0A2h)
and %10111111
or %01000000
out (0A1h),a
ld a,0Eh
out (0A0h),a
in a,(0A2h)
or %11000000
ld hl,keys+8
bit 0,a
call z,set_arriba
bit 1,a
call z,set_abajo
bit 2,a
call z,set_izq
bit 3,a
call z,set_derecha
bit 4,a
call z,set_fire
bit 5,a
call z,set_fire2
ret
joystick:
ld hl,keys+8
ld a,0Fh
out (0A0h),a
in a,(0A2h)
and %10111111
out (0A1h),a
ld a,0Eh
out (0A0h),a
in a,(0A2h)
or %11000000
bit 0,a
call z,set_arriba
bit 1,a
call z,set_abajo
bit 2,a
call z,set_izq
bit 3,a
call z,set_derecha
bit 4,a
call z,set_fire
bit 5,a
call z,set_fire2
ret
set_arriba:
res 5,(hl)
ret
set_abajo:
res 6,(hl)
ret
set_izq:
res 4,(hl)
ret
set_derecha:
res 7,(hl)
ret
set_fire:
res 0,(hl)
ret
set_fire2:
ld hl,keys+6
res 1,(hl) ; Control
ret
if (!SUPPORT_BIOS_KEYBOARD)
; -------------------------
; READKEYBOARD
; Lee el teclado tal cual
; Y lo mete en su sitio
; -----------------------
readkeyboard:
ld b,KEYROWS ; Numero de filas a leer
ld hl,keys ; Buffer teclas
readkeyboard0:
push bc
ld a,KEYROWS
sub b ; Para empezar por la 0
call keyboard
ld (hl),a
inc hl
pop bc
djnz readkeyboard0
ret
keyboard:
ld c,a
in a,(0AAh)
and 0F0h
or c
out (0AAh),a
in a,(0A9h)
ret
endif
; --------------
; TECLAOLD
; A : Fila
; -----------
teclaold:
push hl
ld c,a
ld hl,keys
call sumahl
ld a,(hl)
ld b,a
ld a,c
ld hl,keysold
call sumahl
ld a,(hl)
xor b
cpl
or b
pop hl
ret
; *** GENERICAS ***
; -------------
; SUMAHL
; HL = HL + A
; ------------
sumahl:
add a,l
ld l,a
ret nc
inc h
ret
; *** DATA ***
locatetxtlogo: db 1,5
txtlogo: db "KEYBOARD AND JOYSTICK EXAMPLE.",0
txtlogo2: db "------------------------------",0
locatetxttrigger: db 4,1
txttrigger: db "KEY TRIGGER : ",0
locatetxtnotrigger: db 6,1
txtnotrigger: db "KEY NO TRIGGER : ",0
locatetxtbutton1: db 8,1
txtbutton1: db "KEY BUTTON1 : ",0
locatetxtbutton2: db 10,1
txtbutton2: db "KEY BUTTON2 : ",0
locatetxtrtrigger: db 4,21
locatetxtrnotrigger: db 6,21
locatetxtrbutton1: db 8,21
locatetxtrbutton2: db 10,21
; 8k Rom
ds 06000h - $,0FFh
; *** VARIABLES ***
keys: equ 0E000h ; Tecla actual
keysold: equ keys + KEYROWS ; Para tecla old
keytrigger: equ keysold + KEYROWS
keynotrigger: equ keytrigger + 1
keybutton1: equ keynotrigger + 1
keybutton2: equ keybutton1 + 1