#+title: ¿Cómo escribir un emulador de Chip8 con C? #+description: Una breve entrada de como iniciar en el desarrollo de emuladores con C #+summary: Una pequeña guía para iniciar en el desarrollo de emuladores utilizando C como lenguaje de programación, y el Chip8 como proyecto a implementar. #+categorias[]: C Programación #+date: 2025-09-19T02:31:01+0000 #+draft: true #+LAST_MODIFIED: 2025-09-25T23:44:38+0000 ¡Hola a todxs! En esta entrada vamos a ver como podemos escribir un emulador del chip8 en C. Tengan en cuenta que esta va a ser solo una guía enumerando los pasos, mostrando formas en que se podría hacer la implementación, pero mejorar ese código, finalizarlo e incluso construir otros proyectos sobre el (como un ensamblador, debugger y desensamblador) serían muy buenas cosas que estudiar después. * El Chip8 El Chip8 es un lenguaje de programación desarrollado a mitad de los 70s por Joseph Weisbecker. Chip8 fue diseñado para ser más fácil de programar y usar menos memoria que otros lenguajes de la época, como BASIC. Algunas aplicaciones hechas en Chip8 incluyen juegos originales, demos, y recreaciones de juegos populares de otros sistemas. Hacer una implementación de Chip8 no es tan complicado, y es el lugar perfecto para iniciar a desarrollar emuladores, ya que su set de instrucciones es reducido, y es bastante fácil de implementar. Primero, vamos sobre los detalles técnicos de como funciona Chip8, antes de empezar a pensar en la implementación para que así sepamos qué estamos haciendo, y por qué. {{< alert "circle-info" >}} *[[http://devernay.free.fr/hacks/chip8/C8TECH10.HTM][Aquí]]* podrás encontrar una especificación completa del Chip8. {{< /alert >}} ** Memoria El Chip8 tiene 4095 bytes de memoria, que pueden ser representados de la siguiente forma: #+begin_src artist +-----------------------+ | | 0xFFF - Fin de la memoria +-----------------------+ | | | | | | | 0x200/0xFFF | | Datos del | | programa | | | +-----------------------+ 0x200 - Inicio programas Chip8 | | | 0x00/0x1FF | | Reservado | | | +-----------------------+ 0x00 - Inicio de la RAM #+end_src La dirección ~0x00~ marca donde inicia la RAM, de la dirección ~0x00~ a la dirección ~0x1ff~ (511 bytes) hay un espacio reservado para el Chip8 donde almacena - por ejemplo - el set de caracteres por defecto (vamos a ver esto a profundidad más adelante). Todos los programas del chip8 se cargan en la dirección de memoria ~0x200~, y tienen desde ~0x200~ a ~0xfff~ para almacenar todas sus instrucciones y sprites (ya también pasamos a esto). El stack del Chip8 es un array de 16 valores diferentes de 16 bits cada uno, estos se utilizan para almacenar las direcciones a las que el chip8 debería volver de una subrutina, lo que quiere decir, que tenemos un nivel máximo de encadenamiento de 16 subrutinas. En otras palabras, cada que llamemos una subrutina, se va a crear una entrada en el stack, y al ser terminada su ejecicion será removida del stack, volviendo al punto de ejecución anterior. El stack es una estructura de datos tipo LIFO (Last In, First Out), lo que significa que el último elemento, será siempre el primero en ser removido. ** Set de caracteres El Chip8 viene con varios sprites por defecto, estos los llamamos /set de caracteres/. Estos son un grupo de sprites representando los digitos desde el 0 hasta la F. Son de 5 bytes (sprites de 8x5) y están almacenados en el área reservada del chip8 (0x00 - 0x1ff). Los siguientes, son ejemplos de estos caracteres: #+begin_src text +----+--------+----+ +----+--------+----+ |"0" |Binary |Hex | |"1" |Binary |Hex | +----+--------+----| +----+--------+----+ |****|11110000|0xF0| | 1 |00100000|0x20| |* *|10010000|0x90| | 11 |01100000|0x60| |* *|10010000|0x90| | 1 |00100000|0x20| |* *|10010000|0x90| | 1 |00100000|0x20| |****|11110000|0xF0| | 111|01110000|0x70| +----+--------+----+ +----+--------+----+ #+end_src ** Registros El Chip8 tiene 16 registros que pueden almacenar valores de hasta 8 bits, es decir, pueden almacenar un byte de información. Los registros son los siguientes: | Registros | | | | | |-----------+----+----+----+----| | | V0 | V1 | V2 | V3 | | | V4 | V4 | V6 | V7 | | | V8 | V9 | VA | VB | | | VC | VD | VE | VF | Es importante tener en cuenta que el registro ~VF~ no debe ser utilizado por un programa, ya que algunas instrucciones hacen uso de este registro, pero los otros 15 registros pueden ser utilizados sin problema. El Chip8 también tiene algunos registros extra: - *I:* Usado para almacenar direcciones de memoria. - *PC:* Apunta a la dirección de la instrucción que se está ejecutando actualmente. Todas las instrucciones del Chip8 son de 2 bytes, por lo que este registro debe ser incrementado por 2 cada vez que una instrucción es ejecutada. - *SP:* Stack pointer, apunta a la ubicación del stack. - *ST (Sound Timer):* Este registro le permite al Chip8 reproducir el sonido de un beep, ese sonido se ejecutará cuando este registro sea diferente de 0, va a estar decreciendo a una velocidad de 70Hz. Si este registro es igual a 0, no se reproducirá ningún sonido. - *DT (Delay Timer):* Este registro se usa cuando queremos parar la ejecución de un programa. Funciona similar a el *Sound Timer*, ya que cuando este registro es diferente de cero, la ejecución del Chip8 se va a poner en pausa, y el valor de este registro disminuirá a una velocidad de 60Hz. Cuando este timer sea 0 de nuevo, la ejecución del programa será resumida. ** La Pantalla La resolución del Chip8 es 64x32 pixeles, y es monocromático, lo que quiere decir que solo puede mostrar blanco y negro, estos valores pueden ser representados como booleanos, 0 para negro, 1 para blanco. Cuando dibujamos en la pantalla estamos dibujando sprites, no pixeles individuales. Si un sprite se mueve hacia un borde de la pantalla y excede la resolución de esta, será envíado al lado opuesto de la pantalla. Los sprites pueden tener un máxico de 8 bits de ancho, y 15 de alto. ** El teclado El teclado del Chip8 solamente tiene 16 teclas: los números del 0 a la F: | 1 | 2 | 3 | C | | 4 | 5 | 6 | D | | 7 | 8 | 9 | E | | A | 0 | B | F | ** Instrucciones El set de instrucciones del Chip8 es demasiado reducido, ya que solamente tiene 36 instrucciones. Hay instrucciones para realizar operaciones matemáticas, dibujar, y manipular registros. * La Implementación Vamos a tomar como referencia este código: {{< gitea server="https://git.ghostpacket.org" repo="ghostie/Chip8Suite" >}} ** La estructura del Chip8 El núcleo del emulador es una estructura llamada ~struct chip8~ definida en ~include/chip8~. Esa estructura es muy simple, luce de la siguiente forma: #+begin_src C struct chip8 { struct chip8_memory memory; struct chip8_registers registers; struct chip8_stack stack; struct chip8_keyboard keyboard; struct chip8_screen screen; }; #+end_src Esa estructura contiene todos los elementos que se mencionaron anteriormente, la memoria, los registros, el stack, el teclado y la pantalla. La estructura ~chip8_memory~ definida en ~include/mem.h~ es solo una estructura que contiene una variable de 4095 bytes definida ~unsigned char memory~, y tiene algunas funciones para modificar valores en cierto índica, para obtener algún valores, y para obtener un short (dos bytes) en cierto índice. La estructura ~chip8_registers~ definida en ~include/registers.h~, contiene los registros que se mencionaron anteriormente, es un array de ~unsigned char~ llamado V con un tamaño de 16 elementos, al igual que algunos otros ~unsigned char~ para el delay timer, sound timer, y stack pointer. Otros registros como el *I* y el *PC* son de tipo ~unsigned short~.