COURS205.TXT/fr
****************************************************************** * * * 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...
Back to ASM_Tutorial