Circuits cartouche reprogrammables pour SMS et Game Gear

Résumé et notes de conception

Pour un projet de jeu, j'ai été appelé à réaliser les circuits de cartouche SMS et Game Gear présentés sur cette page.

Mes requis de design étaient:
  • Doit supporter des jeu de taille supérieure à 48 ko (donc avoir un "mapper")
  • Doit permettre d'enregistrer la partie ou les scores
  • Doit pouvoir être installé dans des boîtiers originaux
  • Doit être relativement économique
J'ai choisi d'utiliser une puce de mémoire FLASH qui permet entre autre d'effacer et reprogrammer le jeu au besoin. La puce choisie peut fonctionner à 5 volt ainsi pas besoin de recourir à des "level translators" sur les signaux.

La fonction mapper, qui permet à la console d'accéder à davantage de mémoire, est réalisée à l'aide d'un CPLD. Il s'agit d'un mapper de style Sega simplifié ne supportant que la "slot 2". (sélection de banque en écrivant à l'adresse FFFF.

L'enregistrement des scores ou de la partie se fait traditionellement à l'aide d'une mémoire SRAM alimentée par une pile à l'intérieur de la cartouche. Par souci d'économie et de disponibilité des pièces, ma solution est plutôt d'utiliser la puce de flash. Un secteur de 64 ko doit être réservé, ce qui laisse tout de même 448 ko d'espace pour le jeu.

Côté mécanique, il n'a fallu que bien mesurer des circuit originaux et de donner les mêmes dimensions aux miens.




Circuit pour Master System

Voici à quoi ressemble le circuit pour cartouche de jeu SMS. Vous pouvez vous le procurer (avec ou sans boîtier) dans ma boutique en ligne.

Le circuit (Face avant)

Le circuit (Face avant)

Le circuit (Face arrière)

Le circuit (Face arrière)



Et voici quelques photos du circuit en action:
Programmation avec SMSCPROGR

Programmation avec SMSCPROGR

Test d'un jeu homebrew

Test d'un jeu homebrew

Circuit installé dans un boîtier

Circuit installé dans un boîtier

Cartouche reprogrammable bâtie avec ce circuit

Cartouche reprogrammable bâtie avec ce circuit




Circuit pour Game Gear

Et voici la version pour Game Gear. Ce circuit est également disponible depuis ma boutique en ligne.

Le circuit pour Game Gear

Le circuit pour Game Gear

Le circuit dans un boîtier Game Gear

Le circuit dans un boîtier Game Gear



Et voici quelques photos du circuit en action:
Programmation avec SMSCPROGR

Programmation avec SMSCPROGR

Test d'un jeu homebrew

Test d'un jeu homebrew

Test d'un jeu homebrew

Test d'un jeu homebrew




Programmation d'une cartouche

Avoir câblé une mémoire FLASH au port de cartouche SMS, c'est déjà bien, mais il faut pouvoir y programmer quelque chose commme un jeu, sans quoi ce n'est pas très utile.

Afin de tester mon premier prototype, j'avais assemblé un petit adaptateur permettant de programmer un ROM de 32k. La limite de 32k était imposée par le fait que mon programmeur d'EPROM ne sais pas se servir du mapper pour changer de banque. Heureusement les slots 0 et 1 sont fixes.



Ce n'était pas très pratique, et la limite de 32k était problématique. J'ai donc réalisé un programmeur (et lecteur) de cartouche dédié, capable de programmer l'entièreté de la mémoire.



Ce programmeur/lecteur de cartouche possède sa propre page.


Le "mapper"

L'unité centrale Z80 de la SMS peut accéder à 64 kilo-octets de mémoire. Les derniers 16k (slot 3) sont réservés à la mémoire interne à la console de 8K (celle-ci se répète deux fois de suite). Il reste donc 48 ko de libre pour le ROM dans la cartouche.

Il est possible d'avoir un jeu de taille supérieure à 48ko à l'aide d'un circuit nommé "mapper" qui permet à certaines plages d'adresses (du point de vue du Z80) d'accéder à des adresses ROM au delà de 48ko.

À l'aide d'un CPLD programmé dans le langage CUPL, j'ai implémenté l'équivalent d'un mapper Sega (partiel) tel que décrit ici:
https://www.smspower.org/Development/Mappers

Memory map

Les adresses mémoires de $0000 à $7FFF (slots 0 et 1, 16 ko chaque) du point de vue de la console correspondent toujours directement aux adresses FLASH $0000 à $7FFF. Toutefois, les adresses $8000 à $BFFF (slot 2) peuvent correspondre à n'importe quel bloc de 16 ko en FLASH. Le bloc auquel on accède par cette plage d'adresse est contrôlé par le mapper et sélectionné en écrivant à l'adresse mémoire $FFFF.

La plupart des jeux et des logiciels homebrew ne font pas "bouger" les slots 0 et 1, alors supporter seulement la slot 2 est généralement suffisant.




Développement: Sauvegarder sur la Flash

Tel que mentionné dans la première section, l'enregistrement des scores ou du progrès peut se faire directement dans la mémoire FLASH. Cela permet un circuit tout simple (CPLD et FLASH) sans pile, mais complique les choses côté logiciel. En effet, lorsqu'il s'agit d'une simple mémoire SRAM alimentée par une pile, il suffit de faire des accès à la mémoire normaux pour écrire ou lire. Mais avec une FLASH, ce n'est pas si facile.

Côté lecture, l'accès aux données en FLASH se fait, heureusement, parfaitement normalement. Mais cela se corse avec l'écriture. D'abord, il n'est pas possible d'effacer un octet seul. La puce de FLASH utilisée est divisée en secteurs de 64 ko, et l'effacement se fait par secteur de cette taille. Il faut donc effacer un bloc complet, même pour modifier un seul octet.

Ensuite, les commandes pour "effacer" et "écrire" prennent un certain temps à s'exécuter, temps pendant lequel il n'est pas possible de lire! Si le jeu s'exécute depuis la FLASH pendant cette période, la console plante immédiatement.

La solution est de charger le code s'occupant de ces accès en mémoire vive. Ainsi, comme il ne s'exécute pas depuis la mémoire FLASH, pas de problème! (On prendra aussi la précaution de déactiver les interruptions)

Oui mais s'il y a un bug le jeu peut-il accidentellement s'effacer?

Cela me semble extrêmement improbable, car initier l'effacement d'un secteur se fait par une séquence d'accès très spécifique:
  1. Écrire $AA à l'adresse 555
  2. Écrire $55 à l'adresse 2AA
  3. Écrire $80 à l'adresse 555
  4. Écrire $AA à l'adresse 555
  5. Écrire $30 à l'adresse du secteur à effacer.
Pour que l'effacement ait lieu, rien ne doit se produire entre les étapes ci-dessus. Par exemple, si la console exécute du code à partir de la FLASH, cela introduira des accès en lecture dans la séquence et rien ne se produira. L'écriture d'un octet se fait avec une séquence similaire mais qui prends fin à l'étape 3.

Et l'interruption non maskable? (NMI)

Ceci est le seul défaut de cette approche. Si le joueur arrivait à appuyer sur le bouton PAUSE qui génère cette interruption au mauvais moment (pendant les quelques millièmes de seconde où le code s'exécute depuis la mémoire vive) le processeur tentera d'exécuter le code à l'adresse $0066 et plantera.

D'accord, alors où est le code?

Ici: https://github.com/raphnet/flashrwSMS




Galerie

J'espère qu'un jour un grand nombre de jeux homebrew de haute qualité auront pu être édités grâce à ce projet.

Vous avez réalisé un jeu en utilisant mon circuit? Partagez quelques photos et screenshots et avec votre permission, je les ajouterai ici, accompagné d'un lien vers la page du jeu.


DONKEY.SMS

Je me suis amusé à créer un petit jeu simple nommé DONKEY.SMS. Ce jeu est présenté sur une autre page.

DONKEY.SMS

DONKEY.SMS

DONKEY.SMS

DONKEY.SMS

DONKEY.SMS

DONKEY.SMS