Appendix C ASM : le manuel de référence C.1 Présentation d’asm Le logiciel asm est un mini assembleur susceptible de générer du code pour la machine virtuelle simplifiée mac. Ce code "objet" peut être exécuté, sous contrôle d’un metteur au point, sur le simulateur de cette machine, sim. C.2 Spécifications du logiciel asm Il lit ses entrées sur stdin, génère le code, sous forme symbolique, sur stdout, et signale les erreurs sur stderr. Sous UNIX, il est mis en oeuvre par la commande asm. Le code fourni est compatible avec les entrées attendues par le logiciel sim, et il est possible d’écrire : asm < toto.asm | sim pour assembler et exécuter immédiatement le programme. C.3 Format des instructions Le format général des instructions acceptées par asm est le suivant : [ étiquette : ] [ code-opération [ opérande ] ] <fin-de-ligne> Une fin de ligne étant le caractère CR (et/ou LF), ou un commentaire débutant par le caractère * et se terminant par CR (et/ou LF). Ex: boucle: ld.l r4,tab(r2) 85 * Element suivant 86 APPENDIX C. ASM : LE MANUEL DE RÉFÉRENCE Le format est libre, un blanc devant séparer le code-opération de l’opérande. L’assembleur ne tient pas compte de la casse (majuscule ou minuscule) des lettres. L’étiquette est un identificateur alphanumérique, c’est-à-dire une suite de lettres et de chiffres débutant par une lettre. Le code-opération peut éventuellement être suivi de .L pour préciser que le format de l’instruction est long. Dans la plupart des cas, cette précision est inutile. Les constantes, sur 16 bits, peuvent s’exprimer sous forme décimale (235), hexadécimale (0xa30c), binaire (0b1111000000110101) ou caractère (’xy’). Les opérandes acceptés correspondent à la syntaxe suivante : registre R0 à R15 registre, registre registre, constante registre, identificateur registre, identificateur(registre) registre,(registre) identificateur identificateur(registre) constante constante, constante ex: ex: ex: ex: ex: ex: ex: ex: ex: ex: R12 R2,R4 R5,3 R8,total R6,table(R2) R4,(R7) suite table(R2) 0x2fc3 ’c’,0 Dans la quasi-totalité des cas, il est nécessaire d’utiliser le préfixe "#" devant une constante pour indiquer une opération immédiate : LD.L R2,0x3f4 Le registre R2 reçoit le contenu du mot mémoire d’adresse 0x3f4. LD.L R2,#0x3f4 Le registre R2 reçoit la valeur hexadécimale 0x3f4. Le suffixe .L indique que l’opération est une instruction longue (sur 4 octets) : LD R2,#12 Le registre R2 reçoit la valeur 12 car l’instruction créée est 0x012C. LD.L R2,#0x3f4 Le registre R2 reçoit la valeur 0x3f4 car l’instruction créée est 0x812003f4. LD R2,#0x3f4 L’assembleur ne peut générer une constante courte : il assemble l’instruction au format long comme ci-dessus. LD.L R2,#12 Le registre R2 reçoit la valeur décimale 12, mais l’instruction est explicitement créée au format long : 0x8120000C. C.3. FORMAT DES INSTRUCTIONS 87 C.3.1 Directives de l’assembleur ORG {origin} Syntaxe : ORG constante Indique que les instructions seront implémentées à partir de l’adresse précisée par la constante. ex: ORG 0x200 L’adresse précisée est une adresse d’octet. Elle doit être paire. BSS {Block Started by Symbol} Syntaxe : BSS constante Réserve à l’adresse courante une zone de mémoire de taille égale à la valeur de la constante. Cette valeur est un nombre d’octets. DATA Syntaxe : DATA constante Définit à l’adresse courante une constante de type mot [16 bits]. La constante peut éventuellement s’exprimer sous la forme d’un couple de deux constantes 8 bits, séparées par une virgule, et représentant les octets de poids fort et de poids faible du mot à créer. START Syntaxe : START étiquette Définit le point d’entrée du programme (adresse de la première adresse à exécuter). 88 APPENDIX C. ASM : LE MANUEL DE RÉFÉRENCE C.3.2 Instructions de l’assembleur L’assembleur reconnaît toutes les instructions décrites dans la documentation relative à la machine. Il offre une souplesse (limitée) sur les points suivants : Quand une instruction nécessite manifestement un format "long", alors que ".L" n’a pas été précisé comme suffixe de l’opération, il considère que le format long est implicite. C’est le cas d’instructions telles que : add r3,#2f1 ld r5,toto pour lesquelles un format court ferait perdre une partie du sens (premier cas, constante réduite à "1"), ou ne permettrait pas un assemblage correct de l’instruction (second cas). Les instructions push, pop, jsr et ret font référence explicitement à un registre de pile. Si ce registre n’est pas précisé dans l’instruction, l’assembleur considérera qu’il s’agit du registre 15, et générera le code en conséquence. L’instruction trap permet de préciser un niveau et un sous-niveau d’interruption, sous la forme de deux constantes séparées par une virgule. Si une seule constante est spécifiée, l’assembleur considérera qu’il s’agit du sous-niveau, et utilisera 13 comme niveau, correspondant aux "appels du superviseur". Signalons enfin que l’assembleur transmet en sortie les commentaires débutant par trois astérisques. De tels commentaires sont reconnus comme des ordres d’impression par le simulateur, et permettent ainsi de documenter du code source et du code objet. C.3.3 Format du code objet L’assembleur génère un code objet "absolu" (c’est à dire qu’il doit être chargé à une adresse précise de la mémoire). Ce code est composé de directives destinées au simulateur, consistant en une succession de directive /addr pour spécifier une adresse de chargement suivie d’une directive /w pour indiquer la valeur d’un ou de plusieurs mots devant chargés en mémoire à partir de l’adresse indiquée. Si une directive start a été utilisée dans le programme, l’assembleur générera une commande /run pour le simulateur. C.4. EXEMPLES C.4 89 Exemples C.4.1 Calcul de PGCD Le programme ci-dessous reprend un exemple donné en cours : le calcul du PGCD de deux nombres. *** PGCD pgcd: suite: fin: * main: org cmp jeq jlt ld ld ld sub jmp ret 0x1000 r2,r3 fin suite r0,r3 r3,r2 r2,r0 r3,r2 pgcd r15 la.l r15,pile ld.l r2,#1144 ld.l r3,#858 jsr r15,pgcd ld.l r3,#1287 jsr r15,pgcd * Pour avoir un resultat ld r0,r2 trap 3 trap 2 *** Resultat attendu : 143 halt bss 16 pile: bss 0 start main Quelques remarques sur cet exemple : Le programme définit explicitement une zone attribuée à la pile, de taille 16 (instruction bss 16). Comme la pile opère "en remontant" vers les adresses basses, la valeur affectée au registre désignant la pile doit être celle de la fin de la zone. Ici, cette adresse est obtenue en plaçant l’étiquette après la déclaration de la zone de 16 octet. Cette étiquette est associée à la déclaration d’un bloc de taille zéro, mais le même effet aurait été obtenu en plaçant simplement l’étiquette seule dans sa ligne. APPENDIX C. ASM : LE MANUEL DE RÉFÉRENCE 90 Le programme, pour calculer le pgcd des trois nombres 1144, 858 et 1287, effectue deux appels successifs au sous-programme PGCD. Le résultat du premier appel (le pgcd de 1144 et 858) se retrouve dans R2 et R3 à la sortie du sous-programme. Il suffit donc, pour le second appel, de placer la valeur du troisième nombre dans l’un des registres R2 ou R3. Le code objet généré par l’assembleur pour l’exemple ci-dessus est le suivant : *** PGCD *** Resultat attendu : 143 /addr 1000 /w 2523 8f10 1016 8f40 1010 2103 2132 2120 2432 8f00 /w 1000 1cf0 abf0 1048 8120 0478 8130 035a 8cf0 1000 /w 8130 0507 8cf0 1000 2102 1ad3 1ad2 1e00 /run 1018 C.4.2 Édition d’un nombre sous forme d’une chaîne de caractères Bien qu’il existe une méthode pour imprimer un nombre sous forme décimale (la trappe 13,3), le programme suivant se propose de créer une chaîne de caractères, terminée selon les conventions du langage C par un octet de valeur 0, contenant la représentation décimale du nombre. Voici le sous-programme edit, réalisant cette opération, et le programme principal faisant appel à ce sous-programme pour éditer le nombre 9237. Quelques remarques préliminaires : – Une chaîne de caractères est constituée d’une suite d’octets non nuls, terminée par un octet de valeur 0. – Il s’agit ici de créer la représentation décimale d’un nombre, 9237 en l’occurrence. Cette représentation va s’effectuer par l’intermédiaire de caractères du code ASCII, représentant les chiffres décimaux successifs du nombre. On notera que ces caractères, les chiffres allant de 0 à 9, sont représentés par les codes consécutifs du code ASCII, ’0’ (valeur hexadécimale 0x30, valeur décimale 48, valeur binaire $00110000) à ’9’ (valeur hexadécimale 0x39, valeur décimale 57, valeur binaire $00111001). Pour un nombre compris entre 0 et 9, on obtient donc, de manière très simple, le code ASCII du chiffre correspondant en ajoutant la valeur 48 à ce nombre. * * * * edit: Édition d’un nombre sous forme de chaine de caracteres cmp r2,#0 C.4. EXEMPLES * next: * zero: fin: jeq zero jge next Le nombre est négatif ldb.l r0,#’-’ stb r0,(r3) add r3,#1 neg r2 push r4 ld r4,#10 jsr subedit pop r4 jmp fin Le nombre est nul ldb.l r0,#’0’ stb r0,(r3) add r3,#1 ldb.l r0,#0 stb r0,(r3) ret * * Sous-programme d’édition du car. suivant * subedit: ld r0,#0 ld r1,r2 div r4 add.l r1,#’0’ ld r2,r0 jeq sub1 push r1 jsr subedit pop r1 sub1: stb r1,(r3) add r3,#1 ret * * Programme principal * début: ld.l r2,#9237 la r3,str jsr edit la r0,str 91 92 APPENDIX C. ASM : LE MANUEL DE RÉFÉRENCE trap trap trap str: bss start *** Resultat : 9237 1 * 2 * 0 * 10 début Impression de chaine Impression de "\n" Arrêt Le code généré par l’assembleur est le suivant : *** Resultat : 9237 /addr 0000 /w 0520 8f10 0022 8f50 /w 2df4 014a 8cf0 0032 /w 0331 8200 0000 4a03 /w 2120 8f10 004a 2df1 /w 8120 2415 ab30 0066 /w 1ad0 /run 0050 0014 2ef4 1cf0 8cf0 8cf0 8200 8f00 0100 0032 0000 002d 002a 2112 2ef1 ab00 4a03 8200 1940 4a13 0066 0331 0030 8310 0331 1ad1 1620 4a03 0030 1cf0 1ad2 et son exécution fournit : *** Resultat : 9237 9237 *** Arret du programme par TRAP 13,0 psw = ...P...Z 1100 0066 *** SIM/CPU - 63 insts. 1815 ticks. Remarque Bien que le programme ci-dessus fasse appel à la pile (puisqu’il utilise des sous-programmes, empile des valeurs), le registre r15 n’a pas été initialisé par le programmeur. En effet, le simulateur initialise tous les registres, donc r15, avec la valeur 0. Lors de l’utilisation de r15 pour empiler des valeurs (par push) ou des adresses de retour (par jsr), r15 va désigner les adresses successives 0xfffe, 0xfffc, 0xfffa, etc, et référencer ainsi les derniers mots de la mémoire. Ceux-ci sont inutilisés (puisque le programme est chargé en début de mémoire), et peuvent donc être employés sans inconvénient pour représenter la pile d’exécution.