Troisième leçon et vous allez me dire que je le fais exprès car encore aujourd'hui, nous allons découvrir de la théorie, mais c'est pour votre bien !!! Rassurez-vous, c'est la dernière fois, je vous le promets, sachant que la prochaine fois, on commence l'apprentissage par l'exemple. Maintenant, mettez un quart d'heure votre tête dans le frigo (pour vous rafraichir les idées), et allons y ...
Dans ce chapitre, toutes les instructions (qu'on appelle aussi mnémoniques) nécessaires pour programmer l'assembleur seront présentées dans différentes parties selon le type d'instruction (affectations, tests, ...). Chaque instruction sera présentée sous la forme suivante :
Instruction | Champ ou valeur | Code |
Pour les registres, il y a essentiellement des remises à "zéro", mais on peut aussi affecter des valeurs :
A=0 A codé : D0
On initialise le champ A du registre A à "zéro".C=0 X codé : AB2
On initialise le champ X du registre C à "zéro". La valeur du champ X correspond à "b" dans le tableau des champs)ABIT=1 13 codé : 8085D
On initialise le bit 13 ("D" en hexadécimal) du champ A à "un".LC 0B52 codé : 3325B0
On affecte la valeur "0B52" dans le registre C à la position du champ P (ici on considère P=0), nous avons donc une valeur de 4 quartets (n=3 soit 4-1, puisque n est compris entre 0 et 4 inclus) qui est 25B0 (zyxw=25B0).Le champ P est en général affecté à 0, mais pour affecter des valeurs dans les champs M, S ou WP (dans les registres A ou C), il faut déplacer le champ P au bon endroit, comme par exemple :
P= 15 codé : 2F
Ici, le champ P se retouve sur le champ S. La valeur du champ P est égale à 15, soit "F" en hexadécimal.Les pointeurs, comme l'indique leur nom, sont faits pour pointer un endroit de la mémoire ou un objet. Si on veut leur affecter une valeur, ce sera la valeur d'une adresse sur 5 quartets, allant de #00000h à #FFFFFh. En général, on affecte la totalité des quartets (c'est à dire 5), mais si on est sûr de la valeur des quartets de poids fort du pointeur, il est possible d'en déclarer 4 ou 2 seulement. Cela sert surtout à gagner quelques quartets et réduire la taille des objets code. Quelques exemples :
D0= 800F5 codé : 1B5F008
D1= 068D codé : 1ED860
D0= 20 codé : 1902Pour le drapeau HST, on ne peut qu'initialiser les bits à "zéro". Par contre, pour le drapeau STATUS, il est possible d'affecter une valeur vraie ou fausse (0 ou 1) pour chacun des 16 bits. Pour les deux drapeaux , on aussi les initialiser complètement à "zéro" avec CLRHST (pour CLeaR HST) et CLRST (pour CLeaR ST).
SB=0 codé : 822
En général, on initialise SB à "zéro" juste après le test du reste d'une division (ou décalage de bits ou quartets à droite), car s'il y a des bits de perdus, SB se met à "un", et ne se remet pas tout seul à "zéro".
ST=1 12 codé : 85C
CLRHST codé : 82FCe qui suit correspond à la copie de valeurs entre les champs identiques des différents registres.
A=B X codé : AB4
La valeur du champ X de B est copiée dans le champ X de A.
C=ST codé : 09
Ici, on copie les drapeaux 0 à 11 de STATUS dans le champ X de C.
C=P 14 codé : 80CE
Cette instruction sert à copier la valeur du registre P dans le xième quartet de C.Cette instruction est l'inverse de la fonction C=P x, c'est à dire qu'on affecte au champ P la valeur du xième quartet du registre C.
P=C 8 codé : 80D8Pour les pointeurs, il est très facilement leur affecter une adresse avec les registres A et C, de plus on peut leur affecter 4 ou 5 quartets.
D0=A codé : 130
On copie la valeur du champ A de A, c'est à dire une adresse (5 quartets), dans D0 pour qu'il pointe à cette adresse.
D1=CS codé : 13D
Ici, on copie les 4 premiers quartets de C dans le pointeur D1, c'est à dire qu'on oublie le quartet de poids fort.L'instruction ci-après correspond à la fontion inverse de l'instruction C=ST : on affecte le champ X de C au drapeau STATUS.
ST=C codé : 0AEnsuite, nous allons faire une opération de sauvegarde des registres de calcul dans les registres de sauvegarde (R0 à R4).
R0=A codé : 100
Dans cette instruction, aucun champ n'est précisé, cela veut dire qu'on effectue la sauvegarde de la totalité des quartets du registre A.
R2=A A codé : 81AF02
Cet exemple permet de sauvegarder uniquement le contenu de A champ A dans R0 champ A.
R1=C X codé : 81A309
Même chose, mais pour le champ X de C.
RSTK=C codé : 06
On sauvegarde le contenu de C champ A sur le 1er étage de la pile de sauvegarde RSTK. On l'utilise pour la récupération d'une adresse après un saut.On peut aussi réaliser l'opération inverse, soit la récupération des registres. Voici donc l'inverse des précédents exemples :
A=R0 codé : 110
A=R2 A codé : 81AF12
C=R1 X codé : 81A319
C=RSTK codé : 07Pour charger une valeur (pointée) dans un champ spécifique d'un registre, il suffit d'utiliser ces instructions très simples :
A=DAT0 A codé : 142
Ici, on charge dans le champ A du registre A les cinq premiers quartets pointés par D0. Et on peut en faire autant pour n'importe quel champ des registres A et C.
C=DAT1 7 codé : 15F6
On réalise la même opération pour les 7 premiers quartets pointés par D1 (codé "6", puisque l'échelle est établie entre 0 et F en mode hexadécimal).Nous avons aussi l'opération inverse qui permet d'écrire le contenu du champ d'un registre (A ou C) dans une zone pointée par les pointeurs D0 ou D1.
DAT0=A X codé : 1503
On écrit 3 quartets (champ X) du registre A dans la zone pointée par D0.
DAT1=C 13 codé : 15DC
On écrit les 13 premiers quartets du registre C dans la zone pointée par D1.Les instructions qui suivent servent à réaliser des échanges avec les périphériques qui sont le clavier et le buzzer. Mais il est préférable d'utiliser les routines internes à la machine (voir plus loin), car ces instructions ne fonctionnent bien que sur des adresses paires. Les "IN" sont la lecture en entrée (registre A et C), et les "OUT" sont l'écriture en sortie (registre C).
A=IN codé : 802
On lit les 4 premiers quartets qu'on charge dans le registre A.
OUT=C codé : 801
On envoie les 3 premiers quartets (champ X) du registre C en écriture sur la sortie.
OUT=CS codé : 800
On écrit le quartet 0 (le premier) du registre C sur la sortie.
Une première opération à effectuer sur le contenu des registres ou sur la valeur des pointeurs, est l'incrémentation. S'il y a dépassement de la valeur maximale que peut contenir le champ d'un registre, la retenue (CARRY) est activée à "1" (ex. : si on incrémente de "1" le champ X du registre A qui contient la valeur "FFF", alors le champ X passe à "000" et la retenue CARRY=1).
A=A+1 A codé : E4
Le registre A voit son champ A augmenté de la valeur "1".
B=B+1 S codé : B45
Ici, c'est le champ S du registre B qui incrémenté.
C=C+3 A codé : 818F22
Cet exemple montre qu'on peut aussi incrémenter le champ d'un registre d'une valeur comprise entre 1 et 16.
D=D+5 M codé : 818534
Même fonction pour un registre et un champ différent. Attention, pour l'ensemble des registres, cette instruction ne fonctionne normalement que pour les champs B, M, W et X.
P=P+1 codé : 0C
On peut aussi incrémenter de "1" le champ P.
D0=D0+ 16 codé : 16F
On peut déplacer le pointeur D0 en l'incrémentant. Le pointeur D1 est, lui aussi, incrémenté de la même façon.Dans la foulée, nous pouvons en faire de même pour la décrémentation qui est basée sur le même principe, à part la retenue (CARRY) qui passe à "1" si la valeur d'un champ décrémenté franchit le zéro.
A=A-1 A codé : CC
B=B-1 S codé : A4D
C=C-3 A codé : 818FA2
D=D-5 M codé : 8185B4
Cette fonction n'échappe pas à la règle utilisée pour l'incrémentation : pour l'ensemble des registres, cette instruction ne fonctionne normalement que pour les champs B, M, W et X.
P=P-1 codé : 0D
D0=D0- 16 codé : 18FUne opération qui est aussi très utile, c'est l'addition entre deux registres à travers le même champ, La retenue (CARRY) passe là aussi à "1" quand il y a dépassement de la valeur maximale du champ du registre qui recoit la valeur finale.
A=A+B A codé : C0Ensuite, nous pouvons réaliser des soustractions entre les registres. La retenue (CARRY) se met à "1" lors d'un retour en dessous de zéro.
A=B-A A codé : ECLes opérations logiques sont opérées directement sur les bits des champs des registres séléctionnés. Nous allons commencer par le ET Logique qui se traduit de la façon suivante :
et 0 1 0 0 0 1 0 1 A=A&B A codé : 0EF0
Le OU Logique se représente comme suit :
ou 0 1 0 0 1 1 1 1 A=A!B A codé : 0EF8
Le NON logique fait aussi partie des instructions : les bits allumés s'éteignent, et les bits éteints s'allument. Le résultat est fonction du mode courant : si nous sommes en mode hexadécimal, #3h devient #FFFFCh, et si le mode décimal est activé, #3d devient #99996d.
A=-A-1 A codé : FCLa fonction NEG ou complément à 2, permet de donner la valeur "négative" d'un champ sélectionné. Selon le mode sélectionné, #3h devient #FFFFDh en hexadécimal et #99997d en décimal.
A=-A A codé : F8La Multiplication par 2, ou le décalage d'un bit à gauche, se réalise en ajoutant le contenu du champ d'un registre à lui-même. S'il y a dépassement de la valeur maximale du champ du registre, la retenue (CARRY) se met à "1".
A=A+A A codé : C4La Division par 2, ou le décalage d'un bit à droite, se fait grace aux fonctions nSRB (où n est un registre). Le reste de la division non nul est perdu (ou bit perdu), et est signalé par la mise à "1" du drapeau SB (drapeau qu'il faut remettre à "0").
ASRB A codé : 81CLa Multiplication par 16, ou le décalage d'un quartet à gauche, est représentée par les mnémoniques nASL (où n représente les registres). Lors d'un dépassement, SB est mis à "1".
ASL A codé : F0La Division par 16, ou le décalage d'un quartet à droite, est réalisée par les instructions du type nASR (où n représente chaque registre). Lorsque le reste de la division est non nul, SB est mis à "1".
ASR A codé : F4Il est possible de réaliser des Rotations de quartets à gauche sur la totalité des quartets des registres de calcul.
ASLC codé : 810Il en est de même pour la Rotation des quartets à droite.
ASRC codé : 814
Voici maintenant l'échange du contenu d'un registre (A, B, C, D) avec : 1 - le contenu d'un autre registre sur un champ donné (pour l'ensemble des registres : A, B, C, D, R0 à R4).
2 - l'adresse contenue dans un pointeur (D0 ou D1). Cela fonctionne uniquement pour les registres A et C.
3 - avec ST. Le registre C a le monopole de cette fonction qui échange le contenu de ST (les drapeaux de 0 à 11) avec son champ X., soit 3 quartets.
4 - le champ P. Là aussi, seul le registre C peut échanger son xième quartet avec le champ P.Un mnémonique consacré à l'échange s'écrit selon cette structure :
(registre)(registre ou pointeur)EX suivi du champEn voici quelques exemples :
ABEX A codé : DC
BAEX A codé : DC
CAEX A codé : DE
CR1EX A codé : 81AF29
DCEX A codé : DF
Pour comparer des champs de registre à des constantes ou avec d'autres champs de registre, on utilise les tests. Il sont structurés de la manière suivante :
SI { registre }{ =,<=,>=, } { registre ou constante } selon un champ déterminé.
Traduit en assembleur, cela donne :
?X=Y n ?X<=Y n ?X>=Y n ?XY n
Les tests sont toujours suivis de RTNYES ou bien de GOYES qui réalisent des sauts en fonction du résultat (voir plus loin).Voici quelques exemples :
?A=0 A codé : 8A8
?CBIT=0 0 codé : 808A0
?XM=0 codé : 831
?ST=0 15 codé : 86F
?A=B A codé : 8A0
?D>C A codé : 8B3Une fois le test réalisé, il faut utiliser :
1 - soit l'instruction RTNYES. Ce mnémonique est utilisé pour réaliser un retour de sous-programme si et seulement si la retenue (CARRY) est à "1", c'est à dire : si le test est vrai. Si le test est faux, la suite des instructions qui se placent derrière RTNYES sont exécutées. 2 - soit l'instruction GOYES (nom de label) qui saute à un label si le test est vrai, sinon on continue d'exécuter les instructions qui suivent.
En assembleur, il existe plusieurs types de sauts :
1 - les sauts relatifs inconditionnels (GOTO et GOLONG), qu'on envoie à un nom de label, comme par exemple : GOTO TOTO. Les sauts peuvent être courts avec GOTO (de -800h à +7FFh), ou longs avec GOLONG (de -8000h à +7FFFh).
2 - des sauts relatifs conditionnels (GOC et GONC), qui réagissent en fonction de la retenue CARRY. Le mnémonique GOC permet le saut si la retenue est à "1", tandis que GONC permet le saut si la retenue est à "0". des sauts absolus fixes (GOVLNG), qui font appel à une sous-routine en ROM et sortent de l'objet Code pour retourner au RPL. Avant d'utiliser ce mnémonique, il est préférable de récupérer les registres sauvegardés (sauf pour GOVLNG 05143 qui récupère les registres et sort).
3 - des sauts absolus pilotés (PC piloté par le registre A ou C champ A), où PC contient l'adresse de la prochaine instruction assembleur à exécuter. Cette instruction est surtout utilisée lors d'un retour au RPL suite à une récupération de registres:A=DAT0 A
D0=D0+ 5
PC=(A)
Cet ensemble d'instructions est quasiment toujours remplacé par : GOVLNG 05143.
Les sous-programmes servent essentiellement à alléger le programme principal, mais aussi à structurer les sources assembleur.
1 - Les Appels
Primo, on peut appeler un sous-programme par les instructions GOSUB ou GOSUBL selon la taille du saut de l'appel : GOSUB pour un saut de -800h à +7FFh, et GOSUBL pour un saut de -7FFFh à +8000h. Secundo, il est possible d'appeler un sous-programme en ROM par le biais de l'instruction GOSBVL.2 - Les Retours
Il existe une multitude de retours de sous-programmes :
Nous avons à notre disposition les retours inconditionnels. Le mnémonique RTN est sans doute le plus utilisé, mais quelques variantes de cette instruction peuvent nous servir : RTNCC (retour avec mise à "0" de CARRY), RTNSC (retour avec mise à "1" de CARRY), RTNSXM (retour avec mise à "1" de XM), et RTI (routine de retour de gestion d'interruptions).
Nous avons aussi les retours inconditionnels de sous-programmes en fonction de l'état de la retenue CARRY. L'instruction RTNC permet un retour si la retenue est à "1", sinon on exécute la suite des instructions. L'instruction RTNNC est de même type, sauf qu'elle permet un retour si la retenue est à "0".
Ces instructions ne servent à rien, sauf peut-être en tant que pauses ultra rapides puisque ces instructions (qui ne font rien) ont une certaine durée d'exécution.
D'ailleurs, les voici :
NOP3 codé : 420
NOP4 codé : 6300
NOP5 codé : 64000
Quelques instructions un peu spéciales servent à :
1 - contrôler les interruptions : INTON qui autorise les interruptions masquables, contrairement à INTOFF qui les interdit, et puis nous avons RSI qui les réinitialise.
2 - changer le mode de calcul (SETDEC et SETHEX), ce qui provoque quelques répercussions sur les différents calculs.
Voilà une troisième leçon relativement rébarbative mais néanmoins utile avant d'entamer les choses sérieuses. D'ailleurs, en ce qui concerne les références, vous pouvez trouver d'excellents ouvrages sur l'assembleur, et en particulier j'utilise -Voyage au Centre de la HP48G/GX- et - Les Secrets de la HP48G/GX tome 2-. Dans ces 2 bibles de l'assembleur, vous retrouverez toutes les instructions dont celles que nous avons découvertes dans cette leçon. Rendez-vous est pris dans le numéro 4 de HP48E-zine pour voir comment utiliser la pile avec l'assembleur.