
Esta publicación es una introducción a mi proyecto de consolas de video de consola "caseras" hechas desde cero. Me inspiraron tanto las consolas retro como las muestras modernas, pero obtuve mi propia arquitectura. Mis amigos me decían constantemente que debería hablar sobre mi proyecto y no hacer todo exclusivamente "por mí mismo", así que aquí estoy publicando esta publicación.
Atención, esta es una traducción.
Como empezó todo
Mi nombre es Sergio Vieira, crecí en Portugal en los años 80 y 90, tengo una larga nostalgia por los juegos retro, especialmente para las consolas de tercera y cuarta generación.
Hace unos años, decidí entender mejor la electrónica e intentar hacer mi propio prefijo.
De profesión, soy programador y no tenía ninguna experiencia como ingeniero electrónico, excepto por (y no debería considerarse) actualizaciones independientes de mi destkop.
Aunque no tenía ninguna experiencia, me dije "¿por qué no?", Compré varios libros, varios kits electrónicos y comencé a estudiar en base a mis sentimientos sobre lo que valía la pena estudiar.
Quería hacer un prefijo similar a los que me ponen nostálgico, quería algo entre NES y Super Nintendo , o tal vez entre Sega Master System y Mega Drive .
Estas consolas tenían una CPU, un chip de video original (no se llamaban GPU en ese momento) y un chip de audio, a veces incorporado, y a veces externo.
Los juegos se distribuyeron en cartuchos, que en general eran extensiones de hierro, a veces solo chips ROM, y a veces tenían componentes adicionales.
El plan original era hacer un prefijo con las siguientes características:
- Sin emulación, los juegos y programas deberían funcionar en hardware real, no necesariamente el mismo de aquellos tiempos, pero lo suficientemente rápido para la tarea, y nada más.
- Con una verdadera CPU retro.
- Con salida de TV analógica.
- Con sonido
- Con soporte de controlador dual
- Desplazamiento de becks y sprites de animación.
- Con características para soportar juegos de plataformas como Mario y, por supuesto, todo tipo de otros juegos.
- Con la descarga de juegos y programas desde tarjetas SD.
Por qué las tarjetas SD, y no los cartuchos, bueno, básicamente es mucho más práctico, puedes copiarlas desde tu computadora. Y los cartuchos significarían, en primer lugar, más hierro en el decodificador y, en segundo lugar, producir hierro para cada programa.
Producción
Señal de video
Lo primero que hice fue generar una señal de video.
Cualquier consola del período que tomé como muestra tenía varios chips gráficos propietarios, lo que significa que todos tenían especificaciones diferentes.
Por esta razón, no quería usar un chip gráfico ya preparado, quería que mi consola tuviera especificaciones gráficas únicas. Y como no podía hacer mi propio chip gráfico, y en ese momento todavía no podía usar FPGA, decidí limitarme a la generación de una señal gráfica generada por software utilizando un microcontrolador de 8 bits y 20 megahercios.
Esto no es demasiado, y solo es una solución lo suficientemente poderosa para los gráficos del nivel que me interesaba.
Y así, comencé a usar el microcontrolador Atmega644 a una pureza de 20 MHz para generar una señal de video PAL para el televisor. Tuve que superar el protocolo PAL porque el chip en sí no sabe cómo hacerlo.


El microcontrolador produce un color de 8 bits (RGB332, 3 bits rojo, 3 bits verde y 2 azul) y el DAC pasivo lo convierte todo en RGB. Afortunadamente en Portugal, casi todos los televisores están equipados con un conector SCART y admiten entrada RGB.
El subsistema gráfico correcto
Como el microcontrolador es bastante potente y decidí usarlo exclusivamente para generar una señal de video (lo llamé VPU - Unidad de procesamiento de video), decidí organizar un doble buffer al mismo tiempo.
Resultó que el segundo microcontrolador (PPU, unidad de procesamiento de imagen, chip Atmega1284 también a 20 MHz) generó una imagen en el chip RAM 1 (lo llamé VRAM1), y el primero envió el contenido del segundo chip (VRAM2) al televisor al mismo tiempo.
Después de un cuadro, y dos cuadros en el sistema PAL son 1/25 de segundo, la VPU cambia las VRAM y se intercambian, la PPU genera una imagen en VRAM2, y la VPU descarga VRAM1 a la salida de TV.
La tarjeta de video resultó ser muy complicada porque tuve que usar hardware externo para que ambos microcontroladores pudieran usar ambos módulos de memoria y acelerar el acceso a la RAM, ya que también tiene golpes de bits, así que tuve que agregar 74 chips de la serie como contadores, selectores de línea, transceptores, etc. .
El firmware para VPU y PPU también resultó ser engorroso porque tuve que escribir mucho código para obtener la máxima velocidad de los gráficos. Al principio todo fue escrito en ensamblador, luego parte fue reescrita en C.


Como resultado, la PPU genera una imagen de 224x192 píxeles, que luego se envía al televisor a través de la VPU. Puede encontrar baja la resolución, pero en realidad es casi tanto como las consolas de la época, generalmente 256x224. Una resolución ligeramente inferior, pero me permitió agregar más funciones que el sistema logra calcular en un cuadro.
Como en los viejos tiempos, PPU tiene su propia mecánica rígida que debe poder usar. El respaldo (respaldo) se representa a partir de caracteres de 8x8 píxeles, también llamados mosaicos. Resulta que el tamaño del fondo es de 28x24 mosaicos.
Para que el respaldo se pueda desplazar suavemente, píxel por píxel, lo hice, hay 4 pantallas virtuales, cada una de 28x24 mosaicos que se almacenan secuencialmente en memoria y se envuelven entre sí, en la imagen es más clara.


En la parte superior del fondo, PPU puede representar 64 sprites que pueden tener 8 o 16 píxeles de alto o ancho, es decir, 1, 2 o 4 mosaicos y también se pueden voltear horizontal y / o verticalmente.
En la parte superior de la parte posterior, también puede renderizar una superposición con un tamaño de mosaico de 28x6 búfer, esto estaba destinado a renderizar HUD, puntajes para no interferir con los sprites principales y el desplazamiento de la parte posterior.
Una característica "avanzada" es que el respaldo puede desplazarse no completamente, sino cada línea por separado, lo que permite todo tipo de efectos interesantes como pantallas divididas o casi paralaje .
También hay una tabla de atributos que le permite establecer cada mosaico en un valor de 0 a 3, y luego puede especificar una página de mosaicos para todos los mosaicos con un atributo o incrementar su valor simbólico. Esto es conveniente cuando hay partes de la copia de seguridad que deben cambiarse regularmente y la CPU no tiene que calcular cada mosaico individualmente, es suficiente decir algo como: "todos los mosaicos con el atributo 1, incrementa el valor numérico de tu personaje en 2", tales cosas pueden implementarse mediante diferentes técnicas Observe, por ejemplo, en bloques de bloques en Mario donde el signo de interrogación está animado o en juegos donde hay una cascada en la que todos los cuadros cambian constantemente creando el efecto de la caída del agua.
CPU
Cuando mi tarjeta de video funcionó, comencé a trabajar con la CPU como Zilog 80 para mi decodificador.
Una de las razones por las que se eligió el Z80, bueno, además del hecho de que es una CPU retro genial, es su capacidad para abordar dos espacios de 16 bits, uno para memoria y el segundo para puertos de E / S, no menos legendario 6502 , por ejemplo, no puede , solo puede direccionar un espacio de 16 bits, y debe asignarlo a la memoria, así como a varios dispositivos externos, video, audio, joysticks, generador de números aleatorios de hardware, etc. Es más conveniente tener dos espacios de direcciones, uno con hasta 64 kilobytes de código y datos en la memoria y el segundo para acceder a dispositivos externos.
Primero, conecté la CPU a la EEPROM en la que se encontraba mi programa de prueba y también lo conecté a través del espacio de E / S al microcontrolador que instalé para poder comunicarme con mi computadora a través de RS232 y monitorear cómo funcionaba la CPU y todo lo demás. Este microcontrolador Atmega324 que funciona a 20 MHz lo llamo IO MCU - unidad de microcontrolador de entrada / salida, es responsable de controlar el acceso a los controladores de juego (joysticks), lector de tarjetas SD, teclado PS / 2 y comunicador a través de RS232.

La CPU se conecta a un chip de memoria de 128 kilobytes, de los cuales solo 56 kilobytes están disponibles, esto por supuesto no tiene sentido, pero podría obtener solo chips de 128 o 32 kilobytes. Resultó que la memoria consta de 8 kilobytes de ROM y 56 kilobytes de RAM.
Después de eso, actualicé el firmware de IO MCU usando esta biblioteca y obtuve soporte para lectores de tarjetas SD.
Ahora la CPU podría revisar los directorios, ver qué hay en ellos, abrir y leer archivos. Todo esto se hace escribiendo y leyendo direcciones específicas en el espacio de E / S.
Conecte la CPU a la PPU
Lo siguiente que he hecho es la conexión entre la CPU y la PPU. Para hacer esto, apliqué una "solución simple" que era comprar RAM de doble puerto, este es un chip RAM que se puede conectar directamente a dos buses diferentes. Esto le permite deshacerse de chips adicionales como selectores de línea y, además, permite un acceso casi simultáneo a la memoria desde ambos chips. Otra PPU puede acceder directamente a la CPU en cada cuadro activando sus interrupciones no enmascarables . Resulta que la CPU recibe una interrupción en cada cuadro, lo cual es útil para varias tareas de temporización y para comprender cuándo es el momento de hacer una actualización de gráficos.
Cada cuadro de interacción de la CPU, PPU y VPU se produce de acuerdo con el siguiente esquema:
- La PPU copia información de la memoria PPU a la memoria interna.
- La PPU envía una señal de interrupción a la CPU.
- Al mismo tiempo:
- La CPU salta a la función de interrupción y comienza a actualizar la memoria PPU con un nuevo estado de gráficos. El programa debe regresar desde la interrupción hasta el siguiente bloque.
- PPU renderiza una imagen basada en información previamente copiada a una de VRAM.
- VPU envía una imagen desde otra VRAM a la salida de TV.
Casi al mismo tiempo, comencé a admitir controladores de juegos, al principio quería usar los controladores de Nintendo, pero sus enchufes son propietarios y generalmente difíciles de encontrar, así que me decidí por controladores de 6 botones compatibles con Mega Drive / Genesis, tienen enchufes DB-9 estándar. que están en todas partes

Escribiendo el primer juego real
En este momento, ya tenía una CPU capaz de controlar PPU, trabajar con joysticks, leer tarjetas SD ... era hora de escribir el primer juego , por supuesto en el ensamblador Z80, me tomó varios días de tiempo libre.
Agregar gráficos dinámicos
Todo estaba súper, tenía mi propia consola de juegos, pero esto no era suficiente para mí, porque en el juego tenía que usar gráficos cosidos en la memoria PPU y era imposible dibujar mosaicos para un juego específico y solo se podía cambiar al actualizar la ROM. Comencé a pensar en cómo agregar más memoria para que la CPU pudiera cargar caracteres para los mosaicos, y luego la PPU podría leerlo todo desde allí y cómo hacerlo más fácil porque el prefijo ya era complicado y grande.
Y se me ocurrió lo siguiente: solo la PPU tendrá acceso a esta nueva memoria, y la CPU cargará datos allí a través de la PPU y, mientras este proceso de carga está en progreso, esta memoria no se puede usar para dibujar, pero será posible extraerla de la ROM en este momento.
Después del final de la carga, la CPU cambiará la memoria ROM interna a esta nueva memoria, a la que llamé Character RAM (CHR-RAM) y, en este modo, la PPU comenzará a dibujar gráficos dinámicos, esta probablemente no sea la mejor solución, pero funciona. Como resultado, se instaló una nueva memoria de 128 kilobytes y puede almacenar 1024 caracteres de 8x8 píxeles cada uno para el fondo y la misma cantidad de caracteres para los sprites.

Y finalmente el sonido
Las manos alcanzaron el último sonido. Al principio, quería un sonido como el de Uzebox , es decir, que el microcontrolador genera 4 canales de sonido PWM.
Sin embargo, resultó que puedo obtener fácilmente los chips antiguos y pedí varios chips de síntesis FM YM3438, estos tipos son totalmente compatibles con el YM2612 utilizado en Mega Drive / Genesis. Al instalarlos, puede obtener música Mega Drive de calidad y efectos de sonido producidos por el microcontrolador.
Instalé otro microcontrolador y lo llamé SPU (Unidad de procesador de sonido), controla el YM3438 y puedo generar sonidos yo mismo. La CPU lo controla a través de una memoria de doble puerto, esta vez solo pesa 2 kilobytes.
Al igual que en la unidad gráfica, la unidad de sonido tiene 128 kilobytes de memoria para almacenar muestras de PCM y parches de sonido, la CPU carga datos en esta memoria accediendo a la SPU. Resultó que la CPU le dice a la SPU que ejecute comandos desde esta memoria o actualiza los comandos para la SPU en cada cuadro.
La CPU controla cuatro canales PWM a través de cuatro memorias intermedias circulares en la memoria de la SPU. La SPU pasa por estos búferes y ejecuta los comandos escritos en ellos. También hay un búfer para el chip de síntesis FM.
En total, como en el gráfico, la interacción entre la CPU y la SPU va de acuerdo con el esquema:
- La SPU copia datos de la SPU a la memoria interna.
- SPU está esperando una interrupción de PPU (esto es para sincronización)
- Al mismo tiempo
- La CPU actualiza los búferes de canal PWM y los búferes de sintetizador FM.
- La SPU ejecuta comandos en memorias intermedias según los datos en la memoria interna.
- Junto con todo esto, la SPU actualiza los sonidos PWM a una frecuencia de 16 kilohercios.

Lo que salió al final
Después de que todos los bloques estuvieron listos, algunos fueron al tablero.
Para el bloque de CPU, pude desarrollar y ordenar una PCB personalizada, no sé si vale la pena para los otros módulos, creo que tuve mucha suerte de que mi PCB funcionara de inmediato.
En el tablero ahora (hasta ahora) solo hay sonido.
Así es como se ve hoy:

Arquitectura
El diagrama ilustra los componentes en cada bloque y cómo interactúan entre sí. Lo único que no se muestra es la señal de la PPU a la CPU en cada cuadro como una interrupción y la misma señal que va a la SPU.

- CPU: Zilog Z80 a 10 MHz
- CPU-ROM: EEPROM de 8 KB, contiene el código del gestor de arranque
- CPU-RAM: 128 KB de RAM (56 KB disponibles), código y datos para programas / juegos
- IO MCU: Atmega324, es la interfaz entre la CPU y RS232, el teclado PS / 2, joysticks y el sistema de archivos de la tarjeta SD
- PPU-RAM: 4 kilobytes de memoria de doble puerto, memoria intermedia entre CPU y PPU
- CHR-RAM: 128 KB de RAM, almacena mosaicos dinámicos para respaldo (sustrato) y sprites (en caracteres de 8x8 píxeles).
- VRAM1, VRAM2: 128 KB de RAM (43008 está realmente disponible), se usan para el framebuffer, escriben PPU y leen VPU de ellos.
- PPU (Unidad de procesamiento de imágenes): Atmega1284, dibuja un marco en el framebuffer.
- VPU (Unidad de procesamiento de video): Atmega324, lee el framebuffer y genera señales RGB y PAL y sincronización.
- SPU-RAM: RAM de doble puerto de 2 KB, sirve como interfaz entre la CPU y la SPU.
- SNDRAM: 128 KB de RAM, almacena parches PWM, muestras PCM y bloques de instrucciones para el sintetizador FM.
- YM3438: YM3438, chip de síntesis FM.
- SPU (Unidad de procesamiento de sonido): Atmega644, genera sonidos utilizando el principio de modulación de ancho de pulso (PWM) y controla el YM3438.
Especificaciones finales
CPU:
- CPU Zilog Z80 de 8 bits a una frecuencia de 10Mhz.
- ROM de 8 KB para el gestor de arranque.
- 56 KB de RAM.
IO:
- Lectura de datos del lector de tarjetas SD FAT16 / FAT32.
- Leer / escribir en el puerto RS232.
- 2 controladores de juegos compatibles con MegaDrive / Genesis.
- Teclado PS2.
Video:
- Resolución 224x192 píxeles.
- 25 cuadros por segundo (medio FPS de PAL).
- 256 colores (RGB332).
- Fondo virtual 2x2 (448x384 píxeles), con desplazamiento bidireccional basado en píxeles, basado en cuatro páginas de pantalla completa.
- 64 sprites con un ancho y una altura de 8 o 16 píxeles con la posibilidad de voltear tanto vertical como horizontalmente.
- El fondo y los sprites consisten en caracteres de 8x8 píxeles cada uno.
- Memoria simbólica de video de 1024 caracteres para el fondo y 1024 para sprites.
- 64 desplazamiento horizontal independiente a lo largo de las líneas establecidas
- 8 desplazamientos verticales independientes a lo largo de las líneas establecidas
- Superposición de 224x48 píxeles con transparencia de clave de color opcional.
- Tabla de atributos de fondo.
- RGB y PAL compuesto a través del conector SCART.
Sonido:
- PWM para 8 bits y 4 canales, con formas de onda integradas: cuadrada, senoidal, sierra, ruido, etc.
- Muestras de 8 bits y 8 kHz en uno de los canales PWM.
- Chip de síntesis FM YM3438 cargado con instrucciones a una frecuencia de 50 hertzios.
Desarrollo para la consola.
Para la consola, se escribió un gestor de arranque. El cargador de arranque se coloca en la CPU ROM y puede tomar hasta 8 kilobytes. Utiliza los primeros 256 bytes de RAM. El cargador es lo primero que ejecuta la CPU. Es necesario para mostrar los programas ubicados en la tarjeta SD.
Estos programas están en archivos que contienen código compilado y también pueden contener gráficos y sonido.
Después de seleccionar un programa, se carga en la memoria de la CPU, la memoria CHR y la memoria SPU. Después de lo cual se ejecuta el código del programa. El tamaño máximo del código cargado en la consola es de 56 kilobytes, además de los primeros 256 bytes, y, por supuesto, debe tener en cuenta el espacio para la pila y los datos.
Y este gestor de arranque y otros programas escritos para esta consola se crearon de la misma manera que se describe a continuación.
Mapeo de memoria / IO
Lo importante al desarrollar este prefijo es tener en cuenta cómo la CPU accede a los diversos bloques y asignar correctamente el espacio de direcciones para la entrada de entrada y el espacio de direcciones de memoria.
La CPU accede a la memoria de acceso aleatorio del cargador de arranque a través del espacio de direcciones de la memoria.
Espacio de direcciones de memoria

Y a PPU-RAM, SPU-RAM e IO MCU a través del espacio de direcciones de E / S.
Espacio de dirección de E / S

Como puede ver en la tabla, las direcciones para todos los dispositivos, IO MCU, PPU y SPU se asignan dentro del espacio de direcciones de E / S.
Gestión de PPU
De la información en la tabla se puede ver que para el control de PPU es necesario escribir en la memoria de PPU que está disponible en las direcciones 1000h-1FFFh en el espacio de direcciones de E / S.
Asignación de espacio de direcciones PPU

El estado de PPU puede tomar los siguientes valores:
- Modo de gráficos integrados
- Modo de gráficos dinámicos (CHR-RAM)
- Modo de grabación en memoria CHR
- La grabación se completa, esperando la confirmación del modo de la CPU
Aquí, por ejemplo, cómo puedes trabajar con sprites:
El prefijo puede dibujar 64 sprites a la vez. CPU - 1004h-1143h (320 ), 5 (5 * 64 = 320):
- , : Active, Flipped_X, Flipped_Y, PageBit0, PageBit1, AboveOverlay, Width16, Height16.
- , ( ).
- ( — )
- X
- Y
, , Active 1, X Y , 32/32 , .
.
Por ejemplo, si necesitamos mostrar el número de sprite 10, entonces la dirección será 4145 (1004h + (5 x 9)), escriba el valor 1 para la activación y las coordenadas, por ejemplo, x = 100 e y = 120, escriba el valor 100 en la dirección 4148 y dirección 4149 valor 120.
Usando ensamblador
Uno de los métodos de programación para la consola es el ensamblador.
Aquí hay un ejemplo de cómo mostrar un sprite y animarlo para que se mueva y empuje los bordes de la pantalla.
ORG 2100h PPU_SPRITES: EQU $1004 SPRITE_CHR: EQU 72 SPRITE_COLORKEY: EQU $1F SPRITE_INIT_POS_X: EQU 140 SPRITE_INIT_POS_Y: EQU 124 jp main DS $2166-$ nmi: ; (NMI) ld bc, PPU_SPRITES + 3 ld a, (sprite_dir) and a, 1 jr z, subX in a, (c) ; X inc a out (c), a cp 248 jr nz, updateY ld a, (sprite_dir) xor a, 1 ld (sprite_dir), a jp updateY subX: in a, (c) ; X dec a out (c), a cp 32 jr nz, updateY ld a, (sprite_dir) xor a, 1 ld (sprite_dir), a updateY: inc bc ld a, (sprite_dir) and a, 2 jr z, subY in a, (c) ; Y inc a out (c), a cp 216 jr nz, moveEnd ld a, (sprite_dir) xor a, 2 ld (sprite_dir), a jp moveEnd subY: in a, (c) ; Y dec a out (c), a cp 32 jr nz, moveEnd ld a, (sprite_dir) xor a, 2 ld (sprite_dir), a moveEnd: ret main: ld bc, PPU_SPRITES ld a, 1 out (c), a ; 0 inc bc ld a, SPRITE_CHR out (c), a ; 0 inc bc ld a, SPRITE_COLORKEY out (c), a ; 0 inc bc ld a, SPRITE_INIT_POS_X out (c), a ; 0 inc bc ld a, SPRITE_INIT_POS_Y out (c), a ; Y 0 mainLoop: jp mainLoop sprite_dir: DB 0
Usando lenguaje C
También puede usar el lenguaje C, para esto necesitamos el compilador SDCC y algunas utilidades adicionales.
El código C puede ser más lento, pero escribirlo es más rápido y fácil.
Aquí hay un ejemplo de código que hace lo mismo que el código de ensamblador anterior, usa una biblioteca que ayuda a realizar llamadas a PPU:
#include <console.h> #define SPRITE_CHR 72 #define SPRITE_COLORKEY 0x1F #define SPRITE_INIT_POS_X 140 #define SPRITE_INIT_POS_Y 124 struct s_sprite sprite = { 1, SPRITE_CHR, SPRITE_COLORKEY, SPRITE_INIT_POS_X, SPRITE_INIT_POS_Y }; uint8_t sprite_dir = 0; void nmi() { if (sprite_dir & 1) { sprite.x++; if (sprite.x == 248) { sprite_dir ^= 1; } } else { sprite.x--; if (sprite.x == 32) { sprite_dir ^= 1; } } if (sprite_dir & 2) { sprite.y++; if (sprite.y == 216) { sprite_dir ^= 2; } } else { sprite.y--; if (sprite.x == 32) { sprite_dir ^= 2; } } set_sprite(0, sprite); } void main() { while(1) { } }
Gráficos dinámicos
(En los gráficos personalizados originales. Aprox. Por.)
En la ROM del prefijo, se cosen 1 página de mosaicos para respaldo y otra página de sprites listos para usar), por defecto solo puede usar estos gráficos fijos, pero puede cambiar a dinámico.
Mi objetivo era tal que todos los gráficos necesarios en forma binaria se cargaron inmediatamente en la RAM de CHR, y el código en el gestor de arranque desde la ROM puede hacer esto. Para hacer esto, hice varias imágenes del tamaño correcto con diferentes símbolos útiles:

Como la memoria de gráficos dinámicos consta de 4 páginas con 256 caracteres de 8x8 píxeles cada una y 4 páginas de los mismos caracteres para sprites, convertí las imágenes a formato PNG, borré las duplicadas:

Y luego usó una herramienta auto escrita para traducirlo todo al formato binario RGB332 con bloques de 8x8.

Como resultado, tenemos archivos con caracteres, donde todos los caracteres van secuencialmente uno tras otro y cada uno ocupa 64 bytes.
Sonido
Muestras RAW de onda convertidas en muestras PCM de 8 bits y 8 kilohercios.
Los parches para efectos de sonido en PWM y música se escriben con instrucciones especiales.
En cuanto al chip de síntesis Yamaha YM3438 FM, encontré un programa llamado DefleMask que produce música sincronizada PAL para el chip Genesis YM2612, que es compatible con el YM3438.
DefleMask exporta música en formato VGM y la convierto con otra utilidad patentada en mi propio formato binario.
Todos los archivos binarios de los tres tipos de sonido se combinan en un archivo binario, que mi gestor de arranque puede leer y cargar en la memoria de sonido RAM SDN.

Enlace al archivo final
El código ejecutable binario, los gráficos y el sonido se combinan en un solo archivo PRG. El archivo PRG tiene un encabezado en el que se describe todo si hay datos de audio y gráficos, cuánto ocupan y los datos en sí.
Dicho archivo puede escribirse en una tarjeta SD, y el gestor de arranque de la consola lo considera y descarga todo en los lugares apropiados y lanza el código ejecutable del programa.

Emulador
Escribí un emulador de mi consola en C ++ usando wxWidgets para que sea más fácil desarrollarlo.
La CPU es emulada por la biblioteca libz80 .
Se han agregado características al emulador para la depuración, puedo detenerlo en cualquier momento y hacer una depuración paso a paso del ensamblador, hay una asignación al código fuente en C. si este lenguaje se usó para el juego.
Según el gráfico, puedo mirar en la memoria de video, en las tablas de símbolos y en la memoria CHR misma.
Aquí hay un ejemplo de un programa que se ejecuta en un emulador con las herramientas de depuración activadas.

Demo de programación
Estos videos fueron grabados con la cámara de un teléfono inteligente dirigida a la pantalla CRT del televisor, pido disculpas por la calidad de imagen imperfecta.
El intérprete BASIC programable desde el teclado PS / 2, después del primer programa, muestro cómo escribir directamente en la memoria PPU a través del espacio de direcciones de E / S activando y moviendo el sprite:
Una demostración de gráficos, en este video, descarga programáticamente 64 sprites de 16x16, en el contexto de un fondo con desplazamiento dinámico y una superposición que se mueve por debajo y por encima de los sprites:
La demostración de sonido muestra las capacidades del sonido YM3438 y PWM, los datos de sonido de esta demostración y la música FM y los sonidos PWM juntos ocupan casi todos los 128 kilobytes de memoria de sonido disponibles.
Tetris, casi exclusivamente las características de fondo, la música en el YM3438, los efectos de sonido en los parches PWM se utilizaron para los gráficos.
Conclusión
Este proyecto es realmente un sueño hecho realidad, he estado trabajando en él durante varios años, con interrupciones, mirando mi tiempo libre, nunca pensé que llegaría tan lejos en la creación de mi propia consola de videojuegos retro. Naturalmente, no es perfecto, ciertamente no soy un experto en electrónica, obviamente había demasiados elementos en el decodificador, y sin duda podría hacerse mejor, y probablemente uno de los lectores solo está pensando en ello.
Pero aún así, en el proceso de trabajar en este proyecto, aprendí mucho sobre electrónica, consolas de juegos y diseño de computadoras, lenguaje ensamblador y otras cosas interesantes, y lo más importante, recibí una gran satisfacción al jugar juegos que yo mismo escribí en hardware que yo mismo desarrollé. y recogido.
Tengo planes de hacer consolas / computadoras y más. En realidad, ya estoy haciendo un nuevo decodificador, está casi listo, y es un decodificador retro simplificado basado en una placa FPGA y varios componentes adicionales (en una cantidad mucho menor que en este proyecto, sin duda), la idea es ser mucho más barata y más repetible.
Aunque escribí mucho sobre este proyecto aquí, sin duda se puede discutir mucho más, apenas mencioné cómo funciona el motor de sonido, cómo interactúa la CPU con él y hay mucho más que se puede hacer sobre el sistema gráfico y otras entradas / salidas y toda la consola Sería para contarlo.
Mirando la reacción de los lectores, puedo escribir más artículos centrados en actualizaciones, detalles sobre bloques de prefijos individuales u otros proyectos.
Proyectos, sitios, canales de Youtube que me inspiraron y me ayudaron con conocimientos técnicos:
Estos sitios / canales no solo me inspiraron, sino que también me ayudaron a encontrar soluciones a problemas complejos que surgieron durante el trabajo en este proyecto.
Gracias por leer hasta aquí. :)
Si tiene preguntas o comentarios, escriba los comentarios a continuación (Artículo original en inglés en Github. Aprox. Per.)