ASSEMBLEUR

 

 
COURS D'ASM 68000
(par le Féroce Lapin)

retour au VOLUME 2

            
******************************************************************
*                                                                *
*             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é!!!!!!!