# Print avec VisUAL Le programme présent permet d'exécuter un programme assembleur écrit pour VisUAL et d'afficher les entiers ou chaînes de caractères en utilisant la procédure `println`. Ce programme ne sert que d'exécution, il ne modifie pas le programme assembleur. Ainsi, votre programme assembleur doit être adapté dès le début en suivant les instructions ci-dessous. ## Utilisation Vous pouvez exécuter votre programme assembleur et afficher son output avec la commande suivante : ```bash java -jar pcl.jar <programme> ``` Par exemple `java -jar pcl.jar example.s` ## Adapter votre code Ajoutez par défaut les instructions suivantes à votre programme assembleur. ### Le code à ajouter Tout d'abord le programme doit commencer par une déclaration de l'espace nécessaire à l'affichage. ```armasm STR_OUT FILL 0x1000 ``` Ajoutez le code nécessaire à l'affichage : ```armasm println STMFD SP!, {LR, R0-R3} MOV R3, R0 LDR R1, =STR_OUT ; address of the output buffer PRINTLN_LOOP LDRB R2, [R0], #1 STRB R2, [R1], #1 TST R2, R2 BNE PRINTLN_LOOP MOV R2, #10 STRB R2, [R1, #-1] MOV R2, #0 STRB R2, [R1] ; we need to clear the output buffer LDR R1, =STR_OUT MOV R0, R3 CLEAN LDRB R2, [R0], #1 MOV R3, #0 STRB R3, [R1], #1 TST R2, R2 BNE CLEAN ; clear 3 more STRB R3, [R1], #1 STRB R3, [R1], #1 STRB R3, [R1], #1 LDMFD SP!, {PC, R0-R3} ``` Ajoutez le code nécessaire pour transformer un entier en chaîne de caractère : ```armasm to_ascii STMFD SP!, {LR, R4-R7} ; make it positive MOV R7, R0 CMP R0, #0 MOVGE R6, R0 RSBLT R6, R0, #0 MOV R0, R6 MOV R4, #0 ; Initialize digit counter to_ascii_loop MOV R1, R0 MOV R2, #10 BL div32 ; R0 = R0 / 10, R1 = R0 % 10 ADD R1, R1, #48 ; Convert digit to ASCII STRB R1, [R3, R4] ; Store the ASCII digit ADD R4, R4, #1 ; Increment digit counter CMP R0, #0 BNE to_ascii_loop ; add the sign if it was negative CMP R7, #0 MOVGE R1, #0 MOVLT R1, #45 STRB R1, [R3, R4] ADD R4, R4, #1 LDMFD SP!, {PC, R4-R7} ``` Et le code pour une division : ```armasm ; Integer division routine ; Arguments: ; R1 = Dividend ; R2 = Divisor ; Returns: ; R0 = Quotient ; R1 = Remainder div32 STMFD SP!, {LR, R2-R5} MOV R0, #0 MOV R3, #0 CMP R1, #0 RSBLT R1, R1, #0 EORLT R3, R3, #1 CMP R2, #0 RSBLT R2, R2, #0 EORLT R3, R3, #1 MOV R4, R2 MOV R5, #1 div_max LSL R4, R4, #1 LSL R5, R5, #1 CMP R4, R1 BLE div_max div_loop LSR R4, R4, #1 LSR R5, R5, #1 CMP R4,R1 BGT div_loop ADD R0, R0, R5 SUB R1, R1, R4 CMP R1, R2 BGE div_loop CMP R3, #1 BNE div_exit CMP R1, #0 ADDNE R0, R0, #1 RSB R0, R0, #0 RSB R1, R1, #0 ADDNE R1, R1, R2 div_exit CMP R0, #0 ADDEQ R1, R1, R4 LDMFD SP!, {PC, R2-R5} ``` ### Comment afficher maintenant ? Remarque : les explications suivantes peuvent sensiblement différer selon la façon dont vous gérez votre pile et la façon dont vous gérez les opérandes. #### Idée générale Vous devez stocker en pile la valeur que vous souhaitez afficher précédée d'un '0'. La limite théorique de taille à afficher est grande (de la taille de STR_OUT). #### Du code On suppose que la valeur à afficher se trouve dans R0. L'idée reste la même si vous souhaitez afficher quelque chose de plus grand que 4 octets. Pour appeler la procédure `println` : ```armasm SUB SP, SP, #4 ; réservez 4 octets pour le 0 MOV R1, #0 STR R1, [SP] SUB SP, SP, #4 ; réservez 4 octets pour la valeur (ou plus) STR R0, [SP] ; stockez la valeur MOV R0, SP ; adresse de la valeur (ici SP, mais peut être n'importe quelle adresse) BL println ADD SP, SP, #8 ; libérez la pile ``` Si vous disposez dans R0 de l'adresse de la valeur à afficher, vous pouvez directement appeler `println`. L'important est que la valeur dans R0 lors de l'appel soit bien l'adresse de la valeur à afficher. ### Remarque importante Vous remarquerez que si vous donnez l'adresse d'une valeur entière, par exemple 65, vous allez afficher 'A' et pas l'entier 65. Pour afficher un entier, vous devez donc le convertir en chaîne de caractères. Vous pouvez utiliser la procédure `to_ascii` pour cela. Un exemple d'appel en supposant que j'ai dans R0 la valeur entière que je souhaite afficher : ```armasm addr0 FILL 12 ; on réserve 12 octets pour la valeur LDR R3, =addr0 ; on charge l'adresse de la valeur BL to_ascii ; on convertit l'entier en chaîne de caractères LDR R0, =addr0 ; on charge l'adresse de la chaîne de caractères BL println ; on affiche la chaîne de caractères ``` ## Fonctionnement La technique utilisée est l'exploitation des breakpoints de VisUAL. Le programme Java lit d'abord le programme assembleur à la recherche de la ligne `STRB R2, [R1, #-1]` indiquant la fin de l'affichage. La ligne correspondante (en commençant à compter les lignes à 0) est alors stockée et considérée comme un breakpoint. La ligne utilisée permet d'ajouter un `\n` à la fin de l'affichage. Si vous souhaitez afficher sans saut de ligne, vous pouvez supprimer la ligne précédente (le MOV de 0) ou recréer une procédure `print` sans le MOV (il faut tout de même le STRB pour le breakpoint). On exécute finalement le programme assembleur à l'aide de la version headless de VisUAL en lui donnant les arguments nécessaires. Basiquement, lors de l'exécution, le programme s'arrête à chaque breakpoint et produit du contenu dans un fichier de log. Le fichier de log est ensuite lu par le programme Java et il extrait le contenu de STR_OUT lors de chaque affichage. Le fichier de log contient la valeur en hexadécimal, convertie en ASCII lors de la lecture des logs. L'exécution est plutôt verbeuse, si vous souhaitez simplement afficher le résultat de l'exécution, vous devrez extraire la bonne information de l'output.