CODINGAMSTRAD CPC 464 - CRÉER DE NOUVELLES INSTRUCTIONS

Nouvelles Instructions 002

CONNAISSANCES DE BASE

LA NOTATION HEXADÉCIMALE : OCTETS FORTS ET OCTETS FAIBLES

Il serait toujours possible, si on le souhaitait vraiment, de travailler en langage machine en fournissant à l'ordinateur des données sous une forme que tout le monde connaît : la notation décimale.
Pour un certain nombre de raisons, cela n'est pourtant pas recommandé. On peut en citer trois :

  • Certaines conversions en hexadécimal seraient de toute façon nécessaires lors de la conception du programme.
  • La numérotation en hexadécimal prend moins de place que celle en décimal (pour les nombres supérieurs à 99 en particulier).
  • Tous les ouvrages sans exception ayant trait à ce sujet ou à un sujet approchant se servent de la notation hexadécimale. Autant donc s'y habituer tout de suite...

Pour découvrir de quoi il s'agit exactement, commencez par taper ceci sur votre machine (le mode direct suffira, c'est-à-dire qu'il est inutile de mettre un numéro de ligne) :

PRINT HEX$ (43870)

La réponse qui s'affiche est: AB5E. La fonction HEX$ a transformé la représentation décimale de 43870 en sa représentation hexadécimale. Les deux chiffres ou lettres de droite sont appelés l'octet de poids faible, et les deux de gauche l'octet de poids fort (dans l'exemple, l'octet de poids faible est 5E et l'octet de poids fort AB).
Il faut noter que l'ordinateur ne prend pas la peine d'écrire les 0 éventuellement situés à gauche du dernier chiffre ou de la dernière lettre.

Exemples

2060 est représenté en hexadécimal par 80C, qui est l'équivalent de 080C. L'octet de poids faible est donc OC et l'octet de poids fort 8.

HEX$ (255) = FF, ce qui est l'équivalent de OOFF. Dans ce cas, l'octet de poids faible est FF et l'octet de poids fort 0 (ou, si l'on veut, il n'y a pas d'octet fort – nous verrons toutefois que la nuance peut être importante).
La conversion inverse (hexadécimal – décimal) peut s'effectuer de la manière suivante :
Nombre en décimal = octet de poids faible + (256 * octet de poids fort)

Exemple

HEX$ (648) = 288 et &88 + 256 * &2 = 648

Le signe & signale à l'ordinateur que le chiffre qui lui est transmis est en notation hexadécimale.
Il faut enfin signaler que l'on ne peut représenter en hexadécimal que des nombres de - 32768 à 65535.
Les nombres négatifs posent toutefois un problème particulier, que nous laisserons de côté, puisqu'il ne se présentera pas dans nos programmes. Néanmoins, pour ceux que cela intéresse, qu'ils sachent que :

  1. La représentation hexadécimale des nombres allant de – 32768 à - 1 est la même que celle des nombres de 32768 à 65535 (exemple : HEX$ (- 21) = FFEB et HEX$ (65515) = FFEB).
  2. Pour faire la conversion hexadécimal – décimal, il existe donc en réalité deux possibilités :
  • Si l'on ne veut pas tenir compte du signe (si l'on sait par exemple que le nombre est positif), on utilise la formule présentée plus haut.
  • Si l'on veut tenir compte du signe, il faut utiliser une fonction qui a pour nom UNT. "

Exemple

UNT (&FFEB) = - 21 et &EB + 256 * &FF = 65515


Mais, répétons-le, tout cela n'a pour le moment aucune importance.

LES REGISTRES SIMPLES

Pour en donner une idée claire, on pourrait dire que la programmation en langage machine consiste pour une grande part en une manipulation de différentes "boîtes" dans lesquelles il est possible de mettre des valeurs, et que l'on peut par exemple additionner et soustraire entre elles. Chacune de ces boîtes, que l'on appelle registre, a un nom. Nous nous intéresserons pour l'instant à sept de ces registres : A, B, C, D, E, H et L.
Pour information, précisons que le registre A s'appelle également l'accumulateur.
Ces registres simples, pour utiles qu'ils soient, ont pourtant un grave défaut : on ne peut y mettre que des nombres de 0 à 255. C'est pourquoi il existe également les registres doubles.

LES REGISTRES DOUBLES

Ce sont, en fait, des registres simples mis deux à deux. Nous en utiliserons trois : BC, DE et HL.
Dans ces registres doubles, on pourra mettre des nombres jusqu'à 65535. C'est là que vont intervenir les notions d'octet faible et d'octet fort.
Pour mettre par exemple la valeur &AB5E (43870 en décimal) dans le registre HL, il faudra mettre (on dit aussi charger) l'octet fort de cette valeur dans H et l'octet faible dans L. Une fois chargé, HL se présentera donc comme ceci :

Ce dernier exemple amène une remarque importante : pour charger un registre double avec un nombre inférieur ou égal à 255 (donc n'ayant pas d'octet fort), il faut considérer que l'octet fort vaut 0 et le charger dans le registre correspondant.

Pour conclure, signalons enfin que nous serons amenés à nous servir également d'un registre double un peu particulier, le registre IX. Ce registre est appelé le registre d'index.

LA PILE

Pour nous, la pile ne sera rien de plus que ce que son nom indique : un certain nombre de données empilées les unes sur les autres. Peu importe de savoir comment elle est gérée par la machine, l'essentiel étant de savoir l'utiliser.
Traditionnellement, la comparaison utilisée pour parler de la pile est celle de la pile d'assiettes, et il faut reconnaître qu'il est difficile de trouver mieux.

Imaginons que vous soyez en train de faire la vaisselle. Vous lavez d'abord une assiette rouge que vous posez quelque part en attendant le rinçage. Vous en lavez ensuite une bleue que vous posez sur la rouge, puis une jaune que vous posez sur la bleue. Le lavage terminé, vous attaquez maintenant le rinçage. Si vous prenez vos assiettes comme elles se présentent sur la pile, la première à être rincée sera la dernière qui a été posée, en l'occurrence la jaune.

C'est le principe de base de la pile : dernier élément entré, premier élément sorti (en anglais, c'est une pile LIFO Last In/First Out).
Si, par superstition ou par fantaisie, vous aviez absolument voulu rincer l'assiette rouge d'abord, il vous aurait au préalable fallu soulever la jaune et la bleue pour prendre la rouge.

Dans nos programmes, nous utiliserons souvent la pile pour y stocker provisoirement des données, car c'est un moyen commode et rapide. Son fonctionnement peut paraître simple, pour ne pas dire simplet, mais vous verrez que l'on a vite fait de s'y perdre, si l'on n'y prend pas garde, lorsque les données s'accumulent.
De plus, il faut savoir (et ne jamais l'oublier) que le processeur, de son côté, se sert lui aussi de la pile pour faire son travail, et qu'il ne doit pas y avoir d'interférence entre vous et lui. Nous en reparlerons...

LE REGISTRE D'INDICATEURS

C'est un registre que nous n'utiliserons pas directement et, là encore inutile de s'encombrer de détails superflus. Il suffit de nous imaginer ce registre comme une boîte comportant huit cases, ou indicateurs, ayant chacune un numéro, et parfois un nom :

Dans chacune de ces "cases", il peut y avoir 1 ou 0 : on dit que tel indicateur est mis à 1 ou 0.
Nous ne toucherons jamais au contenu de ce registre, c'est l'affaire personnelle du processeur. Nous ne nous intéresserons qu'aux indicateurs 7 ou 6 dont voici le rôle :
Tout au long du déroulement d'un programme, et après certaines opérations, le processeur met les indicateurs 6 et 7 à 1 ou à 0, en fonction du résultat.

  • Si le résultat de l'opération est nul, l'indicateur 6 sera mis à 1. Sinon, il sera mis à 0. (Le mot opération doit être pris ici au sens large. Une comparaison entre valeurs est par exemple une opération au même titre qu'une addition.)
  • Si Je résultat de l'opération est positif, l'indicateur 7 sera mis à 1. S'il est négatif, l'indicateur 7 sera mis à 0.

L'indicateur 6 est appelé l'indicateur de zéro (Z), et le 7 l'indicateur de signe (S).
Encore une fois, tout cela est géré par la machine et nous n'aurons pas à nous en préoccuper. L'intérêt est néanmoins évident : comme il existe des instructions permettant de tester les indicateurs, nous pourrons effectuer des branchements conditionnels du genre : "Si l'indicateur 7 est à 1, alors il faut faire ceci ou cela..."
C'est en fait exactement comparable à l'instruction IF du BASIC.

Remarque

Certaines opérations n'entraînent aucune modification des indicateurs, et cela quel que soit le résultat. En outre, certaines instructions conditionnelles ne peuvent s'utiliser qu'en fonction par exemple de l'indicateur 6, et non pas en fonction du 7 (ou vice versa).
Tout cela est précisé dans la cinquième partie qui résume et explique d'une manière formelle une partie des instructions du Z 80.

LA MÉMOIRE

Le CPC est un ordinateur ayant une RAM (mémoire vive) de 64K, ce qui signifie 64x1 024 = 65 536 octets ou cases mémoire, ayant chacune une adresse de 0 à 65535.
En réalité, une partie de ces cases mémoire est réservée à des choses comme la gestion de l'écran ou l'interpréteur BASIC, et il ne reste à l'utilisateur que 43 536 octets (de l'adresse 368 à 43903), c'est-à-dire pas tout à fait 43K.

Lorsque vous écrivez un programme BASIC, il est codé par la machine et stocké à partir de l'adresse la plus basse (368). Plus le programme s'allonge, plus la mémoire se remplit. En outre, il peut arriver que l'exécution d'un programme nécessite des cases mémoire dans les adresses les plus hautes (43903 et au-dessous).
Le problème, c'est qu'il nous faudra disposer, pour nos programmes en langage machine, d'une zone protégée où nous pourrons les loger, et où ils ne courront pas le risque d'être dérangés ou "écrasés" par le BASIC. Nous allons nous servir pour cela de l'instruction MEMORY, qui permet de fixer librement une limite que le BASIC ne pourra en aucun cas franchir. Si par exemple nous tapons :

MEMORY 10000

les cases mémoire allant de 10001 à 43903 resteront vierges de toute intrusion, et le BASIC restera confiné entre les adresses 368 et 10000. Il ne s'agit là que d'un exemple, et nous n'aurons fort heureusement pas à réserver des zones de mémoire aussi importantes. Un programme en langage machine de 500 octets, par exemple, représente déjà un programme tout à fait considérable. Ceux que nous vous proposerons dans cet ouvrage tournent autour de 100 ou 200 octets. Un MEMORY 43600 serait donc tout à fait suffisant, la perte de mémoire négligeable, et rapidement amortie.

Nous utiliserons l'espace mémoire ainsi ménagé de deux manières : d'une part pour y écrire nos instructions, et d'autre part, bien que plus rarement, pour y stocker des données.
A cet égard, une case mémoire fonctionne un peu comme un registre, puisqu'on ne peut y mettre que des nombres inférieurs ou égaux à 255. Pour y mettre des nombres supérieurs, il faudra utiliser deux cases mémoire et mettre dans l'une l'octet faible et dans l'autre l'octet fort.

Il importe dès à présent de bien distinguer l'adresse d'une case mémoire de son contenu.

Exemple

Case mémoire d'adresse 43000 (&A7F8) Case mémoire d'adresse 43001 (&A7F9)
La case mémoire d'adresse 43000 contient la valeur &D1, et celle d'adresse 43001 contient la valeur &B0.
Traditionnellement, le contenu d'une case mémoire d'adresse n s'écrit (n). On peut donc écrire : (43000) = &D1 et (43001) = &B0.

LES INSTRUCTIONS DU Z80

Le processeur Z 80 possède un nombre considérable d'instructions de base : 158. Pour ce qui nous concerne, nous nous contenterons d'en utiliser 35, et vous pourrez constater qu'avec ce nombre réduit, il est déjà possible de faire un certain nombre de choses.
Une instruction se présente sous la forme d'un ou plusieurs nombres que nous écrirons sous la forme hexadécimale. Ces nombres sont toujours inférieurs ou égaux à 255.

Deux exemples

&19 signifie : "Additionner les registres HL et DE".
&DD, &2B signifie : "Décrémenter (= enlever 1) le registre IX".

Physiquement, un programme en langage machine se présente donc tout simplement comme une série de nombres, écrits dans des cases mémoire les uns à la suite des autres.

LES ROUTINES DU SYSTÈME D'EXPLOITATION

Le CPC n'est pas un égoïste, et il nous offre la possibilité, ô combien précieuse, d'accéder aux routines de son système d'exploitation. Ces routines, ou sous-programmes, font partie des programmes intégrés qui permettent à l'ordinateur de fonctionner. Chacune d'elles ayant une adresse bien précise, elles peuvent être appelées exactement de la même manière que l'on appelle un sous-programme en BASIC grâce à l'instruction GOSUB.

Cette possibilité nous épargnera un travail considérable, puisque nous allons utiliser abondamment ces routines "prêtes à l'emploi".

Si, par exemple, nous avons besoin de calculer le sinus d'un angle, il suffit d'appeler la routine d'adresse &BD88. Bien entendu, il faut savoir, pour chacune d'entre elles, quelles sont les modalités d'appel (pour reprendre l'exemple précédent, où faut-il mettre la valeur de l'angle avant d'appeler la routine sinus, et où se trouve le résultat lors du retour de cette routine ?).

Tout cela est expliqué au fur et à mesure pour les routines que nous utiliserons, et en Annexe V pour celles que nous n'utiliserons pas, mais qui peuvent vous servir pour vos propres programmes.

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

Page précédente : Nouvelles Instructions 001
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 588 millisecondes et consultée 1342 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.