APPLICATIONSUTILITAIRES RSX/LIGNE DE COMMANDE ★ BSTACK par Yannick GOUR ★

Bstack|CPC Infos)Applications Utilitaires Rsx/ligne De Commande

REDEFINITION DE LA PILE BASIC DU CPC

La pile BASIC du CPC a pu se trouver trop petite pour certains de vos usages. Deux RSX proposées ici vous permettent de la placer (à peu près) n'importe où, en lui allouant n'importe quelle taille (dans certaines limites), et de contrôler son remplissage. Le dernier chapitre de cet article est consacré à la méthode d'écriture de programme en langage machine exécutable où qu'il soit chargé, sans relogement. Cette méthode, mise à profit pour le programme des RSXs proposées ici, n'est quasiment jamais employée, malgré son très grand intérêt; c'est pourquoi j'ai jugé bon d'en faire une présentation presque exhaustive.

LA PILE BASIC

1) Pourquoi une pile BASIC?

dès que le BASIC utilise une instruction rompant l'exécution en séquence d'un programme sans saut explicite (c'est-à-dire autrement qu'avec un GOTO) et qu'il faut revenir ultérieurement à l'endroit de cette rupture de séquence, il faut stocker quelque part l'endroit du saut (logique non?). La pile BASIC sert à cela. C'est très simplifié mais on va bien vite détailler.

2)  Quelles instructions utilisent la pile BASIC?

Elles sont au nombre de 3:

  • GOSUB/RETURN: c'est celle pour laquelle la notion d'empilage/ dépilage est la plus évidente. Le GOSUB provoque un saut mais l'adresse de l'instruction le suivant est préservée (en simplifiant) sur la pile BASIC. Avec RETURN cette adresse est récupérée et l'exécution continue à partir d'elle. Un GOSUB consomme 6 octets.
  • WHILE/WEND: tant que la condition du WHILE est vraie, les instructions entre le WHILE et le WEND sont exécutées. Le BASIC doit savoir où aller quand il rencontre le WEND, d'où la nécessité d'empiler l'adresse du WHILE. De même, si la condition du WHILE n'est plus vraie, il y a saut à l'instruction suivant le WEND. Il faut donc aussi que le BASIC connaisse l'adresse du WEND. Ainsi les adresses des 2 instructions sont-elles stockées en pile BASIC. Elles consomment (en un seul bloc) 7 octets.
  • FOR/NEXT(/STEP): le principe est le même que pour WHILE/WEND, sauf que l'arrêt de la boucle est lié à l'égalité ou à la supériorité de la variable de comptage avec une valeur limite. Il y a donc également stockage des adresses des FOR et NEXT, avec en plus celle de la variable de comptage, et aussi des valeurs limite et de pas. Selon que sont utilisées des variables entières ou réelles, ce sont 16 ou 22 octets qui sont consommés.

3) Comment est utilisée la pile BASIC?

La pile BASIC est une pile ascendante. Pour y accéder le BASIC utilise évidemment un pointeur. Avant de stocker les octets correspondants à une des 3 instructions précitées, il vérifie qu'il y a encore assez de place: sinon il arrête tout avec un beau MEMORY FULL. Si oui il empile ce qu'il faut.
En sus des informations évoquées dans le sous-chapitre précédent, le BASIC termine le bloc de données empilées par la longueur de ce bloc, l'octet servant à cela y compris. Le pointeur pointe toujours après ce dernier octet. Celui-ci indique donc automatiquement au BASIC en cas de dépilage (RETURN ou fin de boucle WHILE/WEND ou FOR/NEXT), la longueur du bloc disponible et de quel type de bloc il s'agit. Il peut ainsi détecter si l'instruction de "retour" ne correspond pas au bloc, ce qui se traduit par un message du genre «Unexpected...».
Le BASIC contrôle l'atteinte de la limite supérieure de la pile par comparaison de la valeur du pointeur avec la limite haute de la pile, qui est fixée UNE FOIS POUR TOUTES en ROM BASIC. Il n'est donc absolument pas question (à moins de modifier la ROM BASIC) que le BASIC empile au delà de cette limite.
Par contre en ce qui concerne la limite basse, mise en jeu elle par les dépilages, la méthode est autre; heureusement, sans quoi ces articles et programmes n'auraient pas été. Deux paragraphes plus haut, il a été dit qu'un bloc empilé se termine par un octet en indiquant la longueur (qui n'a jamais que les valeurs 6,7,16 ou 22). Or, à l'initialisation de la pile (avec RUN ou CHAIN ou NEW, voire après une remise à zéro), le BASIC initialise le pointeur de pile (naturellement en RAM) et empile un ZERO (sur la pile BASIC). Pour accéder à la pile par la suite, le BASIC ne fait appel qu'à son pointeur et identifie le début de pile par l'indication de la longueur de bloc nulle.
Un lecteur éveillé devrait avoir désormais l'idée de la méthode mise en oeuvre pour redéfinir la pile BASIC.

4) Où se situe quoi?

  • Le pointeur de pile BASIC: il est en #B08B sur 464 et en #B06F sur 6128. A l'initialisation, il est mis à jour et est dupliqué en #AE32 sur 464 et #AE19 sur 6128.
  • La pile BASIC: sur 464 elle est située en #AE8B et sur 6128 en #AE6F. Elle s'étend sur 512 octets.

LA REDEFINITION DE LA
PILE BASIC ET SON
CONTROLE

1) La méthode

Les esprits sagaces l'auront devinée : après lancement d'un programme, qui initialise pointeur de pile et pile BASIC comme vu précédemment, il faut modifier le pointeur pour le faire pointer sur la zone qu'on a choisie comme nouvelle pile BASIC. Dans le même temps il faut initialiser cette zone comme une pile BASIC qui se respecte, en la faisant débuter par un 0. Naturellement le pointeur est alors incrémenté (cf le sous-chapitre 2 du chapitre précédent).
A cela se limite l'intervention dans le système, le BASIC gérant par la suite cette nouvelle pile comme si de rien n'étaitl Une première instruction d'extension au système résident (Résident System extension en anglais), ou RSX, se chargera de cela. La seconde RSX contrôlera la place libre dans la nouvelle pile; le BASIC ne connaissant comme limite supérieure que celle de la pile standard, cela pourrait conduire à des catastrophes.

2)  La création des RSXs (une fois pour toutes)

Tapez le programme CREERSX.BAS et lancez-le. Il écrira en mémoire les RSX avec leur routine d'implantation et d'initialisation et sauvegardera le tout (même pas 256 octets!), en fichier binaire évidemment, sous le nom de BSTACK.RSX, ou sous tout autre nom de votre choix (BSTACK, pour les amateurs d'étymologie, pour Basic STACK, «stack» signifiant «pile» en anglais informatique).

3)  L'implantation des RSXs et leur initialisation.

En général on initialise les RSXs dès après les avoir chargées. C'est ce processus que nous suivrons aussi. Soit donc l'adresse de chargement quelconque «ad», étant entendu qu'il est de loin préférable, sinon indispensable, de la choisir dans la RAM utilisateur. Un exemple de chargement et d'initialisation (aussi à l'adresse "ad") est:

ad=XXXX:LOAD"BSTACKRSX",ad:CALL ad

Et oui, une fois n'est pas coutume, l'utilisateur a le libre choix de l'adresse d'implantation des RSXs, et cela non pas à la création du code (ce que déjà peu proposent), mais à chaque chargement! Pour ceux qui ne connaîtraient pas la méthode et qui désireraient étendre leur culture du langage d'assemblage, la méthode autorisant cette petite prouesse est expliquée dans ses grandes lignes dans le dernier chapitre, «PROGRAMME MACHINE EXECUTABLE A TOUTE ADRESSE DE CHARGEMENT», réservé aux amateurs du langage d'assemblage bien entendu. Pour les autres, retenir qu'ils ont libre choix (total, absolu) - en RAM utilisateur cependant) de l'adresse de chargement de BSTACK.RSX et qu'il faut juste faire le CALL à cette adresse.

4) Emploi des RSXs

|BSTACK,deb,lon: installe la pile BASIC à l'adresse "deb" et lui assigne une longueur de "Ion", ou sa taille, au choix pour la terminologie.
Comme déjà évoqué, le BASIC ne détecte le remplissage de sa pile que par comparaison du pointeur de pile avec une valeur qu'il a en ROM (grosso modo), et qui correspond au haut de la pile BASIC standard. Il n'y a donc aucune chance que le BASIC interrompe un programme pour "pile pleine", avant que la pile redéfinie n'ait probablement planté le CPC en recouvrant des zones très délicates, qui sont des «chasses gardées» du système d'exploitation et du BASIC et qui sont situées sous la pile standard (rappelons que la pile BASIC croît vers les adresses supérieures). Une bonne chose est
déjà d'implanter assez bas sous les zones sensibles la nouvelle pile. Mais il est toujours préférable de garder quelque moyen de contrôle des événements, ce dont se charge la seconde RSX:

|BSFREE,@a%: transmet à une variable entière (ici a% pour l'exemple) la place libre dans la pile BASIC redéfinie. Si jamais il n'y en a plus (a%<0), le programme s'interrompt sur le message d'erreur «Memory full » C'est le seul moyen de contrôler l'état de remplissage de la pile. Le paramètre "Ion" donné avec BSTACK n'est pas mis à profit par le BASIC (si vous ne l'avez pas compris, relisez tout !) mais uniquement par cette seconde RSX, pour calculer la place encore libre, et le cas échéant arrêter tout.

Remarques :

  • lorsqu'on a bien défini la place disponible pour la pile BASIC, il peut être plus prudent de donner à "Ion" une valeur inférieure, pour être certain de pouvoir stopper avant un débordement fâcheux.
  • l'examen de la place libre en pile peut se faire en différents endroits du programme assez simplement, s'il n'est pas trop long ou complexe, par le recours à la RSX " |BSFREE,@a%", sans idée d'utilisation de la valeur de a% mais juste pour stopper en cas de dépassement. Cependant il est plus simple et même plus élégant de faire appel à une interruption BASIC! On oublie trop souvent l'intérêt que présente une instruction telle que «EVERY x,i GOSUB nnnn». Le petit programme de démonstration FULL1.BAS met en oeuvre le contrôle de pile à l'intérieur du programme tandis que FULL2. BAS le réalise par interruption. Il n'y alors pas à jalonner de programme de |BSFREE, un seul et unique sous-programme se chargeant du contrôle de la pile, ce qui est très avantageux pour un programme long ou complexe.
  • les RSX ont une vérification du nombre d'arguments qui leur sont passés incorporée. Si ce n'est pas 2  pour BSTACK et pas 1 pour BSFREE, il y a interruption du programme avec le message d'erreur «Syntax error» (NB: pour «VARIABLES LOCALES ET RECURSIVITE» - CPCinfos de Janvier 1991 - j'appelais directement la routine de génération des erreurs en ROM BASIC, en lui passant le numéro d'erreur, pour interrompre un programme. Cette fois je transmets à l'interpréteur BASIC l'adresse d'une commande en BASICI Pour plus de détails, se reporter au listing du source). L'intérêt du «Syntax error» est qu'il y a automatiquement, s'il vient d'un programme, édition de la ligne erronée.
  • tout de suite après avoir défini une pile BASIC de taille "lon", si vous en vérifiez l'espace libre avec BSFREE vous trouverez "lon"-1, et non "Ion" comme le croiraient des étourdis. Comme déjà plusieurs fois expliqué, 0 est empilé à l'initialisation de la pile comme marqueur de son début.
  • pour les avares en espace mémoire, il est utile de savoir que les 100 premiers octets ne sont utiles qu'à l'initialisation des RSXs. La place qu'ils occupent peut ensuite être employée à loisir.

PROGRAMME MACHINE EXECUTABLE A TOUTE ADRESSE DE CHARGEMENT

Certaines routines, toujours courtes, qui ne comportent pas de sauts ou d'appels absolus (sinon à des vecteurs système), ni de lecture ou écriture d'octets en leur sein ou relativement à elles, sont évidemment chargeables et exécutables n'importe où. Il ne sera pas question d'elles. D'autres programmes déterminent seulement leur adresse de chargement pour se reloger à une adresse fixée une fois pour toutes à leur conception. Il ne s'agit pas non plus de cela ici mais bien d'adapter le code à son adresse de chargementl La méthodologie proposée vaut pour tout type de routine ou programme en code machine, quelle que soit sa longueur. Elle concerne bien naturellement le source. En avant !

  1. Pour tout saut absolu dans le programme ou toute lecture ou écriture en son sein, toujours utiliser une étiquette. Cela concerne les instructions faisant référence à une adresse absolue (JP/ CALL/ LD r,(nn)/ LD (nn),r/ etc.).
  2. Repérer également par des étiquettes toutes les instructions concernées par le 1), ou tout petit bloc de telles instructions.
  3. Ecrire la table de toutes les adresses absolues repérées par les étiquettes évoquées au 2). Etant une table d'adresses, c'est une suite d'instructions pseudoopératoires "DEFW". Chaque DEFW est suivi d'un nom d'étiquette, auquel est ajouté le décalage pour pointer effectivement sur l'adresse de l'adresse repérée (+1 pour JP, +2 pour LD DE,(nn) etc.).

EXEMPLE ILLUSTRANT CES 3 REGLES (EXTRAIT DU SOURCE) :
TABADR:  DEFW VERSI0N2+1  (en VERS10N2 se trouve un bloc
         ;DEFW VERSI0N2+4  d'instructions § adresse absolue
               ; ....      qui seront toujours regroupfes;
                        ;  on ne les repére donc que par 1
         ;DEFW A01+1       étiquette)
          SUB 80H
VERS10N2: LD (CPC1+1),A
          LD (CPC2+1).A
          ...
A01:    LD (FlNPILE+i),HL
CPC1:   LD A,00
          ...

Ces 3 règles ayant été appliquées, il n'y a plus qu'à écrire en tête du programme (en tête car il est plus simple que l'adresse d'appel soit celle de chargement) la routine d'adaptation du programme à sa localisation. Je décris maintenant les étapes de cette routine (il est conseillé de suivre en lisant le source);

1 ) détermination de l'adresse de chargement:

  • préservation des 2 octets en 0000 et 0001.
  • écriture en 0000 du couple d'instruction: POP HL, JP (HL)
  • appel en 0000.
  • retour avec en HL l'adresse suivant l'appel en 0000 (adret).
  • restauration des octets originaux en 0000 et 0001.

2)  détermination du décalage entre adresse réelle et adresse relative au ORG du source:

  • la différence entre l'adresse contenue dans HL et celle donnée par une étiquette à ce même endroit (adrel) relativement à l'origine (ORG) indiquée dans le source est le décalage (offset = adret-adrel) à ajouter aux adresses de la table des adresses (règle 3) pour accéder aux instructions à adresse absolue répertoriées dans cette table (règle 2) et aussi le décalage à ajouter aux adresses absolues relatives au ORG qu'on trouve à ces adresses (règle 1 ) pour obtenir les véritables adresses absolues (c'est certes peut-être compliqué mais cela illustre bien la logique des 3 règles que je propose - qui sont valables pour tout type de langage d'assemblage - et cela va vite s'éclaircir). .stockage de ce décalage.

3) calcul de l'adresse de la table des instructions à adresse absolue:

  • ajout du décalage à l'adresse de la table (tabrel) relativement au ORG: adresse véritable de cette table (tabadr = tabrel+offset).

4) boucle d'écriture des adresses absolues véritables:

  • lecture de l'adresse relative à ORG d'une adresse absolue dans la table (adrel1).
  • si c'est 0000 (par convention), alors fin de table atteinte.

Saut au programme ou retour.

  • ajout du décalage à l'adresse relative lue pour obtenir l'adresse absolue correspondante (adr1 = adrel1 +offset), qui pointe ainsi sur une adresse absolue.
  • lecture de l'adresse absolue en adr1, toujours relative à ORG, (adrel2).
  • ajout du décalage à cette adresse relative pour obtenir l'adresse absolue (adr2 = adrel2+offset) cohérente avec la localisation du programme.
  • écriture à l'adresse absolue "adr1" de cette adresse absolue "adr2". _poursuite de la lecture de la table. La routine que j'ai écrite est aussi concise que possible et suit scrupuleusement la méthode décrite ci-dessus. Il m'étonnerait fort qu'il y ait, d'une part, plus simple méthode et d'autre part beaucoup plus courte routine. Avis aux chercheurs!

CONCLUSION

Il doit être assez rare pour des programmes standards de se trouver gêné par la taille de la pile BASIC standard. Cependant, dans le cadre de l'utilisation par exemple de la récursivité (cf mes articles et programmes dans les CPCinfos de Janvier et Mars 1991), on peut très vite en atteindre les limites. Aussi ai-je écrit ces 2 RSXs pour s'en affranchir. J'espère qu'elles rendront bien des services (on peut d'ailleurs contrôler l'espace libre dans la pile standard en la choisissant comme pile redéfinie - |BSTACK, &AE8B, &200 sur 464 ou |BSTACK, &AE6F, &200 sur 6128 ). J'espère aussi faire des émules pour la conception de programmmes en langage machine exécutables où qu'ils soient chargés (j'ai prouvé que c'était facilement réalisable, même avec des RSXs).

Yannick GOUR , CPCinfos n°33

★ EDITEUR: CPCINFOS
★ ANNÉE: 1992
★ CONFIG: ???
★ LANGAGE:
★ LICENCE: LISTING
★ AUTEUR: Yannick GOUR

★ AMSTRAD CPC ★ DOWNLOAD ★

Type-in/Listing:
» BStack    (CPC  Infos)    LISTING    FRENCHDATE: 2018-08-26
DL: 9 fois
TYPE: PDF
SIZE: 720Ko
NOTE: 2 pages/PDFlib v1.6

Je participe au site:
» Newfile(s) upload/Envoye de fichier(s)
★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser...

Lien(s):
» Applications » Rsx - Fast Saving And Loading On the CPC464 (Popular Computing Weekly)
» Applications » RSX - Random Disc Access (Popular Computing Weekly)
» Applications » RSX Motor Control
» Applications » Rsx - Fast Screen (CPC Amstrad International)
» Applications » Disc-RSX
» Applications » Basichic (Amstrad Magazine)

QUE DIT LA LOI FRANÇAISE:

L'alinéa 8 de l'article L122-5 du Code de la propriété intellectuelle explique que « Lorsque l'œuvre a été divulguée, l'auteur ne peut interdire la reproduction d'une œuvre et sa représentation effectuées à des fins de conservation ou destinées à préserver les conditions de sa consultation à des fins de recherche ou détudes privées par des particuliers, dans les locaux de l'établissement et sur des terminaux dédiés par des bibliothèques accessibles au public, par des musées ou par des services d'archives, sous réserve que ceux-ci ne recherchent aucun avantage économique ou commercial ». Pas de problème donc pour nous!

CPCrulez[Content Management System] v8.7-desktop/cache
Page créée en 935 millisecondes et consultée 928 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.