Le langage FORTH stocke les nombres sur une pile nommée "pile de données" ou "pile paramétrique". L'empilage d'un nombre est très simple:
55
empile le nombre 55. Pour dépiler ce nombre, il y a plusieurs méthodes. La plus simple consiste à afficher le contenu du dernier élément empilé:
. affiche 55
Si vous empilez plusieurs nombres, voici ce qui se passe:
11 45 6543
Empilage du premier nombre
11 Empilage du second nombre 45
11
le nombre 11 passe en dessousEmpilage du troisième nombre 6543
45
11
le nombre 45 passe en dessous.
le nombre 11 passe en 3e position
Le dépilage successif des nombres affiche ceux-ci dans l'ordre inverse de leur empilage:
. affiche 6543
. affiche 45
. affiche 11
Pour mieux visualiser le mécanisme d'empilage et de dépilage des nombres, pensez à une pile d'assiettes: la dernière assiette déposée sur la pile sera la première reprise.
A tout moment vous pouvez prendre connaissance du contenu de la pile de données sans avoir à provoquer le dépilage des valeurs qui y sont stockées en utilisant le mot .S:
1 2 3 .S affiche 1 2 3
Ce principe de rangement est appelé également pile LIFO (Last In, First Out) dans certains ouvrages écrits en anglais pour désigner une pile dont le mécanisme est: "dernier entré, premier sorti".
Avec TURBO-Forth, la quantité de nombres empilables est assez élevée, mais reste limitée. Si vous empilez trop de nombres, vous saturerez la pile de données. De même, toute tentative pour dépiler un nombre alors que la pile de données est vide, affichera un message d'erreur. Exemple:
ABORT . affiche le message d'erreur Pile vide
Si vous désirez connaître à tout instant le nombre d'éléments présents sur la pile de données, tapez STATUS? ON ou la touche spéciale Shift-F3 et le message de disponibilité OK sera précédé des indications suivantes:
0 (dec) OK
Le nombre débutant la ligne indique le nombre d'éléments figurant sur la pile de données:
0 (dec) OK entrez 25
1 (dec) OK entrez 75 12
3 (dec) OK entrez +
2 (dec) OK entrez . affiche 87
1 (dec) OK ...etc...
L'indication entre parenthèses indique la base numérique courante du système. Elle peut être (dec) pour la base décimale (valeur par défaut), (hex) pour la base hexadécimale et (oct) pour la base numérique octale.
Cette option peut être désélectionnée par STATUS? OFF ou Control-F3.
Chaque nombre empilé ne peut être qu'un nombre entier au format 16 bits. Selon les cas, ce nombre peut être considéré signé ou non signé:
- signé ou non signé: paramètre de calcul arithmétique
- non signé: adresse 16 bits intra-segment
En format 16 bits signé, le bit de poids le plus fort indique le signe du nombre.
La valeur -1, déposée sur la pile en tant que valeur entière a la représentation binaire suivante:
Cette même valeur peut être affichée en valeur absolue en utilisant le mot U. à la place du mot . (point):
-1 U. affiche 65535
Selon que l'on considère les entiers 16 bits signés ou non, leur intervalle de définition est:
- 16 bits signés entre [-32768..32767]
- 16 bits non signés entre [0..65535]
L'utilisation d'un nombre quelconque situé en dehors de cet intervalle n'aura aucune signification:
100000 . ou
-100000 U. affichera une valeur sans rapport apparent avec 100000.
Cette valeur résiduelle correspond à la partie poids faible sur 16 bits du nombre 100000 et n'ayant pas débordé lors de son empilage sur la pile de données.
Ceci n'a aucune utilisation pratique et peut être comparable à la tentative d'hébergement d'une moissonneuse batteuse dans un enclos réservé aux bicyclettes, avec pour vous le risque en moins de ne pas être poursuivis par des propriétaires de vélos endommagés par cette maneuvre plus que douteuse. Cette limitation peut sembler contraignante au premier abord, mais les performances de FORTH sont à ce prix.
Le traitement des nombres entiers est beaucoup plus rapide que celui de nombres exprimés dans des formats différents. Vous constaterez, à l'usage, que les nombres entiers suffisent dans la majorité des cas. Nous verrons plus loin, que FORTH n'est pas limité au traitement des nombres entiers 16 bits: il suffit d'exprimer des données complexes sur plus d'une cellule de la pile.
Si vous êtes totalement débutant en FORTH, prenez le temps nécessaire à bien comprendre ce qu'est la pile de données, son mécanisme et son évolution: la notion de pile est certainement la plus importante du langage FORTH.
Les opérateurs arithmétiques + - * et / agissent sur les deux valeurs situées au sommet de la pile de données. Les valeurs traitées sont toujours des entiers 16 bits signés.
Pour additionner deux nombres, il faut d'abord les déposer sur la pile de données:
22 44 + . affiche 66
Une fois empilés 22 et 44, le mot + opère l'addition de ces deux valeurs et le mot . (point) affiche le résultat.
55 1 + 3 + . affiche 59 et peut aussi s'écrire
55 1 3 + + .
L'addition est commutative: les valeurs peuvent être déposées sur la pile de données dans n'importe quel ordre:
55 22 + est équivalent à
22 55 +
Ce principe de calcul est appelé NOTATION POLONAISE INVERSE (RPN dans la littérature anglaise, pour Reverse Polish Notation). On peut aussi additionner deux nombres entiers non signés, à condition de visualiser le résultat à l'aide du mot U. au lieu de . (point).
35000 10 + U.
Il est également possible de faire la somme de deux nombres de signe différent:
10 -5 + . affiche 5
-5 10 + . affiche aussi 5
Selon que l'on traite des valeurs considérées comme signées ou non, les intervalles de définition des résultats doivent respectivement se situer dans [- 32768..32767] ou [0..65535]. Tout résultat situé hors de ces intervalles n'aurait aucun sens.
TURBO-Forth dispose également des primitives 1+ et 2+ qui incrémentent respectivement la valeur située au sommet de la pile de données de 1 ou deux unités:
10 1+ est équivalent à
10 1 +
Soit deux nombres a et b. La différence de deux nombres sera écrite en FORTH sous la forme:
a b - pour a-b
La soustraction n'est pas commutative.
10 3 - . affiche 7
3 10 - . affiche -7
Les primitives 1- et 2- décrémentent respectivement la valeur située au sommet de la pile de données de 1 ou deux unités:
10 1- est équivalent à
10 1 -
Soit deux nombres a et b. Le produit de deux nombres sera écrit en FORTH sous la forme:
a b * pour a*b
La multiplication est commutative:
7 5 * . ou
5 7 * . affiche 35
Les primitives 2* et 8* multiplient respectivement la valeur située au sommet de la pile de données par deux ou huit:
5 2* est équivalent à 5 2 *
Pour la division, seul le quotient entier est conservé sur la pile de données:
22 7 / . affiche 3
La division n'est pas commutative:
15 5 / . affiche 3
5 15 / . affiche 0
Le reste de la division peut être obtenu en appliquant la fonction modulo:
22 7 MOD . affiche 1
La fonction modulo peut servir à déterminer la divisibilité d'un nombre par un autre:
: DIV? ( n1 n2 ---)
OVER OVER MOD CR
IF SWAP . ." n'est pas "
ELSE SWAP . ." est " THEN
." divisible par " . ;22 7 DIV? affiche 22 n'est pas divisible par 7
22 11 DIV? affiche 22 est divisible par 11
La fonction /MOD combine les actions de / et de MOD:
22 7 /MOD . . affiche 3 1
Par parenthèse, pour les BASICois de longue date incomplètement convaincus par FORTH, voici la même fonction transcrite dans leur patois:
? INT(22/7),22-(INT(22/7)*7) ce qui est évident... pour les habitués. ou
? INT(22/7),22 MOD 7 si votre BASIC a été rafraîchi récemment.
Si on essaye une opération du type suivant:
30000 3 * 10 / .
on risque d'être quelque peu surpris par le résultat. Mais tout s'explique, car le produit calculé en premier délivre une valeur dont la capacité est supérieure à celle admise par les valeurs 16 bits signées. Pour traiter ces cas particuliers, on utilisera de préférence le mot */ qui combine les opérations de multiplication et de division, mais traite le résultat transitoire de la multiplication au format double précision. Exemple, soit à calculer le prix TTC d'une marchandise (TVA à 18,6 %), on définira le mot TTC comme suit:
: TTC ( n1 --- n2)
DUP 186 1000 */ + ;100 TTC . affiche 118
Les valeurs traitées étant exprimées en centimes.
La primitive */MOD a les mêmes propriétés que */, mais délivre le quotient et le reste de l'opération.
Une autre application immédiate et pratique des notions déjà exprimées, est la conversion des degrés Fahrenheit et Celsius:
-la conversion des degrés Fahrenheit en degrés Celsius obéit à la formule
°C=(°F-32)*5/9
-la conversion des degrés Celsius en degrés Fahrenheit obéit à la formule
°F=9/5*°C+32: C>F ( °C --- °F)
9 5 */ 32 + ;: F>C ( °F --- °C)
32 - 5 9 */ ;37 C>F . affiche 98 (les résultats sont arrondis)
Les opérations peuvent être chaînées, mais une opération en notation algébrique comportant des parenthèses doit être convertie en notation RPN en tenant compte de l'ordre de priorité des opérations. FORTH n'utilise pas les parenthèses dans les opérations arithmétiques:
soit l'expression algébrique ( 2 + 5 ) * ( 7 - 2 )
elle s'écrit en FORTH 2 5 + 7 2 - *
Lors d'une opération de conversion de notation algébrique infixée en notation polonaise inverse, commencez toujours par le niveau de parenthèse le plus imbriqué et par la gauche. Ecrivez la transcription en notation polonaise inverse de chaque opération sur des lignes séparées, successivement de haut en bas, en les mettant dans le prolongement de l'expression algébrique exprimée dans la formule initiale:
En reprenant chaque niveau dans l'ordre, on réécrit la formule:
2 5 + 7 2 - * 5 2 + 3 * /
C'est choquant? Mais tous les interpréteurs/compilateurs travaillent ainsi lorsqu'ils ont à évaluer une formule algébrique. En notation algébrique, les parenthèses ne servent qu'à isoler une expression sous forme de sous-expression qui devient membre d'une expression plus générale.
En informatique comme en arithmétique, un opérateur travaille toujours sur deux opérandes et seulement deux opérandes simultanément. Le résultat d'une opération portant sur deux opérandes délivre une valeur qui peut devenir à son tour opérande d'un autre opérateur. L'ordre d'exécution des opérandes et des opérateurs est fondamental:
|
Tous les problèmes arithmétiques peuvent être résolus de cette manière; ce n'est qu'une question d'habitude. L'exemple donné précédemment illustre parfaitement la rigueur dont doit faire preuve le programmeurs Forth. Cette rigueur garantit un fonctionnement sans ambiguïté des programmes, quel que soit leur niveau de complexité.
La pile de données est l'élément fondamental du langage FORTH pour le traitement de données. Son fonctionnement est identique à celui de la pile gérée par le micro-processeur. Dans certaines situations, les données traitées par les différentes définitions doivent être réordonnées ou dupliquées.
Le mot DUP duplique le contenu du sommet de la pile de données:
10 DUP . . affiche 10 10
5 DUP * . affiche 25
Le mot OVER duplique le second élément de la pile de données:
5 15 OVER . . . affiche 5 15 5
Le mot SWAP inverse les deux éléments du sommet de la pile de données:
1 3 SWAP . . affiche 1 3
Le mot ROT effectue une rotation sur les trois éléments situés au sommet de la pile de données:
1 2 3 ROT . . . affiche 1 3 2
Le mot -ROT effectue une rotation inverse sur trois éléments. Son comportement est similaire à l'exécution de deux ROT successifs.
Le mot PICK dépose au sommet de la pile de données le nième élément de la pile de données, n non compris. La base de départ pour le comptage des éléments à traiter est 0 et non 1, l'élément numéro zéro étant situé immédiatement après le paramètre traité par PICK. La séquence 0 PICK est similaire à DUP, 1 PICK à OVER. Il n'y a pas de traitement d'erreur si n est supérieur au nombre d'éléments déposés sur la pile de données. Exemple:
1 2 3 4 5 6 4 PICK empile 2 car 6 est l'élément n°0, 5 l'élément n°1, etc...
Le mot ROLL effectue une rotation sur les n premiers éléments de la pile de données, n non compris. Comme pour PICK, la base de départ pour le comptage des éléments à traiter est 0:
1 2 3 4 5 6 4 ROLL . . . . . . affiche 2 6 5 4 3 1
Voici quelques exemples d'utilisation de ces manipulateurs de pile de données:
: AU-CARRE ( n --- n2)
DUP * ;
Le mot AU-CARRE élève un nombre entier quelconque au carré:
2 AU-CARRE . affiche 4
3 AU-CARRE . affiche 9
4 AU-CARRE . affiche 16: AU-CUBE ( n --- n3)
DUP DUP * * ;
Le mot AU-CUBE élève un nombre entier quelconque au cube:
2 AU-CUBE . affiche 8
3 AU-CUBE . affiche 27
Attention, n'utilisez pas des valeurs trop élevées, car un résultat supérieur à 32767 devient faux. Ayez toujours à l'esprit que les données que vous traitez sont des entiers 16 bits, donc de capacité limitée. Nous verrons ultérieurement comment traiter des nombres plus importants.
Vous avez peut-être déjà feuilleté le manuel ou consulté le dictionnaire FORTH à la recherche de INPUT pour faire un programme comme en BASIC. Eh bien soyez rassuré, INPUT n'existe pas! (ah, bon, vous n'êtes pas rassuré...?). Allons, nous ne voulions pas vous peiner. TURBO-Forth a quand même plus d'un tour dans son sac:
80 STRING TAMPON$
: ELEVE-AU-CARRE
CR ." Entrez un nombre: " TAMPON$ INPUT$
CR ." Elevation au carré: " TAMPON$ $EXECUTE
DUP * . CR ;
Voilà, vous l'avez, votre programme comme en BASIC! Bien entendu, notre petit artifice a consisté à faire transiter notre nombre saisi au clavier par une variable chaîne préalablement déclarée, puis dont le contenu a été évalué. Essayez ceci maintenant:
2 CONSTANT DEUX
3 CONSTANT TROIS
ELEVE-AU-CARRE
et si vous entrez DEUX lorsque le programme demande un nombre, il fonctionnera très bien. Par l'intermédiaire de $EXECUTE, TURBO-Forth évalue non seulement un nombre, mais aussi les expressions:
ELEVE-AU-CARRE
et entrez DEUX TROIS + vous affichera le carré de cinq.
Toute cette petite digression pédagogique avait pour seul but de marquer encore plus la distance entre FORTH et les autres langages. En voulant faire comme BASIC, on a alourdi la procédure d'élévation au carré. La philosophie du langage FORTH tend à la simplicité. Il faudra toujours privilégier l'aspect sobre et un peu rustique des définitions. Un mot traitant une donnée passée par la pile de données n'est pas moins efficace qu'un programme alambiqué écrit en N'IMPORTE- QUOI-D'AUTRE (C, PASCAL, FORTRAN, COBOL, etc... et bien s-r B-A-S-I-C).
A coté de la pile de paramètres, il existe dans FORTH une deuxième pile, appelée pile de retour parce qu'elle sert à l'interpréteur interne à retrouver l'adresse de retour à chaque appel d'une procédure.
Il y a parfois des cas extrêmes où l'on peut être amené à stocker un ou plusieurs paramètres ailleurs que sur la pile de données, ceci pour simplifier quelque peu certaines manipulations scabreuses. la solution la plus commode, parmi d'autres, est la pile de retour. Cette pile est accessible par les mots >R et R> moyennant quelques précautions pour ne pas compromettre le fonctionnement de cette pile interne.
Le mot >R transfère un nombre entier de la pile de données vers la pile de retour. Le mot R> transfère un nombre entier de la pile de retour vers la pile de données.
Une opération >R R> est nulle. En fin de définition, il doit y avoir autant de >R que de R> sous peine de perturber quelque peu le déroulement normal de votre définition. Exemple d'utilisation:
: ^2 ( n --- n^2) \ élévation au carré DUP >R \ transfert de la copie sur pile de retour CR ." Le carré de " . \ affichage valeur initiale ." est " R> DUP * . ; \ récupération valeur déposée sur pile retour \ et affichage de son carré
Ici, les mots >R et R> ont servi à mémoriser la valeur initiale déposée sur la pile de données. Nota: si vous essayez cet exemple, ne tapez pas les commentaires qui suivent le caractère \, FORTH ne les compilant pas.
Si aucune donnée n'a été transférée depuis la pile de données vers la pile de retour, l'utilisation du mot R> transfère l'adresse d'exécution du prochain mot à exécuter au sommet de la pile de données:
: PROCHAIN-MOT ( --- adr)
R> DUP >R ;
Mais ceci n'a réellement d'intérêt que si vous devenez un barbu de la programmation en FORTH et que vous cherchez à comprendre les mécanismes subtils qui animent ce langage.
En FORTH, les entiers 16 bits déposés sur la pile de données peuvent être affichés par exécution du mot . (point). Mais d'autres mots permettent d'exécuter un affichage plus présentable.
Le mot .R affiche un nombre 16 bits signé cadré à droite dans un champ de n caractères. Exemple:
3 10 .R affiche 3 (3 précédé de 9 espaces)
101 10 .R affiche 101 (101 précédé de 7 espaces)
Et dont voici une application très pratique:
: C>F ( °C --- °F) \ Conversion Celsius en Fahrenheit
9 5 */ 32 + ;
: .TREMPE ( °C ---) \ Affiche °C et °F formatés
DUP 6 .R ." °C "
C>F 6 .R ." °F" ;: TREMPE-ACIER ( ---) \ Table des températures de trempe
CR ." COULEURS DE TREMPE DE L'ACIER:" CR
CR ." rouge foncé " 680 .TREMPE
CR ." rouge cerise foncé" 740 .TREMPE
CR ." rouge cerise " 770 .TREMPE
CR ." rouge cerise clair" 800 .TREMPE
CR ." rouge clair " 850 .TREMPE
CR ." rouge très clair " 900 .TREMPE
CR ." rouge jaune " 950 .TREMPE
CR ." jaune " 1000 .TREMPE
CR ." jaune clair " 1100 .TREMPE
CR ." jaune blanc " 1200 .TREMPE
CR ." blanc " 1300 .TREMPE
CR CR ;
Le mot U.R procède de la même manière que .R, mais affiche le nombre entier non signé:
1 10 U.R affiche 1
-1 10 U.R affiche 65535
Exemple, soit à afficher une suite d'adresses en hexadécimal et l'équivalent en décimal entre parenthèses:
: .H(D) ( n ---)
BASE @ >R \ sauvegarde base numérique courante
DUP HEX 4 U.R \ affichage en hexadécimal dans un champ de 4 car.
SPACE ." (" \ affichage d'un espace et parenthèse ouverte
DECIMAL 5 U.R \ affichage en décimal dans un champ de 5 caractères
." )" \ affichage de la parenthèse fermée
R> BASE ! ; \ restauration base numérique courante
DECIMAL
CR 256 .H(D) affiche 100 ( 256)
CR 65535 .H(D) affiche FFFF (65535)
HEX
CR FF .H(D) affiche FF ( 255)
CR FFFF .H(D) affiche FFFF (65535)
FORTH ne connaît pas les nombres en virgule flottante. Cependant, des routines peuvent être compilées pour exécuter des calculs sur ce type de nombres, mais ce sera au détriment de la vitesse de traitement. Si vous voulez traiter des quantités importantes, comme votre compte en banque ou le prix de votre prochaine voiture, vous pouvez utiliser les nombres double précision également nommés nombres 32 bits. Ceux-ci se distinguent des nombres entiers 16 bits en y introduisant un point, une virgule ou une barre de fraction:
135246. D. affiche 135246
135246, D. affiche ;135246
Le point, la virgule ou la barre de fraction peuvent être placés ailleurs qu'à l'extrémité du nombre:
135,246 D. affiche 135246
Les opérations élémentaires exécutables sur les nombres 32 bits sont l'addition et la soustraction:
3. 5. D+ correspond à 3 + 5
3. 5. D- correspond à 3 - 5
Les nombres double précision 32 bits situés au sommet de la pile peuvent être affichés par les mots suivants:
D. prononcer 'D-point'. Affiche un nombre double précision signé.
UD. prononcer 'U-D-point'. Affiche un nombre double précision non signé.
D.R prononcer 'D-point-R'. Affiche un nombre double précision signé cadré à droite dans un champ de n caractères.
UD.R prononcer 'U-D-point-R'. Affiche un nombre double précision non signé cadré à droite dans un champ de n caractères.
Exemples:
5. D. affiche 5
-1. UD. affiche 4294967295
10. 12 D.R affiche 10
35.3 12 UD.R affiche 353
Un nombre entier 16 bits signé peut être transformé en nombre 32 bits signé en exécutant le mot S>D. Exemples:
10 S>D D. affiche 10
-5 S>D D. affiche -5
Pour transformer un nombre entier 16 bits non signé en nombre 32 bits non signé, il suffit d'empiler la valeur 16 bits 0 sur la pile après empilage du nombre entier 16 bits non signé. Exemple:
10 0 D. affiche 10
-5 0 D. affiche 65531 L'opération inverse est possible si la valeur 32 bits est comprise entre 0 et 65535:10. DROP . affiche 10
Quand un nombre 32 bits est déposé sur la pile de données, la variable DPL conserve la position du point décimal; si c'est un nombre 16 bits qui est déposé sur la pile de données, la variable DPL contient la valeur - 1. On peut exploiter cette valeur pour définir un mot assurant une conversion conditionnelle:
: ?S>D ( d ou n --- d)
DPL @ 0< IF S>D THEN ;
10 ?S>D D. affiche 10
10. ?S>D D. affiche 10
Il est intéressant de comparer les valeurs de DPL pour différentes positions du point décimal lors du traitement d'un nombre 32 bits:
3314. DPL @ . affiche 0
331.4 DPL @ . affiche 1
33.14 DPL @ . affiche 2
3.1.4 DPL @ . affiche 1
(seule la position du second point est conservée dans DPL)
Les opérations de multiplication et de division 32 bits ne sont pas définies à l'exception des mots D2* et D2/:
10. D2* D. affiche 20
10. D2/ D. affiche 5
Mais avec les primitives de FORTH, il n'y a aucun problème pour définir des mots permettant de multiplier un nombre 32 bits par trois, quatre, cinq ou plus le cas échéant:
: D3* ( d --- d*3)
2DUP 2DUP D+ D+ ;
: D4* ( d --- d*3)
D2* D2* ;
: D5* ( d --- d*5)
2DUP D2* 2SWAP D3* D+ ; etc...
Les nombres 32 bits sont des valeurs généralement trop importantes pour qu'il soit intéressant de leur appliquer un traitement du type produit ou quotient. Toutefois, certains opérateurs arithmétiques mixtes permettent d'exécuter ces opérations.
Le mot MU/MOD divise un nombre 32 bits non signé par un nombre 16 bits signé; laisse sur la pile le quotient 32 bits non signé et le reste signé. Exemple:
11. 2 MU/MOD D. . affiche 5 1
Le mot M/MOD divise un nombre 32 bits signé par un nombre 16 bits signé; laisse sur la pile le quotient et le reste au format 16 bits signés. Exemple:
-12. 2 M/MOD . . affiche -6 0
Le mot U*D multiplie deux nombres 16 bits non signés et laisse sur la pile le résultat au format 32 bits non signé. Exemple:
10 30 U*D D. affiche 300
Le mot *D multiplie deux nombres 16 bits signés et laisse sur la pile le résultat au format 32 bits signé. Exemple:
-10 30 *D D. affiche -300
-- hautdepage -- page d'accueil --