martes, 29 de mayo de 2012

Curso de Ensamblador para C64: Cap. 4 - Ensamblando con la ayuda de monitores



En el capítulo anterior vimos como compilar manualmente programas escritos (previamente en nuestra libreta) en lenguaje ensamblador a base de usar tablas de mnemónicos. Esto fue algo muy interesante desde el punto de vista didáctico, por que es muy bueno conocer las interioridades de un compilador. Sin embargo, tal como deciamos, y más hoy en dia, no es ésa la forma más eficiente ni mucho menos conveniente de programar nuestro C64.

Ya durante los tempranos primeros años de la historia de la programación, era de esperar que se crearan herramientas para ayudar en esta tediosa tarea, y una de las primeras que se crearon fueron los programas Monitores (no confundir con el dispositivo desde el que salen las imagenes).

Un programa monitor, no es más que un programa que monitoriza (de hay su nombre) lo que sucede en el interior del ordenador. Podremos consultar rápidamente qué contiene cada celda de memoria, incluso podremos cambiarla por otro valor... En eso se basa realmente la capacidad de un programa Monitor de Código Máquina. Un Monitor está básicamente compuesto de varios submódulos, uno de ellos directamente aplicable para programar (motivo de este capítulo). Pero además de este módulo (ensamblador) debe incluir, como mínimo:

1) Gestión de memoria (incluida la memoria interna de la cpu, los registros)

2) Desensamblador

3) Ensamblador

4) Volcado en impresora, salvar y cargar en dispositivos de almacenamiento como disco, casette, etc.



Para el C64 salieron multitud de monitores / depuradores y herramientas similares, personalmente usé el monitor incluido en el popular cartucho "The Final Cartridge", que incluia entre muchas cosas, un útil botón de reset.



Dicho botón resultaba imprescindible para salir temporalmente de cualquier programa en ejecución regresandonos al sistema operativo.
Aparentemente era como si el ordenador se hubiera apagado y vuelto a encender presentandonos la conocida pantalla azul de bienvenida del C64, pero lo peculiar del tema era que la memoria no habia sido alterada por el "apagado" (como normalmente sucede al quitar la corriente), con lo cual podiamos monitorizar cualquier cosa de la memoria y volver al programa anteriormente en curso mediante una orden del BASIC (SYS), o mejor aún usando la orden GO (G) del propio monitor especificando desde que dirección del programa queriamos comenzar la re-ejecución.

Los que tenian un monitor basado en disco o casette (o un cartucho que no tenia botón de reset) tendrian que conseguirse (o fácilmente construirse) un botón de reset, si pretendian usar correctamente su monitor.





Varios modelos de la familia de Commodore, como es el caso del C128 y del Plus/4 tenian un botón de reset, incluso un monitor de CM integrado en su hardware. Ya algunos primitivos PET tenian tambien un monitor de CM integrado en su hardware. Por desgracia, no habia mucho espacio para estos lujos para nuestro humilde C64, pero afortunadamente ese espacio se empleó muy bien para otras cosas, entre ellas hacerlo el ordenador personal más vendido en el mundo...

Lástima que el monitor del C128 solo fuese válido para su modo nativo (modo C128) del ordenador, pero ese fue el precio a pagar por su altísima compatibilidad del modo C64.



Introducir POKES de vidas infinitas en los videjuegos, con la ayuda de un buen botón de reset era un método relativamente sencillo y ampliamente usado en el commodore 64. Te evitaba tener que escribir cargadores basic, con sus inacabables ristras de numeros. Aunque para muchos aficionados que no tenian reset, eran una bendición (¡un arte, y un desafio teclearlos con el menor número de fallos!). Los cargadores vienen a ser "la forma manual de ensamblar los POKES"...

Para seguir el curso no va a hacernos falta un botón de reset, ya que vamos a trabajar con emulador (presenta la facilidad de reset), pero para aquellos que no tengan y quieran uno, es fácil conseguirlos en tiendas especializadas en "Retrogaming", o en sitios de subastas en Internet. Si de verdad estáis muy interesados os recomiendo que le echéis un vistazo al articulo: (Enlace) Commodore mania: Smart-reset-v2

Comencemos pues con el tutorial en sí del capítulo de hoy. Vamos a usar el emulador Vice y su monitor integrado, que aunque no es el mejor del mundo, todo hay que decirlo, funciona perfectamente y resulta de lo más útil, sobretodo por estar integrado con el propio emulador.

A la hora de usar un monitor para ensamblar, descubriréis que todo resulta muy simple y directo.

Vamos a introducir con ayuda del monitor del Winvice, el programa del primer ejercicio con el que concluiamos el anterior capítulo, y asi de paso podréis comprobar si lo hicisteis bien, ya que una vez que lo ensamblemos, podremos ver muy fácilmente los bytes de los opcodes compilados en memoria.

¡Antes empapémonos un poco del funcionamiento de este monitor!

Abrid ahora vuestro emulador Winvice (incluido en el paquete IDE Kickass) y desde la pantalla azul de bienvenida pulsar ALT+M para abrir la ventana del monitor interno.



Al acceder al monitor la ejecución del programa en curso queda congelado, con la memoria intacta y podremos consultar/modificar cualquier lugar de la memoria, tal como si hubieramos pulsado un botón de reset en el C64 real. Si queremos regresar al programa en curso, usaremos el comando x , o g seguido de una dirección para "Ir" a esa dirección y que continue ejecutando la cpu desde ese punto (abandonando el monitor). Como véis, el comando G de Go (Ir en inglés), es el equivalente al SYS del Basic.

Con el comando R (de Registers, registros en inglés) se nos listarán los registros del procesador y justo debajo, sus respectivos valores.

Si usamos R seguido de un nombre de registro (ojo no usar AC ni XR o YR, sino A, X, Y) igualándolo a un valor (con el signo igual), cambiaremos el valor de ese registro. Por ejemplo:

R A = 65

cambiará el valor del acumulador por $65 (recordad que winvice trabaja nativamente con valores hexa, y no usa prefijos).

Entrad R ahora. Veréis que aparece dicha información sobre los registos de la cpu

ADDR , es la direccion (address) donde el programa en ejecución fue interrumpido o sea el registro del puntero de programa (PC), le siguen los registros típicos, el acumulador, x, y, el puntero de pila...

00 y 01 son direcciones de la página cero que sirven para gestionar los bancos de memoria.

Le sigue el byte del registro del estado del procesador, desglosado bit a bit. Los últimos, LIN y CYC no son registros estándar del C64, y no son tenidos en cuenta por los monitores convencionales, pero reflejan dónde está la linea del raster (el "retrazo") dibujando píxeles en la pantalla.

Si queremos tener un volcado de los contenidos de la memoria usaremos el comando M, seguido de un rango de direcciones (separadas por un espacio). Por ejemplo para saber que valores contienen las 16 primeras posiciones de memoria de la página cero, hariamos M 0 F hacedlo ahora y veréis que se muestran 16 numeros agrupados en 2 filas de 12 columnas. Efectivamente son los contenidos por orden de menor a mayor de las 16 primeras direcciones de la memoria. Fijaos que las dos primeras se corresponden con el "registro" 00 y 01 del listado de registros (entrad R de nuevo).

Actualmente el color del borde de la pantalla es el color azul claro, que tiene el código numérico 14. Usando el monitor del vice vamos a comprobarlo. Si recordáis, como ya en el mismo manual de nuestro C64 nos decian, las posiciones del color de la pantalla están en 53280 (borde) y 53281 (fondo), que como el monitor trabaja en hexadecimal preferiremos recordar como $d020 y $d021.

Es de suponer entonces que la dirección $d020 contenga 14 ($0e), si entramos M D020 D021 obtenemos los valores FE y F6, el color azul claro y el azul oscuro. Aunque eso si, tened en cuenta que en lugar de un 0E 06 hemos encontrad un FE F6, esto es por que el primer nibble no es significativo para el chip gráfico, o sea que no interviene para obtener el color deseado, y tiene una F por defecto, al inicializarse el sistema, como podia haber tenido cualquier otro valor.

Si queremos cambiar un valor de una dirección de memoria, no usaremos M, en su lugar existe el comando "mayor que.." (>) por ejemplo para cambiar la posición de memoria $c000 por $ea hariamos:

>C000 EA

Ahora que sabéis como modificar la memoria, podéis comprobadlo vosotros mismos, poniendo cualquier número cuyo primer nibble sea cualquiera que se os ocurra y el siguiente sea el código del color, y comprobaréis que efectivamente, el primer nibble es ignorado por completo...

Estos son unos de los comandos más utilizados (aunque hay muchos más) a la hora de trabajar con el monitor a la hora de depurar. No me extenderé explicando más comandos del monitor de Winvice pues esto no pretende ser un manual completo de este programa.

Para más información, sabed que al instalar Kickass IDE tenéis a vuestra disposición el manual del monitor winvice (en inglés) en C:\C64-Kickass-IDE\manuales\Referencias\vice_monitor_reference.html, dicho manual podéis encontrarlo por supuesto también en su lugar original, en la página web oficial del winvice, dentro del manual del emulador.

Tambien podéis obtener ayuda de cada comando anteponiendo un signo ? delante del comando en cuestión, muy útil si os habéis olvidado del uso en concreto de un comando en particular. Por ejemplo:

? r

Visto básicamente como se trabaja con el monitor, estamos calificados para pasar ya al apartado que más nos interesa del capitulo de hoy, que es ensamblar mediante el monitor.

El programa del capítulo anterior que teniamos que ensamblar "a mano", era:



Pues bien, para ensamblar este programa en nuestro C64 con el monitor del winvice, tan sólo tenemos que usar la instrucción A (Assemble). La linea 0, con org 49152 nos indicaba que el programa debia ser ensamblado empezando por esa dirección de memoria en particular, que en hexadecimal es C000.

En la ventana del monitor (ALT+M para entrar en ella) entramos el comando:

A C000

Una nueva linea se crea con C000 en el inicio indicandonos con un cursor parpadeante que espera nuestra instrucción en ensamblador.

Ahora el monitor está en modo "ensamblador", espera que entremos linea a linea (pulsando "enter" cada vez) nuestro programa CM. Debéis saber que ahora mismo, cualquier intento de usar un comando del monitor que no sea la entrada de una instrucción será interpretado como un error de sintaxis.

Para salir de este modo pulsaremos enter con una linea vacia).

Introducimos ldx #00 y pulsamos enter

Fijaos que ahora el ensamblador nos ha creado una nueva linea empezando en C002, y nuevamente espera otra linea de programa... ¡Genial! al parecer el ensamblador del monitor funciona muy bien, por que ha entendido que le hemos puesto una instrucción que en memoria ocupa 2 bytes, ¡Hurra! ¡Se acabaron los dias de calcular direcciones a mano!

Procedemos pues a entrar la siguiente linea de nuestro programa...

Vaya, ¡esta linea contiene una etiqueta! Paremos un momento.

El monitor de Winvice, acepta el uso de etiquetas (precedidas por un punto y terminadas con dos puntos). Sin embargo esto no era lo habitual en los monitores que existieron para la máquina original, por ello he decidido hacer como si no existiera esta facilidad en el moderno monitor del Winvice, de hecho estuve tentado por centrar el articulo en el funcionamiento del monitor del Final Cart III, pero me decidí finalmente (motivos prácticos), por este otro más moderno (y de desarrollo cruzado). La cuestión es, además de ofrecer un campo de entrenamiento previo al uso del ensamblador puramente dicho (kickassembler) presentar también fidedignamente las ventajas e inconvenientes que tenian los monitores clásicos. Sigamos...

El ensamblador de un monitor no es tan complejo como un programa ensamblador dedicado, y he aqui uno de los primeros inconvenientes frente a sus primos hermanos ensambladores. No podemos usar las etiquetas asi directamente, sin más... con sus nombres tan aclarativos. Si, lo sé, una pena, pero ¿Que hacemos ahora?

Lo que hacemos, es tomar nota de la etiqueta y apuntar en un papel FILL = "la linea actual que me dice el monitor" o sea FILL = C002, y seguimos adelante.

Entramos las lineas siguientes, pero con números convertidos en hexadecimal... (pulsamos enter tras entrar cada linea)

txa
sta 0400,x
sta 0500,x
sta 0600,x
sta 06e8,x
inx


y a partir de aqui viene una instrucción con etiqueta, concretamente de salto condicional hacia atrás. Manualmente, recordad que teniamos que calcular la distancia y obtener el numero en complemento a dos (si esta distancia era hacia atrás, o negativa). Por fortuna para ahorrarnos del tedio de calcular, el monitor entenderá si le ponemos una dirección a la que debe saltar y calculará esto por nosotros (todo un detalle por su parte).

La dirección a la que debe saltar es la etiqueta FILL, que previamente teniamos anotada como C002. Ésa es la dirección que debemos poner tras bne...

bne c002
rts
y pulsamos enter

tras entrar la instrucción rts, pulsamos enter con una linea vacia para indicar al ensamblador del monitor que hemos acabado de entrar nuestro programa. Al hacerlo ya estamos de vuelta al modo normal de operación del monitor, y podemos usar de nuevo todos los comandos de que deseemos.

Ahora lo que haremos será obtener un listado desensamblado del programa que acabamos de introducir. Lo haremos con el comando D, seguido del rango de direcciones del programa, asi:

D c000 c013

Se nos muestra el desensamblado completo del programa que acabamos de introducir. A la izquierda de las instrucciones se encuentran los bytes de los códigos de operación y operandos (nuevamente en hexa) tal y como se han almacenado en memoria al ir siendo ensamblados. Fijaos que, como ya se explicó, se usa el orden de almacenamiento inverso cuando se tienen que guardar números que implican 16 bits (2 bytes) usualmente direcciones de memoria. Primero el más significativo luego el menos significativo. Byte más significativo podriamos definirlo como aquél que más frecuentemente variará en un "conteo", al contrario del menos significativo.

Pues bien, en ese listado tan detallado tenéis la solución completa al ejercicio del capítulo anterior.

Si queremos probar que hace el programa debemos ejecutarlo y podemos hacerlo directamente usando el comando G seguido de la dirección de memoria del inicio de nuestro programa (C000) lo que cerrará el monitor y volverá a la ventana del emulador ejecutándolo. Tambien podemos salir del monitor con el comando X (eXit) y usar SYS 49152 desde el basic, es lo mismo.

Muchos os preguntaréis llegados a este punto, ¿Y si me he equivocado en una linea como la corrijo? La cuestión es sencilla pero el método demuestra cierta incomodidad como comprenderéis. Si queremos modificar una linea volveremos a entrar el comando A seguido de la dirección de memoria con la instrucción equivocada y entraremos la correcta. El problema nos llega cuando esta nueva instrucción ocupa distinta cantidad de bytes que la que se habia ensamblado anteriormente, por que tendremos que reapuntar de nuevo las etiquetas subsiguientes a ese punto del programa, y seguramente reescribir buena parte del código siguiente, por si ocupan más bytes habremos pisado, con toda seguridad, las instrucciones justo debajo de las actuales. ¡Un galimatias tremendo!

De todo esto se deduce que tenemos que tener muy claro el programa ya de entrada antes de ponerse a ensamblarlo con un monitor, o las pasaremos muy canutas.

En el pasado, cuando se trabajaba sólo de esta forma (algunos coders de demos todavia lo prefieren, aunque parezca mentira) se usaba a menudo la instrucción NOP para "comentar" zonas que no se quiere que se ejecute o para ajustar los bytes tras una corrección, por ejemplo. De hay la verdadera utilidad de una instruccion que no hace nada (aparentemente, por que no hacer nada, ya es hacer algo, sobretodo en estos casos).

Aún asi se pueden hacer programas bastante complejos (de hecho muchos se hicieron asi) con la ayuda de un monitor solamente, sólo es cuestión de una grandisima disciplina, mucho orden y buenas estrategias. La clave está en organizar todo el programa en pequeñas rutinas que funcionan por si solas facilmente relocalizables. A pesar de todo lo malo que podáis sacar en claro de esto, programar asi lleva a programas tremendamente optimizados, y en su dia (doy fe) era un método muy entretenido.

Como habéis visto, ensamblar programas con la ayuda de un monitor es una operación mucho más directa y sencilla de realizar, que no hacerlo a mano. Aún asi, también habéis comprobado que no resulta 100% satisfactorio principalmente por que no podemos usar etiquetas alfanuméricas o nombres de variables (ni constantes), y por que tenemos que ir "haciendo hueco" a nuestras rutinas sobre la marcha... Para ello existe una solución: usar un programa ensamblador, la herramienta definitiva para programar en lenguaje ensamblador cómoda y eficazmente. De eso tratará el próximo capitulo.

A pesar de que los monitores ya no se usan como herramienta principal de programación (de eso se ocuparán ya los ensambladores dedicados), siguen siendo una herramienta muy útil (para nada obsoleta) dada su estrecha cercanía al hardware, y realmente pueden ayudar mucho al sufrido programador, sobretodo en la fase de la depuración, esto es, cuando debamos liberar nuestros programas de dichosos errores (bugs). No obstante, los monitores tambien se conocen como depuradores (debuggers).

Podéis ir practicando ensamblando los programas del capitulo anterior con el monitor del Winvice. Trastead con ellos como queráis y cuanto queráis. Practicad observando y modificando la memoria junto con un mapa de memoria (en el paquete de Kickass IDE encontraréis uno). Lo importante es que practiquéis y que todo se os vaya haciendo muy familiar.

Os invito a probar todo esto con mucha calma, ¡Feliz ensamblado y hasta el próximo capítulo, amigos!

6 comentarios:

Jose Zanni dijo...

Buenoo! por fin el segundo capítulo! me alegro que estés de vuelta!

Lobogris dijo...

¡Gracias Zanni! ;) se hace lo que se puede...

Jose Zanni dijo...

Ponle número en el título, por ejemplo I: II: o Cap1:, o (1), (2) etc.

Yo mantengo mi propuesta de crear los PDFs del curso.

Lobogris dijo...

En la imagen de cabecera está el número. De todas formas para seguirlos desde una lista es mejor que les ponga el numero también en el texto de títulos, buena observación.

Yo mantengo mi ilusión en que me ayudes a sacarlo en pdf :)

Bieno64 dijo...

De nuevo un gran aporte para aprender mas de nuestro 64.
En un PDF sería genial ;-)

Jose Zanni dijo...

El PDF saldrá si o si!

He creado una entrada en el foro http://retroinvaders.com/commodoremania/foro/index.php/board,3.0.html

Publicar un comentario en la entrada