Cuál fue mi sorpresa cuando me di cuenta de que era imposible teclear la clave: cada vez que pulsaba una letra aparecía dos o tres veces, da igual lo breve que fuera la pulsación.
Esto me permitió encontrar un error muy sutil en el firmware del adaptador, pero también me ha permitido encontrar otro error, mucho más sutil, en el juego Astro 2008.
El comportamiento mencionado ocurre por causa de los microrrebotes del teclado. En un Spectrum de teclado de membrana original, las pulsaciones cierran un circuito que es lo que la ULA lee como 0 (pulsado) o 1 (no pulsado). Hasta ahí bien. La cosa es que las pulsaciones no son perfectas, y en realidad lo que se generan son un tren de pulsos 11111010101000000 (cuando se pulsa una tecla) o 00000101011111 (cuando se suelta). El intervalo de tiempo donde el valor fluctúa es donde actúan los microrrebotes de la membrana.
Cuando se usa el teclado para mover un personaje, lo que se busca es ver qué tecla se pulsa, y mientras esté pulsada se mueve en una determinada dirección. Si se suelta un momento pero después se vuelve a pulsar, el personaje se sigue moviendo, así que en este caso los microrrebotes parecen no afectar.
Otra cosa es cuando se usa el teclado para "teclear". En este caso la secuencia es: esperar a que se pulse una tecla, recoger qué tecla es, almacenarla, esperar a que se suelte, y volver al principio.
En este caso es cuando ocurre el problema: si el bucle que implementa el algoritmo anterior es muy rápido, es posible escanear el teclado cada pocos microsegundos. Si una trama de microrrebotes dura más que el tiempo entre escaneos de teclado, el programa puede detectar pulsaciones incorrectas, al estar leyendo datos que corresponden a un microrrebote. Dado que lo que buscamos son secuencias pulsado/no pulsado, estos microrrebotes se interpretarán erróneamente como pulsaciones y nos podemos encontrar con que lo que tecleamos aparece repetido dos o tres veces.
Código: Seleccionar todo
Bucle:
EsperaSoltar: xor a
in a,(254)
and 00011111b
cp 00011111b
jr nz,EsperaSoltar
EsperaPulsar: xor a
in a,(254)
and 00011111b
cp 00011111b
jr z,EsperaPulsar
;Se registra la pulsacion...
jr Bucle
Un bucle así ejecutándose en un Spectrum real podría enfrentarse con la siguiente pulsación de teclado:
Código: Seleccionar todo
Teclado: 11111111111111111111111001011010000000000000000000000000
Lectura: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
Esto es lo que quería destacar: si el intervalo entre dos lecturas es menor que el tiempo que dura una trama de microrrebotes, se interpretarán como pulsaciones independientes. Aquí se comienza con la tecla soltada. En un determinado momento, el usuario la pulsa y genera la secuencia que se ve. El bucle de lectura detecta una pulsación dentro de la trama de microrrebotes, en la siguiente lectura detecta una no-pulsación, y en la siguiente, otra pulsación. El resultado final es que se almacenan dos pulsaciones de tecla en lugar de una.
La descripción técnica del Spectrum apunta a que la supresión de rebotes de teclado se hace por software, en la rutina de lectura de la ROM. Pero si no se usa dicha rutina y se lee el teclado directamente, hay que tener en cuenta esto.
En el caso del adaptador, me di cuenta porque por la propia naturaleza del mismo, no debe originar microrrebotes. Una vez corregido el firmware lo comparé con un Spectrum 48K teclado de goma, con membrana original.
Para ello usé el programa adjunto, que presenta una pantalla en blanco, con una plantilla para saber en qué columna estamos, y un cursor parpadeante que avanza una posición con cada pulsación de cualquier tecla.
Al probarlo en el Spectrum 48K la mayoría de las teclas no presentaban microrrebotes aparentes, excepto una o dos. Supongo que cuanto más gastada esté la membrana, más probabilidades de falsas pulsaciones pueden aparecer. No lo he probado, pero es posible que este comportamiento sea más acusado en el +2 y compañía, en donde el contacto de las teclas es metálico y por tanto más proclive a rebotes.
En electrónica estas cosas se arrreglan con monoestables, o en su versión barata, con un filtro pasa bajos que se "coma" esos transitorios. En software basta con insertar una pausa de 1ms en la que se ignora al teclado. 1 milisegundo cuando el teclado se usa no para mover un personaje en un arcade, sino para registrar entrada del usuario, no afecta a la respuesta del programa.
Algo así:
Código: Seleccionar todo
Bucle:
EsperaSoltar: xor a
in a,(254)
and 00011111b
cp 00011111b
jr nz,EsperaSoltar
;Hacer pausa AQUI
EsperaPulsar: xor a
in a,(254)
and 00011111b
cp 00011111b
jr z,EsperaPulsar
;Hacer pausa AQUI
;Se registra la pulsacion...
jr Bucle
El efecto que esto tiene sobre el comportamiento de las lecturas es el siguiente:
Código: Seleccionar todo
Teclado: 11111111111111111111111001011010000000000000000000000000
Lectura: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
Cuando se detecta una pulsación (sea en medio de un microrrebote o no), el teclado deja de explorarse durante 1 ms (o quizás baste con menos). Al soltar también se generar microrrebotes, que se amortiguarían con la segunda pausa.
Y esta pausa es la que creo que le falta a Astro 2008 en la parte en la que se lee el teclado para obtener la clave de paso de pantalla. El programa original lee el teclado cada aproximadamente, 10,8 microsegundos. Leyéndolo una vez por cada 100 microsegundos, o una por cada milisegundo, sería más que suficiente para eliminar los microrrebotes y seguir funcionando correctamente.