CHIP-8: medio siglo después, el lenguaje que enseña a programar máquinas virtuales
CHIP-8, un lenguaje de programación creado en 1977 para el kit de computadora COSMAC VIP, sigue siendo una de las herramientas pedagógicas más utilizadas por quienes se inician en la escritura de emuladores. Su propuesta es engañosamente sencilla: un conjunto de 34 instrucciones de dos bytes cada una, organizadas sobre una arquitectura virtual de 12 bits, que permite dibujar sprites en una pantalla de 64 por 32 píxeles, leer un teclado hexadecimal y emitir pitidos a través de un zumbador. Esa simplicidad explica la existencia, según el desarrollador John Earnest —autor de Octo, un ensamblador de alto nivel para CHIP-8— de "miles de runtimes" disponibles para prácticamente cualquier plataforma imaginable.
La arquitectura limita el programa a un máximo de 3.232 bytes de código y datos, aunque muchos intérpretes modernos relajan esta restricción hasta los 3.584 bytes. El procesador cuenta con 16 registros de propósito general de 8 bits (v0 a vf) y un registro índice de 12 bits. La pila interna para llamadas a subrutinas es opaca: no se puede inspeccionar ni manipular fuera del propio mecanismo de retorno.
El conjunto de instrucciones cubre operaciones aritméticas con y sin acarreo, operaciones bit a bit, saltos condicionales, lectura del temporizador de retardo, espera de pulsaciones de teclado y generación de números aleatorios. Incluye, además, instrucciones no documentadas originalmente como XOR, desplazamiento a la izquierda y sustracción inversa, que nacieron como consecuencia natural de la codificación del procesador RCA-1802 subyacente.
Sin embargo, el panorama real está lejos de ser uniforme. Earnest, que ayudó a popularizar los llamados "flags de peculiaridades" para catalogar las divergencias entre intérpretes, advierte que el ecosistema está fragmentado: muchos intérpretes fueron escritos por principiantes que desconocían la comunidad de aficionados y abandonados en cuanto conseguían ejecutar PONG.CH8. Existen hoy bancos de pruebas maduros —como el chip8-test-suite— que permiten verificar el cumplimiento del estándar, pero la proliferación de implementaciones defectuosas persiste.
Entre los errores más comunes figuran las denominadas "peculiaridades de desplazamiento": muchos intérpretes modernos ignoran el segundo operando y desplazan el primer registro en su lugar, cuando la especificación original prevé desplazar vy hacia vx. Earnest recomienda usar siempre la forma vx <<= vx o vx >>= vx para garantizar un comportamiento portable. Otro problema menos conocido es que ciertas operaciones bit a bit (OR, AND y XOR) destruyen el contenido de vf en algunos intérpretes, lo que sorprende a quienes portan programas al COSMAC VIP original.
La ambigüedad sobre el orden en que se escriben los resultados —primero el indicador de acarreo o primero el valor principal— obliga además a evitar vf como destino de operaciones aritméticas, salvo en instrucciones inmediatas como vf := NN. Para operaciones no-op, la opción más segura es vx := vx.
El acceso a memoria se realiza mediante dos instrucciones: save (FX55) y load (FX65), que almacenan o recuperan los registros v0 a vx de forma consecutiva a partir de la dirección apuntada por i. El comportamiento de i tras estas operaciones también varía entre intérpretes: los hay que incrementan el índice por cada registro guardado y los que lo dejan intacto, una diferencia crítica para escribir código portable.
En definitiva, CHIP-8 funciona como un campo de pruebas viviente: un lenguaje lo bastante pequeño para aprenderse en un fin de semana y lo bastante documentado como para que sus divergencias históricas puedan catalogarse. Earnest concluye que, aunque la realidad del estándar en la práctica está fragmentada, escribir para CHIP-8 sigue siendo una puerta de entrada privilegiada a los principios que rigen la emulación de máquinas clásicas. Para quienes busquen compatibilidad, la recomendación es ceñirse al subconjunto conservador de instrucciones y apoyarse en los bancos de pruebas disponibles antes de dar por buena cualquier implementación.
