Ce tutorial montre comment développer un shoot'em up vertical sur Thomson MO5, étape par étape, en C compilé avec CMOC. Chaque étape correspond à un tag Git.
- CMOC installé et fonctionnel
- Émulateur MO5 (par exemple YAME)
- Repo de base :
https://github.com/thlg057/mo5_template - Notions de base en C (pointeurs, tableaux statiques)
| Caractéristique | Valeur |
|---|---|
| CPU | Motorola 6809 @ ~1 MHz |
| RAM utile | 48 Ko |
| Résolution | 320x200 pixels |
| Couleurs | 16 couleurs |
| Compilateur | CMOC (C pour 6809) |
Deux règles fondamentales s'appliquent à tout le code.
Pas de malloc. Toute la mémoire est allouée statiquement à la compilation. Les tableaux d'ennemis, de balles, de sprites -- tout est dimensionné à l'avance avec des #define.
Pas de float, pas de division si possible. Le 6809 ne dispose pas d'unité virgule flottante. On préfère les décalages (>> 1 au lieu de / 2) et l'arithmétique sur unsigned char (8 bits natifs) plutôt que int (16 bits).
La VRAM du MO5 est organisée en deux banques sélectionnées via le registre PRC :
- Banque couleur : chaque octet encode la couleur de fond (bits 0-3) et la couleur de forme (bits 4-7) pour un groupe de 8 pixels horizontaux.
- Banque forme : chaque bit correspond à un pixel --
1affiche la couleur de forme,0affiche la couleur de fond.
Un groupe de 8 pixels ne peut afficher que 2 couleurs. Les sprites respectent cette contrainte en variant les couleurs ligne par ligne. Le script png2mo5.py vérifie et encode cette règle lors de la génération des .h.
| Fichier | Rôle |
|---|---|
mo5_defs.h |
Registres hardware, palette, dimensions écran |
mo5_video.h |
Init video, clear écran, fill rectangle |
mo5_sprite_bg.h |
Sprites transparents (fond conservé) |
mo5_sprite_form.h |
Sprites forme seule |
mo5_sprite.h |
Sprites opaques |
mo5_actor_dr.h |
Dirty rectangle : save/restore zone VRAM |
mo5_font6.h |
Affichage de texte - 6 pixels de haut |
mo5_font8.h |
Affichage de texte - 8 pixels de haut |
Pipeline de conversion sprite :
make convert IMG=./assets/player.png
# génère include/assets/player.hPour ce projet, on utilisera les fonctions de mo5_sprite_bg.h, qui permettent de gérer la transparence (le fond est celui du décor)
| Fichier | Taille | Description |
|---|---|---|
player.png |
24x24 | Vaisseau joueur, bleu/rouge/jaune |
enemy.png |
16x16 | Alien insecte, vert/jaune |
bullet_player.png |
8x8 | Tir joueur, cyan/blanc |
bullet_enemy.png |
8x8 | Tir ennemi, rouge/orange |
unsigned char partout où la valeur tient sur 8 bits.
typedef struct {
MO5_Actor actor;
unsigned char active;
} ActiveActor;
typedef struct {
MO5_Actor actor;
unsigned char active;
unsigned char hp;
} EnemyActor;Les coordonnées x sont en octets (1 unité = 8 pixels), les coordonnées y sont en pixels. Cette distinction est imposée par la VRAM.
Le jeu repose sur une boucle principale simple :
- Lecture des entrées clavier
- Mise à jour joueur
- Mise à jour balles
- Mise à jour ennemis
- Détection collisions
- Attente VBL
- Dessin
Principe fondamental MO5 :
- Pas d'allocation dynamique
- Structures statiques dimensionnées à la compilation
- Utilisation prioritaire de
unsigned char - Synchronisation à 50 Hz (PAL)
Structure du projet :
mo5-spaceinvaders/
├── Makefile
├── src/
│ ├── main.c
│ └── game.c
├── include/
│ ├── game.h
│ └── assets/
│ ├── player.h
│ ├── enemy.h
│ ├── bullet_player.h
│ └── bullet_enemy.h
├── assets/
│ ├── player.png
│ ├── enemy.png
│ ├── bullet_player.png
│ └── bullet_enemy.png
├── output/
└── tools/
- main.c : point d'entrée minimal (initialisation + appel
game_loop()) - game.c : toute la logique du jeu (moteur, update, collisions, HUD)
- game.h : interface publique minimale (
void game_loop(void);) - include/assets/ : sprites générés automatiquement depuis les PNG
Depuis le dépôt GitHub : - Cliquer sur use this template et sélectionner Open in a codespace
Dans le terminal :
make setup-codespaceCette commande installe automatiquement :
- CMOC (compilateur C 6809)
- lwtools (assembleur)
- Python 3 + Pillow
- Dépendances nécessaires au SDK
Une seule fois ou après une mise à jour du SDK.
make installCela : - Télécharge BootFloppyDisk - Compile le sdk_mo5 - Installe les outils dans tools/
make| Tag | Contenu |
|---|---|
step-01-player |
Joueur, déplacement, tirs joueur |
step-02-score |
HUD score et vies |
step-03-enemies |
Ennemis, formation, tirs ennemis |
step-04-collisions |
Collisions, points de vie, game over |
Afficher le vaisseau joueur en bas de l'écran, le déplacer avec Q/D, et tirer des balles vers le haut avec Espace.
Afficher le score et les vies en haut de l'écran, mis à jour à chaque événement (ennemi détruit, vie perdue).
Afficher une vague d'ennemis en formation, les faire osciller horizontalement, descendre au rebord, et tirer périodiquement.
Détecter les impacts, gérer les points de vie, l'invincibilité temporaire du joueur, le game over et la victoire.
| Règle | Pourquoi |
|---|---|
unsigned char plutôt que int |
Opérations 8 bits natives sur 6809 |
while (n--) plutôt que for (i=0; i<n; i++) |
Flag Z automatique après décrémentation |
*p++ plutôt que tab[i] |
LEAX 1,X au lieu d'un calcul d'index |
static sur les fonctions internes |
Inlining possible, symboles non exportés |
| Compteur de frames pour les vitesses | Indépendant du nombre de sprites actifs |
Pas de row_offsets[] |
Économise 400 octets RAM |
Flag score_dirty pour le HUD |
Redessinage uniquement en cas de changement |
#define internes dans game.c |
Pas dans game.h si personne d'autre n'en a besoin |