========================================================================== M A N U E L A S M P A R T I V ========================================================================== 5.La programmation assembleur sous AMIGA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Après la programmation théorique du MC68000, qui est valable, en principe, sur n'importe quelle autre machine à base de 68000, nous allons étudier une programmation plus spécifique à l'AMIGA. 5.1 Notions préliminaires ~~~~~~~~~~~~~~~~~~~~~~~~~ Afin d'arriver à une programmation assembleur "élégante" sous AMIGA, la notion de liste doit être parfaitement maîtrisée. Une liste est en fait une simple structure contenant les informations suivantes : . un pointeur sur une autre liste, . les informations de la structure. Exemple : ~~~~~~~~~ Adresse0 : Adresse1 : ... Adr1, Adr2, ... Nom0, Nom1, ... Prénom0, Prénom1, ... Adresse0. Adresse1. ... Dans la programmation de l'AMIGA, les listes occupent une place très importantes, notemment pour la gestion des librairies et des fonctions systèmes, il est donc capital de savoir comment elles sont représentées en mémoire et comment les gérer. A cette notion de liste s'ajoute celle de noeud. Celui ci n'est rien d'autre qu'un simple structure définie de la facon suivante : STRUCTURE LN LN_SUCC LN_PRED LN_TYPE LN_PRI LN_NAME Cette structure comprend deux parties. D'une part, le chaînage de liste (LN_SUCC et LN_PRED) et d'autre part, les données (LN_TYPE, LN_PRI ET LN_NAME). LN_SUCC est un pointeur sur le prochain noeud de la liste. LN_PRED est un pointeur sur le noeud précédent de la liste. LN_TYPE représente le type du noeud. LN_PRI contient la priorité de la structure. LN_NAME est le nom de la structure. Définition des types. ~~~~~~~~~~~~~~~~~~~~~ NT_UNKNOW 00 NT_TASK 01 NT_INTERRUPT 02 NT_DEVICE 03 NT_MSGPORT 04 NT_MESSAGE 05 NT_FREEMSG 06 NT_REPLYMSG 07 NT_RESOURCE 08 NT_LIBRARY 09 NT_MEMORY 10 NT_SOFINT 11 NT_FONT 12 NT_PROCESS 13 NT_SEMAPHORE 14 NT_SIGNALSEM 15 NT_BOOTNODE 16 Ces différents types seront explicités au fur et à mesure de leurs études respectives. Exemple d'initialisation de noeuds. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Debut: lea Noeud1,a0 ;Pointeur sur le Noeud1 lea Noeud2,a1 ;Pointeur sur le Noeud2 lea NomNoeud1,a2 ;Pointeur sur le nom du Noeud1 ;terminé par 0 lea NomNoeud2,a3 ;Pointeur sur le nom du Noeud2 ;terminé par 0 ; ; Initialisation du noeud 1 ; move.l a1,(a0) ;Noeud suivant clr.l 4(a0) ;Pas de noeud précédent move.b #0,8(a0) ;NT_UNKNOW move.b #0,9(a0) ;Priorité move.l a2,10(a0) ;Nom ; ; Initialisation du noeud 2 ; clr.l (a1) ;Pas de noeud suivant move.l a0,4(a1) ;Noeud précédent move.b #0,8(a1) ;NT_UNKNOW move.b #0,9(a1) ;Priorité move.l a3,10(a1) ;Nom rts Noeud1: blk.b 14,0 Noeud2: blk.b 14,0 NomNoeud1: dc.b "Noeud1",0,0 NomNoeud2: dc.b "Noeud2",0 Assemblez et lancez le programme par 'g'. Afin de visualiser les structures de noeuds correctement orchestrés, tapez 'q noeud1' ou 'q noeud2'. 5.2 Les librairies ~~~~~~~~~~~~~~~~~~ Une librairies est une bibliothèque de fonctions constituées d'une série d'instructions auxquelles on accède par des sauts. La plupart de ces librairies se trouvent en ROM, toutefois certaines d'entre elles doivent être chargées à partir d'une disquette. Le répértoire système LIBS: contient l'ensemble de ces librairies telles que l'asl.library ou encore la info.library. Une librarie a l'aspect suivant : JMP Fonction1 JMP Fonction2 JMP Fonction3 Ce principe est intéressant car au lieu d'appeler directement les fonctions en ROM comme sur les bons vieux 8 bits, on accéde indirectement aux fonctions par l'intermédiaire d'offsets. Ainsi, lorsque le système évolue, comme c'est le cas pour le système 2.0, les programmes restent compatibles puisque ces offsets sont immuables. Un saut à une routine s'effectue de la manière suivante : move.l LibBase,a6 jsr Offset(a6) Notez bien que par convention, le registre A6 est toujours utilisé pour y stocker la base de la librairie. Les offsets sont toujours négatifs; les offsets positifs représentent d'autres choses que nous verrons par la suite. 5.3 L'exec.library ~~~~~~~~~~~~~~~~~~ Parmi toutes les librairies, il en existe une très importante : il s'agit de l'exec.library. Celle-ci est en fait la librairie principale du système d'exploitation. L'adresse de base de cette librairie est directement accessible en mémoire à l'emplacement $4. Cet état est et restera, c'est la seule adresse directe qui est imuable. A partir de cette dernière, il est possible de faire beaucoup de choses, pour ne pas dire tout. Le petit exemple suivant vous montre un petit coté de la structure de liste de l'exec.library. Debut: move.l $4,a6 ;ExecBase move.l 10(a6),d1 ;nom de la library move.l (a6),a6 ;prochaine library move.l 10(a6),d2 ;nom de la library rts Par 'q d1' et 'q d2', il vous est possible de voir le nom des libraries rencontrées. Comme vous pouvez le constater, toutes les librairies sont chaînées ensemble par une structure de noeuds, il est ainsi possible d'accèder à une librairie par une autre, d'en supprimer une et d'en ajouter une personnelle. Avant toute utilisation de fonctions, il est nécessaire d'ouvrir la librairie concernée. On procéde de la manière suivante : OldOpenLibrary: move.l $4,a6 ;ExecBase lea LibName,a1 ;Pointeur sur le nom de la library terminée ;par 0 jsr -408(a6) ;OldOpenLibrary move.l d0,LibBase ;On sauve la base de la library rts ;Stopper LibBase: dc.l 0 ;Buffer pour la base de la library LibName: dc.b "nom.library",0 ;Nom de la library Dans LibBase, le point d'entrée de la library a été conservé, il reservira par la suite. Après toute utilisation, il est bon (mais pas forcément nécessaire ) de fermer la library : CloseLibrary: move.l $4,a6 ;ExecBase move.l LibBase,a1 ;DosBase jsr -414(a6) ;CloseLibrary rts ;Stopper 6.La dos.library ~~~~~~~~~~~~~~~~ La dos.library est, avec l'exec.library, une des plus importantes librairies du système d'exploitation. Celle-ci s'occupe de gérer tout ce qui concerne les entrées-sortie, comme les opérations sur disquettes ou les saisies au clavier. 6.1 Conseils ~~~~~~~~~~~~ L'assembleur est difficile à lire, afin de prendre de bonnes habitudes, je vous conseille de travailler de la manière suivante : - Utilisez au maximum l'instruction ';' (remarque), - Inclure le fichier 'dos.dat' puis ne garder que les traitements indispensables, - Programmez de manière structurée : ; --------------------------------- ; Squelette du programme (exemple) ; --------------------------------- Debut : . . move.l #12,d0 bsr Tmt1 . . add.l #1,d0 bsr Tmt2 rts ; ----------- ; Traitements ; ----------- Tmt1 : . . rts Tmt2 : . . rts ; -------------------- ; Données du programme ; -------------------- Donnee1: dc.l 0 Donnee2: dc.l 0 6.2 Ouvrir et fermer des fichiers. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Avant d'utiliser n'importe quel fichier, il est nécessaire de l'ouvrir. Pour ce faire, la fonction Open est utilisée; on y accède grâce à l'offset -30 de la DosBase. A cet appel, deux paramètres sont associés, l'un pour le nom, l'autre pour le mode. Forme : ~~~~~~~ Handle = Open(Nom,mode) D0 -30 D1 D2 Le nom est le nom du fichier à ouvrir, Le mode est soit ancien (1005), soit nouveau (1006). Dans le premier cas, un fichier existant est ouvert en lecture et en écriture, dans le deuxième cas, un nouveau fichier est crée. Le résultat est un numéro d'identification du fichier, en anglais handle. Une fois que votre fichier a été ouvert, il est nécessaire de le fermer. Pour cela, l'instruction Close est appelé à l'offset -36 de la DosBase. Le handle du fichier ouvert est passé en argument. Forme : ~~~~~~~ Close(Handle) -36 D1 Exercice : ~~~~~~~~~~ Créer le fichier 'Fichier' en mode nouveau puis le fermer. ; ; Exercice sur l'ouverture et la fermeture des fichiers ; ;****************************** ; ; Structure ; ;****************************** Debut: bsr Open_DosLib ;ouverture de la dos.library bsr Open_Fichier ;ouverture du fichier bsr Close_Fichier ;fermeture du fichier Fin: rts ;fin du pgm ;******************************** ; ; Traitements ; ;******************************** Open_DosLib: lea DosLib,a1 ;pointeur sur "dos.library" bsr OldOpenLibrary ;ouvrir la dos.library move.l d0,DosBase ;conserver la DosBase beq Fin ;pas de dos.library alors stop rts ;fin du sous-pgm Open_Fichier: move.l #fichier,d1 ;nom du fichier bsr Open_NewFile ;ouvrir en mode nouveau move.l d0,FileHandle ;conserver le handle beq fin ;pas de fichier alors fin rts ;fin du sous-pgm Close_Fichier: move.l FileHandle,d1 ;handle du fichier bsr Close ;fermeture du fichier rts ;fin du sous-pgm ;------------------------------ ; Traitement de l'exec.library ;------------------------------ OldOpenLibrary: move.l $4,a6 ;exec.base jsr -408(a6) ;ouvrir une librairie rts ;fin du sous-pgm ; ; Fichier Dos.dat à inclure ici (R + dos.dat) ; ;---------------------- ; Données ;---------------------- DosBase: dc.l 0 ;point d'entrée de la dos.library FileHandle: dc.l 0 ;handle du fichier DosLib: dc.b "dos.library",0 ;dos.library Fichier: dc.b "Fichier",0 ;nom du fichier Note: ~~~~~ Vous remarquerez que je n'ai pas fermé la dos.library, le système ne vous en voudra pas ! 6.3 Lire et écrire dans des fichiers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Une fois que l'ouverture d'un fichier est effective, il est normal de vouloir lire son contenu, voir de le modifier. La dos.library dispose pour cela d'une fonction d'écriture (Write) et de lecture (Read). Formes : ~~~~~~~~ Résultat = Read(Handle,buffer,longueur) D0 -42 D1 D2 D3 Dans D1, le handle du fichier est précisé, dans D2, l'endroit ou les informations seront stockées et enfin dans D3, la longueur de ces informations. En retour, nous obtenons le nombre d'octets lus ou -1 s'il s'est produit une erreur. Résultat = Write(Handle,buffer,longueur) D0 -48 D1 D2 D3 Dans D1, le handle du fichier est précisé, dans D2, l'endroit depuis lequel il faut écrire les informations et enfin dans D3, la longueur de ces informations. En retour, nous obtenons le nombre effectif d'octets ayant été écrits ou -1 si une erreur s'est produite. Exercice. ~~~~~~~~~ Ouvrir le fichier 'CON:0/10/600/30/Entrée' en mode nouveau et le fichier 'Fichier' en mode nouveau. Dans le premier fichier, y lire au maximum 80 caractères et écrire dans le second le nombre de caractères effectivement lus. Debut: bsr Open_DosLib ;ouvrir la dos.library bsr Open_Con ;ouvrir la fenêtre bsr Open_Fichier ;ouvrir le fichier bsr Read_Con ;lire dans la fenêtre bsr Write_Fichier ;ecrire dans le fichier bsr Close_Fichier ;fermer le fichier bsr Close_Con ;fermer la fenêtre Fin: rts ;fin ;******************************** ; ; Traitements ; ;******************************** Open_DosLib: lea DosLib,a1 ;pointeur sur la dos.library bsr OldOpenLibrary ;ouvrir la librairie move.l d0,DosBase ;conserver la DosBase beq Fin ;pas de dos.library rts ;fin du sous-pgm Open_Fichier: move.l #fichier,d1 ;nom du fichier bsr Open_NewFile ;ouvrir le fichier move.l d0,FileHandle ;conserver le handle du fichier beq fin ;pas de handle rts ;fin du sous-pgm Close_Fichier: move.l FileHandle,d1 ;handle du fichier bsr Close ;fermer le fichier rts ;fin du sous-pgm Open_Con: move.l #Con,d1 ;nom de la fenêtre bsr Open_NewFile ;ouvrir la fenêtre move.l d0,ConHandle ;conserver le handle de la fenêtre beq fin ;pas de fenêtre rts ;fin du sous-pgm Close_Con: move.l ConHandle,d1 ;handle de la fenêtre bsr Close ;fermer la fenêtre rts ;fin du sous-pgm Read_Con: move.l ConHandle,d1 ;handle de la fenêtre move.l #Lettres,d2 ;buffer pour le texte à lire move.l #80,d3 ;80 caractères maximum bsr Read ;lire les caractères move.l d0,d3 ;conserver le nombre de caractères lus rts ;fin du sous-pgm Write_Fichier: move.l FileHandle,d1 ;handle du fichier move.l #Lettres,d2 ;buffer des caractères bsr Write ;écrire dans le fichier rts ;fin du sous-pgm ;------------------------------ ; Traitement de l'exec.library ;------------------------------ OldOpenLibrary: move.l $4,a6 ;ExecBase jsr -408(a6) ;ouvrir une librairie rts ;fin du sous-pgm ; ; Fichier Dos.dat à inclure ici (R + dos.dat) ; ;---------------------- ; Données ;---------------------- DosBase: dc.l 0 ;DosBase FileHandle: dc.l 0 ;Handle du fichier ConHandle: dc.l 0 ;Handle de la fenêtre DosLib: dc.b "dos.library",0 ;dos.library Fichier: dc.b "Fichier",0 ;fichier Con: dc.b "CON:0/10/600/30/Entrée",0,0 ;fenêtre CON Lettres: blk.b 80,0 ;buffer pour phrase Voilà, comme vous avez pu le constater, le fichier 'CON' est un petit peu particulier, vous pouvez visualiser le contenu de 'fichier' au moyen de l'instruction CLI 'type'. A SUIVRE ... Thibaut M.