{"id":2810,"date":"2021-01-21T00:14:42","date_gmt":"2021-01-21T00:14:42","guid":{"rendered":"http:\/\/blog.rastersoft.com\/?p=2810"},"modified":"2023-12-25T19:48:05","modified_gmt":"2023-12-25T19:48:05","slug":"pintando-en-el-spectrum-7","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=2810","title":{"rendered":"Pintando en el Spectrum (7)"},"content":{"rendered":"\n<p>En esta entrada vamos a ver el \u00faltimo punto que nos falta: c\u00f3mo sincronizarnos con el barrido de la pantalla. Ya coment\u00e9 en la primera entrada que esto es algo fundamental para asegurarnos de que copiamos todos los datos desde nuestro buffer hasta la memoria de v\u00eddeo sin que se produzcan efectos indeseados como parpadeos o tearing.<\/p>\n\n\n\n<p>El primer detalle importante que tenemos que saber es que en el Spectrum, cada vez que se comienza a generar una imagen, la ULA genera una interrupci\u00f3n enmascarable. Por defecto, la ROM pone las interrupciones en Modo 1, lo que hace que el procesador salte a la direcci\u00f3n 0x38 cada vez que se genera una interrupci\u00f3n enmascarable. En esta direcci\u00f3n de la ROM est\u00e1 la rutina que lee el teclado y hace otras operaciones b\u00e1sicas, como incrementar el contador de frames. Por lo tanto, la manera m\u00e1s sencilla de sincronizarnos con el haz es ejecutar una instrucci\u00f3n HALT, que espera a que se produzca una interrupci\u00f3n antes de proseguir la ejecuci\u00f3n de instrucciones (\u00a1\u00a1Pero no hay que olvidarse de habilitar antes las interrupciones, o el sistema se quedar\u00e1 colgado!!!). Sin embargo, en este momento el haz estar\u00e1 todav\u00eda en la parte superior del BORDER, por lo que tenemos que esperar a\u00fan a que llegue a la zona del PAPER.<\/p>\n\n\n\n<p>La primera idea, la m\u00e1s <em>naive<\/em>, es hacer un bucle de retardo que espere el tiempo exacto que necesita el haz para llegar a la zona del PAPER. Teniendo en cuenta que la parte superior del borde son 64 l\u00edneas y que cada una dura 224 Testados, s\u00f3lo tenemos que esperar 14 336 Testados desde el instante posterior al HALT.<\/p>\n\n\n\n<p>Sin embargo, en la pr\u00e1ctica esto no es una buena idea por dos motivos: para empezar, en los modelos de 128Kbytes cada l\u00ednea dura 228 Testados, por lo que tendr\u00edamos que detectar si estamos en un equipo de 48 o de 128Kbytes y, seg\u00fan el resultado, esperar 14 336 o 14 592 Testados. Esperar siempre el tiempo m\u00e1ximo tampoco es bueno, porque entonces en los modelos de 48Kbytes desperdiciamos 256 Testados, que son dos <em>scanlines<\/em> completas.<\/p>\n\n\n\n<p>Por otro lado, no hay que olvidar que la instrucci\u00f3n HALT continuar\u00e1 la ejecuci\u00f3n <em>despu\u00e9s<\/em> de que se haya ejecutado la rutina de interrupci\u00f3n, la cual tardar\u00e1 un tiempo desconocido. Eso significa que estaremos perdiendo una vez m\u00e1s un valioso tiempo que necesitamos utilizar para pintar, no para desperdiciar.<\/p>\n\n\n\n<p>Es por esto necesitamos alg\u00fan m\u00e9todo para saber con precisi\u00f3n cuando el barrido ha llegado al PAPER realmente. Afortunadamente, esta vez el que el  Spectrum se dise\u00f1ase para ser lo m\u00e1s barato posible nos ofrece la soluci\u00f3n. Echemos un vistazo a c\u00f3mo est\u00e1 conectada la CPU y la ULA a la memoria:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1.png\" rel=\"lightbox-0\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"995\" src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1-1024x995.png\" alt=\"\" class=\"wp-image-2815\" srcset=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1-1024x995.png 1024w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1-300x292.png 300w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1-768x747.png 768w, https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/ram_spectrum-1.png 1109w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>La imagen muestra \u00fanicamente la conexi\u00f3n del <strong>bus de datos<\/strong>, pues es la parte que nos interesa. Si nos fijamos, vemos que el bus de datos de la ULA est\u00e1 conectado directamente a la RAM de v\u00eddeo. Por su parte, el bus de datos del Z80 est\u00e1 conectado directamente s\u00f3lo a la RAM superior (bueno, y a la ROM y al bus trasero, pero no me apetec\u00eda pintarlo todo). Esto significa que la ULA tiene prioridad absoluta a la hora de trabajar con la RAM de v\u00eddeo, mientras que el Z80 la tiene al trabajar con la RAM superior, la ROM y todo aquello que est\u00e9 conectado al bus externo. Pero, y esta es la clave, el bus de datos del Z80 est\u00e1 conectado al bus de datos de la RAM de v\u00eddeo (y de la ULA) a trav\u00e9s de un conjunto de resistencias de 330 ohmios. Estas resistencias permiten aislar ambos buses cuando la ULA accede a la memoria RAM a la vez que el Z80 accede a la ROM, a la RAM superior o a un perif\u00e9rico externo. En efecto, las resistencias absorben la diferencia de potencial cuando en un lado del bus hay un 1 (5 voltios) y en el otro hay un 0 (0 voltios), de manera que cada lado del bus puede tener un dato diferente sin riesgo de cortocircuitos. Sin embargo, a la vez, permiten que el Z80 lea los datos de la RAM de v\u00eddeo cuando la ULA no est\u00e1 accediendo a ella, pues si no hay ning\u00fan perif\u00e9rico que imponga un valor en el bus de datos del Z80, la corriente atravesar\u00e1 las resistencias y el valor que haya en el bus de datos de la ULA\/RAM de v\u00eddeo ser\u00e1 visible en el bus de datos del Z80. Y \u00e9sta es precisamente la clave: si leemos del bus de datos sin activar ning\u00fan perif\u00e9rico, podremos ver qu\u00e9 est\u00e1 leyendo la ULA en ese momento. La cuesti\u00f3n es que la ULA s\u00f3lo lee de la RAM de v\u00eddeo cuando est\u00e1 pintando el PAPER, pues cuando pinta el BORDER utiliza un valor almacenado internamente, con lo cual, la clave para saber cuando la ULA ha empezado a pintar el PAPER es leer repetidamente el bus sin acceder a ning\u00fan dispositivo: si la ULA no est\u00e1 leyendo la RAM de v\u00eddeo, el bus no estar\u00e1 conectado a nada, con lo que leeremos 255 (por la tecnolog\u00eda utilizada en el Z80, cuando una entrada est\u00e1 <em>al aire<\/em>, se lee un 1 l\u00f3gico, por lo que si las ocho l\u00edneas del bus de datos est\u00e1n <em>sin conectar a nada<\/em>, estar\u00e1n todas a 1 y leeremos 255), mientras que si la ULA est\u00e1 leyendo, leeremos el dato de la RAM de v\u00eddeo correspondiente: bien el byte de p\u00edxeles que se va a pintar, bien el byte de atributos de color correspondiente. Obviamente, si cometemos la torpeza de llenar las primeras l\u00edneas de la memoria de v\u00eddeo con el valor 255 (s\u00ed, yo lo hice \ud83d\ude00 ) retrasaremos la detecci\u00f3n, por lo que es recomendable evitar ese valor al menos en la primera fila (aunque como veremos luego, tampoco es tan desastroso si lo ponemos s\u00f3lo en los p\u00edxeles, pero no en los atributos).<\/p>\n\n\n\n<p>Ahora llega la siguiente cuesti\u00f3n: \u00bfc\u00f3mo podemos leer el bus sin que ning\u00fan dispositivo intente meter un dato? No podemos hacerlo leyendo de la RAM, salvo que estemos en un Spectrum 16K, pues si leemos en los primeros 16Kbytes nos responder\u00e1 la ROM, si leemos en los siguientes 16Kbytes nos responder\u00e1 la RAM de v\u00eddeo (y, adem\u00e1s, s\u00f3lo cuando la ULA no est\u00e9 leyendo nada), y si leemos los 32Kbytes \u00faltimos nos responder\u00e1 la RAM superior. Ante esto, la \u00fanica soluci\u00f3n es leer de un puerto de entrada\/salida (E\/S) en el que sepamos que no hay ning\u00fan perif\u00e9rico.<\/p>\n\n\n\n<p>Por convenio, en el Spectrum se utilizan los ocho bits inferiores del bus de direcciones para direccionar perif\u00e9ricos, y para simplificar la circuiter\u00eda se le asigna un \u00fanico bit a cada uno, que debe ponerse a cero para seleccionar el dispositivo. As\u00ed, el bit A0 selecciona la ULA como perif\u00e9rico, el bit A1 la impresora ZX, el bit A2 se reserv\u00f3 por Sinclair en los modelos de 48Kbytes, y se utiliza para la paginaci\u00f3n y el chip de sonido en los modelos de 128Kbytes; los bits A3 y A4 son para la interfaz 1, y los bits 5, 6 y 7 est\u00e1n disponibles para otros perif\u00e9ricos (por ejemplo, el bit 5 direcciona la interfaz Kempston para joysticks). Por supuesto, no debemos poner m\u00e1s de uno de estos bits a cero, o correremos el riesgo de provocar un cortocircuito y que el ordenador se resetee.<\/p>\n\n\n\n<p>Sabiendo esto, es f\u00e1cil deducir que hay que evitar poner cualquiera de esos bits a cero para que, de esa manera, ning\u00fan perif\u00e9rico intente darnos sus datos y as\u00ed que no fuercen un valor del bus; por tanto, tenemos que leer del puerto 255 (todos los bits del bus de direcciones a 1&#8230; o al menos los ocho bits inferiores). Como no hay ning\u00fan perif\u00e9rico que responda a esa direcci\u00f3n, se leer\u00e1 el valor del bus <em>al aire<\/em>, y eso incluir\u00e1 lo que sea que la ULA est\u00e9 leyendo de la RAM de v\u00eddeo (si est\u00e1 leyendo en ese momento, claro).<\/p>\n\n\n\n<p>As\u00ed, en principio bastar\u00eda con esperar por una interrupci\u00f3n para saber que estamos en la parte superior de la pantalla, y entonces entrar en un bucle que lea repetidamente de dicho puerto 255 hasta que el valor le\u00eddo sea diferente de 255, momento en el que sabremos que la ULA ha empezado a pintar el PAPER y, por tanto, podemos empezar a copiar los datos del buffer a la memoria de v\u00eddeo. Una posible rutina ser\u00eda esta:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\">    ld B, 255\n    halt\nloop:\n    in A, (255)\n    cp B\n    jp NZ, loop<\/pre>\n\n\n\n<p>Por desgracia, ahora vienen los <em>detalles escabrosos<\/em>, y es que, para empezar, la rutina anterior s\u00f3lo hace una lectura cada 25 Testados, lo que significa que si tenemos la mala suerte de que haga una lectura justo antes de que empiece a leer datos, tendremos que esperar al menos 25 ciclos de reloj antes de poder volver a leer el bus. Pero esto no es todo, pues, adem\u00e1s, la ULA no est\u00e1 leyendo constantemente de la RAM. Si recordamos lo que vimos en la entrada 4, la ULA aprovechaba una caracter\u00edstica de las RAMs din\u00e1micas para leer dos posiciones de memoria (p\u00edxeles y atributos de color) en tres ciclos de reloj en lugar de los cuatro necesarios normalmente, y adem\u00e1s agrupaba dos grupos de lecturas juntas de manera que en seis ciclos seguidos le\u00eda los datos (p\u00edxeles y atributos de color) de dos caracteres consecutivos, dejando as\u00ed libres dos ciclos de reloj consecutivos para que el procesador pueda acceder a la RAM de v\u00eddeo mientras la ULA est\u00e1 pintando el PAPER. La cuesti\u00f3n es que de esos tres ciclos, s\u00f3lo los dos \u00faltimos tendr\u00e1n un dato de la RAM, mientras que el primero no tendr\u00e1 nada (y, por tanto, dejar\u00e1 el bus a 255). En otras palabras: de cada ocho Testados, s\u00f3lo el segundo, tercero, quinto y sexto pondr\u00e1n un valor diferente a 255 en el bus. En el primero y en el cuarto la ULA estar\u00e1 enviando la parte inferior de la direcci\u00f3n de memoria a leer, y el s\u00e9ptimo y el octavo son los dos ciclos en los que el Z80 podr\u00eda acceder al RAM de v\u00eddeo para leer o escribir cuando la ULA no est\u00e1 pintando el borde. Esto significa que, por si fuera poco, la ULA podr\u00eda estar ya pintando el PAPER pero nosotros no detectarlo <em>a la primera<\/em> porque hemos tenido la mala suerte de que la instrucci\u00f3n <em>IN<\/em> se ejecut\u00f3 justo en alguno de esos cuatro ciclos de reloj en los que la memoria RAM de v\u00eddeo no est\u00e1 enviando datos. Pero a\u00fan peor: si el periodo entre lecturas es m\u00faltiplo de 4, habr\u00e1 algunos casos en los que jam\u00e1s podremos detectar si la ULA est\u00e1 pintando el PAPER: en efecto, supongamos que tenemos un bucle que lee cada 28 ciclos (que es un m\u00faltiplo entero de 4); si tenemos la mala suerte de que la instrucci\u00f3n <em>IN<\/em> se ejecute justo en el cuarto ciclo de un proceso de lectura de la ULA, el procesador leer\u00e1 255 pues en ese momento la ULA est\u00e1 enviando la parte baja de la direcci\u00f3n de memoria a leer, no est\u00e1 leyendo a\u00fan; pero 28 ciclos despu\u00e9s estar\u00e1 en el octavo ciclo del grupo de ocho, que est\u00e1 libre para el Z80 y no hay acceso por parte de la ULA; 28 ciclos despu\u00e9s volver\u00e1 a estar en el ciclo 4&#8230; y as\u00ed <em>ad finitum<\/em>. Por tanto, es necesario que el periodo del bucle de lectura y el periodo de acceso de la ULA sean primos relativos.<\/p>\n\n\n\n<p>Sin embargo, incluso unos primos son mejores que otros; y es que por las caracter\u00edsticas de las operaciones modulares, puede ocurrir que un valor m\u00e1s peque\u00f1o sea peor que uno mayor.  Por tanto \u00bfqu\u00e9 periodo de lectura ser\u00e1 el \u00f3ptimo, teniendo en cuenta que no podemos hacer que dure menos de 25 Testados? \u00bfSer\u00e1 25 el valor \u00f3ptimo, o un valor mayor puede ser mejor? Por desgracia, si tenemos en cuenta que, a priori, no sabemos si vamos a necesitar m\u00e1s de una l\u00ednea para detectar si la ULA est\u00e1 leyendo, y que la ULA s\u00f3lo lee durante 128 Testados que dura el PAPER, y el resto hasta los 224 Testados que dura cada l\u00ednea no lee nada, la cosa se complica, as\u00ed que decid\u00ed hacer un programita que me calcule cuantos Testados como m\u00e1ximo puedo tardar en detectar una lectura de la ULA (y ya de paso, cuantos en promedio) con cualquier periodo de lectura, y este es el resultado:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted mycode\"> Periodo:  6; Maximo: 11; Promedio: 4.5\n Periodo:  5; Maximo: 12; Promedio: 4.0\n Periodo:  7; Maximo: 20; Promedio: 7.0\n Periodo: 10; Maximo: 25; Promedio: 9.5\n Periodo:  9; Maximo: 32; Promedio: 11.0\n Periodo: 14; Maximo: 35; Promedio: 13.5\n Periodo: 11; Maximo: 40; Promedio: 14.0\n Periodo: 13; Maximo: 44; Promedio: 15.0\n Periodo: 18; Maximo: 49; Promedio: 18.5\n Periodo: 15; Maximo: 52; Promedio: 18.0\n Periodo: 22; Maximo: 59; Promedio: 22.5\n Periodo: 17; Maximo: 64; Promedio: 22.0\n Periodo: 19; Maximo: 72; Promedio: 25.0\n*Periodo: 26; Maximo: 73; Promedio: 27.5\n Periodo: 21; Maximo: 76; Promedio: 26.0\n Periodo: 30; Maximo: 83; Promedio: 31.5\n Periodo: 23; Maximo: 84; Promedio: 29.0\n*Periodo: 25; Maximo: 96; Promedio: 33.0\n Periodo: 34; Maximo: 97; Promedio: 36.5\n Periodo: 27; Maximo: 104; Promedio: 36.0\n Periodo: 38; Maximo: 107; Promedio: 40.5\n Periodo: 29; Maximo: 108; Promedio: 37.0\n Periodo: 31; Maximo: 116; Promedio: 40.0\n Periodo: 42; Maximo: 121; Promedio: 45.5\n Periodo: 33; Maximo: 227; Promedio: 47.0\n Periodo: 35; Maximo: 241; Promedio: 53.0\n Periodo: 37; Maximo: 251; Promedio: 54.0<\/pre>\n\n\n\n<p>Tal y como sospechaba, vemos que utilizar un periodo de 25 Testados es mucho peor que uno de 26 (ambos marcados con un asterisco a la izquierda), pues con esta \u00faltima perderemos un m\u00e1ximo de 73 Testados en detectar una lectura de la ULA, y un promedio de 27,5 Testados, mientras que con un periodo de 25 Testados podemos llegar a tardar hasta 96 Testados y un promedio de 33 Testados. Por tanto, tenemos que sustituir <em>IN A, (255)<\/em>, que consume 11 Testados, por <em>IN A, (C)<\/em>, que consume 12 Testados, e inicializar BC fuera del bucle a 0xFFFF:<\/p>\n\n\n\n<pre id=\"block-518c02b8-a2b2-4583-91eb-7180dc15fa7b\" class=\"wp-block-preformatted mycode\">    ld BC, 0xFFFF\n    halt\nloop:\n    in A, (C)\n    cp B\n    jp NZ, loop<\/pre>\n\n\n\n<p>As\u00ed pues, colocando este peque\u00f1o bucle justo antes de la rutina de copia que vimos en las tres primeras entradas, la sincronizaremos perfectamente con el haz y empezaremos a copiar como mucho 73 Testados m\u00e1s tarde de que la ULA haya empezado a pintar. Pero lo mejor es que podemos, si queremos, aprovechar parte de los 14 336 Testados que hay entre la interrupci\u00f3n y ese momento para realizar otras tareas, como por ejemplo reproducir m\u00fasica simult\u00e1neamente, y todo ello sin tener que preocuparnos de calcular con precisi\u00f3n cuanto vamos a tardar (siempre y cuando estemos seguros de tardar menos de 14 336 Testados, claro), pues el bucle anterior nos sincronizar\u00e1.<\/p>\n\n\n\n<p>\u00bfY qu\u00e9 ocurre si cometemos el error de poner los bytes de p\u00edxeles a 255, y s\u00f3lo los de los atributos son diferentes de 255? En ese caso, con un bucle que dure 26 Testados tardaremos un m\u00e1ximo de 99 Testados en detectar el PAPER y un promedio de 49,5 Testados. Tampoco es exactamente una cat\u00e1strofe.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El Inves Spectrum Plus y el Plus 2A\/Plus 3<\/h2>\n\n\n\n<p>Por \u00faltimo, a\u00f1adir un par de comentarios extra; el primero sobre un ordenador muy especial: el <a rel=\"noreferrer noopener\" href=\"https:\/\/es.wikipedia.org\/wiki\/Investr%C3%B3nica_Inves_Spectrum_%2B\" data-type=\"URL\" data-id=\"https:\/\/es.wikipedia.org\/wiki\/Investr%C3%B3nica_Inves_Spectrum_%2B\" target=\"_blank\">Inves Spectrum Plus<\/a>. Este equipo se lanz\u00f3 al mercado en 1986 y no era de Sinclair, sino de la empresa espa\u00f1ola Investr\u00f3nica. Se trata de un modelo que intenta ser compatible con el ZX Spectrum original, pero que por desgracia tiene algunas diferencias que hacen que no lo sea del todo.<\/p>\n\n\n\n<p>La primera gran diferencia es que no tiene contienda de memoria. El sistema que utiliza para conseguirlo es realmente ingenioso, y lo explican muy bien Miguel \u00c1ngel Rodr\u00edguez J\u00f3dar y C\u00e9sar Hern\u00e1ndez Ba\u00f1\u00f3 en un art\u00edculo que escribieron donde <a rel=\"noreferrer noopener lightbox-video-0\" href=\"http:\/\/www.zxprojects.com\/inves\/\" target=\"_blank\">analizan el hardware del Inves Spectrum Plus en profundidad<\/a>. La ventaja de esto es que este ordenador <a href=\"https:\/\/www.youtube.com\/watch?v=RG9ucaFKsdY\" target=\"_blank\" rel=\"noreferrer noopener lightbox-video-0\">es m\u00e1s r\u00e1pido que el Spectrum original<\/a> a pesar de trabajar a la misma velocidad de reloj. Por desgracia, la implementaci\u00f3n de este sistema hace que el Z80 nunca pueda ver los accesos a memoria de la ULA, sino que el bus de datos siempre valdr\u00e1 255 cuando ning\u00fan perif\u00e9rico ponga un dato en el. Eso significa que el bucle anterior sencillamente se colgar\u00e1.<\/p>\n\n\n\n<p>Afortunadamente hay un segundo cambio que viene en nuestra ayuda, y es que la interrupci\u00f3n ya no se genera al comenzar un cuadro, sino justo cuando se va a comenzar a pintar el PAPER. Esto significa que, en un Inves, hay que eliminar completamente el bucle y empezar a copiar el buffer justo despu\u00e9s del HALT.<\/p>\n\n\n\n<p>En el caso del Plus 3 (y del Plus 2A, con quien comparte placa), la situaci\u00f3n es peor, pues se ha eliminado completamente el efecto del bus flotante. Afortunadamente hay una manera sencilla de resolver el problema, tal y como se explica en este <a rel=\"noreferrer noopener\" href=\"https:\/\/spectrumforeveryone.com\/technical\/performing-the-in-ff-mod-on-a-spectrum-2a-3\/\" target=\"_blank\">art\u00edculo que restaura el bus flotante en el Plus 3<\/a>. Es importante recalcar que s\u00f3lo lo restaura en el bit 7. Dado que este bit, en el byte de atributos de color, activa el FLASH, en general estar\u00e1 a cero, lo que garantiza que al menos se puede detectar una de las dos lecturas en caso de que todos los bytes de p\u00edxeles tengan el bit 7 a uno. De hecho es muy posible que este truco se pueda implementar en el Inves Spectrum tambi\u00e9n.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Un ejemplo<\/h2>\n\n\n\n<p>Despu\u00e9s de la teor\u00eda, ha llegado la hora de juntarlo todo y hacer una microdemo. Y aqu\u00ed est\u00e1:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"516\" style=\"aspect-ratio: 640 \/ 516;\" width=\"640\" controls loop src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/demo_scroll-2021-01-18_00.46.13.mp4\"><\/video><\/figure>\n\n\n\n<p>Part\u00ed de unos gr\u00e1ficos inspirados en Among Us para hacer un peque\u00f1o personajillo, y a\u00f1ad\u00ed una baldosa. Como vemos, no s\u00f3lo se pintan los p\u00edxeles sino tambi\u00e9n los atributos de color, y todo ello de manera perfectamente sincronizada con el haz para conseguir un <a href=\"https:\/\/en.wikipedia.org\/wiki\/Screen_tearing\" target=\"_blank\" rel=\"noreferrer noopener\">movimiento sin <em>tearing<\/em><\/a>. La velocidad que alcanza es algo m\u00e1s de ocho FPS haciendo un scroll de pantalla-cuasi-completa con color, lo que no est\u00e1 nada mal.<\/p>\n\n\n\n<p>Para comparar, vamos a quitar la sincronizaci\u00f3n con el haz de electrones:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 640 \/ 480;\" width=\"640\" controls loop src=\"https:\/\/blog.rastersoft.com\/wp-content\/uploads\/2021\/01\/demo_scroll3-2021-01-20_14.49.21.mp4\"><\/video><\/figure>\n\n\n\n<p>Vemos que en varios puntos se producen efectos de <em>tearing<\/em> que deslucen mucho el resultado.<\/p>\n\n\n\n<p>En el primer v\u00eddeo vemos que s\u00f3lo pinto 20 l\u00edneas en lugar de 22. El motivo es que, a pesar de todo, la \u00faltima fila de atributos de color de esas 22, no siempre da tiempo a pintarla correctamente, as\u00ed que ya puestos, opt\u00e9 por redondear a un m\u00faltiplo del tama\u00f1o de la baldosa (que es de 4&#215;4 caracteres), de manera que quede una zona para el inventario de objetos recogidos por el usuario. S\u00ed, porque&#8230; \u00a1\u00a1\u00a1voy a intentar convertir todo esto en un juego!!!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En esta entrada vamos a ver el \u00faltimo punto que nos falta: c\u00f3mo sincronizarnos con el barrido de la pantalla. Ya coment\u00e9 en la primera entrada que esto es algo fundamental para asegurarnos de que copiamos todos los datos desde nuestro buffer hasta la memoria de v\u00eddeo sin que se produzcan efectos indeseados como parpadeos &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=2810\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Pintando en el Spectrum (7)<\/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-2810","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\/2810","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=2810"}],"version-history":[{"count":20,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2810\/revisions"}],"predecessor-version":[{"id":2834,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/2810\/revisions\/2834"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2810"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2810"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2810"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}