Números negativos

Hasta ahora, de forma implícita hemos asumido que todos los números con que trabajamos son enteros positivos. Sin embargo, en ocasiones interesará trabajar con enteros negativos o, incluso, con números con decimales.

En el sistema de numeración decimal son enteros negativos, por ejemplo, el -37 o el -64. Pues bien, en los sistemas binario y hexadecimal también es posible trabajar con números negativos.

La característica de todo número negativo (ej. -37) es que es el opuesto de otro positivo (ej. 37). Por tanto, dado que los números positivos ya están dominados, la clave está en localizar los opuestos.

Ahora fijémonos en el número 37 en binario: %00100101. Si cambiamos los 0’s por 1’s y los 1’s por 0’s obtenemos lo que se llama su complemento o complemento a uno: %11011010. Y si sumamos un número más su complemento, lo que se obtiene es todo 1’s, es decir, %11111111 o, lo que es lo mismo, 255 en decimal o $ff en hexadecimal.

Pues bien, si además de sumar el complemento también sumamos 1, lo que se obtiene es %100000000 (9 bits) o, lo que es lo mismo, 256 o $100 en hexadecimal. Y si ahora tenemos en cuenta que el C64 es una máquina de 8 bits, y que el bit más significativo (bit 8 contando desde 0) se perderá o, a lo sumo, irá a parar al flag C del registro de estado (esto se verá más adelante), la conclusión es que se obtiene %00000000 (8 bits) o, lo que es lo mismo, 0 en decimal o $00 en hexadecimal.

Es decir, hemos encontrado un número (llamado “complemento a dos”) que, sumado al número de partida (37), nos da cero. Por tanto, el complemento a dos viene a ser el opuesto del número de partida (-37). Y se obtiene fácilmente calculando el complemento y sumando 1.

De este modo, los opuestos o complementos a dos de los siguientes números son:

Complemento a dos

Obsérvese que todos los números considerados negativos, los complementos a dos, tienen un 1 en el bit más significativo, el bit 7, mientras que los otros números tienen un 0 en ese bit. Por eso al bit 7 se le suele llamar bit de signo.

El microprocesador 6510 puede sumar y restar, pero, curiosamente, la resta la implementa obteniendo el complemento a dos y sumándolo. Esto es así porque el complemento a dos es muy fácil de obtener: sólo hay que cambiar los 0’s por 1’s y viceversa, y luego sumar 1.

Por último, conviene notar que los bytes como tal no son positivos ni negativos. Sólo son eso: bytes o paquetes de bits. Es el programador el que debe saber si el número ahí almacenado debe interpretarlo como un número sin signo (del 0 al 255) o con signo (del -128 al 127), y actuar en consecuencia.


Programa de ejemplo: Prog04

BCD – Binary Coded Decimal

Los sistemas de numeración decimal, binario y hexadecimal son conceptos matemáticos. Son formas de representar los números. Un mismo número, por ejemplo, el 37, puede representarse como 37 (decimal), %00100101 (binario), o $25 (hexadecimal). Pero el número es el mismo: 37.

Por tanto, a la hora de programar, o a la hora de referirnos al número 37, indistintamente podemos usar 37, %00100101, o $25. Ahora bien, el ordenador lo que almacena en memoria son bytes, y los bytes son paquetes de 8 bits. Por tanto, si el ordenador tiene un 37 en una posición de memoria, lo que tendrá almacenado necesariamente será 00100101, indistintamente de cómo lo representemos en los programas o cómo nos refiramos a ese número.

Lo anterior es cierto si se utiliza la codificación binaria, que es la más habitual. Pero el C64 admite una codificación alternativa llamada BCD – Binary Coded Decimal, es decir, decimal codificado en binario. Esta forma de codificación permite codificar los dígitos decimales (del 0 al 9) usando 4 bits, lo que también se llama “nibble” (un byte tiene dos nibbles). Es decir, el número 37 se codificaría como 0011 en el nibble más significativo y 0111 en el nibble menos significativo. De este modo, con un byte es posible codificar desde el decimal 0 hasta el decimal 99.

El C64 no sólo permite esta codificación, es decir, almacenar los números decimales de esta manera. También permite hacer operaciones aritméticas sobre datos que están codificados así. Esto se consigue activando el flag D del registro de estado, como se verá en entradas posteriores.

La codificación BCD también está ampliamente descrita en Internet: https://es.wikipedia.org/wiki/Decimal_codificado_en_binario.


Programa de ejemplo: Prog03

Sistemas de numeración

En la vida cotidiana, y cuando se programa en lenguajes de alto nivel como BASIC u otros, lo normal es usar números en base 10. En cambio, cuando se programa en ensamblador, lo más cómodo es usar números en base 16 (hexadecimal) y, en ocasiones, en base 2 (binario).

Por ejemplo, para manejar direcciones de memoria del C64 resultan muy cómodos los números hexadecimales. En cambio, para operar con los bits de una posición de memoria resultan muy cómodos los números binarios.

Al final, todos los sistemas de numeración se rigen por unos principios comunes:

  • La base indica el número de dígitos que se utilizan. En base 10 se utilizan los dígitos del 0 al 9 (10 dígitos); en base 2 el 0 y el 1 (2 dígitos); y en base 16 los dígitos del 0 al 9 además de las letras a, b, c, d, e y f (16 dígitos).
  • Los números se escriben de izquierda a derecha, siendo los dígitos de la izquierda los que más peso tienen (los más significativos) y los de la derecha los que menos peso tienen (los menos significativos).
  • Dado un número de N dígitos en base B, su valor en decimal es D(N-1)*B^(N-1) + D(N-2)*B^(N-2) + … + D(1)*B^(1) + D(0)*B^(0). Obsérvese que lo dígitos se numeran desde 0 (el menos significativo) hasta N-1 (el más significativo). Por ejemplo, el número hexadecimal ffff, que se suele poner como $ffff para marcar que es hexadecimal, vale 15*16^3+15*16^2+15*16+15, es decir, 65.535.
  • Se puede operar con números binarios y hexadecimales de forma similar a cómo se opera con números decimales. Se puede sumar, restar, multiplicar y dividir. Las rutinas para sumar/restar/multiplicar/dividir son similares en todas las bases.

En todo caso, dado que hay mucha documentación en Internet relativa a los sistemas de numeración (ver por ejemplo https://es.wikipedia.org/wiki/Sistema_binario y https://es.wikipedia.org/wiki/Sistema_hexadecimal) no abundaremos más en ello.

Simplemente insistir en que, a la hora de programar en ensamblador, en general, lo que resulta cómodo es usar números hexadecimales y, en algunos casos particulares, números binarios.

En la mayoría de ensambladores, CBM prg Studio incluido, los números hexadecimales llevan el prefijo $, los números binarios el prefijo %, y los números decimales no llevan prefijo.


Programa de ejemplo: Prog02

Depuración de un programa en ensamblador

Tras programar un programa en ensamblador, hay que ensamblarlo. Al hacer esto, lo normal es que el ensamblador señale errores que habrá que corregir. En realidad, es un proceso iterativo: se programa, se ensambla, se corrige, se programa, se ensambla, se corrige, etc.

Llegará un punto en que todos los errores de ensamblado se hayan corregido y el programa ya se ensamble bien, lo cual tampoco significa que el programa sea correcto. Para determinar si el programa es correcto, es decir, que hace lo que se espera de él, hay que probarlo.

En ocasiones, lo probaremos y llegaremos a la conclusión de que el programa es correcto. En otras ocasiones, en cambio, detectaremos algún error de funcionamiento (no ya de ensamblado).

Si detectamos un error de funcionamiento, lo ideal es que sea obvio en qué parte del código está el error. En tal caso, volveremos al código fuente, lo cambiaremos, y repetiremos el proceso.

Lamentablemente, en muchas ocasiones no será fácil localizar el error de funcionamiento, y tendremos que recurrir al depurador (Debugger) de CBM prg Studio para localizarlo. Básicamente, lo que nos permite el depurador es hacer una ejecución paso a paso, viendo el impacto que cada instrucción tiene sobre el C64 (en la memoria, en los registros del microprocesador, en la pantalla, etc.).

Para ejecutar el depurador hay que ir al menú Debugger > Debug project. Se abrirán las siguientes ventanas:

Debugger

La ventana más importante es la llamada “Debugger”. En esa ventana se ven los registros del microprocesador con sus valores (el contador de programa, el acumulador, el registro X, el registro Y, etc.). También se ve una zona de memoria con el código máquina que tiene cargado. En ese código máquina, además de las instrucciones y sus operandos, se ven las etiquetas, lo que facilita la lectura del código.

Sobre esta ventana, pulsando F7, se puede ejecutar instrucción a instrucción, e ir observando su efecto sobre los registros y/o la memoria. También se puede cambiar la instrucción que se va a ejecutar (el contador de programa) pulsando F6, para ir directamente a la instrucción o la zona del código que sea de interés.

Otras ventanas de interés son la “Watch list” (lista de variables o posiciones de memoria cuyos valores se muestran ahí para facilitar su supervisión), la pila o “Stack” (puntos de retorno de las llamadas a subrutinas), los puntos de ruptura o “Breakpoints” (puntos donde se para la ejecución para facilitar la inspección), o la memoria de vídeo (para observar cómo va cambiando la pantalla).

En definitiva, el depurador de CBM prg Studio es una herramienta muy útil para analizar en detalle por qué un programa no funciona como esperamos.

Un primer programa en ensamblador para el Commodore 64

Según los objetivos esbozados en la entrada «Objetivos», lo primero sería revisar el hardware del Commodore 64. No obstante, dado que acabamos de instalar y configurar el emulador VICE y el entorno de desarrollo CBM prg Studio, lo primero que vamos a hacer es una pequeña demostración de un programa sencillo en ensamblador.

El programa va a pintar la cadena “PROGRAMACION RETRO DEL COMMODORE 64” en la primera línea de la pantalla.

El proceso para programarlo y probarlo es como sigue:

  • Ejecutar CBM prg Studio.
  • Crear un nuevo proyecto, elegir la máquina C64, y darle un nombre al proyecto.
  • En el explorador del proyecto, crear un fichero en ensamblador llamado Prog01.asm. Esto se hace con el botón derecho del ratón y añadir nuevo fichero.
  • Ahora queda hacer el programa, ensamblarlo y probarlo.

El programa es algo tan sencillo como lo que sigue (el ; sirve para marcar los comentarios):

Prog01

En resumen:

  • Con * = $c000 se indica que la dirección de carga del programa es $c000 (hexadecimal), que en decimal es 49152.
  • “pantalla” es una constante que vale $0400 (hexadecimal), es decir, 1024. Esa posición de memoria es el comienzo de la RAM de pantalla. Cuando el ensamblador ensambla el programa, sustituye las constantes por su valor. Las constantes no ocupan memoria del C64.
  • “prog01”, “bucle”, “fin” y “cadena” son etiquetas. Las etiquetas representan posiciones de memoria del C64. Por ejemplo, “prog01” en este caso vale $c000 o 49152. Y siempre es más cómodo y flexible utilizar etiquetas antes que posiciones de memoria propiamente dichas. Te permiten reubicar el código en otra zona de memoria y que siga funcionando sin mayores modificaciones. Nuevamente, el ensamblador las sustituye por su valor al ensamblar.
  • “ldx”, “lda”, “beq”, “sta”, “inx”, “jmp” y “rts” son instrucciones en ensamblador del microprocesador 6510. De momento, con entender los comentarios a su derecha es suficiente.
  • Todo lo que va a la derecha de un ; es un comentario.
  • “text” y “byte” son tipos de datos del ensamblador. Sirven para definir variables, es decir, posiciones de memoria del C64 con una información almacenada. En este caso, hay dos cadenas de texto de 40 caracteres cada una (el ancho de pantalla del C64 es de 40 caracteres) y un byte 0 que sirve para marcar el final.

Y el algoritmo es bien sencillo:

  • Se inicializa el registro X a cero. Este registro funcionará como índice para recorrer las dos líneas de texto.
  • Se carga el carácter X-esimo en el acumulador.
  • Si el carácter cargado es 0, se salta al final.
  • Si el carácter cargado no es 0, se imprime en la pantalla.
  • Se incrementa el registro X, es decir, el índice.
  • Se continúa con el bucle hasta el final.

De momento, no estará muy claro qué son el acumulador o el registro X, pero lo iremos viendo. Valga el ejemplo para hacernos una idea de cómo es un programa sencillo en ensamblador.

Bueno, después de escribir el programa hay que ensamblarlo, corregir errores, y ejecutarlo. En este caso podemos hacerlo del tirón con Control + F5. Si todo está bien, se cargará el emulador y dentro de éste el programa:

Prog01-emulador

Por último, se puede ejecutar el programa con la sentencia BASIC SYS 49152, que es la posición de memoria en que se ha cargado el programa. El resultado es:

Prog01-ejecucion

Como se puede observar, las dos primeras líneas de la pantalla se han modificado con el contenido esperado.

Bueno, algo sencillo pero suficiente para hacerse una idea de cómo es un programa en ensamblador, cómo programarlo, ensamblarlo, y ejecutarlo en el emulador.


Programa de ejemplo: Prog01

Instalación y configuración de CBM prg Studio

La instalación y configuración de CBM prg Studio tampoco tienen mucho misterio. En este caso, lo que hay que hacer es:

  • Conectarse a la página de CBM prg Studio, cuya dirección es http://www.ajordison.co.uk/.
  • Entrar en la sección “Download”.
  • Descargar la última versión disponible, en este caso la 3.13.
  • Guardar el fichero comprimido en alguna carpeta, por ejemplo, el escritorio.
  • Extraer el contenido del fichero comprimido, que en este caso es el ejecutable de instalación, y ubicarlo donde nos interese.
  • Ejecutar el archivo de instalación. En este paso, es interesante seleccionar la opción de que se cree en el escritorio un acceso directo a la aplicación.
  • Ejecutar el acceso directo del escritorio.

Al ejecutar el acceso directo (ejecutable CBMPrgStudio.exe) se verá la pantalla de inicio de CBM prg Studio:

CBM-prg-Studio

En este caso, al contrario que con VICE, sí interesa hacer alguna configuración básica. Esto se hace en el menú Tools > Options:

  • En “Project” pondremos la máquina objetivo (C64) y la ubicación por defecto de los proyectos (ej. C:\Users\hvsw\Desktop\PRC64).
  • En “Emulator Control” podremos la ruta de VICE, por ejemplo, C:\Users\hvsw\Desktop\WinVICE-3.2-x86-r34842\x64.exe.

De este modo, será posible probar en VICE los programas que programemos en CBM prg Studio. Ni siquiera será necesario salir de CBM prg Studio. Desde el propio entorno de programación será posible ensamblar y ejecutar en el emulador.

También será posible depurar con el debugger de CBM prg Studio.

Instalación y configuración de VICE

La instalación y configuración de VICE no tienen mucho misterio. Básicamente, lo que hay que hacer es:

  • Conectarse a la página de VICE, cuya dirección es http://vice-emu.sourceforge.net/.
  • Descargar la última versión disponible, en este caso la 3.2. Se puede elegir entre la distribución fuente o binaria. Elegimos la distribución binaria para Windows, o para la plataforma disponible.
  • Guardar el fichero comprimido en alguna carpeta, por ejemplo, el escritorio.
  • Extraer el contenido del fichero comprimido, que en este caso es la carpeta del emulador, y ubicarla donde interese.
  • Es interesante hacer en el escritorio un acceso directo al programa x64.exe. Este programa es el que emula el C64. Hay otros programas que emulan otros equipos de Commodore.
  • Ejecutar el acceso directo o el programa x64.exe.

Al ejecutar x64.exe se verá la famosísima pantalla de inicio del C64:

Inicio

En principio, no hay gran cosa que configurar, aunque en la subcarpeta “doc” hay un manual en PDF de unas 400 páginas.

El equipo de trabajo

Mi C64 era del modelo “panera”. Como comenté en alguna entrada anterior, un buen día le di carpetazo al asunto, y acabó en un armario. Y allí pasó un montón de años hasta que mis padres decidieron regalarlo.

Panera

Una pena, porque hoy en día ese equipo sería una joya de la informática. Como poco valdría 100 o 200 euros. Estoy seguro de que, si todavía lo tuviera, funcionaría perfectamente.

Pero la situación es esa: actualmente no tengo un C64. Pero espero tenerlo en breve. Le estoy siguiendo la pista a varios equipos restaurados y tuneados. Auténticas preciosidades. Podéis encontrarlos en eBay o Wallapop.

De momento tendré que programar con un PC convencional. De hecho, es una Surface con Windows 10, 4 GB de RAM, y 120 GB de disco. Eso sí, tengo instaladas otras dos maravillas de la ingeniería, y ambas gratuitas:

Con CBM prg Studio se puede programar en BASIC y en ensamblador. También se pueden diseñar sprites, pantallas, y juegos de caracteres a medida. Y se puede integrar con VICE, de modo que lo programado en CBM prg Studio se puede probar en aquel.

Referencias

Es increíble la cantidad de información que hay sobre el C64 en Internet. Mucha es gratuita, pero es que también se venden muchos libros de coleccionista, y también equipos de segunda mano, algunos restaurados.

Durante los últimos meses he buscado y leído bastante sobre el C64. Los libros que más me han interesado son los siguientes:

Además, en Archive.org hay montones de libros gratuitos.

Por otro lado, respecto a páginas, me han parecido interesantes las siguientes:

TEMÁTICA PÁGINAS
Sobre emulación The C64.

Emulador VICE.

Sobre programación Entorno de desarrollo CBM prg Studio.

Ensamblador Kick Assembler.

Libro y foro sobre programación retro de Derek Morris.

Dustlayer.com.

64bites.com.

Blog de Mauro Cifuentes.

Instrucciones del microprocesador 6502.

Funciones del Kernal. Sí, en aquella época se hablaba de “Kernal”.

Codebase64.

Generalistas Blog PacoBlog64.

Lemon64.

C64-Wiki.

Bombjack.org.

Sobre software Software en Archive.org.
Sobre libros Libros en Archive.org.
Sobre música High Voltage SID Collection.
Sobre juegos Gamebase64.

The C64 Scene Database.

Servicios en línea CommodoreServer.com.
Tienda de juegos y hardware Protovision.

Utilizaré todas o muchas de estas referencias de forma habitual, así que vaya por delante su mención.

Estoy seguro de que habrá muchas más referencias, y muy buenas, pero estas son las que he podido procesar en ratos libres desde el verano de 2018.

Objetivos

Desde el punto de vista personal, mis objetivos son:

  • Pasármelo bien.
  • Recordar viejos tiempos.
  • Aprender.
  • Ayudar a aprender a otra gente que compartiera aquella época maravillosa o que sienta curiosidad ahora.
  • Comprobar si de verdad he aprendido algo de informática en 30 años.
  • Enseñar informática a mis hijos, si es que les apetece.
  • Y quién sabe, quizás recopilar todo esto en un libro cuando haya terminado. Los hijos ya los tengo y también he plantado árboles.

Por otro lado, desde el punto de vista de los contenidos mi objetivo es revisar:

  • El hardware.
  • El mapa de memoria.
  • El sistema operativo.
  • La programación en ensamblador.
  • Algunas técnicas de programación.
  • Un proyecto de ejemplo.

Me planteo lograrlo en 6 meses, de aquí a primeros de mayo de 2019. A ver si lo consigo, porque sólo podré escribir un rato por las noches.