COURS206.TXT

From AtariForumWiki
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