Simple8

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 :

FonctionRô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 :

MachineBibliothèqueCouleursParticularités
Commodore 64vic.screentextvic.setBackgroundColor, vic.setBorderColorpolice intégrée à la machine
PC Enginevdc.screentextvce.setPalettepolice à fournir
NESppu.screentextppu.setBackgroundColor, ppu.setPaletteinitText() et clearText(), sans setFontColor
Amstrad CPCscreen.screentextgatearray.setInk, gatearray.setBorderinitScreen(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 64PC EngineNESAmstrad CPC
l'affichagevic.basevdc.base, vce.baseppu.basegatearray.gatearray
le textevic.screentextvdc.screentextppu.screentextscreen.screentext
les spritesvic.spritevdc.spriteppu.spritesprite.mode1
le défilementvic.scrollvdc.scrolldans ppu.basetile.scroll
la manetteio.baseio.baseio.baseio.base
le sonsid.basepsg.baseapu.basesound.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
    }
}

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.

Au démarrage : la machine exécute boot.asm, qui l'initialise puis appelle main() ; quand main() se termine, la machine tourne dans une boucle sans fin Mise sous tension RESET boot.asm initialise la machine main() votre programme Boucle sans fin l'écran reste affiché
Du démarrage de la machine à main().
  1. boot.asm initialise la machine : le processeur, la mémoire, les valeurs de départ des variables, le circuit vidéo…
  2. Puis il appelle les fonctions du programme, et en particulier main().
  3. Quand main() se termine, boot.asm reprend 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.

Écran du Commodore 64 : fond bleu, bordure noire
Le résultat sur Commodore 64.

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.

Écran de la PC Engine : fond bleu
Le résultat sur PC Engine.

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.

Écran du Commodore 64 : BONJOUR ! en blanc sur fond bleu
« BONJOUR ! » sur Commodore 64.

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 :

Écran de la PC Engine : BONJOUR ! en blanc sur fond bleu
« BONJOUR ! » sur PC Engine.

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 64PC Engine
La policedans la machineun fichier à fournir et à copier en mémoire vidéo
L'écran de texte40 × 25 caractères32 × 28 caractères visibles
Les couleurs16 couleurs fixes32 palettes de 16 couleurs à remplir : 16 pour les tuiles, 16 pour les sprites
La bordureoui, de sa propre couleurnon, seulement la couleur de fond
setFontColor(n)n est une couleur (0 à 15)n est une palette (0 à 15)

À retenir

À vous de jouer

  1. Changez la couleur du fond : un autre numéro sur C64, d'autres valeurs de RGB sur 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
        }
    }
    Écran du Commodore 64 : fond rouge, bordure noire
    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
        }
    }
    Écran de la PC Engine : fond orange
    RGB(7, 4, 0) : un fond orange.
  2. 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 setCursor par 0, 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 !");
    }
    Écran du Commodore 64 : BONJOUR ! dans le coin en haut à gauche
    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.

  3. 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 avec setCursor : même colonne, ligne suivante (une ligne de plus). On peut ensuite appeler print une 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.

  4. 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 appelez vdc.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");
    Écran de la PC Engine : BONJOUR ! en blanc, SIMPLE8 en jaune en dessous
    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.