PAMPUK Forth

L'interpréteur

Lorsque l'on démarre Forth, on se trouve dans un mode interactif dans lequel on peut taper des mots sur une ligne et valider cette ligne avec la touche entrée.

Un mot est une suite de caractères séparés des autres par des espaces. Lors de la validation de la ligne, l'interpréteur Forth va prendre chaque mot et les exécuter. Si le mot trouvé est dans son dictionnaire courant (nous verrons plus tard ce qu'est un dictionnaire), il va exécuter le code associé. Si aucun mot ne correspond, il va essayer de convertir le mot en nombre et le mettre sur la pile.

Les piles

La pile des paramètres

Si on ne précise pas de quelle pile on parle, c'est généralement de la pile des paramètres qu'il s'agit. C'est une pile LIFO (Last In First Out), c'est-à-dire que le dernier élément mis sur la pile est le premier à en sortir. Cette pile est un concept central du Forth.

Beaucoup de mots vont prendre leurs paramètres dans cette pile, et y laisser leurs résultats.

En opération simple pour voir le fonctionnement de la pile est d'empiler trois nombres puis des les affiches avec le mot . :

1 2 3 . . .

Le résultat affiché est :

3 2 1

Un autre exemple simple, qui est un peu le Hello World du Forth, est de taper 1000 2000 + . et de valider la ligne. Le résultat affiché est 3000.

Le déroulement de l'interprétation est le suivant :

  • 1000 n'est pas un mot reconnu et est mis sur la pile en tant que nombre,
  • 2000 n'est pas un mot reconnu et est mis sur la pile en tant que nombre,
  • + est un mot reconnu qui prend les deux premiers éléments de la pile, les additionne et met le résultat sur la pile,
  • . est un mot reconnu prend le premier élément de la pile et l'affiche.

La pile retour

Cette seconde pile est utilisée pour stocker les adresses de retour lors de l'exécution de mots. Lorsque Forth exécute un mot, il doit savoir où reprendre l'exécution après ce mot. Pour cela, Forth enregistre à chaque appel l'adresse de retour de l'appelant. Lorsque le mot est terminé, Forth peut lire dans cette pile de retour l'adresse à laquelle il doit reprendre l'exécution.

Certains micro processeurs, comme le 6809, ont l'avantage de gérer nativement au moins deux piles. Mais ce n'est pas le cas du Z80 utilisé par le Hector HRX. L'interpréteur Forth doit donc gérer lui-même cette pile retour, la pile gérée par le processeur (via le registre SP) étant réservée aux paramètres.

Cependant, il est fréquent d'utiliser la pile retour pour y stocker temporairement des valeurs afin de désencombrer la pile des paramètres.

Le mot >R transfert le sommet de la pile de paramètres vers le sommet de la pile retour.

Le mot R> transfert le sommet de la pile retour et le met sur la pile des paramètres.

Exemple d'utilisation. Nous partons d'une pile de 4 éléments n4 n3 n2 n1 et nous voulons les réorganiser pour obtenir n3 n4 n2 n1, c'est-à-dire inverser les deux nombres en positions 3 et 4 de la pile.

: INVERSE-3-4
  >R >R        \ Transfère n1 et n2 dans la pile retour
  SWAP         \ Échange n3 et n4
  R> R>        \ Récupère n2 et n1 de la pile retour
  ;

4 3 2 1 INVERSE-3-4
. \ Affiche 1
. \ Affiche 2
. \ Affiche 4
. \ Affiche 3

Règle d'or : à l'intérieur d'un même mot, il faut exécuter autant de R> qu'il y a de >R avant de sortir du mot. En effet, l'interpréteur Forth devra retrouver l'adresse de retour là où il l'attends.

Les mots

Comme indiqué plus haut, un mot est une suite de caractères. Ces caractères peuvent être quelconques, même s'il est conseillé de donner aux noms un sens.

Lorsque Forth démarre, il a déjà connaissance d'un certain nombre de mots, qui forment un vocabulaire. À chaque mot est défini un code à exécuter. Ce code peut être lui-même composé d'autres mots en Forth, mais il peut être aussi écrit en assembleur.

Il est possible de créer de nouveaux mots. Et c'est d'ailleurs la manière de programmer en Forth.

Les commentaires

Dans PAMPUK Forth, les commentaires commencent par ( et se terminent par ). Comme '(' est un mot, il est essentiel de mettre des espaces avant et après. Comme le mot ignore tout ce qui se trouve avant le prochain ), cette parenthèse fermante n'est pas un mot, et l'espace avant n'est pas nécessaire. Cependant, par soucis d'équilibre esthétique, il est conseillé de le mettre.

Le mot de commentaire \, qui ignore le texte jusqu'à la fin de la ligne, n'est pas disponible en standard sur ce Forth. Il est cependant utilisé dans les exemples donnés.

Les annotations

Les annotations sont des commentaires avec un format spécifique qui indique les opérations effectuées par un mot sur la pile des paramètres et si une chaîne de caractères est lue depuis le flot d'entrée.

Les annotations sont en commentaires et sont destiné à la lecture humaine, l'interpréteur/compilateur n'en fait pas usage.

Dans une annotation, ce qui doit se trouver sur la pile avant l'appel au mot se trouve avant un tiret -, ce qui y est laissé par le mot se trouve après le tiret. Si le mot lit une chaîne de caractères, celle-ci est indiquée entre signes inférieurs < et supérieurs > après le tiret et, s'ils existent, après les annotations de paramètres laissés par le mot.

Que ce soit avant ou après le tiret, les éléments sont représenté du plus profond à gauche, au plus haut (le sommet de la pile) à droite.

Par exemple DUP ( n - n n ) indique que le mot DUP prend un nombre sur la pile et en laisse deux.

Certaines conventions donnent plus de détails sur la nature de l'opération effectuée, ou bien sur la sémantique des paramètres. Dans ce manuel, seul le type des paramètres est indiqué.

Les abbréviations utilisées sont les suivantes :

Abbréviation Signification
n un entier 16 bits signé
u un entier 16 bits non signé
d un entier 32 bits signé
ud un entier 32 bits non signé
b un entier 16 bits représentant un booléen
addr une adresse mémoire sur 16 bits
<nom> une chaîne de caractères lu depuis le flot d'entrée

Lorsque plusieurs paramètres sont de même type, ils sont distingués par un nombre en suffixe. Par exemple, n1 n2 n3 indique trois entiers 16 bits signés.

Si la même abbréviation, eventuellement avec le même suffixe, est présent des deux côtés du tiret, cela signifie que la valeur est identique avant et après l'appel du mot.

Les nombres

Par défaut, les nombres sont entrés en simple précision, c'est-à-dire sur 16 bits. Ils sont manipulés par défaut comme des nombres signés avec le bit 7 de l'octet de poids fort indiquant le signe. Il existe des nots pour traiter les nombres spécifiquements en nombres non signés (ceux-ci commence par U).

Il est possible d'entrer des nombres double précision dans l'instruction en les faisant suivre par un .. Plus exactement, le . peut-être n'importe où dans le nombre.

Forth peut comprendre et afficher des nombres en n'importe quelle base. Pour cela, il suffit de préciser la base soit en utilisant les mots prédéfinis DECIMAL et HEX, soit en manipulant la variable BASE.

PAMPUK Forth, comme beaucoup de Forth de base, ne connaît que les nombres entiers.

Les expressions

Du fait du fonctionnement du Forth avec une pile, les expressions sont écrites de manière postfixée. Cela signifie que l'opérateur suit les opérandes.

Par exemple, l'expression 4 + 3 en notation usuelle mathématique (infixée) devient 4 3 + en notation postfixée.

Exemple plus complexe :

5 7 * 3 +

Cette expression est équivalente à 5 * 7 + 3 en notation usuelle et donne le résultat 38.

Les constantes

Forth peut créer des mots dont l'exécution met une valeur sur la pile. Ces mots sont appelés des constantes. Pour créer une constante, on utilise le mot CONSTANT. Ce mot est suivi du nom de la constante. La valeur associée est celle qui est sur la pile lors de la création de la constante.

Exemple :

5 CONSTANT CINQ

Ce code crée une constante CINQ qui met la valeur 5 sur la pile. On peut utiliser cette constante de la manière suivante :

CINQ .

Le mot CINQ met la valeur 5 sur la pile, puis le mot . prend cette valeur et l'affiche.

Les variables

Forth peut créer des mots qui mettent sur la pile l'adresse d'une variable. Pour cela, on utilise le mot VARIABLE. Ce mot est suivi du nom de la variable. Lors de l'exécution de ce mot, l'adresse de la variable est mise sur la pile.

Cela permet par la suite de stocker des valeurs à des adresses mémoires spécifiquement allouées et associé à un nom.

Exemple :

10 VARIABLE X

Ce code crée une variable X initialisée à 10. On peut utiliser cette variable de la manière suivante :

X @

Le mot X met l'adresse de la valeur de la variable X sur la pile, puis le mot @ prend cette adresse et met la valeur à cette adresse sur la pile. Cela permet de lire la valeur de la variable X.

11 X !

Le mot 11 met la valeur 11 sur la pile, puis le mot X met l'adresse de la variable X sur la pile et enfin le mot ! prend la valeur et l'adresse sur la pile et met la valeur à cette adresse. Cela permet de changer la valeur de la variable X.

Les chaînes de caractères

Le noyau du PAMPUK Forth ne possède pas de vocabulaire spécifique pour les chaînes de caractères, mais il est possible d'ajouter un vocabulaire spécialisé.

Ce manuel propose un vocabulaire de chaînes de caractères, dont le fonctionnement est expliqué dans ce chapitre.

Voici un aperçu des mots disponibles dans ce vocabulaire. Des exemples sont donnés avec l'explication des mots.

Création et manipulation de base

  • STRING : crée une variable chaîne avec une taille maximale spécifiée.
  • S" : crée une chaîne littérale (stockée temporairement dans PAD).
  • S! : stocke une chaîne dans une variable.
  • LEN et MLEN : retournent respectivement la taille actuelle et maximale d'une chaîne.

Extraction et manipulation

  • LEFT$ : extrait une partie gauche d'une chaîne.
  • RIGHT$ : extrait une partie droite d'une chaîne.
  • MID$ : extrait une partie centrale d'une chaîne.
  • SUB! : remplace une partie d'une chaîne.
  • S+ : concatène deux chaînes (avec troncature si nécessaire).

Comparaisons et recherche

  • S= et S< : comparent deux chaînes.
  • IN$ : trouve l'occurrence d'une chaîne dans une autre.

Note importante : Les chaînes littérales utilisant le même buffer temporaire (PAD), il faut éviter de comparer directement deux chaînes littérales entre elles.

Conversions

  • ASC : donne le code ASCII du premier caractère.
  • CHR$ : convertit un code ASCII en chaîne d'un caractère.
  • STR$ : convertit un nombre double précision en chaîne.
  • VAL : convertit une chaîne en nombre double précision.

Tableaux

  • STRING-ARRAY : crée un tableau de chaînes avec indexation.

La création de mots

La manière la plus standard de créer un mot en Forth est d'utiliser le mot :. Ce mot est suivi du nom du mot à créer, séparé par un espace bien entendu. Suit le code qui sera associé à ce mot, c'est-à-dire le code qui sera exécuté lorsque le mot sera interprété. Enfin, le mot ; est utilisé pour indiquer la fin du code et terminer la définition du mot.

En Forth, cette opérations est appelée la compilation. Le code est compilé dans le dictionnaire courant à la suite des autres mots.

Exemple :

: BONJOUR ." BONJOUR A TOUS" ;

Ce code crée un mot BONJOUR qui affiche BONJOUR A TOUS à l'écran.

: *2 DUP + ;

Ce code crée un mot *2 qui double la valeur sur la pile. En effet, DUP duplique la valeur sur la pile, et + ajoute les deux premières valeurs de la pile. Le résultat se retrouve sur la pile. On peut utiliser ce mot de la manière suivante :

5 *2 .

Qui affiche 10.

Comme on peut le voir, les mots créés peuvent être nommés avec des caractères spéciaux. Forth ne connaît que les mots et ne cherche pas à les interpréter autrement que par leur nom. 5 *2 n'est pas une expression mathématique, mais deux mots à exécuter.

Il est aussi possible de créer des mots en langage machine. Pour cela, on utilise le mot CREATE suivi du nom du mot et du langage machine à exécuter. Les valeurs du langage machines sont compilés directement grâce aux mots , et C,. La création du mot se termine par le mot SMUDGE qui indique que le mot est valide et prêt à être exécuté (en inversant le drapeau SMUDGE du mot).

Par exemple, après avoir oublié la définition de *2 en utilisant le mot FORGET, on peut créer un mot *2 en langage machine :

FORGET *2
HEX
CREATE *2 E1 C, 29 C, E5 C, E9DD , SMUDGE

Le code machine correspond à l'assembleur suivant :

E1    ; POP HL       \ Retire la valeur de la pile et la place dans HL
29    ; ADD HL, HL   \ Double la valeur dans HL
E5    ; PUSH HL      \ Remet la valeur de HL sur la pile
DDE9  ; JP (IX)      \ Saute à l'interpréteur Forth pour continuer l'exécution

Structure d'un mot

Comprendre comment est formé un mot en Forth est important pour comprendre comment fonctionne le langage en interne. Cela aide lorsque l'on veut comprendre comment un mot est exécuté, ou comment un mot est compilé. Et cela aide aussi à comprendre comment fonctionnent les mots de définition.

Nom Signification Taille Contenu
NFA Name Field Address 1 octet bit 7 : 1
bit 6 : PRECEDENCE
bit 5 : SMUDGE
bit 4-0 : longueur du nom
n octets nom du mot, le dernier octet a le bit 7 à 1
LFA Link Field Address 2 octets adresse du mot précédent
CFA Code Field Address 2 octets adresse du code à exécuter
PFA Parameter Field Address n octets code à exécuter, adresses de mots, valeurs...

NFA

Le Name Field Address commence un mot et décrire ses caractéristiques. Le bit 7 du premier octet est à 1. Les bits 4 à 0 de ce même premier octet indiquent la longueur du nom du mot. C'est pourquoi un mot Forth a une limite à 31 caractères.

PRECEDENCE : si le bit 6 est à 1, cela signifie que le mot est de type IMMEDIATE. Lors de la compilation d'un mot, un mot immédiat est exécuté sur le moment, plutôt que compiler. (?TODO? Cf. le fonctionnement de la compilation)

SMUDGE : si le bit 5 est à 0, cela signifie que le mot est valide. S'il est à 1, il sera ignoré par la recherche de mots.

Lors de la définition d'un mot avec :, SMUDGE est placé à 1 par :, puis c'est le mot ; qui place ce bit à 0 lors de la fin de la compilation d'un mot.

Les octets qui suivent forment le nom du mot. Le dernier octet de ce nom a le bit 7 à 1.

LFA

Le Link Field Address pointe vers le mot précédent dans le dictionnaire. L'intégralité des mots du dictionnaire courant forment ainsi une liste chaînée. Le premier mot défini, LIT dans cette implémentation, a un LFA à 0, indiquant la fin de la chaîne.

CFA

Le Code Field Address pointe vers le code à exécuter lors de l'exécution du mot. Lors de l'exécution d'un mot, Forth prend l'adresse du CFA et y saute pour exécuter le code.

Cette adresse peut-être tout simplement l'adresse du PFA. C'est le cas pour les mots écrits en assembleur directement. Ce code aura la charge de rappeler la routine qui passe au mot suivant. Sur cette implémentation, il suffit de faire un JP (IX).

L'adresse du CFA peut aussi pointer vers un code qui va lire successivement des adresses de mots présentes dans la PFA. C'est le cas pour les mots écrits à partir d'autres mots Forth.

Note : PAMPUK Forth est un Forth avec une implémentation ITC (Indirect Threaded Code), ce qui est un type d'implémentation usuel sur les machines 8 bits. Dans d'autres types d'implémentations (DTC/Direct Threaded Code, STC/Subroutine Threaded Code, TIC/Token Threaded Code,...), le contenu du CFA peut être différent. Cependant, sa sémanitque reste la même : c'est ce qui indique ce qui doit être exécuté.

PFA

Le Parameter Field Address est l'endroit où sont stockés les paramètres du mot. Cela peut être :

  • du code assembleur lors que le CFA pointe directement vers le PFA,
  • des adresses de mots Forth lorsque le mot a été défini par :,
  • des valeurs quelconques utilisées par d'autres types de mots (variables, constantes,...).

Exemples

Le mot EXECUTE prend une adresse sur la pile et continue l'exécution à cette adresse. Le mot est situé à l'adresse 2026h.

Champ Valeurs en mémoire Signification
NFA 87h Bit 7 à 1 et longueur du nom du mot : 7
45h 58h 45h 43h 55h 54h C5h Les octets ASCII du nom du mot qui forment EXECUTE, le dernier octet a son bit 7 à 1.
LFA 15h 20h Adresse du mot précédent : 2015h, l'adresse du NFA de LIT
CFA 32h 20h Adresse du PFA de EXECUTE, qui suit juste après
PFA Le code machine qui prendre l'adresse sur la pile, la place dans le registre HL puis saute à cette adresse.

Le code assembleur dans le PFA :

POP   HL
LD    E,(HL)
INC   HL
LD    D,(HL)
EX    DE,HL
JP    (HL)

Le mot ALLOT qui réserve de la place dans le dictionnaire, est un mot écrit avec d'autres mots Forth. Il est situé à l'adresse 2774h.

Champ Valeurs en mémoire Signification
NFA 85h Bit 7 à 1 et longueur du nom du mot : 5
41h 4Ch 4Ch 4Fh D4h Les octets ASCII du nom du mot qui forment ALLOT, le dernier octet a son bit 7 à 1.
LFA 69h 27h Adresse du mot précédent : 2769h, l'adresse du NFA de HERE
CFA 32h 20h Adresse du code d'exécution des mots définis par :.
PFA Liste de CFA des mots qui composent ALLOT.

Le code Forth dans le PFA :

  DP    \ met sur la pile la dernière adresse allouée
  +!    \ prend la valeur en seconde position sur la pile et l'ajoute à la valeur pointée par l'adresse en première position de la pile
  ;     \ termine le mot et retourne à la boucle d'exécution Forth

Les mots de définition

Les mots de définition sont des mots qui créent d'autres mots. Le plus utilisé est :, central au Forth. Mais on y trouve aussi CONSTANT et VARIABLE. À chaque fois que l'exécution d'un mot en ajoute un nouveau au dictionnaire, ce mot est un mot de définition.

Pour comprendre les mots de définition, il est nécessaire d'avoir compris la structure d'un mot en Forth. Un mot de définition a en effet en charge de remplir les champs du mot créé.

Ainsi, le mot CONSTANT crée un NFA avec le nom qui suit, lie le nouveau mot au précédent du dictionnaire courant, met dans le CFA l'adresse d'un code qui mettra la valeur trouvée dans le PFA sur la pile, et enfin met dans le PFA la valeur trouvée sur la pile lors de l'exécution de CONSTANT.

Le nouveau mot créé, lorsqu'il sera exécuté, aura donc pour CFA l'adresse du code qui mettra la valeur sur la pile, et pour PFA la valeur à mettre sur la pile.

On peut voir que le mot CONSTANT est associé à deux codes exécutables : le code de CONSTANT en soi, exécuté lors de son appel et qui en charge de construire l'autre mot ; mais aussi le code qui sera « donné » à chacun des mots créés. À chacune des constantes dans ce cas-ci.

Créer des mots de définition

Il est possible de créer ses propres mots de définitions, et pour cela, Forth offre deux mots pour aider à ces manipulations : <BUILDS et DOES>.

<BUILDS se charge de créer un mot avec le nom qui suivra le mot de définition lors de son appel. Les mots qui suivent servent ensuite à opérer sur ce mot afin de le construire, de le mettre en forme.

Il est possible par exemple de stocker des informations dans le PFA du mot créé.

DOES> se charge d'indiquer que les mots suivant seront exécutés lors de l'exécution du mot créé. Lors de l'exécution de cette partie, l'adresse PFA+2 du mot créé sera disponible sur la pile.

Par exemple, CONSTANT qui est un mot de définition, pourrait être définit de la manière suivante :

: CONSTANT
  <BUILDS ,
  DOES> @
  ;

Lorsque le mot CONSTANT est appelé, l'exécution de <BUILDS crée un mot avec le nom qui suit l'appel de CONSTANT. Le premier paramètre du PFA est aussi réservé. Puis , est exécuté. Ce mot prend la valeur sur la pile et l'ajoute à la fin du dictionnaire, c'est-à-dire, à ce moment-là, au PFA+2 du mot en train d'être créé.

Ensuite, DOES> est exécuté. Ce mot place (DOES) (un mot non disponible à l'utilisation directe) dans le CFA du mot en train d'être créé. Dans le premier paramètre du PFA, DOES> place un pointeur vers la liste de mots qui le suivent.

Le code pointé est ici @, qui prend l'adresse sur la pile et met la valeur à cette adresse sur la pile. L'adresse sur la pile est l'adresse du PFA+2, qui est l'adresse où se trouve la valeur qui a été mise là lors de la création du mot par ,. @ est suivi de ;S, qui termine l'exécution du mot et retourne à l'appelant.

Pour aller plus loin, un exemple plus détaillé est disponible dans la section des exemples.

Créer des mots de définition en assembleur

Il existe une alternative à <BUILDS DOES> pour créer des mots de définition. Elle est à utiliser lorsque le code exécuté par le mot créé est en assembleur plutôt qu'en Forth.

Ce nouveau couple de mots est CREATE et ;CODE.

Dans cette version, CONSTANT serait défini de la manière suivante :

: CONSTANT
  CREATE ,              \ Cette partie est similaire à <BUILDS
  [COMPILE] SMUDGE      \ Cependant, avec CREATE, il est nécessaire d'utiliser SMUDGE
  ;CODE                 \ Équivalent à DOES>, mais la suite devra être en assembleur Z80
  HEX
  13 C,                 \ INC DE          se place sur la valeur de la constante
  EB C,                 \ EX  DE,HL
  5E C,                 \ LD  E,(HL)      / récupère
  23 C,                 \ INC HL          | la valeur
  56 C,                 \ LD  D,(HL)      | de la
  D5 C,                 \ PUSH DE         \ constante
  DD C, E9 C,           \ JP (IX)         Continue l'exécution de l'interpréteur Forth
  ;

Le code après ;CODE est tout simplement le code en ROM du mot CONSTANT de ce Forth.

Note : le mot ;CODE termine effectivement le mot CONSTANT. Cela agit comme un ;, mais place aussi la suite de l'exécution, en code machine, à l'adresse qui suit. Pour y placer le code machine, on utilise le mot C, qui place un octet à HERE et incrémente cette variable.

Pour aller plus loin, un exemple plus détaillé est disponible dans la section des exemples.

Les mots immédiats

Les mots immédiats sont des mots qui sont exécutés immédiatement lors de la compilation, plutôt que compilés.

Normalement, lors de la compilation, les adresses des mots spécifiés sont ajouté au code du mot en cours. Cependant, lorsqu'un mot immédiat est rencontré, plutôt que compilé, il est exécuté. Cela permet d'agir sur la compilation en cours.

Exemple IF/ENDIF

Prenons pour exemple les mots IF et ENDIF. Ce sont deux mots immédiats dont les définitions sont les suivantes :

: IF COMPILE OBRANCH HERE 0 , 2 ; IMMEDIATE
: ENDIF ?COMP 2 ?PAIRS HERE OVER - SWAP ! ; IMMEDIATE

Le mot IMMEDIATE après la définition indique que le mot est immédiat. Il est donc exécuté lors de la compilation, plutôt que compilé dans le mot en cours.

Ainsi, lorsque IF est rencontré lors de la compilation, il exécute immédiatement le code suivant :

COMPILE OBRANCH   \ Compile le CFA du mot 0BRANCH
HERE              \ Met immédiatement dans la pile l'adresse courante de la RAM
0 ,               \ Place 0000 (sur 2 octets) à l'adresse courante de la RAM
2                 \ Met la valeur 2 dans la pile
;                 \ Fin de la procédure IF

Ainsi, après l'exécution de IF, la RAM ressemblera à ceci :

...
CFA de 0BRANCH    \ 2 octets
00 00             \ 2 octets, la valeur 0000
...

Et sur la pile nous avons l'adresse où a été placé 0000, ainsi que la valeur 2.

Plus tard, lorsque ENDIF est rencontré lors de la compilation, il exécute immédiatement le code suivant :

?COMP             \ Vérifie si nous sommes en mode de compilation d'un mot, sinon affiche une erreur
2 ?PAIRS          \ Vérifie si le sommet de la pile est égal à 2, sinon affiche une erreur
                  \ le 2 placé par IF est consommé, le sommet de la pile est donc l'adresse de 0000
HERE              \ Met l'adresse courante de la RAM sur la pile
OVER              \ Duplique l'adresse de 0000 et le place au sommet de la pile
-                 \ Soustrait l'adresse de 0000 de l'adresse courante, laissant la différence sur la pile
                  \ Ce résultat est la distance entre l'adresse courante et l'adresse de 0000 placée par IF
SWAP              \ Échange les deux valeurs du sommet de la pile, laissant l'adresse de 0000 en haut
!                 \ Prend l'adresse de 0000 et la distance calculée, et place cette distance à l'adresse de 0000
                  \ Ceci va servir de paramètre au saut relatif effectué par 0BRANCH
;                 \ Fin de la procédure ENDIF

Après ENDIF, la RAM ressemblera à ceci :

...
CFA de 0BRANCH    \ 2 octets
nn nn             \ saut relatif de 2 octets calculé par ENDIF
...

Lorsque 0BRANCH est exécuté lors de l'exécution du mot qui a été compilé avec IF et ENDIF, il lit le sommet de la pile. Si ce sommet est égal à 0, il ajoute nnnn (la valeur calculée par ENDIF) à son IP (Instruction Pointer) pour effectuer le saut relatif jusqu'à l'adresse correspondant à ENDIF. Sinon, il ajoute 2 à son IP pour poursuivre l'exécution du programme.

Les conditions

Le mot principal pour définir un branchement conditionnel en Forth est IF. Ce mot prend la valeur sur la pile et si celle-ci est non nulle, continue l'exécution du code du mot en cours. Dans le cas contraire, l'exécution se poursuit après le mot ELSE s'il est présent, ou après le mot THEN sinon.

D'un point de vue syntaxique, la lecture d'une condition en Forth est un peu étonnante lorsque l'on vient de langages plus usuels.

La condition se trouve en effet avant le mot IF. Et THEN est à comprendre comme un « puis ensuite », terminant la clause conditionnelle.

: IS<3 3 < IF ." INFERIEUR A 3" ELSE ." PAS INFERIEUR A 3" THEN ;
2 IS<3  \ Affiche "INFERIEUR A 3"
4 IS<3  \ Affiche "PAS INFERIEUR A 3"

IF, ELSE et THEN étant des mots immédiats, leur utilisation n'est valide que pendant la compilation d'un mot.

ENDIF est équivalent à THEN, et est parfois préféré pour des raisons de clarté.

Les boucles

Il existe plusieurs manières d'effectuer des boucles en Forth.

DO/LOOP

La plus usuelle est la boucle utilisant DO et LOOP. Elle prend le format suivant :

<borne supérieure> <valeur de démarrage> DO ... LOOP

À l'intérieur de la boucle, entre les deux mots, le mot I permet de lire la valeur de l'index de la boucle, dont les valeurs successives sont comprises entre la valeur de démarrage et la borne supérieure (exclue).

En détails :

  • DO est un mot immédiat qui compile sa primitive (DO) (attention aux parenthèses !) dans le mot en cours de définition. (DO) se chargera à l'exécution de lire les valeurs de la borne supérieure et de la valeur initiale pour les enregistrer dans la pile de retour. La valeur initiale, en haut de la pile, devient alors la valeur de l'index courant. [DO] utilise aussi le même mécanisme que IF/ENDIF pour préparer le saut relatif qui sera effectué par LOOP (voir l'exemple du paragraphe sur les mots immédiats).

  • DO et LOOP doivent obligatoirement se trouver à l'intérieur de la même définition, c'est-à-dire entre un : et ; d'un même mot.

  • LOOP est un mot immédiat qui compile sa primitive (LOOP) dans le mot en cours de définition. Le mot se charge également de calculer le saut relatif attendu par DO. L'exécution de (LOOP) augmente l'index de 1 (l'index est présent en haut de la pile retour) et vérifie si l'index a atteint ou dépassé la borne supérieure (en deuxième position dans la pile retour). Si c'est le cas, la boucle se termine, sinon le programme continue à exécuter le code entre DO et LOOP.

  • Le mot I lit la valeur de l'index à l'intérieur de la boucle. Il est important de noter que I ne peut être utilisé qu'à l'intérieur de la boucle, c'est-à-dire entre DO et LOOP, pour avoir le sens attendu.

Code exemple :

DECIMAL
: TEST 10 1 DO CR ." INDEX = " I . LOOP CR ;
TEST

Affiche

INDEX = 1
INDEX = 2
INDEX = 3
INDEX = 4
INDEX = 5
INDEX = 6
INDEX = 7
INDEX = 8
INDEX = 9

Il est possible de faire des boucles imbriquées, c'est-à-dire de mettre un DO/LOOP à l'intérieur d'un autre DO/LOOP. Dans ce cas, le mot J permet de lire l'index de la boucle extérieure. I continue de lire l'index de la boucle la plus intérieure.

Code exemple :

: TEST
      2 1 DO
            3 1 DO
                  CR
                  ." INDEX EXTERIEUR = " J .
                  ." INDEX INTERIEUR = " I .
            LOOP
      LOOP
      CR ;
TEST

Affiche

INDEX EXTERIEUR = 1 INDEX INTERIEUR = 1
INDEX EXTERIEUR = 1 INDEX INTERIEUR = 2
INDEX EXTERIEUR = 1 INDEX INTERIEUR = 3
INDEX EXTERIEUR = 2 INDEX INTERIEUR = 1
INDEX EXTERIEUR = 2 INDEX INTERIEUR = 2
INDEX EXTERIEUR = 2 INDEX INTERIEUR = 3

DO/+LOOP

Variante de DO/LOOP, la boucle DO/+LOOP permet d'incrémenter l'index de la boucle d'une valeur différente de 1. Elle prend le format suivant :

<borne supérieure> <valeur de démarrage> DO ... <valeur d'incrémentation> +LOOP

Code exemple :

DECIMAL
: TEST 10 1 DO CR ." INDEX = " I . 2 +LOOP CR ;
TEST

Affiche

INDEX = 1
INDEX = 3
INDEX = 5
INDEX = 7
INDEX = 9

BEGIN/UNTIL

Une autre manière de faire des boucles est d'utiliser les mots BEGIN et UNTIL. Cette boucle continue tant qu'une condition n'est pas remplie. Cette condition est testée à chaque itération de la boucle en évaluant le sommet de la pile au moment du UNTIL.

Code exemple :

: TEST
      BEGIN                   \ marque le début de la boucle
            CR ." KEY 5 ?"    \ affiche "KEY 5 ?"
            KEY               \ lit une touche et met la valeur sur la pile
            53 =              \ teste si la touche est 5 (53 en ASCII) et met le résultat sur la pile
      UNTIL                   \ tant que le sommet de la pile n'est pas 1, retourne au début de la boucle
      CR                      \ affiche un retour à la ligne
      ." OK 5" CR             \ affiche "OK 5"
      ;                       \ termine le mot TEST

BEGIN/WHILE/REPEAT

Une variante de la boucle BEGIN/UNTIL est la boucle BEGIN/WHILE/[REPEAT]. Elle continue tant qu'une condition est remplie. Cette condition est testée au début de chaque itération de la boucle en évaluant le sommet de la pile au moment du WHILE.

Code exemple :

: TEST
      BEGIN                   \ marque le début de la boucle
            CR ." KEY 9 ?"    \ affiche "KEY 9 ?"
            KEY               \ lit une touche et met la valeur sur la pile
            57 <              \ teste si la touche est inférieure à 9 (57 en ASCII) et met le résultat sur la pile
      WHILE                   \ si le sommet de la pile est 0 (faux), alors la boucle se termine.
            ." < 9"           \ sinon affiche "< 9"
      REPEAT                  \ puis retourne au début de la boucle
      CR                      \ affiche un retour à la ligne
      ." OK 9" CR             \ affiche "OK 9"
      ;                       \ termine le mot TEST

BEGIN/AGAIN

La boucle BEGIN/AGAIN est une boucle infinie. On ne peut en sortir qu'explicitement, par exemple en utilisant le mot ABORT ou en réinitialisant la machine.

Code exemple :

: TEST
      BEGIN                         \ marque le début de la boucle
            CR ." PRISONNIER !"     \ affiche "PRISONNIER !"
            KEY                     \ lit une touche et met la valeur sur la pile
            53 =                    \ teste si la touche est 5 (53 en ASCII) et met le résultat sur la pile
      IF                            \ si le sommet de la pile est 1
            ABORT                   \ alors on réinitialise l'interpréteur Forth
      ENDIF
      AGAIN                         \ retourne au début de la boucle
      ;                             \ termine le mot TEST

La mémoire

La mémoire de la machine est entièrement accessible. Il est bien entendu important de connaître sa structure pour savoir où lire, où écrire et aussi surtout où ne pas écrire n'importe quoi.

Il y a des espaces mémoire importants et nommés en Forth. Par exemple, HERE pointe vers la prochaine adresse libre dans le dictionnaire, TIB pointe vers le buffer de texte, PAD pointe vers un espace mémoire temporaire.

Les mots principaux pour la mémoire sont @ et !. Le premier permet de lire une valeur à une adresse, le second d'écrire une valeur à une adresse.

Pour manipuler des octets, on utilise les mots C@ et C!. Pour manipuler des valeurs de 32 bits, on utilise les mots 2@ et 2!.

Il est aussi possible d'accéder à la mémoire vidéo avec les mots V@ et V!.

Les adresses importantes s'obtiennent à partir de mots spécifiques. Par exemple, un mot construit par VARIABLE donne l'adresse de la variable ainsi créée.

Il est aussi possible de réserver de la mémoire avec le mot ALLOT.

Plan de la mémoire du HRX

Mémoire principale

----  --------------------------- -----
0000  ROM
      Système d'exploitation et
      routines graphiques
1FFF
----  --------------------------- -----
2000  ROM FORTH
3FFF
----  --------------------------- -----
4000  Pile images (empile vers
      les addresses hautes)

      Pile des paramètres
      (empile vers les addresses
5E00  basses)
----  --------------------------- -----
5FF0  Début de la pile des        S0 @
      paramètres
----  --------------------------- -----
6000  Dictionnaire (empile vers
      les addresses hautes)

DDBC  10 buffers d'édition        FIRST
      ...
      ...                         LIMIT
FE00  TIB                         TIB @

      Pile des appels (empile
      vers les addresses basses)
FF42  Variables FORTH             R0
FF8E  Mot FORTH en RAM
FFA0  Adresse de départ
FFF0  Autres variables et pile
      système (?TODO?)

Variables FORTH

Adresse Contenu
FF42 NFA du dernier mot du vocabulaire éditeur
FF44 Nombre de lignes par « screen » (21)
FF46 Adresse d'exécution de BLOCK
FF48 Adresse d'exécution de R/W
FF4A Adresse du CFA de R/W
FF4C Variable USE (prochain buffer)
FF4E Variable PREV (buffer courant)
FF50 Variable OFFSET
FF52 Nombre de caractères pour le nom des « screens »
FF54 Nombre de caractères par ligne pour l'éditeur
FF56 Variable FIRST (DDBCh)
FF58 Variable LIMIT (FE00h)
FF5A Nombre d'octets par buffer
FF5C Variable R0
FF5E Variable TIB
FF60 Variable WIDTH
FF62 Variable FENCE
FF64 Variable DP
FF66 Variable VOC-LINK
FF68 Variable BLK
FF6A Variable IN
FF6C Variable SCR
FF6E Variable CONTEXT
FF70 Variable CURRENT
FF72 Variable STATE
FF74 Variable BASE
FF76 Variable DPL
FF78 Variable CSP
FF7A Variable R
FF7C Variable HLD
FF7E Variable ASSADR qui contient le CFA du vocabulaire d'assembleur (?TODO?). Pointe sur QUIT s'il n'y a pas de vocabulaire d'assembleur.
FF80 Adresse de NUMBER
FF82 Adresse de ERROR
FF84 Adresse de (chaînage d'un autre langage ?TODO?)
FF86 Adresse de FWADR, adresse du premier mot Forth exécuté. Pointe sur ABORT.
FF88 Graine pour RND et RANDOM
FF8A Pointeur vers le dernier MESSAGE
FF8C Pointeur vers la pile des appels
FF8E Le mot FORTH compilé
FF9C Variable LATEST
FFA1 Variable S0

Mémoire vidéo

----  --------------------------- -----
C000  Écran vidéo

----  --------------------------- -----
F900  Alphabet spécial

----  --------------------------- -----
FD00  Alphabet standard

----  --------------------------- -----
FE61  Variables graphiques

----  --------------------------- -----
FFF0  Autres variables et pile
      système (?TODO?)
----  --------------------------- -----
Adresse Contenu
FE8A calibrage 50ième de seconde (horloge)
FE8B calibrage secondes (horloge)
FE8C calibrage minutes (horloge)
FE8D calibrage heures (horloge)
FE8E 50ième de seconde (horloge)
FE8F secondes (horloge)
FE90 minutes (horloge)
FE91 heures (horloge)

Les mots de vocabulaire

Il existe pour Forth deux vocabulaires actifs : le vocabulaire courant et le vocabulaire de contexte. Le vocabulaire courant est celui dans lequel les mots compilés sont ajoutés. Le vocabulaire de contexte est celui dans lequel les mots sont cherchés lors de l'interprétation et de la compilation.

Les vocabulaires sont chaînés, c'est-à-dire qu'un vocabulaire nouvellement défini pointe vers le vocabulaire courant. Ainsi, les mots de ce vocabulaire « parent » sont disponibles pour le nouveau vocabulaire.

Lors de l'utilisation du mot : celui-ci commence par changer le vocabulaire de contexte pour le vocabulaire courant. Ainsi, après une définition, le contexte est revenu au vocabulaire courant.

Exemple :

VOCABULARY MONVOC           \ Crée un nouveau vocabulaire MONVOC
: BONJOUR ." BONJOUR" ;     \ Crée un mot BONJOUR dans le vocabulaire FORTH et non MONVOC
MONVOC DEFINITIONS          \ Change le vocabulaire courant et de contexte pour MONVOC
: AUREVOIR ." AUREVOIR" ;   \ Crée un mot AUREVOIR dans le vocabulaire MONVOC

BONJOUR                     \ Affiche BONJOUR
AUREVOIR                    \ Affiche AUREVOIR

FORTH                       \ Change le vocabulaire de contexte pour FORTH

BONJOUR                     \ Affiche BONJOUR
AUREVOIR                    \ Erreur, le mot AUREVOIR n'est pas dans
                            \ le vocabulaire de contexte
                            \ ??? Et pourtant ça fonctionne. AUREVOIR est trouvé. Pourquoi ?

: BYE ." BYE" ;             \ Le mot BYE est ajouté au vocabulaire MONVOC, qui est
                            \ toujours le vocabulaire courant. Au passage, le vocabulaire
                            \ de contexte est changé pour MONVOC

BYE                         \ Affiche BYE
AUREVOIR                    \ Affiche AUREVOIR

Le mini-éditeur

PAMPUK Forth est livré en ROM avec un éditeur de texte rudimentaire. Celui-ci permet de manipuler et de mettre au point des mots puis de les compiler, les sauvegarder, les charger.

L'éditeur manipule des SCREEN de 38 colonnes par 21 lignes, ce qui fait 798 octets. Ces screens sont stockés dans des buffers de 826 octets dont la structure est détaillée plus bas. Il y a de la place réservée pour 10 screens simultanés dans la mémoire de l'ordinateur. Lorsque cette mémoire est pleine et qu'un nouveau screen est demandé, un plus ancien sera retiré de la mémoire, après sauvegarde si nécessaire.

Un buffer peut contenir du code source afin de le compiler, mais il peut tout aussi bien contenir des données brutes afin de les sauver et charger depuis une mémoire de masse.

Attention, du au fonctionnement du Forth, il est important d'oublier (FORGET) les versions précédentes d'un mot avant d'en compiler une nouvelle version. Si le compilateur PAMPUK Forth affiche un message lorsqu'un mot est redéfini, ce n'est qu'un avertissement, le mot est effectivement redéfini.

Manipulations dans le mini-editeur

Pour pouvoir changer le contenu d'un écran il faut utiliser le vocabulaire de contexte EDITOR.

Format interne des screens et buffers

Un buffer est composé de 826 octets.

Offset   Contenu
------   -------
0-1      Numéro du buffer, le bit de poids fort est à 1 si le buffer a été modifié.
2-25     Nom du buffer. Initialisé avec des zéros.
         Lorsque le nom est modifié, il est complété avec des espaces.
26-823   798 octets de données.
824-825  Deux octets à 0 ?

Les buffers se situent entre les adresses FIRST et LIMIT.

Note : lors de la première utilisation de OPEN, le buffer ouvert est le suivant par rapport à PREV. Ce qui fait que le premier buffer n'a l'air utilisé qu'après avoir rempli tous les autres.

Il y a deux variables qui indiquent quel est le buffer courant : l'une est PREV, qui sert pour les manipulations de buffers par le système et indique une adresse. L'autre est SCR, qui est utilisée pour les manipulations par l'utilisateur et indique un numéro.

L'éditeur pleine page

Il existe aussi un éditeur pleine page, disponible sur cassette (et disquette pour le Hector HRX/MX uniquement ?TODO?).

Il se loge en adresse 4000h, ce qui permet de laisser libre l'espace mémoire usuel de compilation de mots, au détriment de la place pour manipuler les images.

Se lance avec EDIT (n - ) ou E (pour le screen courant).

Ajoute les mots GLUPS ( - n1 n2) (pour les erreurs de compilation), WHERE (pour afficher le screen au niveau de l'erreur), LLIST pour un listing sur imprimante, COPY ( n1 n2 - ) pour copier le contenu d'un buffer vers un autre, :MOT qui permet d'ajouter des raccourcis clavier d'édition et KILL_EDITOR qui supprime l'éditeur pleine page de la mémoire.

Des commandes d'édition sont ajoutées aux commandes usuelles (?TODO? les lister ici ?).

On quitte l'éditeur avec CTRL + RETURN.

Fonctionnement avec une cassette

Lors d'un FLUSH avec une cassette, le système va proposer des opérations pour chacun des buffers modifiés.

Par exemple :

ECRAN NO      1
SUR K7 ?

Les commandes possibles à ce moment sont :

  • R pour rembobiner la cassette
  • P pour passer un bloc (nom pour la représentation d'un buffer sauvegardé) de la cassette. Son numéro est affiché mais il n'est pas chargé en mémoire.
  • O oui, sauve le buffer sur cassette
  • N non, ne sauve pas le buffer sur cassette

Lors d'un LIST ou un LOAD, si le buffer n'a pas été ouvert, le système va automatiquement charger depuis la cassette le premier bloc trouvé et l'insérer dans le buffer avec le numéro indiqué.

Fonctionnement avec une disquette

Afin de pouvoir utiliser une disquette, il est nécessaire de charger les mots supplémentaires depuis la disquette PAMPUK Forth. Celle-ci doit être insérée dans le lecteur A puis l'entrée « disquette » doit-être sélectionnée dans le menu de démarrage.

Une fois les mots chargés, le Forth est initialisé et se trouve avec le vocabulaire DISK actif. Les nouveaux mots sont donc disponibles.

Le mot R/W est aussi redirigé pour fonctionner avec la disquette.

(?TODO?) ajouter ce paragraphe

Affichage graphique

La surface d'affichage de l'Hextor HRX fait 243 x 228 pixels adressables individuellement, chaque pixel pouvant prendre une couleur dans une palette de 4 couleurs. Ces couleurs sont choisis parmi 18couleurs en deux luminosités.

L'origine de l'écran est en bas à gauche.

La mémoire vidéo se situe entre les adresses C000h et FFFFh. Ces adresses sont partagés avec les adresses de la RAM, il est donc nécessaire de choisir le mapping de la mémoire pour accéder à la mémoire vidéo.

La partie affichée de la mémoire vidéo se situe entre les adresses C000h et F8FFh incluses. Chaque pixel est représenté par 2 bits, ce qui permet les 4 index de couleurs simultanés possibles. Cela signifie aussi qu'un octet encode 4 pixels consécutifs. Les 2 bits de poids fort de l'octet représentent le pixel le plus à droite de l'écran, et inversement, les 2 bits de poids faible représentent le pixel le plus à gauche.

La mémoire d'affichage est linéaire, partant du coin haut gauche de l'écran et décrivant l'affichage ligne par ligne. Chaque ligne utilise 64 octets. Cela donne une largeur en pixels théorique de 256. Cependant, seuls les 243 premiers sont affichés.

Couleurs

Code Pleine luminosité Demi-luminosité
0 Noir Noir
1 Rouge Rubis
2 Vert Olive
3 Jaune Marron
4 Bleu Marine
5 Magenta Violet
6 Cyan Turquoise
7 Blanc Gris

La palette se change avec le mot COLOR.

Modes d'affichage graphique

Il y a deux modes d'affichages, le mode BIG et le mode LITTLE. Dans le mode BIG, les opérations sur la surface graphiques sont effectués par groupe d'octets, ce qui permet un accès rapide. Dans le mode LITTLE, les opérations sont effectuées pixel par pixel, ce qui permet plus de précision, au détriment de la vitesse.

Système d'images

Il est possible de définir des images avec le mot IMAGE. Ce mot est un mot de définition, qui va en créer un autre. Celui-ci aura la capacité de s'afficher à l'écran.

Exemple :

4 BASE !        \ passe en base 4
1111 1111       \ défini la forme de l'image
1000 0001
1222 2221
1111 1111
DECIMAL         \ passe en décimal
2 4 IMAGE CARRE \ défini le mot CARRE de 2 octets par 4 lignes

3 INK 25 25 75 75 BOX  \ affiche un rectangle
50 50 0 CARRE   \ affiche l'image entièrement

Il est possible de créer un masque associé à une image. La définition de MASK est similaire à celle de IMAGE et doit suivre sa définition afin d'y être associé.

Exemple :

4 BASE !        \ passe en base 4
0111 1110       \ défini la forme de l'image
1000 0001
1000 0001
0111 1110
DECIMAL         \ passe en décimal
2 4 IMAGE ROND  \ défini le mot ROND de 2 octets par 4 lignes

4 BASE !        \ passe en base 4
3000 0003       \ défini la forme du masque
0000 0000
0000 0000
3000 0003
DECIMAL
2 4 MASK        \ défini le masque de l'image précédente

3 INK 25 25 75 75 BOX \ affiche un rectangle
50 50 1 ROND    \ affiche l'image avec son masque
30 30 2 ROND    \ affiche l'image en mode « dessus »
30 50 3 ROND    \ affiche l'image en mode « dessous » (et donc n'apparait pas, car dans le rectangle)
100 100 3 ROND  \ affiche l'image en mode « dessous » (et ici, en dehors du rectangle, apparait)

Les modes 4 à 7 permettent de copier le morceau d'image cible (là où l'image s'affiche) dans une pile d'images. Il est ensuite possible de rappeler ces images, ce qui permet une gestion de l'affichage en plusieurs couches, ou de faire des sprites de manière aidée.

Exemple

50 50 0 CARRE     \ affiche CARRE
50 50 4 ROND      \ affiche ROND au même endroit avec copie de l'image cible
BPOP              \ le dessin de CARRE est restauré
Mode Effet
0 Affichage de l'image telle quelle, y compris la couleur d'index 0.
1 Affichage de l'image associée à son masque. Les couleurs d'index 3 du masque laissent voir le fond.
2 Mode « dessus ». Affiche tous les points dont l'index est différente de celle de PAPER.
3 Mode « dessous ». Affiche tous les points si le pixel de dessin est d'index de couleur égale à PAPER.
4 Comme le mode 0, plus copie dans la pile image
5 Comme le mode 1, plus copie dans la pile image
6 Comme le mode 2, plus copie dans la pile image
7 Comme le mode 3, plus copie dans la pile image

Modes texte

L'alphabet SPECIAL affiche les caractères s'affichent toujours en mode plein et en mode BIG. Les caractères ne peuvent être placés que sur des multiples de 4 pixels. Il y a 28 colonnes dans ce mode et l'affichage du texte est rapide.

L'alphabet spécial a des représentations pour les caractères ASCII de 32 à 95.

L'alphabet STANDARD affiche les caractères en mode calque par défaut et en mode LITTLE. Les caractères peuvent être placés au pixel près. Il y a 38 colonnes dans ce mode et l'affichage du texte est plus lent.

Les modes d'affichage texte et les modes d'affichages graphiques sont indépendants. On peut être en STANDARD et en BIG par exemple, ou bien en SPECIAL et LITTLE.

Caractères de contrôles

Les caractères de contrôles s'utilisent avec EMIT. Par exemple 11 EMIT est l'équivalent de CLS.

Code Effet
1 HOME
7 Émet un bip
8 Recule le curseur du mode texte
9 Avance le curseur du mode texte
11 CLS
12 PAGE
13 CR
14 Place le curseur en haut à gauche de l'écran sans l'effacer
15 CLS HOME
23 Mode vidéo inverse, valable en SPECIAL et STANDARD, en mode papier
24 Mode normal, valable en SPECIAL et STANDARD (annule 23)
25 Mode papier, valable en STANDARD
26 Mode calque, valable en STANDARD
27 AT (0 0 25 EMIT met le curseur en haut à gauche de l'écran)
28 SCALE
29 WINDOW
30 PEN
31 PAPER