Tutorial de ZX Basic + Fourspriter #15: Leyendo el teclado.

Hace algún tiempo (mucho), ya hablé de esto en este mismo blog, pero ahora la explicación va a ser mucho más mejor. BASIC nos ofrece una función para leer el teclado: Inkey. El problema es que esta función, que simplemente devuelve una cadena con el carácter correspondiente a la tecla que se esté pulsando en el momento de llamarla, o la cadena vacía si no se está pulsando nada, no nos sirve para detectar pulsaciones simultaneas y, por tanto, no viene bien para usarla en un juego. Esto no quiere decir que un servidor y muchos de los que me leéis no la hayamos usado mil veces… Pero no es lo más adecuado.

Para poder detectar pulsaciones simultaneas, hay que mirar directamente el hardware del Spectrum (¡hostia!). El teclado se lee mirando el valor que hay en el puerto $FE (254). En el valor obtenido, leeremos un 0 si una tecla está pulsada y un 1 si no. Pero el Spectrum tiene 40 teclas (el de gomas tiene 40 teclas físicas, los demás usan barateces para simular combinaciones de teclas en las teclas extra que traen), ¿de dónde sacamos 40 bits?

La solución fue dividir el teclado en 8 “semifilas” de 5 teclas. El teclado del Spectrum es este:

 1   2   3   4   5  |  6   7   8   9   0
 Q   W   E   R   T  |  Y   U   I   O   P
 A   S   D   F   G  |  H   J   K   L   EN
 CS  Z   X   C   V  |  B   N   M   SS  SP

El orden de las filas es un poco raro: empieza abajo a la izquierda, va subiendo, pasa a la derecha, y termina abajo a la derecha, esto es:

  1. Empezamos abajo a la izquierda: la primera fila es de Caps Shift a V.
  2. La segunda fila es de A a G.
  3. La tercera fila es de Q a T.
  4. La cuarta, de 1 a 5.
  5. Ahora nos pasamos a la derecha: la quinta es de 6 a 0.
  6. La sexta, de Y a P.
  7. La séptima, de H a Enter.
  8. Y la octava y última, de B a Space.

Para leer una fila u otra, lo que se hace es leer del puerto XXFEh, con XX un número especial para cada fila. Este número, en binario, son todos 1 menos la posición correspondiente a la fila que sea, donde hay un cero:

  1. Para la primera fila (CS-V) tendremos que leer el puerto 11111110b FEh, o sea, FEFEh, 65278.
  2. Para la segunda fila (A-G) tendremos que leer el puerto 11111101b FEh, o sea, FDFEh, 65022.
  3. Para la tercera fila (Q-T) tendremos que leer el puerto 11111011b FEh, o sea, FBFEh, 64510.
  4. Para la cuarta fila (1-5) tendremos que leer el puerto 1111011b FEh, o sea, F7FEh, 63486.
  5. Para la quinta fila (6-0) tendremos que leer el puerto 11101111b FEh, o sea, EFFEh, 61438.
  6. Para la sexta fila (Y-P) tendremos que leer el puerto 11011111b FEh, o sea, DFFEh, 57342.
  7. Para la séptima fila (H-EN) tendremos que leer el puerto 10111111b FEh, o sea, BFFEh, 49150.
  8. Para la octava fila (B-SP) tendremos que leer el puerto 01111111b FEh, o sea, 7FFEh, 32766.

Recapitulando, para leer bits asociados a teclas pulsadas o no pulsadas, habrá que leer del puerto correspondiente a su semifila. Durante muchos años no tenía ni puta idea de dónde venían los números, sólo los miraba en una lista que tenía. La tabla de arriba es simplemente para que sepáis de donde salen los números. Con apuntar la lista, a efectos prácticos, es suficiente. Pero para aquellos de vosotros que disfrutéis con el saber, pues ya sabéis.

Entonces, si hacemos un a = In (57342), por ejemplo, estaremos obteniendo un byte en el que se representa el estado de las teclas Y, U, I, O y P. ¿Cómo? Pues muy sencillo: los cinco primeros bits corresponden a las cinco teclas, siempre contando desde fuera del teclado hacia dentro:

Bits:  7 6 5 4 3 2 1 0
Tecla: - - - Y U I O P

De este modo, si el bit 0, por ejemplo, está a “0”, es que “P” está pulsada. Si está a “1”, es que “P” no está pulsada.

Si hacemos un a = In (64510), estaremos obteniendo un byte en el que se representa el estado de las teclas Q, W, E, R y T. De la misma forma, y siempre contando desde fuera del teclado hacia dentro:

Bits:  7 6 5 4 3 2 1 0
Tecla: - - - T R E W Q

O sea, si el bit 0 está a “0”, es que “Q” está pulsada. Si el bit 2 está a “0”, es que “E” está pulsada.

Y así con todas las filas.

¿Y cómo leemos un bit en concreto del valor que leemos del puerto que sea? Por suerte, ZX Basic incluye ya operaciones a nivel de bit. En concreto, la que usaremos sea bAnd. (a bAnd b) hace un and de cada bit de A con cada bit de B y devuelve el resultado. Esto se puede usar con una potencia de 2 para ver si un bit en concreto está a 0:

If (a bAnd 2^X) = 0 Then...

Con “a” el valor leído con In del puerto correspondiente y X el número de bit que queremos mirar si está a 0 (y, por tanto, su tecla correspondiente está pulsada). Por tanto, si, por ejemplo, queremos ver si el usuario está pulsando “O”, tendríamos que hacer:

If (In (57342) bAnd 2) = 0 Then Print "O está pulsada"

Por partes: ¿en qué semifila esta la O? En la sexta. Por tanto, hay que leer el puerto 57342. ¿Qué bit ocupa la O dentro de la semifila? el 1. Calculamos 2^1 = 2.

Otro ejemplo. Queremos ver si el usuario está pulsando “4”. El tema sería así:

If (In (63486) bAnd 8) = 0 Then Print "4 está pulsada"

De nuevo, ¿en qué semifila está el 4? En la cuarta. Por tanto, hay que leer el puerto 63486. ¿Qué bit ocupa el 4 dentro de la semificla? El 3. Calculamos 2^3 = 8.

¿Un lío? Sí y no. Sí porque hay mucho jaleo de bits y de paranoias. No porque en realidad nuestro juego tendrá cuatro o cinco teclas y sólo tendremos que calcular los numeritos una vez.

Vamos a probar esto. En nuestro feoncio.bas, borramos la última prueba (la de pintar la colisión sobre el mapa), y escribimos este código en su lugar:

' Empezar

'' Esto es una prueba. Eliminar luego
Dim terminado as uByte
terminado = 0
While Not terminado
   '' Detectar "O": Sexta semifila, bit 1
   If (In (57342) bAnd 2) = 0 Then
      Print At 10, 0; "LEFT"
   Else
      Print At 10, 0; "     "
   End If
   '' Detectar "P": Sexta semifila, bit 0
   If (In (57342) bAnd 1) = 0 Then
      Print At 10, 8; "RIGHT"
   Else
      Print At 10, 8; "     "
   End If
   '' Detectar "Q": Tercera semifila, bit 0
   If (In (64510) bAnd 1) = 0 Then
      Print At 10, 16; "UP"
   Else
      Print At 10, 16; "  "
   End If
   '' Detectar "A": Segunda semifila, bit 0
   If (In (65022) bAnd 1) = 0 Then
      Print At 10, 24; "DOWN"
   Else
      Print At 10, 24; "    "
   End If
Wend
'' Fin de la prueba.

Compilad el proyecto y ejecutadlo en un emulador. Probad a pulsar las teclas O, P, Q y A y veréis como funciona. Disclaimer: veréis que no detecta algunas combinaciones. En especial, no detecta si pulsamos las cuatro teclas a la vez. Esto es un problema del hardware de nuestro PC, donde estamos ejecutando el emulador. En un Spectrum real, no existe este problema.

Soy consciente de que este capítulo tiene mucha chicha a bajo nivel y puede parecer complicado, pero en realidad es algo muy sencillo una vez que le pillas la lógica. Además, ese código de prueba nos servirá para empezar a programar nuestra rutina de movimiento. De todos modos, os pongo tarea: ¿Cómo sería el IF para detectar si estamos pulsando la tecla “M”? A ver si lo habéis pillao.

Como siempre, podéis descargaros el paquetico con todos los tiestos.

2 Responses to Tutorial de ZX Basic + Fourspriter #15: Leyendo el teclado.

  1. LeoCZ dice:

    Para detectar la tecla “M” sería:

    ” Detectar “M”: Octava semifila, bit 2
    If (In (32766) bAnd 4) = 0 Then
    Print At 10, 19; “FIRE”
    Else
    Print At 10, 19; ” ”
    End If

    Excelente tutorial!!

    Saludos
    LeoCZ

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: