{"id":2737,"date":"2021-01-16T01:07:19","date_gmt":"2021-01-16T01:07:19","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2737"},"modified":"2023-12-25T19:48:34","modified_gmt":"2023-12-25T19:48:34","slug":"pintando-en-el-spectrum-4","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2737","title":{"rendered":"Pintando en el Spectrum (4)"},"content":{"rendered":"\n<p>Ha llegado el momento de empezar a pintar cosas en la pantalla. Obviamente lo que queremos pintar ser\u00e1n <em><a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Sprite_(computer_graphics)\" target=\"_blank\">Sprites<\/a><\/em>. Como explica la wikipedia, se trata de gr\u00e1ficos 2D que se integran con una escena de fondo. Como sabemos, el Spectrum no tiene soporte de sprites por hardware, lo que significa que nos toca a nosotros hacer todo el trabajo de pintarlos. Afortunadamente, en las entradas anteriores vimos c\u00f3mo hacer una rutina que copie a la pantalla una imagen completa desde un buffer organizado de manera secuencial, y esto nos va a simplificar la tarea, como veremos.<\/p>\n\n\n\n<p>Lo primero que necesitamos saber para pintar algo en pantalla es a partir de qu\u00e9 direcci\u00f3n de memoria tenemos que hacerlo. Si recordamos, la pantalla del Spectrum est\u00e1 formada por una matriz de 256 p\u00edxels de ancho por 192 de alto, y cada p\u00edxel puede tener dos colores, por lo que cada uno ocupa un bit. Esto significa que cada fila o <em>scanline<\/em> de la pantalla ocupa 32 bytes, y la parte de p\u00edxeles de la pantalla ocupa en total 6 144 bytes o 6 Kbytes exactos.<\/p>\n\n\n\n<p>Sin embargo, justo a continuaci\u00f3n viene una segunda zona denominada <em>atributos<\/em>, que define una matriz de 32 por 24 atributos y asigna un byte a cada uno. Este byte de atributos especifica qu\u00e9 colores tendr\u00e1n un grupo de p\u00edxeles. En concreto, cada byte define dos colores para cada grupo de 8&#215;8 p\u00edxeles de la pantalla: uno ser\u00e1 el color que se mostrar\u00e1 cuando el bit correspondiente al p\u00edxel est\u00e9 a cero, y el otro cuando est\u00e9 a uno. Esta zona empieza en la direcci\u00f3n de memoria 22 528 y ocupa un total de 768 bytes. Este sistema permite al Spectrum mostrar hasta 16 colores simult\u00e1neos en pantalla pero consumiendo muy poca memoria.<\/p>\n\n\n\n<p>Como ya vimos en la primera entrada, la organizaci\u00f3n de la pantalla es algo ca\u00f3tica. Ve\u00e1moslo en un gr\u00e1fico:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/pantalla_spectrum.png\" rel=\"lightbox-0\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/pantalla_spectrum.png\" alt=\"\" class=\"wp-image-2744\" width=\"634\" height=\"589\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/pantalla_spectrum.png 1024w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/pantalla_spectrum-300x279.png 300w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/pantalla_spectrum-768x714.png 768w\" sizes=\"auto, (max-width: 634px) 100vw, 634px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Vemos que los bytes situados en las direcciones de memoria en hexadecimal 0x4000, 0x4100, 0x4200, 0x4300, 0x4400, 0x4500, 0x4600 y 0x4700 se corresponden con el bloque de 8&#215;8 p\u00edxeles superior izquierdo de la pantalla, y que los atributos de dicho bloque est\u00e1n almacenados en la direcci\u00f3n 0x5800, que especifica que los dos colores de ese grupo de p\u00edxeles son blanco y negro. El siguiente bloque de 8&#215;8 p\u00edxeles est\u00e1 en las direcciones 0x4001, 0x4101, 0x4201, 0x4301, 0x4401, 0x4501, 0x4601 y 0x4701, y sus atributos, que indican que los dos colores mostrados ser\u00e1n celeste y rojo, est\u00e1n en la direcci\u00f3n 0x5801. Y as\u00ed sucesivamente. Esta disposici\u00f3n puede parecer absurda, pero si nos fijamos, vemos que la direcci\u00f3n de memoria de un scanline de p\u00edxeles y la de los atributos que le corresponden tienen el mismo valor en los ocho bits inferiores. Gracias a esta caracter\u00edstica, es posible leer los dos bytes necesarios para mostrar los ocho p\u00edxeles en s\u00f3lo tres ciclos de reloj, en lugar de los cuatro que se necesitar\u00edan si ambas direcciones no tuviesen siempre un byte id\u00e9ntico (lo que se denomina <em>fast-page<\/em>). Y si tenemos en cuenta que cada bloque de 8 p\u00edxeles tarda cuatro ciclos de reloj en ser pintado, salta a la vista de donde sale el ciclo de contenci\u00f3n de memoria que vimos en el primer art\u00edculo: la circuiter\u00eda \u00abcompacta\u00bb la lectura de dos grupos de 8 p\u00edxeles consecutivos, de manera que en lugar de bloquear al procesador durante tres ciclos y liberarlo uno, lo bloquea durante seis ciclos y lo libera durante dos consecutivos. Adem\u00e1s, de esta manera se garantiza tambi\u00e9n que los siete bits bajos se recorren completos aproximadamente cada milisegundo, lo que es m\u00e1s que de sobra para garantizar el refresco de la RAM.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines-1.png\" rel=\"lightbox-1\"><img loading=\"lazy\" decoding=\"async\" width=\"464\" height=\"168\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines-1.png\" alt=\"\" class=\"wp-image-2750\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines-1.png 464w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines-1-300x109.png 300w\" sizes=\"auto, (max-width: 464px) 100vw, 464px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Obviamente este sistema se implement\u00f3 porque permite abaratar y simplificar el <em>hardware<\/em>, pero tiene el inconveniente de que, a la hora de programar para \u00e9l, es bastante engorroso calcular la direcci\u00f3n de memoria que se corresponde con cada coordenada. As\u00ed, si dividimos la pantalla en caracteres (bloques de 8&#215;8 p\u00edxeles) y queremos encontrar las ocho direcciones de memoria de sus ocho bytes, la operaci\u00f3n que hay que hacer es la siguiente (gr\u00e1ficamente):<\/p>\n\n\n\n<p>En azul tenemos la coordenada X, que puede valer entre 0 y 31 para las 32 columnas del Spectrum. En verde y magenta tenemos la coordenada Y en l\u00edneas: en la parte verde estar\u00eda la coordenada Y en caracteres (de 0 a 23), y en magenta ser\u00eda el <em>scanline<\/em> dentro de ese car\u00e1cter, todo de arriba a abajo y de izquierda a derecha. A mayores es necesario poner los tres bits superiores a 010 para que apunte al bloque de memoria concreto. Como vemos, la cosa es bastante complicada y requiere rotaciones y m\u00e1scaras. Sin embargo, gracias a las rutinas que vimos en las entradas anteriores esta complicaci\u00f3n desaparece completamente. Si las utilizamos, la conversi\u00f3n es tan sencilla como:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines2-1.png\" rel=\"lightbox-2\"><img loading=\"lazy\" decoding=\"async\" width=\"464\" height=\"168\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines2-1.png\" alt=\"\" class=\"wp-image-2751\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines2-1.png 464w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines2-1-300x109.png 300w\" sizes=\"auto, (max-width: 464px) 100vw, 464px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Ponemos los tres bits superiores a 100 porque, para simplificar, colocamos el buffer en la direcci\u00f3n 0x8000 (32768). De esta manera, si ponemos la coordenada X en el byte inferior y la coordenada Y en la superior, s\u00f3lo tendremos que poner a uno el bit 7 del byte inferior y ya tendremos la direcci\u00f3n de memoria del <em>scanline<\/em> superior del car\u00e1cter con dichas coordenadas; para pasar al siguiente car\u00e1cter s\u00f3lo tenemos que sumar uno; para pasar al anterior, restar uno; para pasar al que est\u00e1 encima, restar 32, y para pasar al que est\u00e1 debajo, sumar 32. Tan simple como esto. \u00bfY si tenemos una direcci\u00f3n de p\u00edxeles, c\u00f3mo calculamos la de sus atributos? Pues s\u00f3lo tenemos que hacer esto:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines3.png\" rel=\"lightbox-3\"><img loading=\"lazy\" decoding=\"async\" width=\"464\" height=\"168\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines3.png\" alt=\"\" class=\"wp-image-2752\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines3.png 464w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/scanlines3-300x109.png 300w\" sizes=\"auto, (max-width: 464px) 100vw, 464px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>Sencillo \u00bfno? Aunque para los que no se quieran complicar, aqu\u00ed est\u00e1 un trozo de c\u00f3digo que lo hace:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">; Asumimos que HL contiene la direcci\u00f3n de memoria\n; de un byte de pantalla\n    ld a, l\n    and 0x1F\n    ld l, a\n    ld a, h\n    rrca\n    rrca\n    rrca\n    ld h, a\n    and 0xE0\n    or l\n    ld l, a\n    ld a, h\n    and 3\n    or 0x98\n    ld h, a\n; Ahora HL contiene la direcci\u00f3n de memoria del atributo\n; que corresponde con el byte de pantalla inicial<\/pre>\n\n\n\n<p>Y como esta entrada qued\u00f3 ya algo larga, seguir\u00e9 en la siguiente.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ha llegado el momento de empezar a pintar cosas en la pantalla. Obviamente lo que queremos pintar ser\u00e1n Sprites. Como explica la wikipedia, se trata de gr\u00e1ficos 2D que se integran con una escena de fondo. Como sabemos, el Spectrum no tiene soporte de sprites por hardware, lo que significa que nos toca a nosotros &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2737\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Pintando en el Spectrum (4)<\/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,6,7],"tags":[20],"class_list":["post-2737","post","type-post","status-publish","format-standard","hentry","category-programacion","category-retrocomputacion","category-trucos","category-tutoriales","tag-pintando-en-el-spectrum"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2737","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=2737"}],"version-history":[{"count":11,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2737\/revisions"}],"predecessor-version":[{"id":2756,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2737\/revisions\/2756"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2737"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2737"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2737"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}