CODINGAMSTRAD CPC 464 - CRÉER DE NOUVELLES INSTRUCTIONS

Nouvelles Instructions 011

EXEMPLES DE PROGRAMMES

8. EXEMPLE 8: TRI DE DONNÉES ALPHANUMÉRIQUES

Notre approche du langage machine serait incomplète si elle ne comprenait pas une petite incursion dans le royaume des données alphanumériques. (L'étude préalable de l'Annexe V, section 2, est impéra-tive.)
Le programme que nous vous proposons maintenant permet de ranger un tableau de données alphanumériques dans l'ordre alphabétique. Il débute à l'adresse 43830, finit en 43896 (chiffre de vérification : 7168), n'est pas directement relogeable, et son format d'appel est le suivant :

CALL 43830, @X$(0)

X$ est le nom du tableau (qui peut être quelconque), et l'ensemble @ X$(0) pointe donc sur l'adresse du descripteur de chaîne de la première donnée du tableau (son rang est 0).

Il est indispensable que nous nous arrêtions un moment sur cette notion de tableau et, pour être plus précis, sur l'organisation en mémoire des descripteurs de chaîne des éléments qui le composent. Pour n'importe quel tableau X$ (à une seule dimension), ces descripteurs de chaîne sont stockés de la manière suivante :


Les chiffres romains indiqués sur la droite ne nous serviront qu'à nommer les emplacements mémoire correspondants lors des explications.

Rappelons que l'appel du programme se faisant sous le format :

CALL 43830, @X$(0)

Le registre IX sera pointé comme suit :

Il sera nécessaire, tout au long des explications, de vous reporter à ces deux figures.
Voyons maintenant le principe de tri adopté. Il s'agit de la technique dite du tri par bulles, dont le processus est très simple: notre tableau étant constitué d'une série de chaînes de caractères, on pointe sur l'une d'elles, et l'on compare le code ASCII (voir à ce sujet la page A3.1 du guide de l'utilisateur) de sa première lettre au code ASCII de la première lettre de la chaîne de caractères suivante.
Le code ASCII le plus petit indique que la lettre correspondante est située avant dans l'alphabet, et les deux chaînes de caractères sont ou ne sont pas inversées, selon le cas.
Si une inversion est nécessaire, elle est effectuée, puis le pointeur est repositionné au début du tableau et le processus est repris.

Si le pointeur arrive en fin de tableau sans qu'aucune inversion ait été nécessaire, c'est donc qu'il n'y a plus d'élément à inverser et que le tableau est rangé.
Le nom de tri par bulles s'explique par une analogie : à chaque passe du pointeur, les éléments les plus "légers" remontent progressivement à la surface.

Pour ce qui nous concerne, précisons que, lorsqu'il sera nécessaire d'inverser deux données du tableau, nous nous contenterons en fait d'inverser leur descripteurs de chaîne respectifs.

Si tout cela est bien clair, nous pouvons passer à l'étude du listing.


Le premier bloc remarquable va des lignes 1 à 8, et initialise le compteur. Nous aurons en effet besoin d'être avertis si tout le tableau est parcouru sans qu'il y ait d'inversion, signe que le programme est terminé. En réalité, il suffira d'atteindre l'avant-dernier élément du tableau, puisqu'il aura alors été comparé au suivant, c'est-à-dire au dernier. La valeur qui va nous intéresser est donc :
(nombre d'éléments du tableau) - 1

Lignes 1 et 2
Chargement de HL avec (IX+ 0) et (IX+ 1). Le registre HL est ainsi pointé sur III (voir la figure).

Ligne 3

Décrémentation de HL qui est maintenant pointé sur II (gardez bien à l'esprit les pointages successifs de HL).

Ligne 4

Chargement du contenu de l'emplacement mémoire sur lequel est pointé HL dans le registre B. Ce dernier contient donc maintenant l'octet fort du nombre d'éléments du tableau.

Lignes 5 et 6

Même principe pour charger l'octet faible dans C. Après cette ligne, BC contient le nombre d'éléments du tableau.

Lignes 7 et 8

Décrémentation de BC (puisque nous voulons obtenir la valeur (nombre d'éléments –1)), puis sauvegarde par empilement.

Le deuxième bloc va des lignes 9 à 23 et effectue les tâches suivantes : initialisation des pointeurs, démarrage des passes, comparaison des éléments et branchement en fonction du résultat :

Lignes 9,10 et 11

Trois incrémentations successives de HL qui se retrouve pointé sur IV.

Lignes 12,13 et 14

Suivant un principe analogue à celui des lignes 4, 5 et 6, l'adresse du premier élément du tableau est chargée dans DE.

Lignes 15 à 19

Toujours selon le même principe, l'adresse de l'élément suivant du tableau est chargé dans BC.
Récapitulons la situation :
• DE est pointé sur l'emplacement mémoire contenant le code ASCII de la première lettre du premier élément du tableau.
• BC est pointé sur l'emplacement mémoire contenant le code ASCII de la première lettre du deuxième élément du tableau.
• HL est pointé sur VIII.
• Le nombre d'éléments du tableau - 1 est sur la pile.

Ligne 20

Le contenu de l'emplacement mémoire adressé par BC est chargé dans A, qui contient donc maintenant le code ASCII de la première lettre du deuxième élément du tableau.

Ligne 21

Échange de DE et HL. Ce dernier se retrouve pointé sur l'emplacement mémoire contenant le code ASCII de la première lettre du premier élément du tableau, et DE se retrouve pointé sur VIII.

Ligne 22

Comparaison entre A et (HL), donc entre les deux codes ASCII.

Rappelons brièvement le principe de cette comparaison (qui a déjà été étudiée dans d'autres programmes).
(HL) est soustrait de A, sans que le résultat soit conservé, c'est-à-dire sans que le contenu des deux registres soit modifié. Les indicateurs S et Z sont mis en fonction du résultat.
En l'occurrence, si le code ASCII du premier élément est plus petit que celui du deuxième élément (donc si la situation alphabétique des deux éléments l'un par rapport à l'autre est correcte), l'indicateur S indiquera un résultat positif.
Dans le cas contraire, S indiquera un résultat négatif et nous devrons inverser les descripteurs de chaîne des 2 éléments.

Ligne 23

Saut à l'adresse 43869 (ligne 33) si négatif. A cette adresse commence en effet le bloc chargé de l'inversion. Voyons d'abord le cas où cette inversion n'est pas nécessaire et où le saut est ignoré :

Ligne 24

L'état actuel du compteur est récupéré et chargé dans HL.

Lignes 25, 26 et 27

Après une décrémentation de HL, on vérifie si son contenu est ou n'est pas égal à 0 (selon un principe déjà étudié dans le programme de pause, lignes 6, 7 et 8).

Ligne 28

Retour de sous-programme si nul : si HL = 0, donc si le tableau a été parcouru jusqu'à l'avant-dernier élément sans qu'il y ait eu besoin d'inversion, le tableau est rangé correctement, et le programme est terminé. Sinon, cette instruction est ignorée.

Ligne 29

Sauvegarde de la nouvelle valeur du compteur.

Ligne 30

Décrémentation de DE qui, depuis la ligne 21, était pointé sur VIII et l'est maintenant sur VII.

Ligne 31

Échange de HL et DE : HL est pointé sur VII.

Ligne 32

Saut relatif de - 24 en ligne 12. La boucle va donc reprendre, mais le pointage dans le tableau sera décalé d'un élément (au premier tour, la comparaison se fera entre les éléments 1 et 2, au deuxième tour entre les éléments 2 et 3, au quatrième tour entre les éléments 3 et 4, etc.).

Si cette boucle est exécutée (nombre d'éléments – 1) fois sans inversion, le programme retournera au BASIC en ligne 28. Mais attention : à chaque inversion, le processus doit reprendre depuis le début, et le compteur devra être en particulier réinitialisé.
Nous allons maintenant passer au bloc de lignes 33 à 56, qui se charge des éventuelles inversions des descripteurs de chaîne.
Pour servir d'exemple, nous prendrons le cas où les descripteurs de chaîne des deux premiers éléments du tableau doivent être inversés. Pour ce faire, il faut exécuter les tâches suivantes :

1. Sauvegarder les contenus de VI, VII et VIII.
2. Transférer les contenus de III, IV et V respectivement dans VI, VII et VIII.
3. Mettre les valeurs sauvegardées (contenus de VI, VII et VIII), respectivement dans III, IV et V.

Rappelons qu'au moment où le branchement vers ce bloc se fait, en ligne 23, DE était pointé sur VIII.

Ligne 33

La valeur actuelle du compteur ne nous intéresse plus, puisque celui-ci devra être réinitialisé. C'est pourquoi nous l'enlevons de la pile.

Lignes 34 et 35

HL est, comme DE, pointé sur VIII.

Lignes 36 à 42

Elles réalisent la sauvegarde des contenus de VI, VII et VIII. Ces lignes ne nécessitent aucune explication particulière : vous connaissez toutes les instructions utilisées, et il suffit de suivre attentivement, si possible en les notant, l'état de la pile et l'évolution du contenu des registres.

Une remarque cependant : vous pourrez constater que sont ui- A les deux instructions suivantes: "PUSH AF" et "POP AF"

Vous n'avez jamais encore entendu parler de ce registre double AF ! En réalité, seul le contenu de A nous intéresse, et pas du tout celui de F Mais comme il est impossible d'empiler un registre simple, nous somm obligés d'empiler le registre double AF, en nous disant que A l'est par la même occasion.
Quand vous rencontrerez ces deux instructions, vous pourrez faire comme s'il y avait "PUSH A" et "POP A" à la place.

Lignes 43 à 46

Comme précédemment, ce bloc, qui réalise le transfert des contenus de V, IV et III dans VIII et V, s'explique de lui-même. L'instruction de transfert répétitif avec décrémentation, déjà plusieurs fois étudiée, est en particulier utilisée.
Signalons également qu'après l'exécution de ces lignes, DE est pointé sur V et HL sur II.

Lignes 47 à 55

Là encore, rien de particuier. Ce bloc réalise évidemment le transfert des contenus initiaux de VIII, VII et VI dans V, IV et III. L'inversion des deux descripteurs de chaîne est terminée.

Ligne 56

Puisqu'une inversion a eu lieu, tout le processus doit être repris : saut relatif de - 67, en ligne 1.
Voici un exemple d'utilisation de ce programme :

100 DIM TABLEAU$ (24)
110 CLS: FOR I = 0TO24: A = INT(RND * 26)+ 65 :TABLEAU$(I) = CHR$(A) : NEXT
120 FOR l = 0 TO 24 : PRINT TABLEAU$(l) : NEXT
130 CALL 43830, @TABLEAU$(0)
140 FOR I = 0 TO 24: LOCATE 20,1 + 1 : PRINT TABLEAU$(I) :
NEXT
150 GOTO 150

La ligne 100 crée un tableau de 25 éléments.
La ligne 110 remplit aléatoirement ce tableau avec les lettres de l'alphabet (majuscules).
La ligne 120 affiche sur la gauche de l'écran le contenu initial'du tableau.
La ligne 130 effectue le rangement.
La ligne 140 affiche, à droite de l'écran, le contenu du tableau après ce rangement.
La ligne 150 empêche le scrolling d'écran (il faut arrêter le programme en tapant 2 fois la touche "ESC").

Remarque
Le tableau n'est ici constitué que de lettres isolées, mais il pourrait tout aussi bien être constitué de mots.

A titre de comparaison, le programme en langage machine range le tableau en plus ou moins 1/10e de seconde, alors qu'il faudrait jusqu'à 200 fois plus de temps à un programme BASIC équivalent !

★ ANNÉE: ???
★ AUTEUR: JEAN-CLAUDE DESPOINE

Page précédente : Nouvelles Instructions 010
Je participe au site:

» Vous avez remarqué une erreur dans ce texte ?
» Aidez-nous à améliorer cette page : en nous contactant via le forum ou par email.

CPCrulez[Content Management System] v8.7-desktop
Page créée en 491 millisecondes et consultée 1177 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.