Mon premier programme
Deux petits programmes pour commencer : le premier colore le fond de l'écran, le second y écrit un texte. Chacun est montré sur Commodore 64 et sur PC Engine, avec les mêmes fonctions.
Une bibliothèque, plusieurs machines
Chaque machine a son propre matériel : la PC Engine affiche des tuiles avec 16
palettes de 16 couleurs, le Commodore 64 un écran de caractères avec 16 couleurs fixes.
Simple8 ne cache pas ces différences, mais ses bibliothèques donnent les mêmes
noms aux mêmes services d'une machine à l'autre. Une fois appris sur une
machine, print ou setCursor s'utilisent partout.
On appelle une fonction de bibliothèque par le nom de la bibliothèque, un point, puis
le nom de la fonction : vic.print(…) sur C64, vdc.print(…) sur
PC Engine. Le nom de la bibliothèque est souvent celui du circuit vidéo de la machine :
le VIC-II du C64, le VDC de la PC Engine, le PPU de la NES.
La bibliothèque de texte, screentext, en est le premier exemple. Ses
fonctions principales sont les mêmes partout :
| Fonction | Rôle |
|---|---|
initScreen() | prépare l'écran pour afficher du texte |
clearScreen() | efface l'écran et ramène le curseur en haut à gauche |
setCursor(x, y) | place le curseur : colonne x, ligne y |
setFontColor(n) | choisit la couleur du texte |
print("…") | écrit un texte à la position du curseur |
putChar(c) | écrit un seul caractère |
D'une machine à l'autre, seuls changent le nom de la bibliothèque et quelques détails dus au matériel :
| Machine | Bibliothèque | Couleurs | Particularités |
|---|---|---|---|
| Commodore 64 | vic.screentext | vic.setBackgroundColor, vic.setBorderColor | police intégrée à la machine |
| PC Engine | vdc.screentext | vce.setPalette | police à fournir |
| NES | ppu.screentext | ppu.setBackgroundColor, ppu.setPalette | initText() et clearText(), sans setFontColor |
| Amstrad CPC | screen.screentext | gatearray.setInk, gatearray.setBorder | initScreen(mode) : 20, 40 ou 80 colonnes |
Ce cours explique ces fonctions communes et les illustre sur une ou deux machines, le plus souvent le Commodore 64 et la PC Engine.
Quelles bibliothèques charger ?
Un programme ne charge que les bibliothèques dont il se sert, avec
addlib dossier.fichier;. La liste complète des bibliothèques d'une machine,
avec leurs fonctions, sera donnée dans sa documentation propre. On peut aussi la
parcourir dans libs8/, par exemple libs8/nec/pce/ pour la
PC Engine.
Les plus courantes existent sur la plupart des machines :
| Pour… | Commodore 64 | PC Engine | NES | Amstrad CPC |
|---|---|---|---|---|
| l'affichage | vic.base | vdc.base, vce.base | ppu.base | gatearray.gatearray |
| le texte | vic.screentext | vdc.screentext | ppu.screentext | screen.screentext |
| les sprites | vic.sprite | vdc.sprite | ppu.sprite | sprite.mode1 |
| le défilement | vic.scroll | vdc.scroll | dans ppu.base | tile.scroll |
| la manette | io.base | io.base | io.base | io.base |
| le son | sid.base | psg.base | apu.base | sound.base |
Le nom à écrire avant le point, pour appeler une fonction, est fixé par la bibliothèque
elle-même. C'est souvent celui du circuit (vic., vdc.,
ppu.), mais les sprites, le défilement et la manette s'appellent de la même
façon partout : sprite., scroll. et io..
Quelques bibliothèques sont écrites pour toutes les machines à la fois, et rangées à la
racine de libs8/ : c'est le cas de math.bcd, pour compter les
scores en décimal.
Une documentation par machine. Ce qui est propre à une machine (sa mémoire, ses sprites, son son, les fonctions qu'elle seule possède) sera décrit dans une documentation spécifique à chaque architecture, avec la liste de ses bibliothèques.
Créer le projet
Avec Simple8 IDE
Fichier → Nouveau projet… (⇧⌘N) demande trois
choses : le nom du projet (par exemple Bonjour), l'architecture
(commodore.c64 ou nec.pce) et le dossier où le ranger. L'IDE crée
le dossier Bonjour/ avec un fichier main.s8p déjà prêt.
Ce modèle contient, en plus de main(), une fonction
initContext(). Nous la verrons dans le chapitre sur les contextes : pour
l'instant, on peut la laisser vide ou la supprimer.
À la main
Un projet n'est qu'un dossier : il suffit de créer Bonjour/ et d'y écrire un
fichier main.s8p avec un éditeur de texte. La compilation se lance ensuite
depuis ce dossier :
cd Bonjour
java -jar /chemin/vers/LitleCompilator.jar main.s8p
Le squelette d'un programme
Voici le plus petit programme Simple8 possible. Il ne fait rien, mais il compile :
// Le plus petit programme Simple8
architecture nec.pce;
unit {
main() {
// le programme commence ici
}
}
architecturedit pour quelle machine le programme est écrit. C'est toujours la première instruction du fichier.addlib, juste après, charge les bibliothèques dont on a besoin (aucune ici).unit { … }est la unit principale, le bloc qui contient le programme. Toutes les variables et toutes les fonctions sont rangées dans des units : il n'y a pas de variable globale.main()est le point d'entrée : c'est là que commence votre programme.- Ce qui suit
//jusqu'à la fin de la ligne est un commentaire.
Ce qui se passe au démarrage
Quand on allume la machine, ce n'est pas main() qui s'exécute en premier.
Chaque architecture a son code d'amorçage, boot.asm, rangé
dans libs8/ (libs8/nec/pce/boot/boot.asm pour la PC Engine). Le
compilateur l'ajoute à chaque programme.
main().boot.asminitialise la machine : le processeur, la mémoire, les valeurs de départ des variables, le circuit vidéo…- Puis il appelle les fonctions du programme, et en particulier
main(). - Quand
main()se termine,boot.asmreprend la main et fait tourner la machine dans une boucle sans fin : le programme ne « s'arrête » pas, et l'écran garde ce qu'on y a mis.
Pour l'instant, seule main() nous intéresse. Nous verrons dans le chapitre
sur les contextes que boot.asm appelle aussi d'autres fonctions du
programme.
Étape 1 : la couleur du fond
Premier programme : passer le fond de l'écran en bleu. Le C64 a aussi une bordure autour de l'écran, que l'on passe en noir.
Sur Commodore 64
// Mon premier programme : la couleur du fond (Commodore 64)
architecture commodore.c64;
addlib vic.base;
unit {
main() {
vic.setBorderColor(0); // la bordure en noir
vic.setBackgroundColor(6); // le fond en bleu
}
}
La bibliothèque vic.base commande le VIC-II, le circuit vidéo du C64.
Ses couleurs sont fixes et numérotées de 0 à 15 : 0 est le noir, 1 le blanc, 6 le bleu.
Sur PC Engine
// Mon premier programme : la couleur du fond (PC Engine)
architecture nec.pce;
addlib vce.base;
unit {
main() {
vce.setPalette(0, 0, RGB(0, 0, 7)); // le fond en bleu
}
}
La PC Engine n'a pas de couleurs fixes : elle a 32 palettes de 16
couleurs, que l'on remplit soi-même : 16 palettes pour les tuiles (de 0 à 15) et
16 pour les sprites (de 16 à 31). La bibliothèque vce.base commande
le VCE, le circuit qui les contient. vce.setPalette(palette, couleur, valeur)
change une couleur d'une palette. La couleur 0 de la palette 0 est celle du fond de
l'écran. La PC Engine n'a pas de bordure : il n'y a que cette couleur de fond.
RGB(r, g, b) fabrique une couleur à partir de ses composantes rouge, verte
et bleue, de 0 à 7 chacune : RGB(0, 0, 7) est un bleu franc.
RGB() marche aussi sur C64. Comme il n'a que 16 couleurs,
RGB(0, 0, 7) y donne le numéro de la couleur la plus proche :
vic.setBackgroundColor(RGB(0, 0, 7)); colore aussi le fond en bleu.
Compiler et lancer
Dans Simple8 IDE, ⌘R compile le programme et l'ouvre dans l'émulateur de la machine. En ligne de commande, depuis le dossier du projet :
java -jar /chemin/vers/LitleCompilator.jar main.s8p
La compilation doit se terminer par 0 warning(s) 0 error(s). Le programme
est alors dans asm/ : main.prg pour le C64, main.pce
pour la PC Engine, à ouvrir dans l'émulateur.
Étape 2 : afficher un texte
Second programme : écrire « BONJOUR ! » au milieu de l'écran. On garde les couleurs de
l'étape 1 et on ajoute la bibliothèque de texte, screentext.
Sur Commodore 64
// Mon premier programme : un texte à l'écran (Commodore 64)
architecture commodore.c64;
addlib vic.base;
addlib vic.screentext;
unit {
main() {
vic.setBorderColor(0);
vic.setBackgroundColor(6);
vic.initScreen(); // l'écran en mode texte
vic.clearScreen(); // effacé
vic.setFontColor(1); // texte blanc
vic.setCursor(15, 12); // colonne 15, ligne 12
vic.print("BONJOUR !");
}
}
Cinq appels suffisent : préparer l'écran, l'effacer, choisir la couleur du texte (1, le blanc), placer le curseur, puis écrire. L'écran du C64 compte 40 colonnes et 25 lignes : la colonne 15 et la ligne 12 placent le texte vers le centre.
En majuscules. Avec la police du C64 utilisée au démarrage, les lettres minuscules s'affichent en majuscules. On écrit donc le texte directement en majuscules.
Sur PC Engine
La PC Engine n'a pas de police intégrée : il faut lui en fournir une, sous la forme d'un
fichier de tuiles. Le dépôt en contient une, utilisée par les exemples :
Examples/nec/pce/boulderpce/gfx/c64fontpce.bin. On la copie dans le dossier du
projet sous le nom font.bin.
// Mon premier programme : un texte à l'écran (PC Engine)
architecture nec.pce;
addlib sys.resource;
addlib vce.base;
addlib vdc.base;
addlib vdc.screentext;
unit {
const int font = addResource("font.bin");
main() {
int source;
int length;
// l'écran : une carte de 32 x 32 cases
vdc.setBATSize(vdc.BAT_32x32);
vdc.initScreen();
// la police, copiée dans la mémoire vidéo à l'adresse $1000
resource.mapToMPR2(.font);
source = 0x4000 + resource.getOffset(.font);
length = resource.getLength(.font);
vdc.writeToVRAM(source, 0x1000, length);
// les couleurs
vce.setPalette(0, 0, RGB(0, 0, 7)); // le fond en bleu
vce.setPalette(0, 1, RGB(7, 7, 7)); // le texte en blanc
// le texte
vdc.clearScreen();
vdc.setFontColor(0); // le texte avec la palette 0
vdc.setCursor(11, 13);
vdc.print("BONJOUR !");
}
}
Ce programme est plus long, parce qu'il fait lui-même ce que le C64 fait tout seul :
const int font = addResource("font.bin");range le fichier de la police dans la ROM.fontest son identifiant.- L'écran : la taille de sa carte (32 × 32 cases), puis la copie de la police dans la
mémoire vidéo, à l'adresse
$1000oùscreentextl'attend. sourceetlengthsont des variables locales demain(): l'adresse et la taille de la police dans la ROM.- Les couleurs : le fond (couleur 0 de la palette 0) et le texte (couleur 1 de la palette 0).
vdc.setFontColor(0)écrit le texte avec la palette 0, celle où l'on a mis le blanc.
Un texte bien visible. Si la couleur du texte est la même que celle
du fond, on ne voit rien. Avec setFontColor, on choisit la palette du
texte : autant en prendre une dont la couleur 1 tranche sur le fond.
Les mêmes fonctions, deux machines
Les appels de main() se ressemblent presque mot pour mot. Les différences
tiennent au matériel :
| Commodore 64 | PC Engine | |
|---|---|---|
| La police | dans la machine | un fichier à fournir et à copier en mémoire vidéo |
| L'écran de texte | 40 × 25 caractères | 32 × 28 caractères visibles |
| Les couleurs | 16 couleurs fixes | 32 palettes de 16 couleurs à remplir : 16 pour les tuiles, 16 pour les sprites |
| La bordure | oui, de sa propre couleur | non, seulement la couleur de fond |
setFontColor(n) | n est une couleur (0 à 15) | n est une palette (0 à 15) |
À retenir
- Un programme commence par
architecture, charge ses bibliothèques avecaddlibet contient une unit avecmain(). - Au démarrage,
boot.asminitialise la machine puis appellemain(). - Les bibliothèques des différentes machines partagent les mêmes noms de fonctions :
initScreen,clearScreen,setCursor,print… - Ce qui diffère vient du matériel : la police, la taille de l'écran, la façon de définir les couleurs.
À vous de jouer
- Changez la couleur du fond : un autre numéro sur C64, d'autres valeurs de
RGBsur PC Engine.Voir la solutionMasquer la solution
Sur Commodore 64, il suffit de donner un autre numéro à
vic.setBackgroundColor. Les 16 couleurs sont fixes : 0 le noir, 1 le blanc, 2 le rouge, 5 le vert, 6 le bleu, 7 le jaune… Ici, 2 donne un fond rouge.Sur Commodore 64
// Mon premier programme : la couleur du fond (Commodore 64) architecture commodore.c64; addlib vic.base; unit { main() { vic.setBorderColor(0); // la bordure en noir vic.setBackgroundColor(2); // le fond en rouge } }
Le numéro 2 : un fond rouge. Sur PC Engine, on choisit soi-même la couleur avec
RGB(r, g, b), chaque composante allant de 0 à 7. Beaucoup de rouge, un peu de vert et pas de bleu donnent un orange :RGB(7, 4, 0). Essayez d'autres mélanges :RGB(7, 7, 7)est le blanc,RGB(0, 0, 0)le noir,RGB(3, 3, 3)un gris.Sur PC Engine
// Mon premier programme : la couleur du fond (PC Engine) architecture nec.pce; addlib vce.base; unit { main() { vce.setPalette(0, 0, RGB(7, 4, 0)); // le fond en orange } }
RGB(7, 4, 0) : un fond orange. - Placez le texte en haut à gauche de l'écran.
Voir la solutionMasquer la solution
Le coin en haut à gauche est la colonne 0 et la ligne 0 : les positions se comptent à partir de 0, pas de 1. Il suffit donc de remplacer les deux nombres de
setCursorpar0, 0. Le reste du programme ne change pas.Sur Commodore 64
main() { vic.setBorderColor(0); vic.setBackgroundColor(6); vic.initScreen(); // l'écran en mode texte vic.clearScreen(); // effacé vic.setFontColor(1); // texte blanc vic.setCursor(0, 0); // colonne 0, ligne 0 : en haut à gauche vic.print("BONJOUR !"); }
Le texte en colonne 0, ligne 0. Sur PC Engine
// le texte vdc.clearScreen(); vdc.setFontColor(0); // le texte avec la palette 0 vdc.setCursor(0, 0); // en haut à gauche vdc.print("BONJOUR !");Sur C64, le texte touche la bordure noire. Sur PC Engine, il n'y a pas de bordure : le texte touche le bord de l'image.
- Ajoutez une deuxième ligne de texte, sous la première.
Voir la solutionMasquer la solution
Après
print, le curseur reste à droite du texte écrit. Pour écrire en dessous, on le replace avecsetCursor: même colonne, ligne suivante (une ligne de plus). On peut ensuite appelerprintune seconde fois.Sur Commodore 64
main() { vic.setBorderColor(0); vic.setBackgroundColor(6); vic.initScreen(); // l'écran en mode texte vic.clearScreen(); // effacé vic.setFontColor(1); // texte blanc vic.setCursor(15, 12); // colonne 15, ligne 12 vic.print("BONJOUR !"); vic.setCursor(15, 13); // même colonne, ligne suivante vic.print("SIMPLE8"); }Sur PC Engine
// le texte vdc.clearScreen(); vdc.setFontColor(0); // le texte avec la palette 0 vdc.setCursor(11, 13); vdc.print("BONJOUR !"); vdc.setCursor(11, 14); // même colonne, ligne suivante vdc.print("SIMPLE8");Pour laisser une ligne vide entre les deux textes, on ajouterait 2 au numéro de ligne au lieu de 1.
- Sur PC Engine, écrivez cette deuxième ligne dans une autre couleur : remplissez la
couleur 1 de la palette 1 avec
vce.setPalette(1, 1, …), puis appelezvdc.setFontColor(1)avant de l'écrire.Voir la solutionMasquer la solution
La couleur 1 d'une palette est celle des lettres de la police. La palette 0 a déjà un blanc en couleur 1 ; on met un jaune dans la couleur 1 de la palette 1. Ensuite,
vdc.setFontColor(1)fait écrire le texte suivant avec la palette 1. Ce qui est déjà à l'écran ne change pas : chaque case garde la palette avec laquelle elle a été écrite.// les couleurs vce.setPalette(0, 0, RGB(0, 0, 7)); // le fond en bleu vce.setPalette(0, 1, RGB(7, 7, 7)); // palette 0 : texte blanc vce.setPalette(1, 1, RGB(7, 6, 0)); // palette 1 : texte jaune // le texte vdc.clearScreen(); vdc.setFontColor(0); // le texte avec la palette 0 vdc.setCursor(11, 13); vdc.print("BONJOUR !"); vdc.setFontColor(1); // la suite avec la palette 1 vdc.setCursor(11, 14); vdc.print("SIMPLE8");
Deux lignes, deux palettes. La couleur 0 de la palette 1 n'a pas besoin d'être remplie : la couleur 0 de chaque palette de tuiles est transparente, et c'est la couleur 0 de la palette 0, le fond, qui apparaît derrière les lettres.
