Raster

El registro “raster” ya se presentó en la entrada dedicada a la animación de sprites.

El “raster” se puede entender como un rayo que va recorriendo la pantalla de izquierda a derecha y de arriba abajo, actualizando la información. En el fondo así funcionaban las televisiones de tubo de rayos catódicos (CRTs) de los 80, mediante un rayo que iba actualizando la imagen. Lo que ocurre en este caso es que ese rayo está controlado por el VIC.

Pues bien, existe un registro en la memoria del C64 que permite trabajar con este rayo. En realidad, es un registro ($d012) y parte de otro (bit 7 de $d011). Más concretamente, estos registros permiten hacer dos cosas:

  • Leer por qué línea de la pantalla va el rayo. Esta información se obtiene leyendo la posición $d012 (bits 0…7) y el bit 7 de $d011 (bit 8). Con 9 bits tenemos 512 líneas.
  • Escribir en el registro. En este caso, lógicamente, no se consigue modificar la secuencia normal de actualización del rayo, que es fija, sino que lo que se consigue es que cuando el rayo alcance la línea escrita, se genere una interrupción.

Las interrupciones del VIC se describirán más en detalle en la entrada siguiente, ya que no sólo el “raster” puede generar interrupciones. Las colisiones de sprites también puede generarlas.

De momento, baste decir que cualquiera de los dos enfoques anteriores (detectar mediante lectura que el “raster” ha alcanzado una línea X o generar y atender una interrupción cuando alcanza una línea X) sirve para sincronizar la ejecución del programa (por ejemplo, las animaciones) y el refresco de la pantalla.

Lo más habitual suele ser actualizar los gráficos cuando el “raster” está fuera de la zona visible de la pantalla (a partir de la línea 250), porque de este modo se consigue una actualización más suave, sin parpadeo.

El “raster” actualiza la pantalla 60 veces por segundo (60 Hz) de modo que, aunque sólo se actualicen los gráficos cuando el “raster” pasa por la línea 255, ya se actualizan con mucha frecuencia. Tanta que el ojo no lo percibe.


Programa de ejemplo: el programa de la entrada dedicada a la animación utiliza el “raster” en modo lectura.

Nomenclatura

Quizás deberíamos haber empezado por aquí, pero no lo hemos hecho así. Los programas de ejemplo que llevamos hasta ahora han sido sencillos y, por ello, no ha sido necesario.

Sin embargo, en cuanto los programas en ensamblador son un poco complejos se hace necesario poner un poco de orden. Y, por ello, se vuelve muy conveniente definir una nomenclatura –una forma de llamar a las cosas– para nombrar las cosas siempre de la misma manera, y para facilitar la identificación de su naturaleza.

En un programa en ensamblador encontramos uno o varios ficheros *.asm con:

  • Constantes
  • Variables
  • Etiquetas
  • Instrucciones
  • Comentarios
  • Rutinas
  • Macros
  • Directivas
  • Etc.

Cada uno puede definir la nomenclatura que más le guste. Lo realmente importante es tener una para facilitar la identificación de qué son las cosas.

Una posible nomenclatura es la que se define en el capítulo 6 del libro “Retro Game Dev: C64 Edition”, es decir:

Elemento Nomenclatura Ejemplo
Ficheros del programa XXX.asm Prog37.asm
Ficheros con librerías de rutinas o macros libXXX.asm libSprites.asm
Constantes Primera letra en mayúscula y resto en minúscula. Negro = $00
Variables Todas las letras en minúscula. Si consta de varias palabras, se separan con mayúscula. numero byte $00

numeroVidas byte $00

Posiciones de memoria y registros del VIC, SID, etc. Todas las letras en mayúscula. CIAPRA, CIAPRB, …
Macros Todas las letras en mayúscula. Si consta de varias palabras, se separan con subrayado (_).

Además, se añadirá una V por cada parámetro de entrada de tipo valor y una D por cada parámetro de entrada de tipo dirección.

CONF_BASICA_VDV
Subrutinas Todas las letras en minúscula. Si consta de varias palabras, se separan con mayúscula. activaSprite
Etiquetas globales Todas las letras en minúscula. Si consta de varias palabras, se separan con mayúscula.

Es probable que la misma etiqueta (ej. bucle, fin, …) se repita en muchas rutinas, por lo que se propone prefijar la etiqueta con algo que indique la rutina (ej. “as” por activaSprite) y evite repeticiones.

asBucle, asFin, …
Etiquetas de macro Igual que las etiquetas globales, pero se sustituye el prefijo de rutina (ej. “as” por activaSprite) por el prefijo @. Este prefijo hace que el ensamblador genere una etiqueta única para cada copia o instancia de la macro. @bucle, @fin, …

De este modo, si el programador se encuentra “Negro” automáticamente sabe que es una constante, si se encuentra “numeroVidas” sabe que es una variable, si se encuentra “CIAPRA” sabe que es un registro o posición de memoria conocida del C64, si se encuentra “acitvaSprite” sabe que es una rutina, etc. Y esto es algo muy práctico…


Programa de ejemplo: el programa de la entrada anterior -colisiones-, al ser un programa algo más complejo, ya se ha hecho conforme a esta nomenclatura.

Colisiones de sprites

Con las colisiones de sprites ocurre algo similar a lo que ocurre con las prioridades, es decir, hay de dos tipos:

  • Colisiones entre sprites.
  • Colisiones entre sprites y el fondo.

Es importante señalar que, en ambos casos, la colisión se produce cuando una parte activa del sprite entra en contacto con la parte activa de otro sprite o del fondo. Es decir, para que se produzca una colisión no es suficiente con que la matriz 24×21 de un sprite se solape con la matriz 24×21 de otro sprite; tienen que solaparse o entrar en contacto partes activas, es decir, pixels que están activados.

Colisiones entre sprites

Las colisiones entre sprites se señalizan mediante el registro del VIC $d01e. Cuando dos sprites colisionan, se activan sus bits correspondientes en este registro. Y estos bits permanecen activos hasta que se lee el registro con una instrucción de carga o lectura de datos (“lda”, “ldx” o “ldy”).

Una cuestión a tener en cuenta es que, si colisionan más de dos sprites, por ejemplo, si colisionan cuatro sprites dos a dos, el registro $d01e nos dirá qué sprites se han visto involucrados en las colisiones, pero no quién ha colisionado con quién. Por tanto, para tener una información completa sobre la situación será necesario tener en cuenta también las posiciones de los sprites.

Colisiones entre sprites y fondo

Las colisiones entre sprites y el fondo se señalizan mediante el registro del VIC $d01f. Cuando un sprite colisiona con el fondo, se activa su bit correspondiente en este registro. Y ese bit permanece activo hasta que se lee el registro con una instrucción de carga o lectura de datos (“lda”, “ldx” o “ldy”).

Como en el caso anterior, una cuestión a tener en cuenta es que, si colisionan con el fondo dos sprites o más, el registro $d01f nos dirá qué sprites se han visto involucrados en las colisiones, pero no quién ha colisionado con qué. Por tanto, para tener una información completa sobre la situación será necesario tener en cuenta también las posiciones de los sprites.

Interrupciones

Los registros descritos anteriormente ($d01e y $d01f) sirven para obtener información sobre las colisiones. Pero además de obtener información, normalmente habrá que gestionar esas colisiones, es decir, el programa tendrá que responder ante ellas ejecutando algún código (ej. descontando vidas, sumando puntos, etc.).

Por supuesto, el programa podría estar validando constantemente (o cada cierto tiempo) los registros $d01e y/o $d01f, pero normalmente será más cómodo usar interrupciones, de modo que, cuando se produzca una colisión, se ejecute instantáneamente una rutina de interrupción.

Los detalles de las interrupciones generadas por el VIC se verán en detalle más adelante.

colisiones


Programa de ejemplo: Prog37

Prioridades de sprites

Las prioridades de los sprites son de dos tipos:

  • Prioridades de los sprites respecto a otros sprites.
  • Prioridades de los sprites respecto al fondo.

Prioridades entre sprites

Las prioridades entre sprites son fijas. El sprite con mayor prioridad es el sprite 0, y el sprite con menor prioridad es el sprite 7.

Esto significa que, si dos sprites se cruzan en pantalla, siempre se verá encima el sprite de mayor prioridad, es decir, el de menor número.

Prioridades de los sprites respecto al fondo

Las prioridades de los sprites respecto al fondo no son fijas, sino que son programables sprite a sprite. Para ello se utiliza el registro $d01b, que tiene un bit por cada sprite. Poniendo ese bit a cero, el sprite tendrá prioridad sobre el fondo; poniendo ese bit a uno, el fondo tendrá prioridad.

Lógicamente, las prioridades respecto al fondo pueden ser diferentes para unos sprites y otros. Para ello, llega con activar/desactivar los bits correspondientes del registro $d01b.

Prioridades.PNG


Programa de ejemplo: Prog35