Idiomas disponibles:

Programando como si fuera 1982. Parte III – Memoria y Direcciones

En el post anterior vimos que el procesador tiene tres registros de uso general y un ciclo de ejecución simple e ininterrumpido. El problema es que tres registros no son suficientes para ningún programa real. Necesitas algún lugar donde guardar datos mientras no los estás usando, guardar el programa en sí, guardar variables y resultados intermedios. Ese lugar es la RAM.

La RAM es un array gigante

Si alguna vez has usado un array en cualquier lenguaje, ya entiendes la RAM. La memoria del C64 era exactamente eso:

RAM = [0] * 65536   # 65.536 posiciones, cada una guarda 1 byte

Cada posición guarda un byte, un número de 0 a 255. Nada más. Texto, imágenes, sonido, código: todo se convierte en bytes. Y cada posición tiene una dirección, un número que identifica dónde está, exactamente como el índice de un array.

Para leer o escribir, le decías al procesador: “ve a la dirección X y trae lo que hay ahí” o “ve a la dirección X y coloca este valor ahí”. Sin capas, sin abstracciones. Directo.

Por qué hexadecimal

Las direcciones en el assembly del C64 se escribían en hexadecimal, con un $ delante: $0000, $D418, $FFFF. A primera vista parece arbitrario, pero hay una razón muy práctica.

El hexadecimal usa 16 dígitos en lugar de los 10 del decimal: del 0 al 9, y después A, B, C, D, E, F. La consecuencia es que un byte, cualquier valor de 0 a 255, siempre cabe en exactamente dos dígitos hexadecimales, de $00 a $FF. Dos bytes, como una dirección de memoria, siempre caben en cuatro dígitos, de $0000 a $FFFF.

En decimal, el mismo byte necesitaría entre 1 y 3 dígitos según el valor. Alinear, comparar y leer direcciones sería un caos. En hexadecimal, todo tiene un tamaño fijo y predecible. Una vez que te acostumbras, resulta tan natural como leer la hora en un reloj de 12 horas.

DecimalHexadecimal
0$00
15$0F
16$10
255$FF
256$0100
65535$FFFF

El mapa de memoria

Aquí está la diferencia central entre programar en el C64 y programar hoy: la memoria no era toda igual.

Cada rango de direcciones tenía un papel fijo, y necesitabas saber de memoria qué había en cada lugar. Algunas direcciones eran RAM común donde guardabas datos y código. Otras eran puertos directos hacia los chips de hardware. Escribir en esas direcciones no guardaba nada, activaba el hardware inmediatamente. Y otras eran ROM, código grabado en chip que podías leer pero no modificar.

No había ningún sistema operativo que te explicara qué era qué. Estaba el mapa de memoria, y lo memorizabas.

Mapa de memoria del C64

La Zero Page: 256 bytes de oro

El rango de $0000 a $00FF tenía nombre propio: Zero Page. Solo 256 bytes, pero valían mucho más que cualquier otro byte de la RAM.

El motivo era técnico. Una dirección normal necesita 2 bytes para ser representada. Por ejemplo, $0801 ocupa un byte para $08 y otro para $01. Una dirección en la Zero Page solo necesita 1 byte, porque el procesador ya sabe que el primer byte es cero. Menos bytes en la instrucción significa una instrucción más pequeña y más rápida de ejecutar.

Instrucción accediendo a RAM normal:
  LDA $0801   →  3 bytes en memoria, 4 ciclos de reloj

Instrucción accediendo a Zero Page:
  LDA $42     →  2 bytes en memoria, 3 ciclos de reloj

Un ciclo de reloj menos puede parecer irrelevante. Pero dentro de un bucle que se ejecuta miles de veces por fotograma de vídeo, y el C64 dibujaba la pantalla 50 veces por segundo, la diferencia era enorme. Los programadores trataban cada byte de la Zero Page como un recurso escaso. Las variables críticas iban allí. Los contadores de bucle iban allí. Los punteros que se accedían cientos de veces por segundo iban allí.

Era, en esencia, una caché manual. Tú decidías qué era lo suficientemente importante como para merecer una dirección en la primera página.


En el próximo post dejamos el mapa de memoria y vemos qué hace realmente el procesador con todo esto: cómo se representan las instrucciones en bytes, qué es un opcode y los primeros comandos reales de assembly del 6510.