Repaso de registros del VIC

En las entradas anteriores hemos revisado las funcionalidades principales del VIC. El objetivo de esta entrada es hacer un repaso sistemático de sus registros, para que sirva de referencia, y para prepararnos ya para cambiar de temática y pasar a tratar la música y el sonido con el SID.

Además de indicar las direcciones de los registros, indicaremos sus nombres según el libro “Mapping the Commodore 64”.

Los registros principales del VIC son:

REGISTRO DIRECCIÓN FUNCIÓN
SP0X, SP0Y, SP1X, SP1Y, … $d000 – $d00f Coordenadas X e Y de los ocho sprites.
MSIGX $d010 Bits más significativos de las coordenadas X de los ocho sprites.
SCROLY $d011 Bits 0, 1 y 2: Scroll en la dirección Y.

Bit 3: Modo 24 o 25 filas (1=25, 0=24).

Bit 4: Blanqueo de pantalla (0=blanqueo).

Bit 5: Modo bitmap (1=activo).

Bit 6: Modo carácter con color de fondo extendido (1=activo).

Bit 7: Bit más significativo –bit 8– del RASTER ($d012).

RASTER $d012 Bits 0 – 7 del RASTER.
SPENA $d015 Bits para activar / desactivar los ocho sprites (1=activo).
SCROLX $d016 Bits 0, 1 y 2: Scroll en la dirección X.

Bit 3: Modo 38 o 40 columnas (1=40, 0=38).

Bit 4: Modo bitmap multicolor (1=activo).

YXPAND $d017 Bits para expandir los ocho sprites en la dirección Y (1=expandido).
VMCSB $d018 Bits 1, 2 y 3: Ubicación de los 2K de los juegos de caracteres personalizados.

Bit 3: Ubicación de los 8K del bitmap.

Bits 4, 5, 6 y 7: Ubicación de la RAM de pantalla.

VICIRQ $d019 Información sobre las interrupciones VIC generadas:

Bit 0: RASTER.

Bit 1: Colisión sprite – fondo.

Bit 2: Colisión sprite – sprite.

Bit 3: Lápiz óptico.

Bit 7: Cualquier fuente.

IRQMSK $d01a Programación de las interrupciones del VIC:

Bit 0: RASTER.

Bit 1: Colisión sprite – fondo.

Bit 2: Colisión sprite – sprite.

Bit 3: Lápiz óptico.

SPBGPR $d01b Bits para controlar la prioridad entre sprites y fondo (0=sprite, 1=fondo).
SPMC $d01c Bits para controlar el multicolor de los ocho sprites (1=multicolor).
XXPAND $d01d Bits para expandir los ocho sprites en la dirección X (1=expandido).
SPSPCL $d01e Bits para controlar las colisiones sprite – sprite (1=sí).
SPBGCL $d01f Bits para controlar las colisiones sprite – fondo (1=sí).
EXTCOL $d020 Color del borde de la pantalla.
BGCOL0 $d021 Color de fondo 0.
BGCOL1 $d022 Color de fondo 1.
BGCOL2 $d023 Color de fondo 2.
BGCOL3 $d024 Color de fondo 3.
SPMC0 $d025 Color compartido 0 de los sprites multicolor.
SPMC1 $d026 Color compartido 1 de los sprites multicolor.
SP0COL, SP1COL, … $d027 – $d02e Colores propios de los ocho sprites.

El VIC tiene algún registro adicional, y alguno de los registros vistos también tiene algún bit adicional, pero lo recogido en la tabla de arriba es más del 90%.

Y a partir de ahora, música y sonido…

Scroll horizontal y vertical

El scroll es una característica gráfica muy interesante. Permite mover la información que se muestra en pantalla suavemente, pixel a pixel. El movimiento puede ser hacia arriba, hacia abajo, hacia la derecha o hacia la izquierda.

Todos hemos visto el efecto del scroll en juegos. La idea es simular el movimiento del personaje, que en realidad está fijo, moviendo el contenido del resto de la pantalla. También es muy parecido a lo que ocurre en una aplicación de PC cuando usamos la barra de scroll porque un documento ocupa más que la ventana.

La pantalla del C64 en principio tiene 25 filas por 40 columnas. Sin embargo, tienes dos modos, el modo de 38 columnas y el de 24 filas, que guardan mucha relación con el scroll. Para verlo más claramente vamos a fijarnos en el scroll horizontal, y más concretamente la situación en que la información nueva entra por la izquierda y la vieja sale por la derecha.

Para que este scroll de izquierda a derecha sea suave, la información nueva que entra por la izquierda tiene que estar lista, digamos pintada, aunque sin verse, en el extremo izquierdo de la pantalla. De este modo, cuando la pantalla se desplaza un pixel hacia la izquierda, cuyo efecto visual equivale a que entra un pixel nuevo por la izquierda, hay información lista para ser mostrada.

Pues bien, este “tener información lista pero que no se ve” se consigue con el modo 38 columnas cuando el scroll es horizontal, y con el modo 24 filas cuando el scroll es vertical. La pantalla sigue teniendo 40 columnas y 25 filas, pero hay dos columnas (la de la izquierda y la de la derecha) que no se ven, o una fila (la de arriba o la de abajo) que no se ve.

Scroll.PNG

El modo 38 columnas se activa poniendo a 0 el bit 3 del registro $d016, que también se utiliza para el modo carácter multicolor y el modo bitmap multicolor. Y el modo 24 líneas sea activa poniendo a 0 el bit 3 del registro $d011, que también se utiliza para el modo bitmap estándar y el modo bitmap multicolor.

Una vez que tenemos la pantalla en el modo 38 columnas (o 24 filas en el caso de scroll vertical), hay que desplazar la pantalla hacia la izquierda. Esto se consigue actuando sobre el registro $d016, concretamente sobre sus bits 0, 1 y 2. Con tres bits podemos establecer 8 valores, desde el 0 hasta el 7, que representan los 8 pixels que se puede mover la pantalla a derecha o izquierda.

En nuestro caso, como queremos hacer scroll de izquierda a derecha (visualmente la información entra por la izquierda y sale por la derecha), lo cual equivale a desplazar la pantalla hacia la izquierda, empezaremos con el valor %000 y lo iremos subiendo hasta el valor %111.

Si el scroll fuera de derecha a izquierda, es decir, desplazar la pantalla hacia la derecha, lo haríamos el revés, empezaríamos en %111 y lo bajaríamos hasta %000.

Llegará un momento en que el scroll alcance su tope, es decir, %111 al hacer scroll de izquierda a derecha. En ese momento, lo que hay que hacer es copiar toda la pantalla desplazada un carácter hacia la derecha, volver a poner el scroll en %000, y alimentar datos nuevos en la columna 1 (oculta).

Scrollx.gif

Análogamente, si el scroll fuera de derecha a izquierda, al alcanzar el tope de %000, habría que copiar toda la pantalla desplazada un carácter hacia la izquierda, volver a poner el scroll en %111, y alimentar datos nuevos en la columna 40 (oculta).

El scroll vertical sigue los mismos principios que el scroll horizontal, pero hay alguna pequeña diferencia. En vez de dos columnas ocultas (modo de 38 columnas) sólo tenemos una fila oculta (modo de 24 filas). Y en vez de desplazar la pantalla con el registro $d016 (bits 0, 1 y 2) lo hacemos con el registro $d011 (bits 0, 1 y 2). Cuando el desplazamiento vale %000 la fila oculta es la primera, y es ahí donde se deben alimentar los datos nuevos. Cuando el desplazamiento vale %111 la fila oculta es la última.


Programa de ejemplo: Prog46

Modo bitmap multicolor

En cierto modo, el modo bitmap estándar es bastante multicolor, ya que cada cuadradito de 8 x 8 admite dos colores, el de los bits activos y el de los bits inactivos. Ambos colores salen de la RAM de pantalla (nibble alto y nibble bajo). Pero, todavía más, los dos colores de un cuadrado de 8 x 8 pueden ser distintos de los dos colores del cuadrado de al lado. En definitiva, admite bastante colorido.

A pesar de esto, hay un modo bitmap multicolor. Y al igual que ocurría con los sprites multicolor y con el modo carácter multicolor, se consiguen cuatro colores a costa de dividir por la mitad la resolución horizontal, que pasa a ser de 200 x 160 pixels.

De este modo, en cada matriz de 8 x 8, los pixels / bits se agrupan de dos en dos en sentido horizontal. En función de lo que valgan esos dos bits así será el color:

  • %00: Se toma el color de fondo del registro $d021 (color de fondo 0).
  • %01: Se toma el color de la RAM de pantalla (nibble alto).
  • %10: Se toma el color de la RAM de pantalla (nibble bajo).
  • %11: Se toma el color de la RAM de color.

Es decir, en el modo bitmap multicolor, el diseño de la pantalla (pixels activos e inactivos) sale de un segmento de 8K de memoria, igual que en el modo bitmap estándar. Y el color sale de la RAM de pantalla, igual que en el modo bitmap estándar, y, además, del registro $d021 y la RAM de color. Ninguna de estas dos últimas fuentes de información se utiliza en el modo bitmap estándar.

El modo bitmap multicolor se activa activando a la vez el bit 5 del registro $d011 (igual que el modo bitmap estándar) y el bit 4 del registro $d016 (igual que el modo carácter multicolor).

Bitmap multicolor.PNG


Programa de ejemplo: Prog45

Modo bitmap estándar

En el modo bitmap, la pantalla no está organizada en caracteres, sino que está organizada en pixels individuales. En el fondo, la pantalla tiene la misma resolución que en el modo carácter, lo que pasa es que cada pixel se maneja independientemente de los demás.

Por tanto, si en el modo carácter la resolución es de 25 x 40, siendo los caracteres de 8 x 8 pixels, al eliminar las fronteras entre caracteres lo que tenemos son 200 x 320 pixels. Y cada uno de estos pixels se puede activar / desactivar de forma independiente de los demás, y sin formar parte de un carácter. Por supuesto, la información para ello sigue saliendo de la memoria RAM, pero ya no de la RAM de pantalla ($0400 – $07e7), como se verá enseguida.

Bitmap estándar.PNG

Así como en el modo carácter la RAM de pantalla ocupa 1.000 bytes, en el modo bitmap tenemos 200 x 320 = 64.000 pixels. Por tanto, necesitamos 64.000 bits o, lo que es lo mismo, 8.000 bytes. Es decir, en el modo bitmap la información que define la pantalla necesita mucha más memoria que la que cabe en la RAM de pantalla. Esto es totalmente lógico, ya que en el modo carácter despachamos cada matriz de 8 x 8 pixels con un byte (un código de pantalla), mientras que en el modo bitmap necesitamos 8 bytes, exactamente 8 veces más.

El color del bitmap sale de la RAM de pantalla (ojo, no la RAM de color). Cada bloque de 8 x 8 pixels toma sus colores del “carácter” que ocuparía la posición análoga en la RAM de pantalla. Los bits activos (los que están a 1) toman su color del nibble más significativo, y los bits inactivos (los que están a 0) toman su color del nibble menos significativo. Con un nibble se pueden codificar los 16 colores que soporta el C64.

El modo bitmap se activa activando el bit 5 del registro $d011. Este registro también se utiliza para activar el modo carácter con color de fondo extendido (bit 6) y para el bit más significativo del raster (bit 7).

Por último, quedan un par de cuestiones para nada triviales: cómo se le indica al VIC dónde están esos 64.000 bits / 8.000 bytes que definen la pantalla y, sobre todo, cuál es la correspondencia entre esos bits y los pixels de la pantalla.

La respuesta a la primera pregunta es actuando sobre el bit 3 del registro $d018, que también se utiliza para indicar dónde se ubican los juegos de caracteres personalizados (bits 1, 2 y 3) y la RAM de pantalla en el modo carácter (también bits 4, 5, 6 y 7). Con el bit 3 pueden indicarse dos valores:

  • %0: El bitmap se encuentra en el segmento $0000 – $1fff (0K – 8K).
  • %1: El bitmap se encuentra en el segmento $2000 – $3fff (8K – 16K).

Bitmaps.PNG

Por supuesto, el segmento de 8K elegido es relativo a la base del banco de 16K direccionado por el VIC. Igual ocurría con los juegos de caracteres personalizados.

Por último, queda explicar cómo es la correspondencia entre los bits de ese segmento de 8K y los pixels de la pantalla. Parecería que lo más intuitivo sería que el primer bit fuera el de la esquina superior izquierda, y así sucesivamente de izquierda a derecha y de arriba abajo, hasta llegar al último de los 64.000 bits, que sería el de la esquina inferior derecha. Pues bien, no es así.

La correspondencia entre los bits de memoria y los pixels de pantalla es bastante más compleja. Es como se indica en la siguiente figura:

Bitmaps2.PNG

Por tanto, como se puede ver en la figura anterior, aunque en el modo bitmap cada pixel es independiente de los demás, el sistema sigue teniendo cierto “tufillo” al modo carácter, ya que los pixels se agrupan y gestionan en bloques de 8 x 8, tanto en pantalla como en memoria. Los pixels activos / inactivos se definen mediante bloques de 8 bytes en memoria. Y su color se define mediante un byte (nibble superior para los bits activos; nibble inferior para los bits inactivos) en la RAM de pantalla.

Sería interesante disponer de alguna herramienta del entorno PC que, dado un diseño de 200 x 320 pixels, generara la definición de memoria anterior. Hasta donde yo sé no existe. O, al menos, yo no la he encontrado en CBM prg Studio.

Por último, las ecuaciones matemáticas trabajan con coordenadas (X, Y). Por tanto, para pintar curvas es interesante disponer de rutinas que, dado un pixel (X, Y), determinen la posición de memoria sobre la que hay que actuar. En el libro “Assembly Language Programming with the Commodore 64”, en sus ejemplos 11-5, 11-6 y 11-7, están explicadas esas rutinas. Además, en el ejemplo 11-8 se muestra cómo usarlas para pintar una especie de estrella.


Programa de ejemplo: Prog44

Mapas o pantallas con CBM prg Studio

Ahora que hemos revisado los tres modos carácter que tiene el C64, es buen momento para revisar la funcionalidad de diseño de pantallas de CBM prg Studio. Esta funcionalidad está disponible en el menú Tools > Screen Editor o añadiendo un nuevo fichero bajo la carpeta “Screen Designs” del árbol del proyecto.

Sea como fuere, se abrirá una ventana como esta:

Editor Pantallas

El editor tiene muchas opciones, pero resumiendo mucho lo que permite es pintar caracteres, ya sean estándar o personalizados (zona de la izquierda), en una o varias pantallas de 25 x 40 posiciones (zona de la derecha). También permite seleccionar caracteres, borrarlos, cambiar su color, escribir textos, diseñar caminos (listas de posiciones X, Y), pintar líneas, pintar cajas, etc.

Editor Pantallas - funciones

Es posible cambiar el modo de la pantalla entre los tres modos vistos: estándar, multicolor y con color de fondo extendido.

Editor Pantallas - modos.PNG

También es posible grabar los diseños en ficheros *.sdd para recuperarlos más adelante y seguir trabajando con ellos, o para compartirlos con otro programador.

Y, por último, permite exportar la definición de las pantallas en diferentes formatos para que puedan ser usadas desde programas en BASIC (sentencias PRINT) o en ensamblador (directivas BYTE o ficheros binarios *.bin). A la hora de exportar se ofrecen muchas opciones, como exportar todas las pantallas o sólo ciertos rangos, incluir la información de color o no, juntar todas las pantallas por un lado y los colores por otro, o cada pantalla con sus colores, generar o no información para filas vacías, etc.

En definitiva, una herramienta muy útil para diseñar pantallas y facilitar su programación, tanto en BASIC como en ensamblador.


Programa de ejemplo: Prog43

Modo carácter con color de fondo extendido

El modo carácter con color de fondo extendido es una mezcla entre el modo estándar y el modo multicolor. A mi entender, se parece más al modo carácter estándar, y se entiende mejor si se compara con él.

En el modo carácter con color de fondo extendido, al igual que en el modo carácter estándar:

  • La pantalla tiene 25 filas por 40 columnas.
  • Los caracteres de la pantalla salen de la RAM de pantalla.
  • El color de los caracteres es único y sale de la RAM de color.

La principal diferencia es que cada carácter puede tener un color de fondo diferente, en vez de ser el color de fondo compartido entre todos los caracteres de la pantalla, como ocurre en el caso estándar.

Para determinar el color de fondo de cada carácter se utilizan los dos bits más significativos de su código de pantalla en la RAM de pantalla (ojo, no en la RAM de color). De este modo, si el código de pantalla empieza por:

  • %00: Se toma el color de fondo del registro $d021 (color de fondo 0).
  • %01: Se toma el color de fondo del registro $d022 (color de fondo 1).
  • %10: Se toma el color de fondo del registro $d023 (color de fondo 2).
  • %11: Se toma el color de fondo del registro $d024 (color de fondo 3).

Por otro lado, si los dos bits más significativos del código de pantalla se utilizan para determinar el color de fondo del carácter, de ello se deriva que sólo quedan 6 bits para determinar el carácter. Es decir, en este modo sólo se puede representar 2^6=64 caracteres, ya sean los primeros 64 caracteres del juego estándar o de un juego personalizado.

En definitiva, el modo carácter con color de fondo extendido nos da control sobre el color de los caracteres y el color de su fondo, que deja de ser común para todos, con el coste limitar el número de caracteres representables a 64.

Quedaría por explicar cómo se activa este modo, lo cual se consigue activando el bit 6 del registro $d011.

Color fondo extendido

A mi modesto entender, se trata de un modo un poco extraño, y con poca utilidad práctica. Normalmente el programador querrá escribir textos o hacer fondos de pantalla gráficos. Para lo primero, el modo carácter estándar permite controlar el color de los caracteres, e incluso su forma con juegos personalizados, y el color del fondo que es común a toda la pantalla. Para lo segundo, el modo carácter multicolor permite diseñar patrones y controlar sus colores (hasta cuatro). Más que suficiente. No veo claro un escenario de uso donde interese controlar el color de los caracteres y el color de su fondo individualmente, y menos si es a costa de reducir el número de caracteres representables.


Programa de ejemplo: Prog42

Modo carácter multicolor

El modo carácter multicolor comparte muchas características con el modo carácter estándar:

  • La pantalla tiene 25 filas por 40 columnas.
  • Los caracteres de la pantalla salen de la RAM de pantalla.
  • El color de los caracteres sale de la RAM de color (sólo en parte).

La principal diferencia es que cada carácter, en vez de tener un único color, puede tener varios colores (hasta cuatro).

Para que esto sea posible, de forma análoga a lo que ocurría con los sprites multicolor, los bits o pixels de la matriz de 8 x 8 se agrupan de dos en dos en sentido horizontal:

Editor caracteres multicolor.PNG

De este modo, si el grupo de 2 bits / pixels vale:

  • %00: Se toma el color del registro $d021 (color de fondo 0).
  • %01: Se toma el color del registro $d022 (color de fondo 1).
  • %10: Se toma el color del registro $d023 (color de fondo 2).
  • %11: Se toma el color del carácter de la RAM de color.

Es decir, tres colores (valores de los registros $d021, $d022 y $d023) tienen que ser compartidos entre todos los caracteres, mientras que el cuarto color es específico de cada carácter individual y sale de la RAM de color.

De lo anterior se deriva que:

  • Aunque se podría usar el juego de caracteres estándar en multicolor, no será habitual hacerlo, ya que este juego de caracteres requiere más resolución y no está pensando para el multicolor. Los caracteres estándar no se interpretan bien en multicolor, como puede apreciarse más arriba.
  • Suelen diseñarse juegos de caracteres a medida para el multicolor, aunque, más que caracteres propiamente dichos (letras, números y símbolos), suelen diseñarse “ladrillos” para componer fondos de pantalla.

Además de diseñar un “juego de caracteres” adecuado para el multicolor, ubicarlo en RAM y activarlo como se explicó en la entrada anterior, hay que configurar el VIC para que funcione en modo multicolor. Esto se hace activando el bit 4 del registro $d016.

Por último, hay que utilizar la RAM de color ($d800 – $dbe7) para especificar el color de cada carácter almacenado en la RAM de pantalla ($0400 – $07e7). Una cuestión curiosa es que si el color de una posición:

  • Está entre 0 y 7: El carácter se mostrará en modo estándar o monocolor.
  • Está entre 8 y 15: El carácter se mostrará en modo multicolor.

Lo anterior permite mezclar caracteres monocolor y multicolor en la misma pantalla.

En la figura que sigue se muestra una pantalla llena de “pirámides” de tres colores: negro (fondo 1), amarillo (fondo 2), y verde claro. El color verde claro podría ser diferente en cada carácter, puesto que sale de la RAM de color.

Pirámides.PNG


Programa de ejemplo: Prog41