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 5                         *
******************************************************************

Après  ce cours sur les macros,  nous allons passer à l'usage  des
tableaux   en  assembleur.   J'espère qu'après chaque  cours  vous
effectuez  vous-même des recherches et des exercices,  et que vous
ne  vous précipitez pas tout de suite sur le cours  suivant.  Même
si   les cours vous semblent maintenant courts par rapport à  ceux
de  la première série, il n'en soulèvent pas moins le voile sur de
nombreux   sujets.  Vous   avez  maintenant  le  niveau  pour  les
approfondir et j'espère que vous le faites!

LES TABLEAUX
L'usage   des  tableaux n'est pas très répandu  en  assembleur  au
niveau   des  programmeurs débutants.  En effet  le  système  peut
sembler  assez périlleux à manipuler mais s'avère par contre  très
puissant et très commode!

Tout  comme le principe de la pile,  celui des tableaux est simple
mais   demande  beaucoup  de  logique  pour  pouvoir   fonctionner
convenablement.  Prenons   tout d'abord un  exemple  simple:  nous
allons   appuyer sur les touches de fonctions F1 jusqu'à F10 et  à
chaque   fois nous allons afficher une lettre.  Commençons par  un
exemple sans tableau.

Voir le listing numéro 2.

Au  départ INCLUDE de la routine de démarrage de programme étudiée
au  début de la seconde série. Présentation avec un petit message,
puis  attente d'un appui sur une touche. Comme nous voulons tester
les   touches  de  fonctions et que celles-ci n'ont  pas  de  code
ASCII,  nous  profitons du fait que la fonction Gemdos 7  retourne
dans  le poids faible de D0 le code ASCII mais aussi dans le poids
fort le scan code. Il n'y a d'ailleurs pas que ça de retourné, car
si   vous consultez la bible ST ou simplement la  description  des
fonctions  dans les dernières pages de la doc du GFA 3.00,  vous y
apprenez  que  Gemdos  7 renvoi  dans  les bits 0-7 le code ASCI, 
16-23 le  code clavier et 24-31 l'état des touches de commutations
du clavier.

Petit   rappel  sur les codes ASCII et les scan  code.  Les  codes
ASCII  (American Standard Code for Information  Interchange)  sont
des   codes  sur 7 bits,  qui  suivent  l'ordre  alphabétique.  Ce
standard   est  aussi  appelle  Code  télégraphique  international
numéro  5. Le 8 ème bit est un bit de parité c'est à dire qu'il ne
sert  qu'à la vérification de la bonne transmission  du  code.  De
part   le fait que ce système est Américain,  le codage  ASCII  ne
prend   pas  en  compte les accents ni les caractères  du  type  c
cédille.  Cependant   notre  ordinateur n'ayant pas besoin  de  ce
huitième   bit  pour  vérifier  les  transmissions,  celui-ci  est
utilisé  pour augmenter le nombre de combinaison possible.  Retour
au  cours 2 de la série 1,  sur les chiffres magiques: avec 7 bits
nous   pouvons compter de 0 à 127,  un bit de plus nous permet  de
compter  de 0 à 255.  Consulter un tableau des codes ASCII (bible,
livre  du développeur, doc GFA etc...) et vous vous rendrez compte
que  tous  les  signes 'bizarres' sont codés sur 8bits et ont donc
des codes supérieurs à 127.

Cependant  les codes ASCII ne suffisent pas.  Imaginez que je tape
sur   la touche A de mon clavier.  En code ASCII je vais  recevoir
65. Mais  si  un Anglais tape sur la même touche, pour lui ce sera
un  Q et il recevra le code ASCII 81. Le code ASCII ne dépend donc
pas  de la touche, mécaniquement parlant, mais plutôt de la lettre
de  cette touche. Il existe un autre codage qui lui correspond aux
touches  d'un point de vue mécanique. C'est ce que l'on appelle le
scan-code.

Pour   le  scan-code,  une touche possède  un  numéro,  totalement
indépendant  de la lettre qui sera affectée à cette touche.  Ainsi
la   touche A de votre clavier renvoi le scan-code $10,  et il  en
est   de  même pour tous les ST du monde,  que cette  touche  soit
marquée   "A",  ou  "Q".  C'est l'emplacement sur le  clavier  qui
compte. 

Nous   testons  donc le scan-code de la touche sur  laquelle  nous
avons   appuyé.  Si  c'est  Escape,  nous  sortons  du  programme.
Autrement,   nous  allons  tester  s'il  s'agit  d'une  touche  de
fonction.  La  touche F1 a pour scan-code  $3B,  F2->$3C,  F3->$3D
etc...  jusqu'à  F10->$44. Les scan-codes se suivant, nous testons
donc   notre  résultat par rapport au scan-code de  F1,  si  c'est
inférieur  ce  n'est  donc  pas  valable, de  même  par rapport au
scan-code   de  F10:  si c'est supérieur ce n'est pas valable non 
plus. 

Ces tests étant faits, nous sommes donc en présence d'un scan-code
situé  entre $3B et $44,  ces 2 nombres compris. $3B, cela fait 59
en  décimal.  Pour avoir 65 (code ASCII de A) il suffit  d'ajouter
6.  C'est   que nous faisons ensuite.  Nous obtenons donc le  code
ASCII  de A,  B,  C etc... suivant la touche de fonction qui a été
enfoncée. Il ne reste plus qu'à afficher cette lettre!

Imaginons   maintenant  que  nous voulions  afficher  "touche  F1"
lorsque   nous appuyons sur cette  touche,  "touche  F2",  etc....
Plusieurs solutions s'offrent à nous. En voici une première qui me
vient  à l'esprit et que je vais juste vous dévoiler car elle  n'a
pas  de rapport avec les tableaux.  Pour ne pas  compliquer,  nous
n'afficherons   pas  "touche F10",  en fait nous ne  prendrons  en
compte  que les touches F1-F9.  Souvenez vous d'un des listing  de
la  série 1.  Celui qui affichait une phrase en faisant apparaître
les   lettres   comme   sur   les  affichages  des  gares  ou  des
aéroports.Reprenez   donc   un   peu   ce  listing   (c'était  le 
numéro   3)  et  souvenez-vous   de   ce  qui se passait pour  la 
phrase   située   à  l'adresse  TXT.  Nous affichions cette phrase
mais  auparavant  elle  était  modifiée  à  l'adresse colonne et à
l'adresse lettre.

Dans  la cas présent il suffit de faire à peu près la même  chose.
Préparons une phrase ainsi:

TXT     DC.B    "TOUCHE F"
NUMERO  DC.B    "  ",13,10,0

A  chaque appui sur une touche de fonction,  nous retirons 10  (en
décimal)  au  scan-code,  et nous mettons le résultat à  l'adresse
NUMERO.  Ainsi   le  scan-code  de la touche F1 ($3B  donc  59  en
décimal)  deviendra   49 qui est le code ASCII de la  lettre  '1'.
Nous verrons donc s'afficher 'TOUCHE F1'.

Réalisez  ce  programme avant de passer à la suite, cela vous fera
un excellent exercice!!!!

Passons maintenant au tableau, en modifiant un peu l'affichage.

Un appui sur:                   affichera:
F1                      A
F2                      Z
F2                      E
F4                      R
F5                      T
F6                      Y
F7                      U
F8                      I
F9                      O
F10                     P

Première   constatation:  si les scan-code des touches se  suivent
toujours, on  peut  dire  que  le  lien  logique entre les lettres
affichées est un peu léger...

Prenez   le  listing numéro 3,  et commençons à l'étudier. Tout le
début, jusqu'au   commentaire   'la   touche   est   valable', est
strictement identique au listing précédent. Ensuite nous attaquons
la  partie utilisant le tableau.  L'adresse de celui-ci est passée
en  A0 (Load Effective Adress),  ensuite  on retire  $3B à D0 afin
que  celui-ci  ait  une  valeur  de  0 à  9.  Le tableau  que nous
utilisons  est  composé de  Words.  Or, un  word c'est  2  bytes, 
et  la mémoire est composé  de  bytes.  Pour  se déplacer  dans un
tableau en word alors que notre unité c'est  le byte, il faut donc
se déplacer de 2 en 2. Il ne faut donc pas que notre  'compteur', 
qui   est  ici D0,  prenne une valeur  du  type 0,1,2,3,4  etc... 
mais plutôt une valeur telle que  0,2,4,6,8... 


Puisqu'à  la suite de nos opérations nous avons D0 avec une valeur
du  type 0,1,2,3,4... il faut maintenant le multiplier par 2. Cela
se   fait par l'opération MULU qui se lit  Multiply  Unsigned.  En
effet  c'est une multiplication non-signée,  c'est-à-dire  qu'elle
ne   prend  pas  en  compte le signe  de  ce  qui  est  multiplié,
contrairement à l'opération MULS (Multiply signed).

Maintenant, observons bien cette instruction:
MOVE.W    0(A0,D0.W),D0
Il  s'agit d'un MOVE donc d'un déplacement. Il se fait sur un word
puisque   nous  avons .W Ce MOVE va prendre le D0 ième word de  A0
pour   le mettre dans D1.  Ainsi,  si nous appuyons sur  F3,  nous
obtenons   $3D.  Nous  retirons  $3B  et  nous  obtenons  2,  nous
multiplions  par 2, et donc D0 vaut maintenant 4. Nous allons donc
pointer   sur le 4ème byte de A0 et prélever un word à  partir  de
cet  endroit.  Le déplacement est en effet toujours compté avec un
nombre  de bytes, que le tableau soit en byte, en word ou en long.
C'est   un  peu comme si vous vous déplaciez dans une rue avec des
petites  maisons, des  moyennes ou des grandes, le déplacement  se
comptera toujours en mètres.

Mais   que signifie le 0 devant la parenthèse ?  Et bien c'est  la
valeur  d'un déplacement fixe à ajouter.  Prenons un exemple: Nous
avons   un  tableau dans lequel on 'tape' suivant un  chiffre  qui
nous   est fourni par un appui sur une touche.  Seulement on  doit
prélever   des choses différentes si la touche est  appuyée  alors
que   SHIFT est enfoncée.  Il est alors possible de  se  dire:  si
shift   n'est pas enfoncé alors se sont les premiers  éléments  du
tableau  qui seront pris en compte,  mais avec shift ce seront les
éléments de la fin. On pourra alors faire:

MOVE.W    0(A0,D0.W),D1  ou bien si shift est appuyé, 
MOVE.W    18(A0,D0.W),D1.  Ceci revient à prendre le D0 ième word 
de A0, en commençant à compter à 18 bytes du début de A0.

Il   faut cependant se méfier de plusieurs choses  concernant  les
tableaux. Tout  d'abord bien faire attention au type de données du
tableau   afin  de  bien modifier le  'compteur'  en  conséquence.
Ensuite  bien faire attention au fait que le premier élément c'est
l'élément 0 et  non  pas le 1.   Nous avions déjà vu dans les tous
premiers   cours  de  la série 1 les  problèmes  pouvant  survenir
lorsque  l'on compte,  en oubliant parfois le 0.  Ce problème  est
d'autant plus gênant avec les tableaux que, si au lieu de  retirer
$3B  dans mon exemple pour avoir un chiffre de 0 à 9,  je  n'avais
retiré   que  $3A et ainsi obtenu 1 à  10,  mon  programme  aurait
parfaitement  fonctionné.  Il aurait simplement affiché  n'importe
quoi  à la suite d'un appui sur F10. Or si vous avez un tableau de
200  éléments   que vous appelez avec les  touches,  les  touches+
shifts,  +control  etc...  la vérification touche par touche  sera
peut  être  laissée  de  coté... Dans  notre  exemple, nous  avons
utilisé  des words dans notre tableau.  Il aurait tout à fait  été
possible d'utiliser des bytes.

Modifiez  un peu le programme:  supprimer la ligne avec  MULU,  et
modifiez  les datas.  Au lieu de mettre DC.W à l'adresse  TABLEAU,
mettez  DC.B.  Pour terminer, puisque notre tableau est maintenant
en  bytes  et  non  plus  en  words,il  faut  modifier l'adressage
permettant  de  piocher dedans. Au lieu de MOVE.W 0(A0,D0.W),D1 il
faut mettre maintenant MOVE.B 0(A0,D0.W),D1

Attention cependant car nous avions parlé de l'impossibilité de se
servir  des  adresses impaires.  Pourtant, dans  ce  dernier  cas,
comme  notre tableau est en bytes, si D0 vaut 3, nous nous retrou-
vons  avec  une adresse impaire, et pourtant ça  marche! En effet 
cela  marche  parce  que nous prélevons un byte. En fait, le 68000
peut  très  bien  prélever  un  byte  à  une  adresse  impaire, en
revanche,  ce   qu'il ne peut pas faire c'est prélever une  donnée
plus grande (word ou long) qui commence sur une adresse impaire et
qui  donc  chevauche  les emplacements 'normaux'. Modifions encore
une  fois  le programme.  Remettez le tableau en word, et remettez
l'adressage  en  word  (MOVE.W  0(A0,D0.W),D1).  L'erreur  va donc
venir  du  fait  que  nous avons oublié le MULU, et donc que notre
compteur  va être parfois impaire alors que notre tableau et notre
mode d'adressage demande un compteur paire.

Assemblez  et lancez.  Appuyez sur F1: tout ce passe bien! Normal,
D0 après  retrait  de $3B,  vaut 0 qui est donc pair.  Appuyez sur
F3: même chose car D0 vaut 2. Par contre, un appui sur F2 se solde
par  3 bombes et le retour à DEVPACK.  Débuggons  donc  notre pro-
gramme:  alternate+D, et descendons jusqu'à la ligne:
MOVE.W 0(A0,D0.W),D1

Plaçons   cette  ligne en haut de la fenêtre 1 et tapons control+B
Un  breakpoint  s'y  met. Lançons avec control+R, et tapons sur la
touche  F2.  Breakpoint,  nous revoici sous MONST. En regardant la
valeur  de A0 nous connaissons l'adresse de notre tableau, adresse
paire  en l'occurrence.  Par contre,  si vous avez appuyez sur F2,
vous  devez  avoir  1 comme valeur de D0, donc une valeur impaire.
Avancez  d'un  pas  sur  MOVE.W  0(A0,D0.W),D1 à l'aide Control+Z.
Erreur  d'adresse!  Il  ne  vous  reste  plus  qu'à  quitter  par 
Control+C.

Bon,  nous   avons vu comment prélever un word ou un byte dans  un
tableau.  Avec  un tout petit peu d'intelligence vous  devez  être
capable   de prélever un long mot (au lieu de faire un MULU par  2
on  en fait un par 4).  Prenons un peu de recul et rappelons  nous
des   cours  précédents:  nous y avons étudié le  principe  de  ce
'tube',  de   cette   mémoire   que  nous  commençons  à  utiliser
abondamment.  Si vous avez un peu de mémoire justement, vous devez
vous  rappeler  d'une  remarque  faite au tout début, disant qu'il
fallait  bien se méfier de confondre contenu du tube et adresse de
ce contenu. Il est en effet tout à fait possible d'avoir

IMAGE         incbin         A:\MAISON.PI1"
PTN_IMAGE     DC.L           IMAGE

A  l'adresse  IMAGE, nous trouvons dans le tube l'image elle-même,
mais  à  l'adresse  PTN_IMAGE, nous  trouvons  un long mot, qui se
trouve  être l'adresse de l'image. Avec un peu d'imagination, nous
pouvons  donc  imaginer  un tableau composé de long mot, ces longs
mots  étant des adresses d'images, de textes, mais aussi (pourquoi
pas!) de routines!!!!!!

Voici  le  squelette  d'un programme réalisant une telle chose: Au
départ, même  chose  que  précédemment, attente d'un appui touche,
vérification  de  la validité de la touche, on trafique pour avoir
un code du type 0,1,2,3,4... puis on le mulu par 4 puisque notre 
tableau va être composé de long mot.

         LEA       TABLEAU,A0
         MOVE.L    0(A0,D0.W),A0 
         JSR       (A0)
         BRA       DEBUT                et on recommence

Nous  faisons un JSR (jump subroutine) au lieu d'un BSR. Pourquoi?
essayez, et  regardez  l'annexe sur les instructions pour voir les
différences entre les 2 instructions!!!

Mais de quoi est composé notre tableau? eh bien, par exemple

TABLEAU DC.L TOUT_VERT
 DC.L    TOUT_BLEU
 DC.L    QUITTER
 DC.L    DRING
 DC.L    COUCOU

etc....

Toutes ces entrées étant les adresses des routines. Par exemple

COUCOU move.l #message,-(sp)
 move.w #9,-(sp)
 trap #1
 addq.l #6,sp
 rts

La routine TOUT_VERT met toute la palette en vert etc....

Il  est  de  même  possible  de  mettre en tableau des adresses de
phrases et de passer l'adresse "piochée" à une routine qui affiche
avec gemdos(9) par exemple.

Une dernière chose, qui est plus proche du système de liste que de
celui de tableau, mais qui est aussi bien utile. Nous avons étudié
ici   des  possibilités  émanant  toujours  d'une  même  évidence:
les  données  qui  nous  servent  à  pointer  dans  le tableau, se
suivent ! Malheureusement  dans  de  nombreux cas, celles-ci ne se
suivent pas...

Voici  donc  une  autre  méthode: Imaginons le cas d'un éditeur de
texte, avec  plusieurs actions possibles (effacer le texte, sauver
le  texte, l'imprimer, charger, écraser, scroller etc...) appelées
par  des  combinaisons  de touches. Pour être à la norme Wordstart
(c'est  la  norme  clavier utilisée par Devack: ALT+W=imprimer par
exemple), j'ai  d'abord  relevé  avec un tout petit programme, les
codes  renvoyés  par  les  combinaisons  de  touches  Ensuite j'ai
réalisé  une liste de ces codes, liste en word car les codes ASCII
ne  suffisent  plus dans le cas de combinaisons de touches (il est
possible  bien  sûr de construire la combinaison touche enfoncée/-
touche de control).

TAB_CODE dc.w $1519,$1615,$1312,$2e03,$FFFF

Ensuite  j'ai réalisé une liste avec les adresses de mes routines.
Comme au départ je n'en avais aucune de faite, j'en ai réalisé une
'bidon', nommée JRTS et qui ne fait.... qu'RTS!

TAB_ROUTINE dc.l JRTS,JRTS,JRTS,JRTS

Ensuite  j'ai  fait une boucle pour lire TAB_CODE, en comparant, à
chaque  fois, la  valeur  trouvée dans le tableau avec celle de la
touche. En même temps je parcours TAB_ROUTINE afin qu'au moment où
je  lis le 3ème élément de TAB_CODE, je sois en face du 3 ème élé-
ment de TAB_ROUTINE.

Voici  le module. D7 contient le word correspondant à la touche ou
à la combinaison de touche.

 LEA TAB_CODE,A0
 LEA TAB_ROUTINE,A1
.ICI MOVE.W (A0)+,D0
 CMP.W #$FFFF,D0
 BEQ DEBUT
 MOVE.L (A1)+,A2
 CMP.W D0,D7
 BNE .ICI
 JSR (A2)
 BRA DEBUT

L'adresse  de  la  liste  des  codes  est  mise en A0 et celle des
adresses   de  routines  en  A1.  On  prélève  un  word  de  code.
C'est $FFFF? si c'est le cas, c'est donc que nous sommes en fin de
liste  donc  on  se  sauve  puisque  la  touche  choisit n'est pas
valable. Sinon  on  prélève l'adresse dans le tableau des adresses
de  routines. Le  code  du  tableau est-il le même que celui de la
touche  ? Non, on boucle (notez ici le point devant le label. Cela
indique  que  le  label  est  local. L'assembleur le rapportera au
label le plus proche portant ce nom. Il est ainsi possible d'avoir
plusieurs  label  .ICI  dans  un  programme,  ou  tout  autre  nom
pourvu  qu'il  soit  précédé  d'un  point. Dans  le cas de petites
boucles  par  exemple, cela  évite  de chercher des noms de labels
tordus!!!).

Puisque  le code est identique à celui de la touche, on saute vers
la routine, et au retour, on recommence.

Les  avantages  de  cette  méthode sont multiples. Tout d'abord la
petite taille de la routine de test: s'il avait fallu réaliser des
tests du genre:

 cmp.w #$1519,d7
 bne.s .ici1
 bsr truc
 bra début
.ici1 cmp.w #$1615,d7
 bne.s .ici2
 bsr truc2
 bra début
.ici2
etc.... la  taille aurait été supérieure, surtout que dans l'exem-
ple il n'y a que 4 codes... Imaginez avec une trentaine!!! L'autre
avantage  concerne  la  mise  au point du programme. En effet rien
n'empêche  de  prévoir plein de routines mais de simplement mettre
l'adresse d'un RTS, et progressivement de construire ces routines.
Rien  n'empêchera pour autant le programme de marcher. De même, le
système  du flag de fin ($FFFF) permet de rajouter très facilement
des codes et donc des routines.

Voila  pour  les listes et tableaux!! les applications sont innom-
brables  et  permettent  souvent  en  peu de lignes des tests, des
recherches, des  calculs,  des  conversions  ou  des  branchements
réalisables autrement mais au prix de difficultés immenses!

Voici des idées d'applications:

codage  de  texte  (utilisation du code ASCII pour pointer dans un
tableau donnant la valeur à substituer)

animation  (tableau parcouru séquentiellement donnant les adresses
des images à afficher)

gestion de touches

gestion de souris (tableau avec les coordonnées des zones écran)
menu déroulant

etc...