COURS206.TXT/fr
Jump to navigation
Jump to search
****************************************************************** * * * COURS D'ASSEMBLEUR 68000 SUR ATARI ST * * * * par Le Féroce Lapin (from 44E) * * * * Seconde série * * * * Cours numéro 6 * ****************************************************************** LES AUTO-MODIFICATIONS Autre chose simple à utiliser et qui facilite beaucoup la program- mation: les programmes auto-modifiables. Comme tous les sujets abordés jusqu'ici, celui n'est pas compliqué mais demande un peu d'attention. Je dois cependant avouer que le première fois que j'ai rencontré une telle chose dans un listing, de nombreuses heu- res m'ont été nécessaires avant de comprendre ! La principale dif- ficulté résidant non pas dans la compréhension du sujet lui-même mais plutôt dans le choix de la méthode d'explication, j'espère que celle-ci vous donnera satisfaction! Il est tout à fait possible d'imaginer une addition avec des va- riables. Par exemple A=1, B=2 pour une opération du genre A+B=C Nous imaginons également sans mal que les valeurs de A et B puis- sent changer en cours de programme pour devenir par exemple A=2 et B=3 ce qui laisse notre opération A+B=C toujours aussi valable. Mais, comment faire pour que cette opération A+B=C devienne tout à coup A-B=C ou encore A/B=C ? Là se fait toute la différence entre un langage évolué et l'assem- bleur. Nous avons vu, dans les premiers cours, que l'assembleur ne faisait que traduire en chiffres les instructions. A la différence des compilateurs qui 'arrangent' les instructions, l'assembleur ne réalise qu'une traduction, instruction par instruction. Nous nous retrouvons donc avec une suite de chiffres, ces chiffres étant dans le 'tube'. Tout comme nous avons écrit dans le tube pour modifier des valeurs données à des variables, il est donc tout a fait possible d'écrire dans le tube pour modifier les chiffres qui sont en fait des instructions. Il est évident que la prudence s'impose car les chiffres que nous allons écrire doivent être reconnus par le 68000 comme une nouvelle instruction et non pas comme n'importe quoi, ce qui conduirait à une erreur. Voyons concrètement un exemple simple.Nous avons une liste de lettres co- dée en word, et nous voulons afficher ces lettres les unes après les autres. Voici un programme réalisant cette opération. INCLUDE "B:\START.S" LEA TABLEAU,A6 dans A6 car GEMDOS n'y touche pas DEBUT MOVE.W (A6)+,D0 prélève le word CMP.W #$FFFF,D0 c'est le flag de fin? BEQ FIN oui, bye bye MOVE.W D0,-(SP) non, donc le passe sur la pile MOVE.W #2,-(SP) pour l'afficher TRAP #1 ADDQ.L #4,SP MOVE.W #7,-(SP) attend un appui touche TRAP #1 ADDQ.L #2,SP BRA DEBUT et on recommence FIN MOVE.W #0,-(SP) TRAP #1 *--------------------------------* SECTION DATA TABLEAU DC.W 65,66,67,68,69,70,$FFFF SECTION BSS DS.L 100 PILE DS.L 1 END Imaginons maintenant que cet affichage soit dans une subroutine, et que nous voulions afficher une lettre à chaque appel de cette subroutine: On attend un appui sur une touche, si c'est 'espace', alors on s'en va, sinon on saute à la routine qui affiche un ca- ractère puis revient. Voici un premier essai: INCLUDE "B:\START.S" DEBUT MOVE.W #7,-(SP) TRAP #1 ADDQ.L #2,SP CMP.W #" ",D0 BEQ FIN BSR AFFICHE BRA DEBUT FIN MOVE.W #0,-(SP) TRAP #1 *------------------------------* AFFICHE LEA TABLEAU,A6 adresse du tableau MOVE.W A6)+,D0 prélève le word MOVE.W D0,-(SP) le passe sur la pile MOVE.W #2,-(SP) pour l'afficher TRAP #1 ADDQ.L #4,SP RTS puis on remonte *--------------------------------* SECTION DATA TABLEAU DC.W 65,66,67,68,69,70,$FFFF SECTION BSS DS.L 100 PILE DS.L 1 END Assemblez et lancez le programme. Constatation: à chaque appui sur une touche, on obtient un 'A' mais pas les autres lettres!!! Evidemment, puisqu'à chaque fois que nous sautons dans notre su- broutine AFFICHE, celle-ci recharge l'adresse du tableau. Le ca- ractère prélevé est donc toujours le premier. Pour éviter cela, il faut donc créer un pointeur qui avancera dans ce tableau. Dans no- tre exemple, il suffisait en fait de placer le LEA TABLEAU,A6 au début du programme. A6 n'étant modifié par personne, cela aurait fonctionné.... jusqu'au 7ème appui sur une touche, A6 pointant alors hors du tableau ! De plus, nous sommes ici pour apprendre et nous envisageons donc le cas où, en dehors de la routine, tous les registres sont modifiés! Impossible donc de garder A6 comme poin- teur. Voici donc la routine AFFICHE modifiée: AFFICHE MOVEA.L PTN_TAB,A0 MOVE.W (A0)+,D0 CMP.W #$FFFF,D0 BNE .ICI LEA TABLEAU,A0 MOVE.L A0,PTN_TAB BRA AFFICHE .ICI MOVE.L A0,PTN_TAB MOVE.W D0,-(SP) MOVE.W #2,-(SP) TRAP #1 ADDQ.L #4,SP RTS De plus il faut rajouter après le INCLUDE (donc avant le label début) LEA TABLEAU,A0 MOVE.L A0,PTN_TAB et en section BSS PTN_TAB DS.L 1 Un peu d'analyse après ces changements! Tout d'abord nous consta- tons avec bonheur que ça marche! Au début nous mettons en place un pointeur. LEA TABLEAU,A0 met l'adresse du tableau en A0 MOVE.L A0,PTN_TAB et la sauve dans PTN_TAB Nous avons donc maintenant dans le tube en face de l'étiquette PTN_TAB un long mot, ce long mot étant l'adresse du début du ta- bleau. Ensuite dans la routine, nous prélevons cette adresse. Ici une petite remarque s'impose car la confusion est fréquente: Si nous avons: IMAGE INCBIN "A:\MAISON.PI1" et que nous voulons travailler avec cette image, nous ferons LEA IMAGE,A0 A0 pointera alors sur l'image. Par contre si nous avons : PTN_IMAG DC.L IMAGE C'est-à-dire une étiquette pour un long mot se trouvant être l'adresse de l'image, en faisant LEA PTN_IMAGE,A0 nous ne récupé- rons pas en A0 l'adresse de l'image mais en fait l'adresse de l'adresse de l'image! Pour récupérer directement un pointeur sur l'image il faut faire: MOVEA.L PTN_IMAGE,A0 Cependant, pour récupérer l'adresse du tableau il aurait également été possible de faire: MOVEA.L #TABLEAU,A0 Ceci dit, continuons notre exploration: Dans PTN_TAB nous avons donc l'adresse du début du tableau. Attente d'un appui touche, hop on saute dans la routine. Transfert l'adresse contenue dans PTN_TAB dans A0 puis on prélève le word contenu dans le tube à cette adresse et on le met en D0. Comme nous avons réalisé cette opération avec (A0)+, A0 point donc maintenant sur le prochain word du tableau. Testons si le word prélevé est $FFFF, ce qui in- diquerait la fin du tableau. Si ce n'est pas le cas, on saute à .ICI et on sauve la nouvelle valeur de A0 dans PTN_TAB. Si le word prélevé est $FFFF, on recharge PTN_TAB avec l'adresse du haut du tableau, et c'est reparti comme en 14!!! Ce système de pointeur, très fréquemment utilisé, est simple d'em- ploi et bien commode! Voyons cependant une autre méthode, plus tordue! Supprimons tout d'abord la routine AFFICHE et remplaçons la par la suivante: AFFICHE MOVEA.L #TABLEAU,A0 MOVE.W (A0)+,D0 MOVE.W D0,-(SP) MOVE.W #2,-(SP) TRAP #1 ADDQ.L #4,SP RTS Réassemblons et lançons. Il est bien évident que cela ne marche plus puisqu'à chaque appel de la routine, nous rechargeons A0 avec l'adresse du TABLEAU, donc le word prélevé sera toujours le pre- mier du tableau. Passons sous MONST avec Alt+D. Descendons sur le label AFFICHE. Nous trouvons en face MOVEA.L #TABLEAU,A0 etc.... Quittons avec control+C puis réassemblons, mais attention avant de cliquer sur 'assembler', jetons un coup d'oeil sur les options. Nous avons par défaut DEBUG INFO indiquant Extend. Cela signifie que les noms des labels vont être incorporés dans le programme. Cela nous permet de retrouver les noms de ces labels lorsque nous sommes sous MONST. Choisissons l'option NONE pour DEBUG INFO as- semblons et repassons sous MONST. Surprise, les noms des labels ont disparu et sont remplacés par des chiffres. C'est logique puisque, de toute façon, l'assembleur traduit notre source en chiffres. Cherchons notre routine AFFI- CHAGE. C'est un peu plus dur puisque son étiquette n'est plus vi- sible! Pour se repérer, on peut chercher au début (après le start) CMP.W #$20,D0 qui est la comparaison avec la touche espace après l'appui touche. Ensuite, un BEQ vers la fin et le BSR vers notre routine. Relevons l'adresse située en face du BSR et allons-y. La première ligne de notre routine c'est MOVEA.L #$XXXXXXX,A0 XXXXXXX étant l'adresse du tableau. Je rappelle que sur un 68000 le pro- gramme peut se trouver n'importe où en mémoire, cette adresse sera donc différente suivant les machines. Pour ma part c'est $924C6. J'active la fenêtre 3 avec Alt+3 puis avec alt+a je demande à la fenêtre de se positionner sur cette adresse. MONST m'affiche au centre les codes ASCII des lettres de mon tableau ($41,$42 etc...) et à droite ces lettres en 'texte'. En avançant dans cette routine d'affichage, je vais donc mettre (pour moi) $924C6 en A0, cette adresse étant celle pointant sur le 'A' du tableau. Ce qui m'intéresserait, c'est que, la prochaine fois, cela me permette de pointer sur le 'B'. Pour cela il faudrait avoir: MOVEA.L #$924C6,A0 pour le 'A' et ensuite MOVEA.L #$924C8,A0 pour le 'B'. Les lettres étant sous forme de word dans mon tableau il faut une avance de 2 ! Retournons dans la fenêtre 2, en face de ce MOVEA.L, regardons l'adresse à laquelle il se trouve (colonne de gauche), notons cette adresse, et notons également l'adresse de l'instruction sui- vante (MOVE.W (A0)+,D0). Activons la fenêtre 3, et plaçons nous à l'adresse du MOVEA.L. Dans mon cas, et puisque j'avais: MOVEA.L #$924C6,A0 je trouve 207C 0009 24C6 J'en déduis que ces 3 words constituent la représentation de mon instruction MOVEA.L, puisque l'adresse du word suivant correspond à celle de l'instruction suivante. Or, je retrouve dans ce codage, l'adresse de mon tableau. Avec un peu d'imagination, je conçois aisément qu'il est possible d'écrire directement dans le 'tube' et par exemple de modifier le word qui a pour valeur actuelle 24C6. Si je lui ajoute 2, mon instruction deviendra 207C 0009 24C8 ce qui reviendra à MOVEA.L #$924C8,A0 et qui me fera pointer sur le second word du tableau!!!!!!!! Voici donc la version auto-modifiable de la routine AFFICHE. AFFICHE MOVEA.L #TABLEAU,A0 MOVE.W A0),D0 CMP.W #$FFFF,D0 BNE ICI MOVE.L #TABLEAU,AFFICHE+2 BRA AFFICHE .ICI ADD.W #2,AFFICHE+4 MOVE.W D0,-(SP) MOVE.W #2,-(SP) TRAP #1 ADDQ.L #4,SP RTS Note: PTN_TAB ne nous sert plus, et de même le LEA tableau du début. Assemblez avec NONE en DEBUG INFO, puis passez sous MONST, faites avancer pas à pas et regardez la ligne MOVEA.L #TABLEAU,A0 se modifier! Expliquons bien clairement ce qui se passe. Nous plaçons TABLEAU en A0 puis nous prélevons le word. Admettons tout d'abord qu'il ne s'agisse pas de $FFFF, nous sautons donc à .ICI. Il faut donc ajouter 2 pour augmenter l'adresse et pointer la prochaine fois sur la seconde lettre du tableau. Nous avons vu qu'en étant codée la ligne MOVEA.L etc... tient sur 3 words donc 6 bytes. L'ajout de 2 doit donc porter sur le 3ème word. Le début de ce word c'est le byte 4. Pour cette raison nous donnons comme des- tination de l'addition AFFICHE+4. Si nous avions prélevé $FFFF, il aurait fallu réinitialiser notre ligne MOVEA.L avec MOVE.L #TABLEAU,AFFICHE+2. Pourquoi +2 ? Parce que l'adresse de tableau est un long mot et que, dans le codage de l'instruction, cela commence sur le second word. Il faut donc sauter un seul word c'est-à-dire 2 bytes. Dans le même ordre de chose, il est tout à fait possible de modi- fier plus profondément un programme. En voici un exemple flagrant. (voir listing numéro 4) Sachant que l'instruction RTS (Return from subroutine) est codée avec $4E75 et que l'instruction NOP (No operation) est codée par $4E71, en plaçant un NOP ou un RTS, on change en fait la fin de la routine. NOP ne fait rien du tout. C'est une opération qui est bi- don dans le sens où rien ne change, mais cette instruction consomme un peu de temps. Elle nous servira donc pour réaliser des petites attentes (bien utile pour des effets graphiques par exem- ple). Suivez bien le déroulement de ce programme sous MONST afin de voir les modifications se faire. Un cas plus complexe: MOVE.W #23,D0 MOVE.W #25,D1 VARIANTE ADD.W D0,D1 MULU.W #3,D1 SUB.W #6,D1 MOVE.W D1,D5 Après assemblage de ce petit morceau de programme, passez sous MONST et jetez un coup d'oeil à la fenêtre 3. En pointant sur VARIANTE et en regardant les adresses en face des instructions, on en déduit que: ADD.W D0,D1 est converti en $D240 MULU.W #3,D1 est converti en $C2FC $0003 SUB.W #6,D1 est converti en $0441 $0006 Si nous prenons maintenant: MOVE.W #23,D0 MOVE.W #25,D1 VARIANTE MULU.W D0,D1 SUB.W #8,D1 ADD.W #4,D0 MOVE.W D1,D5 Nous assemblons, passons sous MONST: MULU.W D0,D1 est converti en $C2C0 SUB.W #8,D1 est converti en $0441 $0008 ADD.W #4,D0 est converti en $0640 $0004 Donc, si dans un programme utilisant cette 'routine' je fais LEA VARIANTE,A0 MOVE.W #$D240,(A0)+ MOVE.L #$C2FC0003,(A0)+ MOVE.L #$04410006,(A0)+ J'obtiendrai la première version: ADD.W D0,D1; MULU.W #3,D1; SUB.W #6,D1 alors que si je fais: LEA VARIANTE,A0 MOVE.W #$C2C0,(A0)+ MOVE.L #$04410008,(A0)+ MOVE.L #$06400004,(A0)+ j'obtiendrai la seconde version! Essayez avec le programme ci-après, en le suivant sous MONST: Note: ce programme n'a pas de fin donc quitter avec Control+C: LEA VARIANTE,A0 MOVE.W #$D240,(A0)+ MOVE.L #$C2FC0003,(A0)+ MOVE.L #$04410006,(A0)+ LEA VARIANTE,A0 MOVE.W #$C2C0,(A0)+ MOVE.L #$04410008,(A0)+ MOVE.L #$06400004,(A0)+ MOVE.W #23,D0 MOVE.W #25,D1 VARIANTE MULU.W D0,D1 SUB.W #8,D1 ADD.W #4,D0 MOVE.W D1,D5 END Remarques: Il est tout à fait possible d'envisager plus de 2 ver- sions d'une même partie de programme. Si les tailles de ces diffé- rentes versions diffèrent, ce n'est pas grave car il est toujours possible de combler avec des NOP. Les applications de ce genre de 'ruse' peuvent être assez nombreuses: raccourcissement de program- mes, rapidité (une routine devant être appelée 15000 fois aura tout intérêt à être modifiée avant, au lieu d'y incorporer des tests, par exemple), modifications aléatoires des routines de pro- tection (un coup, j'en mets une en place la prochaine fois, j'en mettrai une autre...).... Faites cependant bien attention, car une erreur d'un chiffre et le nouveau code mis en place ne voudra plus rien dire du tout! Faites également attention à vos commentaires car là, ils deviennent hyper importants, étant donné que le listing que vous avez sous les yeux ne sera pas forcément celui qui sera exécuté!!!!!!!
Back to ASM_Tutorial