{"id":2848,"date":"2021-01-24T01:10:32","date_gmt":"2021-01-24T01:10:32","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2848"},"modified":"2023-12-25T19:47:57","modified_gmt":"2023-12-25T19:47:57","slug":"pintando-en-el-spectrum-9","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2848","title":{"rendered":"Pintando en el Spectrum (9)"},"content":{"rendered":"\n<p>Toca leer el teclado.<\/p>\n\n\n\n<figure class=\"wp-block-video aligncenter meme\"><video height=\"192\" style=\"aspect-ratio: 320 \/ 192;\" width=\"320\" controls src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2020\/06\/working.mp4\"><\/video><\/figure>\n\n\n\n<p>Para un juego, es necesario leer el teclado. Normalmente el Spectrum utiliza la interrupci\u00f3n de la ULA para llamar a la rutina de lectura del teclado situada en la direcci\u00f3n 0x38. Sin embargo, en la \u00faltima entrada vimos c\u00f3mo utilizar el modo 2 de interrupciones para llamar a nuestra propia funci\u00f3n de respuesta. Eso significa que ya no se llama a la vieja funci\u00f3n.<\/p>\n\n\n\n<p>La primera soluci\u00f3n consiste en llamar nosotros al final de nuestra ISR a la <a rel=\"noreferrer noopener\" href=\"http:\/\/www.primrosebank.net\/computers\/zxspectrum\/docs\/CompleteSpectrumROMDisassemblyThe.pdf\" target=\"_blank\">rutina de lectura de teclado en la direcci\u00f3n 0x38<\/a> (p\u00e1gina 6), pero el c\u00f3digo de la ROM es bastante complejo pues realiza muchas funciones extra, tales como decodificar la tecla concreta y gestionar la autorrepetici\u00f3n. Adem\u00e1s, asume que puede escribir en las variables del sistema del Spectrum, las cuales no tienen por qu\u00e9 existir si hemos arrancado un emulador desde un fichero .Z80.<\/p>\n\n\n\n<p>Otra soluci\u00f3n consiste en llamar a la rutina KEY-SCAN de la ROM, en la direcci\u00f3n 0x028E, que se llama desde la anterior y es la misma que se utiliza para la funcion INKEY$. Sin embargo, sigue siendo demasiado complicada para lo que necesitamos, pues realmente para un juego no necesitas detectar SHIFT + tecla, por ejemplo. Por eso decid\u00ed hacer mi propia rutina de escaneo de teclado. Esto me ha permitido reducir al m\u00ednimo su complejidad y, sobre todo, el tiempo que necesita para ejecutarse. Para entenderla, empecemos por ver c\u00f3mo es el teclado en el Spectrum:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado.png\" rel=\"lightbox-0\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"538\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado-1024x538.png\" alt=\"\" class=\"wp-image-2849\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado-1024x538.png 1024w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado-300x158.png 300w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado-768x403.png 768w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado-1536x807.png 1536w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/teclado.png 1729w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Vemos que las cuarenta teclas forman una matriz de 5&#215;8 teclas. Las columnas est\u00e1n conectadas por un lado a 5 voltios a trav\u00e9s de unas resistencias de 10Kohms, las cuales mantienen a las l\u00edneas a uno l\u00f3gico por defecto. Por el otro lado, las cinco columnas est\u00e1n conectadas a la ULA, que es quien se encarga de enviar al procesador el dato cuando se lee del puerto 254. En cuanto a las filas, vemos que est\u00e1n conectadas a las ocho l\u00edneas superiores del bus de direcciones (y para evitar cortocircuitos, mediante un diodo 1N4148).<\/p>\n\n\n\n<p>Si todas las l\u00edneas A8-A15 est\u00e1n a 1, da igual que pulsemos una tecla o no, pues la l\u00ednea correspondiente del bus de datos (D0-D4) recibir\u00eda cinco voltios. Sin embargo, si colocamos una de estas l\u00edneas del bus de direcciones a 0 y pulsamos una tecla cualquiera de dicha fila, el pulsador de la tecla conectar\u00e1 dicha fila a la columna correspondiente, con lo que el procesador leer\u00e1 un 0 en esa l\u00ednea.<\/p>\n\n\n\n<p>As\u00ed, si queremos saber si la tecla B, por ejemplo, est\u00e1 pulsada o no, tenemos que poner A15 a cero y el resto (A14-A8) a uno, leer el puerto 254, y comprobar si D4 est\u00e1 a cero (en cuyo caso la tecla estar\u00e1 pulsada) o a uno (en cuyo caso la tecla no estar\u00e1 pulsada).<\/p>\n\n\n\n<p> Veamos ahora mi rutina de lectura de teclado:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">    ld BC, 0xFEFE\n    ld DE, 0x001F\nIM2_LOOP:\n    in A, (C)\n    cpl\n    and E\n    jr NZ, IM2_LOOP2\n    rlc B\n    ld A, 0x20\n    add A, D\n    ld D, A\n    jr NC,IM2_LOOP\nIM2_LOOP2:\n    or D\n    ld (CURRENT_KEY), A<\/pre>\n\n\n\n<p>Dado que vamos a usar IN A, (C), empezamos cargando 0xFE en el registro C para direccionar el puerto 254 (el de la ULA) y leer el teclado. Adem\u00e1s, cargamos tambi\u00e9n 0xFE en B pues su valor ser\u00e1 el que aparezca en la parte superior del bus de direcciones. Por otro lado, ponemos D a cero, pues lo usaremos para contar las semifilas en sus tres bits superiores.<\/p>\n\n\n\n<p>Ahora leemos el puerto 254 y as\u00ed tendremos el valor en el registro A. Invertimos los bits de manera que la tecla pulsada est\u00e9 a uno en lugar de a cero, y eliminamos los tres bits superiores, que no se necesitan para nada (recordemos que el bit D6 es el del puerto del casete, y su valor, en ausencia de se\u00f1al, suele ser <em>aleatorio<\/em>). Si el valor es cero significa que no hay ninguna tecla pulsada en esta semifila, as\u00ed que rotamos B a la izquierda de manera que el bit que est\u00e1 a cero pase a la siguiente posici\u00f3n, y as\u00ed escanear la siguiente semifila. Le sumamos tambi\u00e9n 32 al valor actual de D para contar la siguiente semifila. Hecho esto, si detectamos acarreo tras la suma significa que ya hemos hecho las ocho semifilas y podemos salir del bucle.<\/p>\n\n\n\n<p>Sin embargo \u00bfqu\u00e9 pasa si el valor le\u00eddo no es cero? Eso significa que alguna tecla est\u00e1 pulsada. En ese caso lo que hacemos es <em>mezclar<\/em> con una operaci\u00f3n OR el valor le\u00eddo y el del registro D, de manera que los cinco bits inferiores tendr\u00e1n un bit a 1 por cada tecla pulsada en la semifila, y los tres bits superiores tendr\u00e1n el n\u00famero de semifila en la que se ha detectado la pulsaci\u00f3n.<\/p>\n\n\n\n<p>Obviamente esta funci\u00f3n es muy sencilla, pero es suficiente para asignar un valor de ocho bits \u00fanico a cada tecla pulsada, aunque no es capaz de identificar bien si hay varias pulsaciones simult\u00e1neas (s\u00f3lo si son de la misma semifila), pero para lo que necesitaremos es m\u00e1s que suficiente.<\/p>\n\n\n\n<p>Sin embargo, lo interesante de esta rutina es que es MUY r\u00e1pida. En concreto, en el peor de los casos tardar\u00e1 528 Testados en ejecutarse. Eso es menos de lo que duran tres <em>scanlines<\/em>, lo que significa que podemos ejecutarlo antes del bucle que detecta cuando empieza la ULA a pintar en la zona del PAPER de la pantalla, pues la zona del BORDER que est\u00e1 antes son 64 <em>scanlines<\/em>, por lo que tenemos tiempo m\u00e1s que de sobra. El resultado es que podemos aprovechar parte de ese tiempo que nos qued\u00e1bamos esperando.<\/p>\n\n\n\n<p>La tecla pulsada se almacena al final en la posici\u00f3n de memoria CURRENT_KEY, y ser\u00e1 cero si no hay ninguna pulsada, o un valor \u00fanico para cada tecla si s\u00ed est\u00e1 pulsada. Para asociar cada valor a una tecla basta con saber que los tres bits superiores contienen la semifila (000 -> A8, 001 -> A9, &#8230; , 110 -> A14, 111 -> A15), y los cinco bits inferiores qu\u00e9 teclas est\u00e1n pulsadas en esa semifila.<\/p>\n\n\n\n<p>Y con esto ya tenemos todo listo para empezar a pintar el MAPA.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Toca leer el teclado. Para un juego, es necesario leer el teclado. Normalmente el Spectrum utiliza la interrupci\u00f3n de la ULA para llamar a la rutina de lectura del teclado situada en la direcci\u00f3n 0x38. Sin embargo, en la \u00faltima entrada vimos c\u00f3mo utilizar el modo 2 de interrupciones para llamar a nuestra propia funci\u00f3n &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2848\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Pintando en el Spectrum (9)<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,17,7],"tags":[20],"class_list":["post-2848","post","type-post","status-publish","format-standard","hentry","category-programacion","category-retrocomputacion","category-tutoriales","tag-pintando-en-el-spectrum"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2848","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2848"}],"version-history":[{"count":5,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2848\/revisions"}],"predecessor-version":[{"id":2854,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2848\/revisions\/2854"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2848"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2848"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2848"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}