top of page

Interrupción de línea

  • Foto del escritor: MoltS Xalats
    MoltS Xalats
  • 29 abr
  • 10 Min. de lectura

Introducción

Hasta ahora, habíamos utilizado el VBlank para llevar el tempo del juego. El VBlank es el tiempo que tarda el haz de luz en pasar desde la última línea de abajo del todo hasta la primera de arriba del todo. Según el tipo de codificación, PAL o NTSC, este viaje de regreso se hace 50 veces o 60 veces por segundo, respectivamente. Pero el VDP 9938 y 9958 permite hacer una interrupción de línea; podemos indicarle en cuál de las 212 líneas que dibuja (y las que están ocultas) queremos que el VDP genere una interrupción.


¿Y de qué nos puede servir que en lugar de hacerlo al final de la pantalla lo haga a mitad? Una vez interrumpido en una línea, podemos modificar los registros del VDP y, cuando continúe dibujando, que esté mostrando otra página, o que cambie la paleta de colores, etc. En nuestro caso lo haremos para que dentro de la pantalla del scroll haya una parte que se mantenga fija, que no se desplace con el scroll; así, esta parte se podría usar como marcador, para mostrar información sin que se mueva.


Antes de conocer la interrupción de línea, había empezado a desarrollar la parte estática de la pantalla haciendo copias, pero los cálculos eran muchos y había parpadeos debido al retraso por toda la computación. Entonces encontré este código de msxpen que utilizaba la interrupción de línea en un programita corto para observar su funcionamiento. Y pensé que esta sería una buena solución para el problema que tenía al querer una parte fija.


Como se programa una interrupción de línea

La interrupción de línea solo se da en los MSX2 o superiores. Utiliza el registro 19 para guardar el número de línea en la que se producirá la interrupción, y se debe configurar el VDP para que genere interrupciones de línea escribiendo un 1 en el bit 4 del registro 0, al que en la documentación de Yamaha se refiere como IE1 Enables interrupt from horizontal line.


Además de tenerla activada, tendremos que cambiar el hook (gancho), que en estos momentos no hace nada. Esto es lo mismo que hacemos en el hook del VSync, del retorno de carro: poner en la dirección que salta la línea un salto a nuestro programa en lugar del código que ejecuta por defecto.


Las operaciones que realizamos durante esta interrupción deben ser breves, ya que el VDP continúa dibujando lo que tiene en la VRAM. Si tardamos mucho, el número de líneas con el VDP inestable, me refiero a en estados desconocidos, será mayor.


Cambiamos el color de fondo

Un programa sencillo que nos permite aprender cómo crear una interrupción de línea es el que veremos a continuación. Utilizaremos la interrupción para cambiar el color del fondo, creando una franja de diferente color que subirá y bajará.


Primero de todo, definimos la rutina que ya hemos utilizado otras veces para crear el gancho de la interrupción: InterruptHandlerHelper, InitializeMyInterruptHandler, EndMyInterruptHandler.

Después de esto, definimos la variable cpt, que nos indicará en qué parte de la interrupción de línea estamos. Es decir, tenemos que hacer dos interrupciones de línea por refresco vertical: la primera interrupción es cuando hacemos un cambio en la pantalla e indicamos cuál será la siguiente línea de interrupción; y la segunda interrupción es al final de la franja vertical que hemos modificado y donde debemos devolver la pantalla a su estado original.


Las variables Ystart y Yend indican el número de línea en la que se causará la interrupción, primero en Ystart y luego en Yend. La variable Y la utilizaremos para hacer el scroll del fondo de pantalla, y la variable d la usaremos para la dirección del scroll, si sube (d > 0) o baja (d < 0).


Y en la línea 83 definimos la función que se ejecutará en cada interrupción de línea: HBlankHook, donde lo primero que hacemos es comprobar si estamos en la primera parte de la interrupción o en la segunda, usando la variable cpt. Si estamos en la segunda (cpt = 0), marcamos la siguiente interrupción de línea en el registro 19, cambiamos los colores del fondo con la función SetColors, y cambiamos el valor de cpt para poder identificar el nuevo estado en la siguiente interrupción. En la siguiente interrupción (cpt != 0), volvemos a colocar la interrupción al final, y otra vez cambiamos el color de fondo y el valor de cpt.


Después, en la línea 100, tenemos el hook del VDP, donde detectamos si ha sido causada por una interrupción horizontal con la función IsHsync. Si es así, ejecutamos la función anterior en la que se gestiona la interrupción de línea. Si no detectamos que venimos de una interrupción de línea, también se ejecuta este hook en la interrupción vertical, ya que, como habrás podido comprobar, es la misma rutina que usábamos para cambiar el hook en otros programas.

Definimos la función FT_Wait, que se encargará de esperar para que podamos ver cómo va evolucionando el scroll; de lo contrario, iría tan rápido que no veríamos nada.


Y en la línea 116 empezamos lo que sería la función main. Definimos la variable IE1, que contendrá el valor que había en el registro 0 del VDP antes de que lo modificáramos para permitir las interrupciones por número de línea (lo asignamos en la línea 123) y que utilizaremos más adelante para restaurarlo.


Activamos la screen 5 del MSX y escribimos en el registro 19 del VDP el número de línea de la próxima interrupción, que es la 50. En la línea 123 leemos el valor del registro 0 y activamos el bit 4, que es el del HSync, dejando el resto de bits iguales, con el valor que tenían.


En las siguientes líneas escribimos el nuevo valor en la memoria de imagen y en el registro 0. Inicializamos las interrupciones, y en la línea 129 definimos un bucle que se ejecutará continuamente hasta que se pulse la tecla ESC (número 27 de la matriz del teclado).


Dentro del bucle, esperamos y aumentamos el valor de la coordenada Y, que es donde se producirá la nueva interrupción. En la línea 132 comprobamos si el punto de interrupción ha sobrepasado el límite que tenemos para la franja de cambio de color, y en ese caso cambiamos la dirección del scroll.


Cuando salimos del bucle, restauramos el valor que había en IE1, desactivando el bit HSync, eliminamos el hook de la interrupción, volvemos al modo 0 de pantalla y salimos de la aplicación.

Este programa lo podéis encontrar en el repositorio de MoltSXalats.


Complicamos la interrupción de línea

Ahora que hemos tenido el primer contacto con el funcionamiento de la interrupción de línea, queremos aplicar estos conocimientos a nuestro programa que hacía el scroll de patrones, con el objetivo de conseguir que una parte de la pantalla no se mueva y poder mostrar información de puntos, vidas, armas u otro tipo de información.


Así, las tareas que deben realizarse en la interrupción son: cambiar la página que se muestra en pantalla, ajustando el scroll horizontal y vertical con los valores necesarios para mostrar la parte correspondiente de la pantalla; eliminar los sprites; y volver a establecer la interrupción de línea en la siguiente posición. Si el programa se encuentra en esa siguiente posición, debe restaurar los valores de scroll correspondientes al mapeado de patrones y volver a dibujar los sprites.


Primeros desarrollos

Al principio, cuando activé la interrupción de línea, aparecía un parpadeo, y no conseguía averiguar su causa. Pregunté en el foro del MSX Resource Center  y, amablemente, el usuario Sandy Brand me ayudó. Lo que ocurría era que, dentro de la interrupción de línea, las órdenes que estaba utilizando volvían a activar las interrupciones, y por eso se producían los parpadeos.


Una vez cambié todas las funciones dentro de la interrupción de línea para que no activaran las interrupciones, observé que los parpadeos se habían reducido enormemente. Pero, de vez en cuando, aún se producía alguno, y era porque a veces el registro de interrupciones quedaba activado y volvía a dispararse. Borrando el registro, conseguí que ya no se produjeran esos parpadeos. Sin embargo, había ocasiones en que la interrupción de línea no se producía. Tras hacer debug, descubrí que esta vez el problema eran las funciones de comandos del VDP de Fusion-C, que deshabilitaban las interrupciones para asegurarse de que se completaba la operación, y por tanto la interrupción de línea no se disparaba.


Entonces quedaba eliminar el dibujado a mitad de línea que ocurría cuando se producía la interrupción, ya que, entre que el VDP detecta que se encuentra en la línea de interrupción y se empiezan a ejecutar las órdenes del hook, la línea quedaba pintada con una longitud diferente. ¿Cómo podíamos solucionarlo? En este artículo sobre interrupciones de línea de Grauw, se comenta la idea de crear una línea negra para ocultar los efectos. Lo implementamos, pero el problema era que, al hacer el scroll, el cambio de pantalla seguía dejando suciedad de otras líneas. ¿Cómo podía resolverse?


Una opción era aumentar el número de líneas negras, de modo que el cambio siempre se produjera en la zona negra. El problema es que eso incrementaba el uso del procesador. Otra opción era desconectar el VDP, tal como hacen muchos programas para pintar la pantalla y luego mostrarla. ¿Pero no apagaría eso toda la pantalla y provocaría un parpadeo? Pues no, parece que el VDP solo desconecta el módulo que genera la señal de vídeo, pero continúa con sus procesos, y cuando se vuelve a activar, lo hace en la línea correspondiente (o va tan rápido que no he podido notarlo).


Aplicación al scroll con mapa de patrones

Para no copiar de nuevo todo el código que ya se ha explicado en otro artículo, aquí solo se explicarán las funciones que han cambiado para tener en cuenta la interrupción de línea.


Estas modificaciones tienen como objetivo eliminar la desactivación de las interrupciones, ya que Fusion-C aseguraba que se completara toda la orden desactivándolas. Pero eso no nos interesa, ya que luego provoca parpadeos en la pantalla porque la imagen no se ha refrescado cuando debía.


Empezamos definiendo todos los valores constantes que utilizaremos para la interrupción, y después definimos la función set_scrollH_NI(char n), que es una copia de set_scrollH de Fusion-C pero eliminando las instrucciones ei y di. Lo que hacemos es leer el parámetro en la línea 342 y modificar los registros #26 y #27 con los valores correspondientes.

Aquí también hacemos lo mismo: nos hemos basado en la función vdpstatusni de Fusion-C y hemos creado la función VDPstatusSenseInt(unsigned char registerNumber), eliminando las instrucciones ei y di. Esta función lee el registro #1 del VDP, que nos indica el estado para saber el tipo de interrupción y, además, limpia el indicador por si había alguna en cola.

Para hacer los comandos del VDP más rápidos y reducir la posibilidad de que queden a medias debido a una interrupción de línea, se ha seguido el código de Grauw presentado en esta página, el cual hemos transcrito a la función en C fastVDP_Ni(FastVram2Vram *paramFastVDP), que también está desarrollada en la librería Fusion-C (fastvdpcopy.c), pero utilizando los activadores de interrupción. La estructura FastVram2Vram está definida en vdp_graph2.h. Lo que hace la función es preparar los registros del VDP para recibir comandos y enviar todos los valores que se encuentran en la estructura, los cuales van a registros consecutivos del VDP mediante la función OutI.

Y luego tenemos los comandos que se ejecutan durante la interrupción de línea. La variable repetirYMMM la utilizamos como bandera para saber si será necesario repetir el comando del VDP que se haya interrumpido del código principal. Después tenemos el selector para saber si nos encontramos en la primera parte de la interrupción o en la segunda. Si estamos en la primera parte, apagamos la pantalla escribiendo el valor 0x20 en el registro #1 del VDP. Reposicionamos el scroll vertical escribiendo en el registro #23 y el scroll horizontal a través de la función explicada anteriormente. Activamos los sprites y cambiamos la página visualizada por la que contiene los tiles pintados. Encendemos la pantalla, indicamos la nueva línea de interrupción y leemos el registro de interrupciones para dejarlo limpio.

En la segunda parte, realizamos pasos similares: apagamos la pantalla, colocamos los scrolls en los valores correctos para el cartel, encendemos la pantalla, indicamos la nueva interrupción de línea, activamos los sprites y limpiamos el registro de interrupciones.

La función guardemLiniesAlBuffer(signed char offset) sirve para guardar las líneas que desaparecerán para ocultar la suciedad que aparece cuando se produce la interrupción de línea; serán sustituidas por las líneas negras. Como es un bloque de dos líneas, se debe tener en cuenta el final de página de VRAM, la línea 255, ya que si el bloque de 2 líneas comienza aquí, la segunda línea del bloque tomará la primera línea de la siguiente página de VRAM en lugar de la primera de la misma página, que es lo que queremos. Por eso, en la línea 529 analizamos si nos encontramos en este caso y separamos el bloque a copiar en dos partes. Si en lugar de hacer un bloque de dos líneas hiciéramos uno de tres, también tendríamos que controlar el punto 254 y añadir un caso más. Pero si solo hacemos una línea, bastaría con el else.

Antes de cada comando, ponemos la bandera repetirYMMM a cero y comprobamos después de cada comando si se ha quedado a medias o no.

Y la función que hace lo contrario, lleva las líneas que se han guardado en el buffer nuevamente a la página de la VRAM visible, es la función retornemLiniesBorrades(signed char offset). Tiene el parámetro porque, dependiendo de cuándo se realiza el cálculo en scroll_amunt o scroll_avall, debe tomar un valor u otro. Al igual que ocurría con guardemLiniesAlBuffer, debemos tener en cuenta la frontera de la línea 255.

Las diferentes funciones que mueven la pantalla también deben ser modificadas para que utilicen la función que no deshabilita las interrupciones. A continuación, mostramos cómo sería la que hace el scroll hacia arriba, scroll_amunt().


Conclusión

En este artículo se ha presentado el funcionamiento básico de la interrupción de línea con el programa intlin.c. Una vez que hemos experimentado con un programa sencillo su funcionamiento, se ha aplicado este conocimiento al programa que se había hecho para el scroll de patrones, habiendo tenido que depurar muchas veces debido a la complejidad que no existía en el programa sencillo, causada principalmente por la deshabilitación de las interrupciones. Estas modificaciones las podéis encontrar en scfoInLi.c. Con lo que hemos conseguido tener una parte de la pantalla que no varía y que nos permitirá mostrar elementos informativos del juego.


Haz clic aquí para ver el ejemplo en funcionamiento.



 
 
 

Comments


Envíanos un mensaje y dinos lo que piensas

¡Gracias por tu mensaje!

© 2035 Creado por Tren de ideas con Wix.com

bottom of page