RYG: rutinas para manejar tableros

Programación retro del Commodore 64

  • Programación retro del Commodore 64
  • “Programación Retro del Commodore 64” es un blog sobre el hardware, el sistema operativo, y la programación del Commodore 64. Y más específicamente sobre programación en ensamblador. Pretende ser un blog con información de calidad, y referencia en español de la programación retro de esta maravillosa máquina.
    • Mi blog
« Publicado el: 24/04/2020 »

Ahora que tenemos definida la pieza clave del proyecto, que es la estructura de datos para manejar tableros, resultado muy conveniente definir una serie de rutinas para manejar tableros (ver fichero “Tableros.asm”). De este modo, se pueden definir rutinas para:

  • Inicializar un tablero.
  • Fijar y recuperar sus datos básicos (nivel, turno y valor).
  • Fijar y recuperar su padre.
  • Fijar y recuperar sus hijos.
  • Fijar y recuperar la pieza de una posición (fila, columna).
  • Etc.

Esta librería de rutinas la iremos completando y perfeccionando según avance el proyecto y según vayan surgiendo nuevas necesidades.

Todas estas rutinas, en general, se apoyarán en el modo de direccionamiento indirecto – indexado. Configuraremos el puntero de página cero $fb – $fc apuntando al primer byte del tablero (nivel) y, a partir de ahí, usando el registro Y como índice, iremos al campo de nuestro interés para leer (instrucción “lda ($fb),y”) o escribir (instrucción “sta ($fb),y”):

Rutinas

Como sabemos, cuando un programa llama a una rutina, no hay garantía de que la rutina mantenga inalterados los registros del microprocesador (acumulador, registro X y registro Y). Pero como en este proyecto vamos a usar mucho los registros X e Y para tareas como recorrer los hijos de un tablero o los campos de una estructura de datos, nos interesa que las rutinas sí conserven estos registros. Por ello, aquellas rutinas que vayan a modificar X y/o Y tendrán un campo TempX y/o TempY donde resguardar los valores de los registros al empezar y recuperarlos justo antes de terminar.

Y como todas las rutinas enumeradas anteriormente son parecidas, nos limitaremos a explicar una rutina de cada tipo:

Rutina “fijaContenido”:

El código de la rutina es así:

Rutina fijaContenido

La rutina recibe como parámetros de entrada:

  • Un puntero al tablero, con sus bytes “lo” y “hi”.
  • La fila y columna cuyo contenido se quiere modificar.
  • El contenido o ficha a poner en (fila, columna), es decir, $00 para vacío, $01 para ratón, y -1 = $ff para gato.

Obsérvese que también hay un byte “fcTempY”. Esto no es un parámetro de entrada ni de salida. Simplemente es un campo en el que vamos a guardar temporalmente el valor del registro Y (“sty fcTempY”) para recuperarlo luego justo antes de terminar (“ldy fcTempY”). De este modo, aunque la rutina modifique el valor del registro Y, y lo modifica para usar el modo de direccionamiento indirecto – indexado (“sta ($fb),y”), la rutina o programa llamante no se verá afectado. El programa llamante puede confiar en que no le cambiarán el valor de Y.

Esquemáticamente, lo que hace la rutina es:

  • Resguarda el valor del registro Y, porque lo va a modificar.
  • Convierte las coordenadas (fila, columna) en un offset contado desde el comienzo del tablero. Este offset es el número de bytes que hay que contar desde comienzo del tablero (byte de nivel) para llegar al byte de la (fila, columna) que hay que modificar. Se obtiene con la rutina complementaria “dameOffset”.
  • Prepara el puntero $fb – $fc para que apunte al tablero.
  • Carga el offset en el registro Y.
  • Modifica el contenido de la posición (fila, columna) con “sta ($fb),y”.
  • Recupera el valor del registro Y.

Fácil, ¿no? Básicamente es usar el modo de direccionamiento indirecto – indexado. Se prepara un puntero al comienzo del tablero (indirección), se calcula el offset de la posición (fila, columna) respecto al comienzo del tablero, y se modifica la posición en ese offset (indexación).

Cuando lo que fija la rutina es el nivel, el turno, el valor, etc., no hace falta calcular ningún offset. La rutina correspondiente ya “conoce” por diseño de la estructura de datos cuál es el offset de esos campos. Por ejemplo, el nivel tiene offset 0, el turno tiene el offset 1, y el valor tiene el offset 2.

El cálculo del offset sólo es necesario cuando se quiere fijar o recuperar el contenido de una posición (fila, columna), es decir, cuando ya entramos en la matriz de datos.

Rutina dameOffset:

La matriz o tablero propiamente dicho empieza en el offset 21 de la estructura de datos. Ese byte correspondiente a la posición (0, 0) del tablero.

A partir del offset 21, cada fila añade otros 8 bytes de offset. Es decir: 21, 29, 37, 45, 53, 61, 69 y 77. Creamos una tabla con estos datos y accedemos a ella usando como índice el registro X (previo cargar en él el número de fila, claro).

Eso nos da el offset del comienzo de cada fila. Luego basta sumar la columna. Para sumar no hay que olvidarse de borrar antes el acarreo con “clc”.

Fácil, ya tenemos la rutina “dameOffset”:

Rutina dameOffset

Ya hemos comentado otras veces que, en ensamblador, es mucho más fácil resolver las fórmulas matemáticas mediante tablas de datos que mediante operaciones aritméticas (offset = 21 + 8 * fila + columna).

Rutina “dameContenido”:

Esta rutina es completamente análoga a la rutina “fijaContenido”, sólo que en vez de escribir datos con “sta ($fb),y” los lee con “lda ($fb),y”. No necesita mucha más explicación.

Y el resto de rutinas del fichero “Tableros.asm”, al menos las rutinas de esta primera versión del proyecto, son todas muy parecidas. Todas utilizan el modo de direccionamiento indirecto – indexado.

La librería de rutinas se irá mejorando con otras nuevas con el avance del proyecto. Y las iremos comentando.


Código del proyecto: RYG01