Forth Interpreteur/Compilateur

English Version



Un manuel complet d'utilisation Manuel du FORTH PDF
Version LibreOffice modifiable Je veux le traduire!

Il s'agit d'un Éditeur, Interpréteur et Compilateur FORTH pour ATARI sous TOS.
  • Versions 68030 (Falcon, TT) et 68000 (STe)
  • Versions française et anglaise
  • Support de la ST et TT Ram
  • Support du FPU, mais travaille également sans.
  • Fonctions Gemdos, Bios, Xbios, VDI, AES
  • Support du Supercharger en coprocesseur
  • Support d'un assembleur extérieur
  • Extensions mathématiques calculs et graphiques
  • Support des modules M&E de PARX (gestion d'images et sons)
  • Dialogue avec M_PLAYER/MP_STE pour la vidéo
  • Multitâche coopératif
  • Création aisée de menus ou dialogues directement en FORTH
  • Programme compact et rapide, entièrement écrit en assembleur
  • Aide en ligne (en cours...)


Version 0.5.4 (28/01/2023):
  • Gestion des modules M&E (charger/sauver des images + effets) + modules AUDIO
  • Courbes 2D et Courbes et surfaces 3D
  • Dialogue avec M_PLAYER/MP_STE pour la vidéo
  • Aide en ligne avec help ou guide et ST-Guide ou HypView
  • Possibilité de multitâche, piles et pages séparées
  • Gestion des modules M&E (charger/sauver des images + effets)
L'aide avec guide, ici HypView est ouvert L'aide en ligne avec help

Uh langage interactif Exemple programme de codage Téléchargement Inclusion d'assembleur Modules M&E Parx Le multitâche M_Player/MP_STE Mathématiques 2D
Création du menu Autres programmes en Forth Tout dans un seul source Visualiseur d'images Variables & stations locales Création d'animations Courbes et surfaces 3D
Sauvegarde TGA Calculs avancés

Un langage interactif:

Le langage permet une utilisation en mode direct, à la manière d'une calculatrice, mais dans lequel on peut créer de nouveaux mots afin d'enrichir les possibilités.

Ci-contre, imaginons qu'on veuille réduire une série de prix de 20%, on en calcule à chaque fois 80%. Le premier exemple donne le nouveau prix pour un article de 59€: c'est 47€.
Vous remarquez qu'on empile d'abord les valeurs avant d'opérer dessus, le "." sert à afficher le sommet de la pile. Pour éviter de retaper les calculs, on crée le mot reduc avec la paire : ; et on l'utilise sur un prix de 70€ réduit à 56€.
On peut également taper un petit programme avec ici la stucture list..dolist..lloop qui crée une boucle à partir d'une liste hétéroclite de valeurs.
Dans la boucle on affiche l'indice i (prix de départ) puis un espace puis le prix réduit.

system permet de quitter l'interpréteur.

Utilisation en mode direct



Codage d'un message avec une seule variable!
Un programme de codage de message

Écrivons pas à pas un programme sous GEM avec menu pour coder ou décoder un message. Allons d'abord au coeur du sujet...

Nous allons coder un mot qui permet de réaliser un décalage quelconque dans l'alphabet sur un message en majuscules. Pour cela, une seule variable: p qui pointera sur les caractères successifs de la chaîne passée sur la pile.

  • p ! : stocke dans p l'adresse de la chaine
  • .b size : on va travailler sur des bytes
  • begin/while/repeat : pour parcourir la chaine
  • p )@ : ramène l'octet pointé par p
  • while : tant qu'il est différent de zéro
  • dup 65 90 <seg> if : teste si on est entre A et Z
  • over + : dans ce cas ajoute le décalage (relatif)
  • dup 90 > if : si au dela de Z revient dans l'alphabet
  • dup 65 < if : si en dessous de A, remonte dans l'alphabet
  • p )+! : stocke à nouveau l'octet, éventuellement modifié, et incrémente le pointeur
  • drop : retire la valeur n de la pile


Utilisons notre mot

Pour cela, on va passer par la chaine prédéfinie pad qui sert au forth pour certains résultats d'opérations de chaines.

  • On stocke dans pad la chaine "UN TEXTE SECRET !"
  • On réalise un décalage de 17 dans l'alphabet avec 17 pad code
  • pad type affiche la chaîne codée
Pour décoder le message, on utiliserait -17 pad code, mais...

Pour décoder un message sans la clé, on peut lui faire subir les 25 décalages possibles: c'est ce que fait la boucle 25 ndo..nloop suivante.
  • 1 pad code permet de réaliser un nouveau décalage
  • pad type cr affiche la chaîne et saute une ligne
Vous voyez le message apparaître en clair (je n'ai recopié qu'une partie des 25 lignes).




Version utilisable dans un programme.
Codage avec prompt

Nous allons enrober le mot code afin qu'il demande clairement les informations à l'utilisateur et affiche le résultat. C'est ce que fait le mot code-decode.
  • 15 >ifflag : permet une compilation conditionnelle, le flag 15 étant celui de la langue, vrai dans la version française et faux dans la version anglaise.
  • ." Clé : " input cr affiche clé et attend une valeur laissée sur la pile, puis saut de ligne
  • ." Texte : " input$ cr idem mais avec une chaine stockée dans pad
  • code réalise le codage demandé
  • pad type cr affiche finalement le message modifié


La recherche brute

De même, implémentons la recherche brute des 25 décalages si un message est fourni sans sa clé, c'est le mot multi-decode.
  • ." Texte : " input$ cr drop , cette fois-ci l'adresse pad est enlevée de la pile par drop car non utilisée directement.
  • 26 1 do..loop bouclera avec i=1 à i=25 (s'arrête avant 26!)
  • i . space affiche l'indice et un espace
  • 1 pad code décale pad de un dans l'alphabet
  • pad type cr et l'affiche avec un saut de ligne




Créons le menu !

Pour créer ce petit menu, point n'est besoin de passer par un fichier RSC. Le Forth s'occupe de tout à partir d'un tableau de chaînes.
  • 30 10 array$ MENU crée un tableau de 10 chaînes de 30 caractères appelé MENU
  • 16 allot constant BUFFER alloue 16 octets pour le buffer de l'AES (appel evnt_mesag)
  • On remplit ensuite le tableau MENU
  • d'abord les titres puis une chaine vide
  • pour chaque titre son drop-down et une chaine vide
  • 0 MENU menu, l'instruction menu crée l'arbre d'objets en mémoire en tenant compte des accessoires et renvoie l'adresse qu'on conserve dans TREE
  • 7 gemindex 0 TREE menu_ienable sert à griser le séparateur (chaine 7)


Un dernier effort

Un petit mot pour réagir à l'entrée Infos... du menu.

Ce sera une simple boîte d'alerte.




La boucle principale!

Voici une boucle classique de gestion d'événements AES.
  • fastopen drop cls ouvre la fenêtre Forth, jette son handle et efface l'écran
  • 1 TREE menu_bar affiche la barre de menu
  • begin...until boucle jusqu'à ce qu'une valeur non nulle soit renvoyée (par Exit)
  • begin...until va attendre l'arrivée d'un message
  • BUFFER evnt_mesag appelle l'AES et attend un événement
  • BUFFER w@ 10 = le premier mot du buffer est-il 10? (menu selected)
  • BUFFER 8 + w@ ramène l'index GEM de l'entrée du menu
  • strindex convertit cette valeur en numéro de chaîne pour que je m'y retrouve
  • case...endcase traite les 4 cas possibles, seul Exit renvoie 1 pour sortir
  • BUFFER 6 + w@ renvoie l'index GEM de l'entrée du menu passée en vidéo inverse
  • 1 TREE menu_tnormal et remet le menu en vidéo normale
  • 0 TREE menu_bar en quittant, efface la barre de menu.


Lancement automatique ou non

Je veux que sous l'interpréteur, la compilation du source ne lance pas l'exécution du programme afin de garder la main en mode interactif et de tester chaque partie indépendemment.

Par contre, lorsque j'utilise le compilateur externe pour générer un programme indépendant et débuggé, il faut que la procédure se lance.

On utilise à nouveau une compilation conditionnelle avec le flag 13 qui est vrai sous l'interpréteur et faux sous le compilateur.
Note: par défaut, le programme compilé efface la souris, donc on a ajouté v_show_c pour qu'elle revienne.




Codage d'un message puis décodage brut
Notre programme en action !

Le programme se lance, affiche son menu et sa fenêtre.

Il ne vous reste plus qu'à utiliser les différentes entrées pour le tester.

Vue partielle du décodage brut, on retrouve notre message


Téléchargements

FORTH: Interpréteur et compilateur français et anglais.
Versions 68030 et 68000, exécutables et sources.

PARX M&E modules de gestion d'images et extension AUDIO.
avec l'aimable autorisation de Pierre-Louis Lamballais
et d'Eric Da Cunha.


Codage: source et éxécutable français et anglais du programme présenté ici.




Autres exemples de programmes en FORTH

Lien avec la HP-41 et son interface série
Menu, communication série

Utilisation des instructions pour piloter le Supercharger comme un coprocesseur.
Inclusion assembleur PC, instructions Supercharger

Sauvegarde et restauration des données de la BOSS-DR5 via MIDI.
Menu, fichiers, communication MIDI

Gestion de l'horloge RTC de l'Apollo Vampire V4.
Inclusion assembleur, création dialogue, dialogue en fenêtre




Inclure de l'assembleur

On peut vouloir remplacer le mot code par une routine assembleur. Ceci est très simple:
  • On prépare une routine commençant par le mot 437 sur 16 bits. Ce mot indique au Forth que c'est une routine assembleur.
  • La zone DATA doit commencer par le marqueur "ADD_DEFS" sur 8 octets
  • Chaque routine est ensuite listée, -1 servant de marqueur de fin
  • Chaque nom de mot est ensuite ajouté avec un zéro de séparation
  • On assemble ce source vers F:\_TEMP.PRG" par exemple
  • A la place de la définition de code, on ajoute
  • Votre routine doit simplement préserver a4 et a5 et utiliser a6 comme pointeur de pile


Encore mieux: tout dans un seul source!

Le texte en assembleur ainsi que la génération du PRG à inclure peuvent être pilotés directement depuis le FORTH. Voici comment:
  • Dans votre listing Forth, la directive >export (1) sauve le texte jusqu'à la directive >comp (2) sous le nom fourni: ici F:\_TEMP.S.
  • Ensuite, la directive >exec (3) permet de lancer l'assembleur avec la ligne suivante comme argument.
  • Pour finir, le PRG étant disponible, il est inclus avec la directive >include (4).

Lors de la compilation, vous verrez l'activité de votre assembleur:


Note: vous remarquez un argument 0 pour ces directives, il peut être utilisé en liaison avec un Flag afin d'exécuter ou d'ignorer ces ordres. Par exemple, si vous ne travaillez que sur la partie Forth, il est inutile de resauver et générer le PRG de la partie assembleur à chaque compilation.




Les modules M&E de Parx Systems: lecture d'une image GIF

Dans ce programme, nous allons charger une image GIF avec le module GIF00.RIM et l'adapter à l'écran avec le module PARX252.TRM pour l'afficher quelque soit la résolution.

Définition des variables
variable p       \ le module RIM
variable t       \ le TRM
variable fichier \ adresse du fichier
variable total   \ taille totale fichier
256 string filename		  \ chemin+nom du fichier
1536 allot constant PALs  \ palette source
1536 allot constant PALd  \ palette destination
variable fhd
Chaque module sera pointé par une variable.

Les palettes sont à leur taille maximale pour recevoir 256 couleurs au format VDI:
256 couleurs × 3 composantes × un mot de 2 octets (0 à 1000) = 1536.
Chargement des modules
: main
   " d:\parx.sys\" 1 modset

   0 t !
   " parx252.trm" graphic_card negate t modload drop

   0 p !
   " rim\gif00.rim" -1 p modload drop
  • modset: fixe le chemin du dossier PARX
  • modload: charge le module de tramage PARX252.TRM
    graphic_card negate permet d'adapter le module à n'importe quelle carte graphique
  • modload: charge le module de lecture GIF00.RIM
Lecture et décompression du GIF
   " F:\FORTH\GIPHY.GIF" filename $!

   filename 0 fopensize dup fhd !
   0> if
      total !
      fhd @ total @ dup allot dup fichier ! fread drop
      fichier @ total @ filename fhd @ PALs 1 p dorim drop
  • fopensize : ouvre le fichier GIPHY.GIF
  • fread : si l'ouverture est ok, lit entièrement le fichier en RAM
  • dorim : lance le RIM pour la décompression

L'image en 16 bits
Tramage écran
      mfdbs mfdbd fillmfdb
      work_out 2- w@ mfdbd 12 + w!
      %b1100110 1 PALs PALd 3 t dotrm drop
  • mfdbs/d : sont des descripteurs de blocs d'image utilisés par les modules
  • fillmfdb : duplique le descripteur de notre image source vers destination
  • work_out 2- w@ : nombre de plans de l'écran stocké en destination
  • dotrm : lance le tramage pour modifier le nombre de plans et l'adapter à l'écran
Copie vers l'écran
      mfdbd mfdbs fillmfdb
      PALs savevdipal
      PALd setvdipal
      0 mfdbd ! 0 0 0 0
      mfdbs 4 + w@ mfdbs 6 + w@ 3 vro_cpyfm
      key drop
      fhd @ fclose drop
      PALs setvdipal
   then
   p modunload
   t modunload
;
  • fillmfdb : reprend la destination comme étant la nouvelle source
  • savevdipal : copie la palette courante dans PALs
  • setvdipal : fixe la palette sur celle de la destination
  • 0 mdfdb ! : l'écran devient la nouvelle destination
  • vro_cpyfm : copie mon image à l'écran
  • key : attend une touche
  • fclose : referme le fichier
  • setvdipal : remet la palette courante
  • modunload : libère les modules

L'image en 4 bits avec le même programme
Pour un programme compilé
>comp

-13 >ifflag
   fastopen drop
   main
>endf
  • >ifflag : compilation conditionnelle, sautée par l'interpréteur (flag 13 pour la différence interpréteur, programme indépendant)
  • fastopen : ouvre la fenêtre FORTH
  • main : appelle notre programme

L'image en monochrome avec le même programme



Les modules M&E de Parx Systems: visualiseur d'image quelconque

Ce programme se propose de charger l'ensemble des modules RIM dans un tableau de modules afin de pouvoir lire n'importe quelle image et de l'afficher sur l'écran quelque soit sa résolution.
Ce programme reprend en grande partie le précédent, nous détaillerons les différences pour l'utilisation du tableau de modules.

Définition des variables
variable t       \ le TRM
variable fichier \ adresse du fichier
variable total   \ taille totale fichier
256 string filename
256 string chemin
32 string nom
1536 allot constant PALs
1536 allot constant PALd
20 allot constant MFDB
variable fhd
100 array MODS
variable nMODS
  • chemin/nom : deux chaines s'ajoutent pour le sélecteur de fichier
  • MODS : tableau de 100 valeurs pour pointer sur les modules
  • nMODS : contiendra le nombre total de modules chargés
Choix de l'image
: main
   cls
   " \*.*" chemin $!
   " " nom $!
   chemin nom fsel_input
   chemin nom path filename $!
   cls
  • $! : stocke dans mes chaînes un chemin par défaut
  • fsel_input : ouvre le sélecteur de fichiers
  • path : concatène le chemin et le nom pour l'accès complet
  • cls : efface la fenêtre

Le fichier sélectionné
Chargement des modules
   " d:\parx.sys\" 1 modset

   ." Charment des modules..." cr

   0 t !
   ( " parx252.trm" graphic_card negate t )
      modload drop

   0 0 MODS !
   ( " rim\*.rim" -1 0 MODS )
      modmload .. nMODS !

   ."  modules chargés" cr
  • modmload : mod multi load charge tous les *.rim dans le tableau
  • nMODS : variable recevant en retour le nombre de modules chargés
Chargement de l'image
   ." Chargement du fichier..." cr

   filename 0 fopensize dup fhd !
   0> if
      total !
      ( fhd @ total @ dup allot dup fichier ! )
         fread drop
Même système qu'avant, charge l'image en entier en mémoire.
Recherche du bon WIM
      nMODS @ 0 do
         -1 mfdbs fillmfdb
         ( fichier @ total @ filename fhd @ PALs 1 i MODS )
            dorim
         0<
      ifloop
  • do : boucle de 0 à nMODS-1 pour parcourir tous les modules
  • dorim : teste le module i
  • ifloop : continue la boucle tant qu'il y a erreur (valeur négative)
  • ifloop : sinon dépose sur la pile l'indice de sortie
Résultat de la recherche
      dup nMODS @ =
      if
         drop
         ." Image non reconnue" cr
      else
         MODS modlname type cr
         key drop
  • dup nMODS @ = : compare l'indice de sortie à nMODS
  • si égal : aucun module n'a fonctionné, on le signale
  • sinon : affiche le nom long du module
  • key : attend une touche

Modules chargés et celui qui a détecté l'image
Décodage et affichage
         mfdbs mfdbd fillmfdb

         work_out 2- w@ mfdbd 12 + w!

         ( %b1100110 1 PALs PALd 3 t )
            dotrm drop

         mfdbd mfdbs fillmfdb

         PALs savevdipal
         PALd setvdipal

         ( 0 mfdbd ! 0 0 0 0
         mfdbs 4 + w@ mfdbs 6 + w@ 3 )
            vro_cpyfm
            key drop
         PALs setvdipal
      then
      fhd @ fclose drop
   then
   0 MODS nMODS @ modmunload
   t modunload
;

>comp

-13 >ifflag
   fastopen drop
   main
>endf
Ici la fin du programme est identique au précédent.

On appelle le TRM pour s'adapter à l'écran.

On affiche le bloc avec vro_cpyfm.

modmunload : mod multi unload libère tous les modules du tableau.

L'image TIF affichée en 16 bits



Les modules M&E de Parx Systems: Sauvegarde d'un bloc en TGA

Ce programme copie un bloc d'écran de 320x200 dans la résolution courante et le sauvegarde vers un image TGA 16 bits.

Définition des variables
variable p       \ le module WIM
variable t       \ le TRM
1536 allot constant PALs  \ la palette source (si besoin)
1536 allot constant PALd  \ la palette destination (inutilisée en 16 bits)
Définition classique des variable, comme dans les deux précédents programmes.

Chaque module est pointé par une variable, les palettes sont réservées au maximum de leurs capacités.
Chargement des modules
: main
   absolute
   " d:\parx.sys\" 1 modset

   ." Chargement des modules..." cr

   0 t !
   " parx252.trm" graphic_card negate t modload
   if exit else ."    TRM Ok" cr then

   0 p !
   " wim\tga_tc16.wim" 1 p modload
   if exit else ."    WIM Ok" cr then
   
   remember
  • absolute : passe le VDI en mode absolu (relatif au début de l'écran)
  • exit : les fonctions modload renvoient une valeur différente de zéro si elles échouent, dans ce cas, exit quitte le mot
  • graphic_card : toujours cette indication pour permettre le bon réglage du TRM
  • tga_tc16.wim : le module de sauvegarde en TGA 16 bits
  • remember : conserve le pointeur mémoire FORTH pour vider tous les blocs à la fin
Copie d'un bloc écran


   ." Bloc 320x200 avec palette" cr
   0 mfdbs !
   0 320 200 work_out 2- w@ 0 mfdbd fillmfdb
   mfdbd imagesize allot mfdbd !
   0 0 0 0 320 200 3 vro_cpyfm
   PALs savevdipal
   mfdbd mfdbs fillmfdb
  • 0 mfdbs ! : la source est l'écran
  • fillmdfb : prépare la destination en 320x200
  • imagesize : calcule la taille de l'image pour réserver le bloc avec allot
  • vro_cpyfm : copie le coin haut-gauche de l'écran vers la mémoire
  • savevdipal : sauvegarde la palette actuelle
  • fillmfdb : remet le bloc destination comme nouvelle source pour la suite

Ce que donne le programme à l'exécution
En cas de carte graphique
   ( graphic_card
   work_out 2- w@ 4 8 <seg> ) and
   if
      ." Passage en mode VDI..." cr
      1 mfdbd 10 + w!
      mfdbd imagesize allot mfdbd !
      vr_trnfm
      mfdbd mfdbs fillmfdb
   then
Si on se trouve sur une carte graphique dans un mode 4 ou 8 plans, il faut ramener notre bloc vers le format indépendant VDI, le TRM n'a pas les routines pour.

Dans les modes TC (15, 16, 24 et 32 bits) il gère parfaitement l'encodage.
  • 1 mfdbd 10 + w! : modifie le format destination vers VDI
  • imagesize : recalcule la taille pour un nouveau bloc
  • vr_trnfm : transforme le bloc
  • fillmfdb : remet le bloc destination comme nouvelle source pour la suite
Passage du bloc en 16 bits
   ." Transformation en 16 bits..." cr
   16 mfdbd 12 + w!
   %b0100011 1 PALs PALd 3 t dotrm
   if exit else ."    Dotrm Ok" cr then

   mfdbd mfdbs fillmfdb
  • 16 mfdbd 12 + w! : force à 16 le nombre de plans de la destination
  • dotrm : appelle le TRM pour transformer le bloc
  • fillmfdb : remet le bloc destination comme nouvelle source pour la suite

L'image sauvée à partir d'un mode TC (affichée par GemView)
Sauvegarde de l'image en 16 bits
   ." Sauvegarde en TGA 16 bits..." cr
   " I:\AAA.TGA" PALd 1 p dowim
   if exit else ."    Dowim Ok" cr then
Il ne reste plus qu'à appeler le WIM, notre bloc écran, quelque soit sa résolution d'origine, sera enregistré au format TGA 16 bits.
  • dowim : lance le WIM pour sauver l'image

L'image sauvée en TT Moyenne (affichée par GemView)
Nettoyage de la mémoire
   restore
   t modunload
   p modunload
   relative
;
  • restore : remet la mémoire FORTH dans l'état de l'appel de remember
  • modunload : libère les modules
  • relative : revient aux appels VDI relatifs à la fenêtre FORTH



Le multitâche coopératif : un exemple de calcul

L'idée générale est de créer des threads avec l'instruction thread, de leur donner à chacun une pile de travail, une page d'affichage et un identifiant. Une fois la liste définie, thrun lance l'ensemble des processus qui se passent la main avec thnext. Ils peuvent également se synchroniser avec thsync pour échanger des données, chacun ayant accès aux piles des autres.

Dans ce premier exemple, nous allons calculer la somme des entiers de 1 à n avec trois threads: Le dernier thread sera responsable de la collecte des données et d'afficher la somme globale. Vous remarquerez que la première valeur de chaque somme est égale à l'identifiant du thread, nous utiliserons cette astuce dans le mot somme.
Définition des variables:

n qui contiendra la limite à atteindre et les trois piles de travail pour chaque tâche. 10 entiers chacune est largement suffisant.
Calcul d'une somme:

C'est ce même mot qu'appellent les trois tâches pour leur calcul.
do ouvre la boucle de thid (l'identifiant 1,2 ou 3) à n+1
3 +loop permet d'aller de 3 en 3
après chaque addition, le thread passe la main au suivant avec thnext
Code exécuté par les threads 1 et 2:

Voici le programme exécuté par les deux premiers threads, les esclaves. Tant que la valeur n est non nulle, ils calculent leur somme puis se mettent en attente de synchronisation sur le canal zéro avec 0 thsync pour transmettre leur résultat avant de recommencer.
Code exécuté par le threads 3:

Voici le programme exécuté par le dernier thread, le maître.
  • 3 0 thnsync indique qu'ils sont 3 à se synchroniser sur le canal zéro.
  • input saisit la limite stockée dans n et si elle est non nulle...
  • somme calcule la somme de ce thread
  • 0 thsyncmain attend la synchronisation sur le canal zéro et prend la main
  • PILE1 st> + récupère le calcul du thread 1 depuis sa pile
  • PILE2 st> + idem avec le thread 2, puis affiche le résultat.
Le programme principal:

Chaque thread est défini, ils partagent tous la page0 (seul l'un d'eux écrit), ont chacun leur pile. On leur donne un ID qui nous arrange bien ainsi que le nom du mot qui les lance.

Le dernier thread défini est celui qui se lance en premier avec thrun. Ainsi, nous sommes certains que le thread maître sera le premier à prendre la main pour piloter la synchronisation avec les deux autres.
Exécution du programme:

Main lance l'exécution des trois threads.

On demande la somme de 1 à 100 qui vaut 5050 puis la somme de 1 à 2022 qui vaut 2.045.253.

Pour finir, donner zéro est le critère de fin et les trois threads sont stoppés et effacés de la liste.



Multitâche: stations VDI et variables locales

Dans ce second programme, chaque thread aura une station VDI personnelle pour disposer de ses progres réglages. De même, nous allons utiliser des variables locales, ceci permet sous le même nom d'avoir une valeur différente pour chaque thread. Ainsi, une même portion de code peut être utilisée par deux threads.

La fenêtre FORTH sera partagée en 3 colonnes. La première réservée au mode direct, les deux suivantes aux threads. L'un affiche des lignes en couleur avec un mode REPLACE, l'autre affiche des lignes en noir avec un mode XOR.

Voyez sur le mode direct qu'on a lancé le programme avec main, puis forcé un arrêt du multitâche avec Ctrl+Alt+Shift et affiché la liste des processus en cours avec thstat. On peut relancer les tâches avec thrun là où elles s'étaient arrêtées.
Définition des variables:

Voici comment FORTH s'en sort avec les variables locales, par exemple : 3 4 thvariable X1 crée un tableau interne de 3 valeurs sur 4 octets qui sera automatiquement indexé par l'identifiant du thread.
En mode direct, ID=0, donc c'est X1(0) qui est utilisé, dans le thread 1, c'est X1(1) qui sera utilisé, et ainsi de suite.
Vous avez le choix dans l'identifiant, mais lors de l'utilisation de variables locales, des valeurs consécutives commençant à 1 sont conseillées!
Les piles et les pages:

  • Deux piles sont définies pour les threads.
  • Trois pages sont définies
  • setsubpage indique que la page0 sera partagée en 3 colonnes et une ligne
  • getsubpage ramène les coordonnées de chaque zone pour définir les pages
  • setpage fixe la PAGE1 pour le mode direct
Procédures communes aux deux threads

Initialisation d'un dessin:

Pour démarrer, il nous faut deux points X1,Y1 et X2,Y2 ainsi qu'un décalage dX1,dY1 et dX2,dY2 pour aller vers la ligne suivante.
Les points sont choisis entre 0 et Xmax/Ymax (taille de la page du thread) et les mouvements de 1 à 8 pixels.

Le rebond:

au cas où un point sorte de la page, c'est la routine bounce qui permet d'en calculer le rebond et de changer le signe du déplacement.
Avancer d'un pas:

Ce mot permet de déplacer X1,Y1 et X2,Y2 et vérifie à chaque fois qu'il n'y a pas dépassement.

Il est à noter qu'en FORTH, les coordonnées sont par défaut relatives à la page en cours. De ce fait, aucun décalage n'est à gérer par l'utilisateur, le coin supérieur gauche de la page est toujours 0,0 pour le programmeur.

Notez que la structure case permet d'autres tests que l'égalité : 0 <of permet de tester si la valeur est négative et Xmax @ >of permet de tester si on dépasse Xmax.

L'appel à v_pline dessine la ligne.
Le code du thread #2:

C'est celui qui dessine les lignes en noir.
  • vsl_color fixe la couleur au noir
  • vswr_mode fixe le mode d'écriture à XOR
  • begin/again entre dans une boucle infinie
  • init initialise un nouveau dessin
  • 100 ndo/nloop permettra le tracé de 100 lignes
  • onestep avance d'un pas et trace une ligne
  • thnext passe la main au voisin
  • wait entre deux dessins, le mot wait permet d'attendre 2 secondes tout en passant régulièrement la main
Le code du thread #1:

C'est celui qui trace les lignes en couleur.
  • vswr_mode fixe le mode d'écriture à REPLACE
  • begin/again entre dans une boucle infinie
  • init initialise un nouveau dessin
  • 10 ndo/nloop prépare une boucle de 10 fois 16 lignes, donc 160 lignes en tout
  • 16 0 do/loop permettra de fixer successivement les 16 couleurs
  • vsl_color fixe la couleur à i
  • La fin qui utilise onestep, thnext et wait est identique à l'autre thread.
Le programme principal:

Chaque thread est défini en utilisant non pas thread mais vthread. Cette variante demande l'ouverture d'une station VDI spécifique pour ce thread afin qu'il fixe ses propres réglages indépendamment des autres.
thrun lance le travail multitâche et les dessins s'enchaînent dans les deux autres colonnes.



Dialogue avec M_PLAYER/MP_STE (et ANIPLAY)

Le FORTH apporte un jeu d'instructions pour dialoguer avec les players de vidéos.

Il faut que le player soit installé en tant qu'accessoire.

On pourra ainsi obtenir des informations sur un fichier vidéo, le relire ou même créer une vidéo à partir d'une routine FORTH qui génère les images !
  • vd_set demande au FORTH d'utiliser le player spécifié pour les instructions suivantes. (Utilisable avec ANIPLAY)
  • vd_info renvoie des informations sur une vidéo sans la rejouer. On peut obtenir:
    • Les dimensions de l'image et la compression utilisée
    • Les caractéristiques du son (bits, fréquence, canaux)
    • Le support ou non des codes image et son
    • Le type de vidéo détecté
    • La version de M_PLAYER


Ici on s'est contenté de récupérer le type de vidéo


Les options -d et +e demandent de ne pas afficher les boîtes de dialogue mais de signaler les erreurs.
fastclose et fastopen permettent de fermer la fenêtre FORTH pendant le replay pour éviter des conflits.
vd_play lance la lecture d'une animation en spécifiant d'éventuelles options. (Utilisable avec ANIPLAY)



Création d'une animation

vd_create passe en mode parallèle avec l'accessoire. C'est M_PLAYER qui fabrique une animation et, pour chaque image, il lance un mot FORTH qui génère les pixels. Cette fonction n'est pas implantée dans ANIPLAY.
Voici l'exemple d'une création toute simple. L'animation sera en 120×96 et en 16 bits. Au départ l'image est noire (remplie de pixels nuls) et à chaque demande d'une nouvelle image, le mot FORTH remplit 4 lignes de pixels blancs par le haut (480 pixels égaux à FFFF). Au bout de 24 images, 4×24=96 lignes seront remplies, l'image sera entièrement blanche.
Le mot TEST remplit 480 pixels blancs et renvoie, par vd_frame, l'adresse de l'image et un code format (Intel ou Motorola).

Les options de vd_create sont:
  • 'test %h8101 le nom de la procédure image et un code de zoom
  • " I:\TOTO.MOV" 16 : le nom du fichier à créer et en MOV 16 bits
  • 120 96 24 les dimensions et le nombre d'images
  • 1 5 100 des infos de key-frames, qualité et timing
Au sortir de la procédure, vous obtenez votre vidéo sur le disque !


Pendant la création on peut afficher la boîte de progression



Test de l'animation créée !
En utilisant MP_STE, l'interface est plus rudimentaire, on obtient cette boîte lors de la création.

A noter que pour l'instant, les possibilités de vd_create sur MP_STE sont moins étendues que sur M_PLAYER.
Mathématiques 2D

Une série de fonctions graphiques est venue enrichir le FORTH. Voici quelques exemples de tracé. Les fonctions principales sont:
  • gr_window : qui définit les bornes des axes
  • gr_axes : qui trace les axes
  • gr_grid : qui affiche une grille
  • gr_y(t), gr_xy(t), gr_r(t) : pour les différents tracés de courbes
Courbe y(x) Courbe x(t), y(t) Courbe polaire r(t)
 &float x
 >comp
 :: yx
    x f@ 1/x
 ;

 ( -5. 10. -4. 4. )   gr_window 
                      gr_axes
 ( 'yx x -5. 10. 99 ) gr_y(x)
 
 &float t
 >comp
 \ x=cos(2t) ; y=sin(3t)
 :: xyt
    t f@ fdup f+ cos
    t f@ 3. f* sin
 ;

 ( -3.14 3.14 -1.1 1.1 )   gr_window 
 ( pi 4. f/ 0.5 )          gr_grid
                           gr_axes
 ( 'xyt t -1.57 1.57 100 ) gr_xy(t)
 
 &float t
 >comp
 \ r=cos(t)*sin(t)
 :: rt
    t f@ sincos f*
 ;

 ( -0.6 0.6 -0.6 0.6 )     gr_window 
                           gr_axes
 ( 'rt t -3.14 3.14 100 )  gr_r(t)
 
Avec gr_area on peut remplir une zone selon un critère dépendant de x et y



gr_area renvoie en plus une estimation statistique de l'aire remplie.
 &float x
 &float y
 >comp
 :: limit1  \ y=x²
    x f@ x^2
 ;

 :: limit2  \ y=x/2+3
    x f@ 2. f/ 3. f+
 ;

 :: area
    limit2 y f@ f>
    if    limit1 y f@ f<
    else  0
    then
 ;

 ( -7. 7. -3. 10. )         gr_window 
                            gr_axes
 ( 'limit1 x -10. 10. 100 ) gr_y(x)
 ( 'limit2 x -10. 10. 100 ) gr_y(x)
 ( 'area x y 4 )            gr_area
 ." L'aire vaut : " x f@ f.
 
Par programamtion, on peut afficher plusieurs courbes dans différentes pages FORTH en faisant varier les paramètres.
Courbes et surfaces 3D

Des fonctions similaires exsitent pour les courbes 3D:
  • gr3_window : qui définit les bornes des axes
  • gr3_axes : qui trace les axes
  • gr3_grid : qui affiche un cadre
  • gr3_xyz(t) : pour les courbes paramétrées
  • gr3_view : qui fixe l'orientation des axes
Ci-dessous, les trois vues possibles de la courbe en modifiant uniquement le paramètre de gr3_view.
 &float t
 >comp
 :: fn
    t f@ sincos
    t f@ fabs sqr fneg exp
    fswap fover f* frot f*
    t f@
 ;

 ( -1.1 1.1 -1.1 1.1 -20. 20. ) gr3_window
 0                              gr3_view
                                gr3_grid
 ( 'fn t -19. 19. 200 )         gr3_xyz(t)
 
Pour les surface 3D, il y a deux choix:
  • gr3_z(xy)l : version rapide qui ne trace que des lignes sans traits cachés
  • gr3_z(xy)f : version avec des mailles remplies qui gère les zones cachées.
Ci-contre, nous définissons la fonction Z=cos √ (x²+y²) :
&float x
&float y
 >comp
\ z=cos(sqr(x²+y²))
:: surf
   x f@ x^2 y f@ x^2 f+ sqr cos
;
 
Voici les tracés en ligne ou surfaces:
 1 vsl_color
 ( -4. 4. -4. 4. -2.1 2.1 ) gr3_window 
 gr3_grid
 gr3_axes
 3 vsl_color
 'surf x y 31 gr3_z(xy)l
 
Les temps indiqués sont ceux obtenus sur un TT 32MHz avec une carte graphique et le FPU.
Ici on a simplement changé gr3_z(xy)l en gr3_z(xy)f et 3 vsl_color en 3 vsf_color pour le remplissage

Calculs avancés

Des fonctions de calcul sont également disponibles:
  • solve : pour résoudre une équation du type f(x)=0
  • gauss : pour intégrer une fonction du type y(x)
  • minimum/maximum : pour rechercher les extrema d'une fonction
Supposons que la vitesse d'un objet selon le temps soit :

en cm/s.

Le tracé de la courbe montre que l'objet va à gauche (vitesse négative) puis s'arrête (B), il repart vers la droite avec un maximum de vitesse (M) et ralentit à nouveau pour s'arrêter (C), il repart définitivement vers la gauche à l'infini.
On s'intéresse à la partie vers la droite dont l'intégrale donne la distance parcourue, ici gr_area nous dit environ 41 cm.


La fonction gr_getpoint permet, à la souris, d'afficher les coordonnées d'un point dans une boîte d'alerte. J'ai alors cliqué au plus proche des points B, C et M.

On peut donc lire que c'est vers t=0.78s (B) que s'amorce le mouvement à droite, qu'il se termine à t=2,54s (C) avec une vitesse maximale de 36cm/s pour t=1,62s (M).


Pour plus de précision, j'utilise SOLVE pour trouver la solution proche de 0,7, en retour présision 1,7e-7 et t=0,7887 sec.
Encore SOLVE pour l'autre solution proche de 2.5, en retour précision 1,6e-7 et t=2,5473 sec.
Un coup de GAUSS pour l'intégrale qui me donne la distance parcourue 41,0915 cm.
On finit avec MAXIMUM avec une précision de 2,7e-5 pour une vitesse maximale de 35,8892 cm/s au temps t=1,6103 sec.

On remarquera que les outils graphiques avaient déjà donné une information assez précise dans ce cas.


15 décembre 2007