
Cet article est une introduction à mon projet de consoles vidéo de console "maison" faites à partir de zéro. Je me suis inspiré à la fois des consoles rétro et des échantillons modernes, mais j'ai ma propre architecture. Mes amis me disaient constamment que je devais parler de mon projet, et ne pas tout faire exclusivement "pour moi", donc ici je publie ce post.
Attention, ceci est une traduction
Comment tout a commencé
Je m'appelle Sergio Vieira, j'ai grandi au Portugal dans les années 80 et 90, j'ai une longue nostalgie du jeu rétro, en particulier pour les consoles de troisième et quatrième génération.
Il y a quelques années, j'ai décidé de mieux comprendre l'électronique et d'essayer de créer mon propre préfixe.
De profession, je suis programmeur et je n'avais aucune expérience en tant qu'ingénieur en électronique, à l'exception (et ne devrait pas être considéré) des mises à niveau indépendantes de mon destkop.
Même si je n'avais aucune expérience, je me suis dit "pourquoi pas?", J'ai acheté plusieurs livres, plusieurs kits électroniques et j'ai commencé à étudier en fonction de mes sentiments sur ce qu'il valait la peine d'étudier.
Je voulais faire un préfixe similaire à ceux qui me rendent nostalgique, je voulais quelque chose entre NES et Super Nintendo , ou peut-être entre le Sega Master System et Mega Drive .
Ces consoles avaient un CPU, une puce vidéo d'origine (on ne les appelait pas alors GPU) et une puce audio, parfois intégrée, parfois externe.
Les jeux étaient distribués sur des cartouches, qui étaient en général des extensions de fer, parfois juste des puces ROM, et parfois avec des composants supplémentaires.
Le plan initial était de créer un préfixe avec les caractéristiques suivantes:
- Sans émulation, les jeux et les programmes devraient fonctionner sur du vrai matériel, pas nécessairement le même à cette époque, mais assez rapide pour la tâche, et rien de plus.
- Avec un vrai CPU rétro.
- Avec sortie TV analogique.
- Avec son
- Avec prise en charge double contrĂ´leur
- Becks défilants et sprites d'animation
- Avec des fonctionnalités pour prendre en charge les jeux de plate-forme comme Mario et bien sûr toutes sortes d'autres jeux.
- Avec le téléchargement de jeux et de programmes à partir de cartes SD.
Pourquoi les cartes SD et non les cartouches, eh bien, fondamentalement, c'est tellement plus pratique que vous pouvez les copier depuis votre ordinateur. Et les cartouches signifieraient, d'une part, plus de fer dans le décodeur, et d'autre part, de produire du fer pour chaque programme.
La production
Signal vidéo
La première chose que j'ai faite a été de générer un signal vidéo.
Toute console de la période que j'ai prise comme échantillon avait diverses puces graphiques propriétaires, ce qui signifie qu'elles avaient toutes des spécifications différentes.
Pour cette raison, je ne voulais pas utiliser une puce graphique prête à l'emploi, je voulais que ma console ait des spécifications graphiques uniques. Et comme je ne pouvais pas créer ma propre puce graphique et à ce moment-là , je ne pouvais toujours pas utiliser FPGA, j'ai décidé de me limiter à la génération d'un signal graphique généré par logiciel à l'aide d'un microcontrôleur 8 bits, 20 mégahertz.
Ce n'est pas trop, et juste une solution assez puissante pour les graphismes du niveau qui m'intéressait.
Et donc, j'ai commencé à utiliser le microcontrôleur Atmega644 à une pureté de 20 MHz pour générer un signal vidéo PAL pour le téléviseur. J'ai dû battre le protocole PAL car la puce elle-même ne sait pas comment le faire.


Le microcontrôleur produit une couleur 8 bits (RGB332, 3 bits rouge, 3 bits vert et 2 bleu) et le DAC passif convertit le tout en RVB. Heureusement au Portugal, presque tous les téléviseurs sont équipés d'un connecteur péritel et ils prennent en charge l'entrée RVB.
Le sous-système graphique correct
Étant donné que le microcontrôleur est assez puissant et que j'ai décidé de l'utiliser exclusivement pour générer un signal vidéo (je l'ai appelé VPU - Video Processing Unit), j'ai décidé d'organiser un double tampon en même temps.
Il s'est avéré que le deuxième microcontrôleur (PPU, Picture Processing Unit, puce Atmega1284 également à 20 MHz) a généré une image dans la puce RAM 1 (je l'ai appelée VRAM1), et le premier a envoyé le contenu de la deuxième puce (VRAM2) au téléviseur en même temps.
Après une trame et deux trames dans le système PAL, c'est 1/25 de seconde, la VPU commute les VRAM et elles sont permutées, la PPU génère une image dans VRAM2 et la VPU décharge VRAM1 sur la sortie TV.
La carte vidéo s'est avérée très compliquée parce que je devais utiliser du matériel externe pour que les deux microcontrôleurs puissent utiliser les deux modules de mémoire et accélérer l'accès à la RAM, car elle a également des bangs, donc j'ai dû ajouter 74 puces de la série comme compteurs, sélecteurs de ligne, émetteurs-récepteurs, etc. .
Le firmware pour VPU et PPU s'est également avéré lourd car j'ai dû écrire beaucoup de code pour obtenir la vitesse maximale des graphiques. Au début, tout était écrit en assembleur, puis une partie a été réécrite en C.


En conséquence, le PPU génère une image de 224x192 pixels, qui est ensuite envoyée au téléviseur via VPU. Vous pouvez trouver la résolution faible, mais en fait, c'est presque autant que les consoles de l'époque, en général 256x224. Une résolution légèrement inférieure, mais m'a permis d'ajouter plus de fonctionnalités que le système parvient à calculer dans une seule image.
Comme autrefois, PPU a sa propre mécanique rigide que vous devez pouvoir utiliser. Le support (support) est rendu à partir de caractères 8x8 pixels, également appelés tuiles. Il s'avère que la taille de l'arrière-plan est de 28x24 carreaux.
Pour que le support puisse défiler en douceur, pixel par pixel, je l'ai fait, il y a 4 écrans virtuels, chacun de 28x24 carreaux qui vont en mémoire séquentiellement et sont enroulés les uns sur les autres, dans l'image, c'est plus clair.


En plus de l'arrière-plan, PPU peut rendre 64 sprites qui peuvent avoir une hauteur ou une largeur de 8 ou 16 pixels, c'est-à -dire 1, 2 ou 4 tuiles et peuvent également être retournés horizontalement et / ou verticalement.
En haut du dos, vous pouvez également restituer une superposition avec un tampon de 28x6 carreaux, ce qui était destiné au rendu des HUD, des scores afin de ne pas interférer avec les sprites principaux et le défilement du dos.
Une caractéristique «avancée» est que l'arrière peut être défilé non pas entièrement, mais chaque ligne séparément, ce qui permet toutes sortes d'effets intéressants comme des écrans divisés ou une quasi- parallaxe .
Il existe également une table d'attributs qui vous permet de définir chaque tuile sur une valeur de 0 à 3, puis vous pouvez spécifier une page de tuiles ou incrémenter leur valeur symbolique pour toutes les tuiles avec un attribut. C'est pratique lorsqu'il y a des parties de la sauvegarde qui doivent être changées régulièrement et que le CPU n'a pas à calculer chaque tuile individuellement, il suffit de dire quelque chose comme: "toutes les tuiles avec l'attribut 1, incrémentez la valeur numérique de votre personnage de 2", ces choses peuvent être implémentées par différentes techniques observez, par exemple, dans les tuiles blocs de Mario où le point d'interrogation est animé ou dans les jeux où il y a une cascade dans laquelle toutes les tuiles changent constamment créant l'effet de la chute d'eau.
CPU
Lorsque ma carte vidéo a fonctionné, j'ai commencé à travailler avec le processeur en tant que Zilog 80 pour mon décodeur.
L'une des raisons pour lesquelles le Z80 a été choisi, eh bien, outre le fait qu'il s'agit d'un CPU rétro cool, c'est sa capacité à adresser deux espaces 16 bits, un pour la mémoire et le second pour les ports d'E / S, non moins légendaire 6502 , par exemple, il ne peut pas , il ne peut traiter que l'espace de 16 bits, et vous devez le mapper en mémoire ainsi que divers périphériques externes, vidéo, audio, joysticks, générateur de nombres aléatoires matériels, etc. Il est plus pratique d'avoir deux espaces d'adressage, l'un entièrement remis à 64 kilo-octets de code et de données en mémoire et le second pour l'accès aux périphériques externes.
Tout d'abord, j'ai connecté le processeur à l'EEPROM dans laquelle se trouvait mon programme de test et je l'ai également connecté via l'espace d'E / S au microcontrôleur que j'ai installé pour pouvoir communiquer avec mon ordinateur via RS232 et surveiller le fonctionnement du processeur et de tout le reste. J'appelle ce microcontrôleur Atmega324 fonctionnant à 20 MHz le microcontrôleur IO - une unité de microcontrôleur d'entrée / sortie, il est chargé de contrôler l'accès aux contrôleurs de jeu (joysticks), un lecteur de carte SD, un clavier PS / 2 et un communicateur via RS232.

Le processeur se connecte à une puce de mémoire de 128 kilo-octets, dont seulement 56 kilo-octets sont disponibles, c'est bien sûr un non-sens, mais je n'ai pu obtenir que des puces de 128 ou 32 kilo-octets. Il s'est avéré que la mémoire se compose de 8 kilo-octets de ROM et de 56 kilo-octets de RAM.
Après cela, j'ai mis à jour le micrologiciel IO MCU à l'aide de cette bibliothèque et j'ai pris en charge les lecteurs de carte SD.
Maintenant, le processeur peut parcourir les répertoires, voir ce qui s'y trouve, ouvrir et lire des fichiers. Tout cela se fait en écrivant et en lisant des adresses spécifiques dans l'espace d'E / S.
Connectez le CPU au PPU
La prochaine chose que j'ai faite est la connexion entre le CPU et le PPU. Pour ce faire, j'ai appliqué une "solution simple" qui consistait à acheter de la RAM double port, c'est une telle puce RAM qui peut être connectée directement à deux bus différents. Cela lui permet de se débarrasser de puces supplémentaires comme les sélecteurs de ligne et, de plus, permet un accès presque simultané à la mémoire des deux puces. Un autre PPU peut accéder directement au CPU sur chaque trame en activant ses interruptions non masquables . Il s'avère que le CPU reçoit une interruption sur chaque trame, ce qui est utile pour diverses tâches de synchronisation et pour comprendre quand il est temps de faire une mise à jour graphique.
Chaque trame d'interaction du CPU, PPU et VPU se produit selon le schéma suivant:
- Le PPU copie les informations de la mémoire PPU vers la mémoire interne.
- Le PPU envoie un signal d'interruption au CPU.
- En mĂŞme temps:
- Le CPU passe à la fonction d'interruption et commence à mettre à jour la mémoire PPU avec un nouvel état graphique. Le programme doit revenir de l'interruption au bloc suivant.
- PPU restitue une image basée sur des informations précédemment copiées sur l'un des VRAM.
- VPU envoie une image d'une autre VRAM Ă la sortie TV.
À peu près à la même époque, j'ai commencé à prendre en charge les contrôleurs de jeu, au début, je voulais utiliser des contrôleurs Nintendo, mais leurs prises sont propriétaires et généralement difficiles à trouver, alors j'ai opté pour des contrôleurs à 6 boutons compatibles avec Mega Drive / Genesis, ils ont des prises DB-9 standard qui sont partout.

Écrire le premier vrai jeu
A cette époque, j'avais déjà un CPU capable de contrôler PPU, de travailler avec des joysticks, de lire des cartes SD ... il était temps d' écrire le premier jeu , bien sûr en assembleur Z80, cela m'a pris plusieurs jours de temps libre.
Ajouter des graphiques dynamiques
Tout était super, j'avais ma propre console de jeu, mais cela ne me suffisait pas, car dans le jeu, je devais utiliser les graphiques cousus dans la mémoire PPU et il était impossible de dessiner des tuiles pour un jeu spécifique et il était possible de le changer uniquement en reflasher la ROM. J'ai commencé à réfléchir à la façon d'ajouter plus de mémoire pour que le CPU puisse y charger des caractères pour les tuiles, puis le PPU pourrait alors tout lire à partir de là et comment le faire plus facilement car le préfixe s'est déjà révélé compliqué et volumineux.
Et j'ai trouvé ce qui suit: seul le PPU aura accès à cette nouvelle mémoire, et le CPU y chargera des données via le PPU et pendant que ce processus de chargement est en cours, cette mémoire ne peut pas être utilisée pour le dessin, mais il sera possible de dessiner à partir de la ROM à ce moment.
Après la fin du chargement, le CPU basculera la mémoire ROM interne vers cette nouvelle mémoire, que j'ai appelée RAM de caractères (CHR-RAM) et dans ce mode, le PPU commencera à dessiner des graphiques dynamiques, ce n'est probablement pas la meilleure solution, mais cela fonctionne. En conséquence, une nouvelle mémoire a été installée à 128 kilo-octets et peut stocker 1024 caractères de 8x8 pixels chacun pour l'arrière-plan et le même nombre de caractères pour les sprites.

Et enfin le son
Les mains atteignirent le son en dernier. Au début, je voulais un son comme celui de Uzebox , c'est-à -dire que le microcontrôleur génère 4 canaux de son PWM.
Cependant, il s'est avéré que je peux facilement obtenir les puces vintage et j'ai commandé plusieurs puces de synthèse FM YM3438, ces gars-là sont entièrement compatibles avec le YM2612 utilisé dans Mega Drive / Genesis. En les installant, vous pouvez obtenir une musique de qualité Mega Drive et des effets sonores produits par le microcontrôleur.
J'ai installé un autre microcontrôleur et je l'ai appelé SPU (Sound Processor Unit), il contrôle le YM3438 et peut générer des sons moi-même. Le CPU le contrôle via une mémoire à double port, cette fois, il ne fait que 2 kilo-octets.
Comme dans l'unité graphique, l'unité sonore dispose de 128 kilo-octets de mémoire pour stocker des échantillons PCM et des patchs sonores, le CPU charge les données dans cette mémoire en accédant au SPU. Il s'est avéré que le CPU dit au SPU d'exécuter des commandes à partir de cette mémoire ou met à jour les commandes du SPU à chaque trame.
Le CPU contrôle quatre canaux PWM via quatre tampons circulaires dans la mémoire SPU. Le SPU passe par ces tampons et exécute les commandes qui leur sont écrites. Il existe également un tel tampon pour la puce de synthèse FM.
Au total, comme dans le graphique, l'interaction entre le CPU et le SPU se déroule selon le schéma:
- Le SPU copie les données du SPU dans la mémoire interne.
- SPU attend une interruption de PPU (c'est pour la synchronisation)
- En mĂŞme temps
- Le CPU met à jour les tampons de canaux PWM et les tampons de synthétiseur FM.
- Le SPU exécute les commandes dans les tampons en fonction des données de la mémoire interne.
- Parallèlement à tout cela, le SPU met à jour les sons PWM à une fréquence de 16 kilohertz.

Ce qui est finalement sorti
Une fois tous les blocs prêts, certains sont allés à la planche à pain.
Pour le bloc CPU, j'ai pu développer et commander un PCB personnalisé, je ne sais pas si cela en vaut la peine pour les autres modules, je pense que j'ai vraiment eu de la chance que mon PCB fonctionne immédiatement.
Sur la maquette maintenant (jusqu'à présent), il n'y a que du son.
Voici Ă quoi cela ressemble aujourd'hui:

L'architecture
Le diagramme illustre les composants de chaque bloc et comment ils interagissent les uns avec les autres. La seule chose qui n'est pas montrée est le signal du PPU au CPU sur chaque trame comme une interruption et le même signal qui va au SPU.

- CPU: Zilog Z80 Ă 10 MHz
- CPU-ROM: 8 Ko EEPROM, contient le code du chargeur de démarrage
- CPU-RAM: 128 Ko de RAM (56 Ko disponibles), code et données pour les programmes / jeux
- IO MCU: Atmega324, est l'interface entre le CPU et RS232, le clavier PS / 2, les joysticks et le système de fichiers de la carte SD
- PPU-RAM: 4 kilo-octets de mémoire à double port, mémoire intermédiaire entre CPU et PPU
- CHR-RAM: 128 Ko de RAM, stocke les tuiles dynamiques pour le support (substrat) et les sprites (en caractères de 8x8 pixels).
- VRAM1, VRAM2: 128 Ko de RAM (43008 est vraiment disponible), ils sont utilisés pour le framebuffer, ils écrivent PPU et lisent VPU d'eux.
- PPU (Picture Processing Unit): Atmega1284, dessine un cadre dans le framebuffer.
- VPU (Video Processing Unit): Atmega324, lit le framebuffer et génère un signal RGB et PAL et une synchronisation.
- SPU-RAM: 2 Ko de RAM Ă double port, sert d'interface entre le CPU et le SPU.
- SNDRAM: 128 Ko de RAM, stocke les patchs PWM, les échantillons PCM et les blocs d'instructions pour le synthétiseur FM.
- YM3438: YM3438, puce de synthèse FM.
- SPU (Sound Processing Unit): Atmega644, génère des sons en utilisant le principe de la modulation de largeur d'impulsion (PWM) et contrôle le YM3438.
Spécifications finales
CPU:
- CPU 8 bits Zilog Z80 à une fréquence de 10Mhz.
- ROM de 8 Ko pour le chargeur de démarrage.
- 56 Ko de RAM.
IO:
- Lecture des données du lecteur de carte SD FAT16 / FAT32.
- Lecture / écriture sur le port RS232.
- 2 contrĂ´leurs de jeu compatibles MegaDrive / Genesis.
- Clavier PS2.
Vidéo:
- Résolution 224x192 pixels.
- 25 images par seconde (demi-FPS de PAL).
- 256 couleurs (RGB332).
- Fond virtuel 2x2 (448x384 pixels), avec défilement bidirectionnel basé sur les pixels, basé sur quatre pages plein écran.
- 64 sprites d'une largeur et hauteur de 8 ou 16 pixels avec possibilité de retournement vertical et horizontal.
- Le fond et les sprites sont constitués de caractères de 8x8 pixels chacun.
- Mémoire vidéo symbolique de 1024 caractères pour l'arrière-plan et 1024 pour les sprites.
- 64 défilement horizontal indépendant le long de lignes définies
- 8 défilement vertical indépendant le long de lignes fixes
- Superposition de 224 x 48 pixels avec transparence des touches de couleur en option.
- Tableau d'attributs d'arrière-plan.
- RGB et PAL composite via le connecteur SCART.
Son:
- PWM pour 8 bits et 4 canaux, avec formes d'onde intégrées: carré, sinus, scie, bruit, etc.
- Échantillons 8 bits, 8 kHz dans l'un des canaux PWM.
- Puce de synthèse FM YM3438 chargée d'instructions à une fréquence de 50 hertz.
Développement pour la console
Pour la console, un chargeur de démarrage a été écrit. Le chargeur de démarrage est placé dans la CPU ROM et peut prendre jusqu'à 8 kilo-octets. Il utilise les 256 premiers octets de RAM. Le chargeur est la première chose que le CPU exécute. Il est nécessaire d'afficher les programmes situés sur la carte SD.
Ces programmes se trouvent dans des fichiers qui contiennent du code compilé et peuvent également contenir des graphiques et du son.
Après avoir sélectionné un programme, il est chargé dans la mémoire CPU, la mémoire CHR et la mémoire SPU. Après quoi le code du programme est exécuté. La taille maximale du code chargé dans la console est de 56 kilo-octets, à l'exception des 256 premiers octets, et bien sûr, vous devez prendre en compte l'espace pour la pile et les données.
Et ce chargeur de démarrage et d'autres programmes écrits pour cette console ont été créés de la même manière décrite ci-dessous.
Mappage mémoire / E / S
Ce qui est important lors du développement de ce préfixe est de prendre en compte la façon dont le CPU accède aux différents blocs et d'allouer correctement l'espace adresse pour l'entrée d'entrée et l'espace adresse mémoire.
Le CPU accède à la mémoire à accès aléatoire du chargeur de démarrage via l'espace d'adressage de la mémoire.
Espace d'adressage mémoire

Et vers PPU-RAM, SPU-RAM et IO MCU via l'espace d'adressage d'E / S.
Espace d'adressage d'E / S

Comme vous pouvez le voir dans le tableau, les adresses de tous les périphériques, IO MCU, PPU et SPU sont allouées à l'intérieur de l'espace d'adressage d'E / S.
Gestion PPU
D'après les informations du tableau, on peut voir que pour le contrôle PPU, il est nécessaire d'écrire dans la mémoire PPU qui est disponible aux adresses 1000h-1FFFh dans l'espace adresse E / S.
Allocation d'espace d'adressage PPU

Le statut PPU peut prendre les valeurs suivantes:
- Mode graphique intégré
- Mode graphique dynamique (CHR-RAM)
- Mode d'enregistrement dans la mémoire CHR
- L'enregistrement est terminé, en attendant la confirmation du mode de la CPU
Voici, par exemple, comment vous pouvez travailler avec les sprites:
Le préfixe peut dessiner 64 sprites à la fois. 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 , .
.
Par exemple, si nous devons afficher le sprite numéro 10, alors l'adresse sera 4145 (1004h + (5 x 9)), écrivez la valeur 1 pour l'activation et les coordonnées, par exemple, x = 100 et y = 120, écrivez la valeur 100 à l'adresse 4148 et adresse 4149 valeur 120.
Utiliser l'assembleur
Une des méthodes de programmation de la console est l'assembleur.
Voici un exemple de la façon de montrer un sprite et de l'animer afin qu'il se déplace et repousse les bords de l'écran.
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
Utilisation du langage C
Vous pouvez également utiliser le langage C, pour cela nous avons besoin du compilateur SDCC et de quelques utilitaires supplémentaires.
Le code C peut être plus lent, mais l'écrire est plus rapide et plus facile.
Voici un exemple de code qui fait la même chose que le code assembleur ci-dessus, il utilise une bibliothèque qui permet d'appeler 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) { } }
Graphiques dynamiques
(Dans les graphiques personnalisés d'origine. Environ. Per.)
Dans le préfixe ROM, 1 page de tuiles pour le support et une autre page de sprites prêts à l'emploi sont cousues), par défaut, vous ne pouvez utiliser que ces graphiques fixes, mais vous pouvez passer en dynamique.
Mon objectif était tel que tous les graphiques nécessaires sous forme binaire soient immédiatement chargés dans la RAM CHR, et le code dans le chargeur de démarrage à partir de la ROM peut le faire. Pour ce faire, j'ai fait plusieurs photos de la bonne taille avec différents symboles utiles:

Étant donné que la mémoire graphique dynamique se compose de 4 pages avec 256 caractères de 8x8 pixels chacun et 4 pages des mêmes caractères pour les sprites, j'ai converti les images au format PNG, supprimé les doublons:

Et puis il a utilisé un outil auto-écrit pour tout traduire au format binaire RGB332 avec des blocs 8x8.

En conséquence, nous avons des fichiers avec des caractères, où tous les caractères vont séquentiellement les uns après les autres et chacun prend 64 octets.
Son
Échantillons Wave RAW convertis en échantillons PCM 8 bits 8 kilohertz.
Les correctifs pour les effets sonores sur PWM et la musique sont écrits avec des instructions spéciales.
Quant à la puce de synthèse FM Yamaha YM3438, j'ai trouvé un programme appelé DefleMask qui produit de la musique synchronisée PAL pour la puce Genesis YM2612, qui est compatible avec le YM3438.
DefleMask exporte de la musique au format VGM et je la convertis avec un autre utilitaire propriétaire en mon propre format binaire.
Tous les fichiers binaires des trois types de sons sont combinés en un seul fichier binaire, que mon chargeur de démarrage peut lire et charger dans la mémoire sonore de la RAM SDN.

Lien vers le fichier final
Le code exécutable binaire, les graphiques et le son sont combinés en un seul fichier PRG. Le fichier PRG a un en-tête dans lequel tout est décrit s'il y a des données audio et graphiques, combien elles occupent et les données elles-mêmes.
Un tel fichier peut être écrit sur une carte SD, et le chargeur de démarrage de la console le considère et télécharge tout aux endroits appropriés et lance le code exécutable du programme.

Émulateur
J'ai écrit un émulateur de ma console en C ++ en utilisant wxWidgets pour en faciliter le développement.
Le CPU est émulé par la bibliothèque libz80 .
Des fonctionnalités ont été ajoutées à l'émulateur pour le débogage, je peux l'arrêter à tout moment et faire le débogage étape par étape de l'assembleur, il y a une correspondance avec le code source en C. si cette langue a été utilisée pour le jeu.
Selon le graphique, je peux regarder dans la mémoire vidéo, dans les tables de symboles et dans la mémoire CHR elle-même.
Voici un exemple de programme exécuté sur un émulateur avec des outils de débogage activés.

Démo de programmation
Ces vidéos ont été tournées avec un appareil photo smartphone dirigé vers l'écran CRT du téléviseur, je m'excuse pour la qualité d'image imparfaite.
L'interpréteur BASIC programmable à partir du clavier PS / 2, après le premier programme, je montre comment écrire directement dans la mémoire PPU via l'espace adresse E / S en activant et en déplaçant le sprite:
Une démo de graphiques, dans cette vidéo, téléchargez 64 sprites 16x16 par programmation, sur fond d'un arrière-plan avec défilement dynamique et une superposition qui se déplace en dessous et au-dessus des sprites:
La démo de son montre les capacités du son YM3438 et PWM, les données sonores de cette démo et la musique FM et les sons PWM occupent ensemble presque tous les 128 kilo-octets disponibles de mémoire sonore.
Tetris, presque exclusivement les fonctions d'arrière-plan, la musique sur le YM3438, les effets sonores sur les patchs PWM ont été utilisés pour les graphiques.
Conclusion
Ce projet est vraiment un rêve devenu réalité, j'y travaille depuis plusieurs années, avec des interruptions, en regardant mon temps libre, je n'ai jamais pensé que j'irais aussi loin dans la création de ma propre console vidéo rétro de jeu. Naturellement, ce n'est pas parfait, je ne suis certainement pas un expert en électronique, il y avait évidemment trop d'éléments dans le décodeur, et sans aucun doute on pourrait faire mieux et probablement certains lecteurs y pensent.
Mais quand même, en travaillant sur ce projet, j'ai beaucoup appris sur l'électronique, les consoles de jeux et la conception d'ordinateurs, le langage d'assemblage et d'autres choses intéressantes, et surtout j'ai reçu une grande satisfaction en jouant à des jeux que j'ai moi-même écrits sur du matériel que j'ai développé et collectés.
J'ai des plans pour faire des consoles / ordinateurs et plus encore. En fait, je fais déjà un nouveau décodeur, il est presque prêt, et c'est un décodeur rétro simplifié basé sur une carte FPGA et plusieurs composants supplémentaires (en quantité beaucoup plus petite que dans ce projet, certainement), l'idée est d'être beaucoup moins cher et plus répétable.
Bien que j'aie beaucoup écrit sur ce projet ici, sans aucun doute beaucoup plus peut être discuté, j'ai à peine mentionné comment le moteur sonore fonctionne, comment le CPU interagit avec lui, et il y a beaucoup plus à faire sur le système graphique et les autres entrées / sorties et l'ensemble de la console serait de dire.
En regardant la réaction des lecteurs, je peux écrire plus d'articles en se concentrant sur les mises à jour, les détails sur les blocs de préfixe individuels ou d'autres projets.
Projets, sites, chaînes Youtube qui m'ont inspiré et m'a aidé avec des connaissances techniques:
Ces sites / canaux m'ont non seulement inspiré, mais m'ont également aidé à trouver des solutions aux problèmes complexes qui se sont posés lors des travaux de ce projet.
Merci d'avoir lu jusqu'ici. :)
Si vous avez des questions ou des commentaires, veuillez écrire dans les commentaires ci-dessous (article original en anglais sur Github. Approx. Per.)