Source: https://sinclairqles.wordpress.com/2009/10/16/la-pantalla-del-ql/
Timestamp: 2019-12-11 04:01:38+00:00

Document:
La pantalla del QL | QBlog
En varias ocasiones hemos tratado el tema del manejo de la pantalla del QL en Qforum (el foro del QL de sinclairql.es). En ellos hay variedad de información, pero al menos a mí, siempre me han quedado algunas lagunas debido al desconocimiento de cómo trata el QL la pantalla. En muchas ocasiones no he entendido exactamente las aportaciones de los otros usuarios cuando hablan en profundidad de este tema, siempre ha habido conceptos que se me han escapado y que no he llegado a comprender del todo bien. Por ejemplo, el porqué de las distintas resoluciones del QL, como se gestionan los colores, las peculiaridades del “flashing”, etc.
Para cubrir estas lagunas presento este artículo en el cual sólo me he dedicado a rescatar material “enterrado” en varias fuentes que hablan sobre esto. He dividido el artículo en dos partes. Una primera parte con “la teoría” extraída de un buen artículo de la vieja revista Qlave escrito por Javier Boira (¡fantástico el artículo!), y una segunda parte donde se ejemplifica el tratamiento directo de la memoria de pantalla del QL mediante un pequeño programa escrito en SuperBASIC. Este programa no es más que una adaptación de otro pequeño programa de Timothy Swenson publicado en el ezine “QL Hacer’s Journal”. Como veis, el contenido no es de elaboración propia, yo sólo me he limitado a rescatar, traducir, mezclar, decorar y organizar la información que he encontrado.
La pantalla constituye el medio directo de comunicación del ordenador con el usuario, es lógico pues que dediquemos algún tiempo a conocer más de cerca este elemento tan cercano al programador.
Nuestro QL nos ofrece, ya nada encenderlo, dos posibilidades gráficas; una de ellas con mayor colorido pero menor resolución (256×256), mientras la otra nos ofrece la ventaja de una mayor resolución con 512 puntos horizontales (512×256) a la par que se pierden cuatro colores y la posibilidad de parpadeo.
De todas formas ambos ocupan una misma región de memoria del QL, la cantidad 32K (relativamente grandes para los ordenadores de esta generación). Son estos 32K y sólo ellos los que van a llevar toda la información que se nos ofrece en nuestros monitores. La conversión en imagen visual se realiza a través del conversor de vídeo, este elemento busca la información de cada punto en la memoria (en el momento que se necesite) para darle la tonalidad y contraste necesaria enviándola al monitor y que nosotros reconozcamos la figura claramente. A pesar de que a nosotros nos parezca al utilizar el ordenador que éste nos dedica todo su tiempo esto no es cierto. El microprocesador ha de hallarse constantemente interrumpiendo su labor hacia nosotros (BASIC) para representar la pantalla, manejar Microdrives, atender al teclado y otras muchas cosas que hace mientras nosotros inocentemente creemos estar con nuestro aparato tranquilamente ocupada en el BASIC. Nosotros no nos enteramos del tiempo ocupado en dichos menesteres porque el tiempo utilizado es mínimo para nuestra capacidad de percepción.
Pero volvamos a la memoria de pantalla, que es allí donde se van a definir todas las posibilidades gráficas del QL. Nosotros podemos acceder a toda la información contenida en la memoria de pantalla, así como podemos modificarla con solo “pokear” la zona correspondiente (o “peekear” para leer).
La zona de memoria de pantalla comienza en la dirección $20000, en decimal la 131072, y ocupa exactamente 32768 octetos de memoria RAM, es por esto por lo que de los 128K que lleva instalado nuestro QL de forma estándar, sólo son utilizables menos de 96K. Si multiplicamos estos 32768 bytes por 8 bits que contiene un byte nos dan 262144 en decimal, $40000 bits en hexadecimal. Luego en total disponemos de 262144 unidades mínimas de información, cada bit puede contener un sí o no, uno todo o nada. Luego en estas condiciones podríamos asignar dos colores, uno al si en un bit 1 y el otro al no 0, con lo que con 32K de memoria se podrían definir (si así lo hubiesen definido los diseñadores) 262144 puntos de pantalla. Los diseñadores de SINCLAIR no optaron por esta forma de estructurar la memoria de pantalla, sino por otra, en la que se utilizan cuatro colores. Con un mínimo de conocimiento binario se puede recordar que con dos bits podemos formar cuatro posiciones distintas jugando con los unos y los ceros 00, 01, 10, 11, con lo que con estas dos unidades podemos definir un punto de la pantalla en cuatro posiciones distintas o intensidades (que puede ser como en el QL en alta resolución el negro, rojo, verde y blanco).
Pues bien, si a cada punto dibujable (representable más bien) en la pantalla le asignamos dos bits, en los 262144 bits que disponíamos nos cabe la definición de 131072 puntos dibujables (dividir por dos), exactamente 512 filas por 256 columnas (512×256=131072). Pero además el QL viene dotado de otra resolución más baja, 256×256 puntos en ocho colores y posibilidad de parpadeo. En esta resolución cada punto representable debe hallarse en dieciséis posibles situaciones (negro, azul, rojo, magenta, verde, cian, amarillo, blanco, negro con parpadeo, azul con parpadeo, rojo con parpadeo, magenta con parpadeo, verde con parpadeo, cian con parpadeo, amarillo con parpadeo y blanco con parpadeo), que con bits se puede representar con cuatro (2e4=16, combinaciones de dos elementos “1” y “0” tomados de cuatro en cuatro), por lo que podemos representar 262144/4=65536 ($10000) puntos, es decir 256×256.
Por lo tanto, dependiendo de las combinaciones de unos y ceros en dos bits (alta resolución) o cuatro bits (baja resolución), aparecen en la pantalla los puntos en tonalidad e intensidades deseados. Ahora veamos cómo se colocan estos cuatro o dos bits en la memoria de pantalla.
La zona de memoria de pantalla comienza en la dirección $20000, y acaba en la $27FFF. Cada punto de la pantalla tiene situada su información en dos bytes diferentes y vecinos. Así en el modo de alta resolución cada dos bytes definirán ocho puntos (2×8/2). El primer bit de definición se hallará en el primero de los bytes, y el segundo en el segundo de los bytes. Así el primer punto de la pantalla (el de la esquina superior izquierda) vendrá definido por el primer bit el byte $20000, que seleccionará el color verde (1=verde), y por el primer bit del byte #20001, que seleccionará el color rojo. Si ninguno de ambos bits está activado el punto se hallará en negro, mientras que si se hallan ambos activados (“1”) estará en blanco.
El siguiente punto de la pantalla hacia la derecha viene definido por el segundo bit de los bytes $20000 y $20001 y así sucesivamente, cuando se completan los ocho bits del primer y segundo byte se pasa al primero del tercer y cuarto byte. De este forma los bytes pares contienen la definición del verde de un determinado punto, mitras lo impares del rojo. Cuando se completan los 512 puntos de una línea se pasa a la siguiente. Por lo tanto para localizar el byte en el que se halla de definición de un determinado punto hace falta realizar el siguiente cálculo:
– Punto definido por coordenadas “X” (horizontal) e “Y” (vertical) desde la esquina superior izquierda de la pantalla (0,0) y en puntos reales.
byte par (verde)   = 131072 + Y * 128 + INT ( X / 8 ) * 2
byte impar (rojo)  = 131072 + Y * 128 + INT ( X / 8 ) * 2 + 1
posición del bit dentro del byte = 7 - (X - INT ( X / 8 ) * 8 )
Con estos cálculos podemos hallar la posición de memoria a pokear o leer. Por ejemplo, si deseamos poner en blanco el punto situado a 77 en vertical del origen (arriba) y a 405 en horizontal tendremos:
byte par (verde)  = 141028
byte impar (rojo) = 141029
posicón bit = 2
Luego tenemos que poner el segundo bit de los bytes 141028 y 141029 a 1, con lo que pokeamos con una máscara de la siguiente manera:
poke 141028, peek(141028) || 4
poke 141029, peek(141029) || 4
Es decir, hacemos una operación lógica OR del valor 4 (en binario %00000100, el uno en el bit 2) con el contenido anterior, con lo que los bits que no son el bit 2 se quedan como estaban (0 OR 0 = 0, 0 OR 1 = 1), mientras que el bit2 se pone a 1 (0 OR 1 = 1, 1 OR 1 = 1) en los dos bytes, visualizándose dicho punto en color blanco.
Análogo razonamiento deberíamos realizar para la baja resolución, sólo que en esta modalidad cada dos bytes guardan la información de cuatro puntos (2*8/4). El razonamiento para hallar la colocación de un punto quedaría como sigue:
byte par (verde)   = 131072 + Y * 128 + INT (X / 4) * 2
byte impar (rojo)  = 131072 + Y * 128 + INT (X / 4) * 2 + 1
posición del bit verde, rojo = 7 - (X - INT (X / 4) * 4)
posición del bit azul, parpadeo = 7 - (X - INT (X / 4) * 4) + 1
Notar aquí que los bytes de verde y rojo se hallan en los puestos indicados en los bytes par (verde) e impar (rojo), seguidos inmediatamente de los bits de azul en el byte par y parpadeo en el impar.
Hay que tener cuidado con la utilización del byte de parpadeo pues es un caso muy peculiar. Se pone a uno este bit en alguno de los puntos, dicho punto, así como el resto de la línea derecha hasta el final derecho de la pantalla si no se halla otro bit de parpadeo encendido, se pondrá a parpadear. Así pues el parpadeo se activa desde el punto hasta el siguiente con dicho bit a uno.
Hay que tener mucho cuidado al trabajar con la memoria de pantalla ya que un error de cálculo nos podría llevar a la memoria situada encima de esta zona, que contiene ciertas variables del sistema que al ser alteradas pueden hacer fallar el sistema del ordenador con un consiguiente descontrol del mismo. Esto podría pasar cuando cargamos una pantalla con lbytes y por error lo que cargamos excede de los 32K.
El control de la memoria de pantalla es muy útil sobre todo para la programación en lenguaje máquina, ya que las rutinas que trae el sistema operativo son de carácter muy general, teniendo que contemplar muchos casos particulares con el retardo que esto conlleva. También, los métodos expuestos anteriormente son la base de los comandos del SuperBASIC para el manejo de gráficos en la pantalla.
Existen diversidad de rutinas para el tratamiento de la pantalla del QL, prácticamente la totalidad de ellas están escritas en ensamblador por razones de eficiencia.
Con el fin de ilustrar el uso de la pantalla del QL veremos a continuación un programa extraído de la ezine “QL Hackers Journal” de Timothy Swenson.
La finalidad de este pequeño ejemplo es guardar en memoria una zona de una ventana de pantalla para luego recuperarla y pintarla en cualquier otro lugar. Aunque en un programa real estas funciones escritas en SuperBASIC son excesivamente lentas, nos sirven sin embargo para asentar lo que hemos estudiado en este artículo. Además podrían utilizarse como idea base si quisiéramos portar este programa al lenguaje C o al lenguaje ensamblador para un uso real. Esta versión en SuperBASIC es más instructiva a la hora de comprender y testear el algorítmo como paso previo a su versionado en leguajes más eficientes para este tipo de tareas como es el caso del lenguaje ensamblador.
El programa es muy simple de seguir, a continuación mostramos su código fuente y unas capturas de pantalla de los resultados.
110 REMark --- Vector para almacenar pantalla
120 DIM store(5000)
140 REMark --- Cargar pantalla
150 LBYTES flp1_sys_cheetah_scr, 131072
170 REMark --- Guardar ventana
180 save_bg 120, 100, 360, 20
200 REMark --- Crear nueva ventana
210 OPEN #3, con_120x100a360x20_32
220 PAPER #3,0: INK #3,7
230 CLS #3
240 PRINT #3,"This is a test"
250 PRINT #3,"of real windows"
260 PAUSE 50
270 CLS #3
280 PRINT #3,"Hit Return"
290 PRINT #3,"to make this"
300 PRINT #3,"go away"
310 INPUT #3,in$
320 CLOSE #3
340 REMark --- Restaurar ventana
350 load_bg 120, 100, 360, 20
370 REMark --- Pintar fondo ventana en
380 REMark     varios lugares para demo
390 PAUSE 250
400 CLS:CLS#0:CLS#2
410 load_bg 120, 100, 0, 0
420 load_bg 120, 100, 80, 40
430 load_bg 120, 100, 200, 80
440 PAUSE 250
490 REMark ------------------------------------------
500 REMark   Procedimientos para guardar
510 REMark   y recuperar fondo de ventanas
520 REMark ------------------------------------------
540 DEFine PROCedure save_bg (length, height, x, y)
550   LOCal   mem, i, j
560   mem = 1
570   FOR i = 131072+(y*128) TO 131072+((y+height)*128) STEP 128
580     FOR j = i + (x/4) TO i + ((x+length)/4) STEP 2
590          store(mem) = PEEK_W(j)
600        mem = mem + 1
610      END FOR j
620   END FOR i
630 END DEFine save_bg
660 DEFine PROCedure load_bg (length, height, x, y)
670   LOCal   mem, i, j
680   mem = 1
690   FOR i = 131072+(y*128) TO 131072+((y+height)*128) STEP 128
700     FOR j = i+(x/4) TO i+((x+length)/4) STEP 2
710       POKE_W j,store(mem)
720       mem = mem + 1
730     END FOR j
740   END FOR i
750 END DEFine load_bg
Imágen, fondo de pantalla
Fondo, con ventana almacenando fondo de ventana
Réplica de fondo de ventana
– Revista QLave, “La Pantalla del QL”, Javier Boira.
– QL Hacer’s Journal, Timothy Swenson
Pingback: Volcar imágenes en la pantalla del QL | QBlog

References: resolución 
 resolución 
 resolución 
 resolución 
 resolución 
 resolución