Obligement - L'Amiga au maximum

Lundi 20 novembre 2017 - 02:44  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : mettre de l'assembleur dans de l'AmigaBasic
(Article écrit par Charles Vassallo et extrait d'A-News (Amiga News) - mars 1989)


Avant-propos

Cet article a été écrit pour les adorateurs de K-Seka. Les sectateurs d'autres produits devront se livrer ici et là à quelques transcriptions plus ou moins faciles. Exemple : comment récupérer - simplement - le fichier de code 68000 avec le MetaComCo ?

Histoire vécue

Il était une fois un programmeur rempli de plus d'idées que de courage, qui n'avait jamais osé s'attaquer au C (avec une majuscule) et qui désespérait que le seul langage qu'il pratiquât, ce langage si facile d'accès, pas cher et à la portée de tous, celui qu'il avait découvert en déballant sa machine, en un mot l'AmigaBasic, ce langage restât nettement à la traîne derrière ses ambitions. Calculs lents, interfaçage IFF pénible... la galère.

Un soir de défonce déprimée. il rêvait dans quelque page d'A-News qu'on pouvait mettre des routines en assembleur dans un programme AmigaBasic. Le lendemain, c'était toujours écrit et dans un sursaut d'héroïsme, il dévorait "Le livre Du Langage Machine" et suivait un recyclage accéléré auprès du petit Zeus.

Après avoir perdu plusieurs kilos et échappé de peu à un contrôle antidopage, il reprenait conscience et rouvrait son manuel AmigaBasic pour revérifier qu'on pouvait bien mettre de l'assembleur dans du BASIC. Eh oui, c'était bien vrai. Et puis, comment pouvait-on interconnecter assembleur et BASIC ? Entre les lignes du manuel de référence, il saisissait qu'il fallait fourrer le code machine dans le célèbre tableau array%() (page 8-26 dudit manuel). Et pour échanger les variables ? Élémentaire, cher Watson, vous suivez les conventions du langage C (sic - même page 8-26) ! Et hop ! Retour à la case départ ! Tous au C... même les basicards !

Cette histoire presque vraie aurait pu arriver à l'un des premiers utilisateurs de l'Amiga en France, ceux qui ont dû se débrouiller avec des docs non traduites et qui n'ont jamais reçu de traduction. La page 8-26 précédente se réfère au manuel en anglais. Le manuel français vendu pour les 500/2000 contient quelques précisions supplémentaires, en particulier en ce qui concerne l'interfaçage avec l'assembleur. Ses possesseurs vont donc rencontrer quelques redites dans ce qui suit ; qu'ils pensent aux malheureux qui les ont précédés. Et puis, le rabâchage est une grande vertu pédagogique.

Oyez, braves gens, l'évangile selon Microsoft

Vous ne pouvez passer que des arguments de type entier (court ou long). Si votre routine est dans le tableau array%, ses deux premiers octets bien calés dans array%(0), vous la lancerez par un "CALL" à l'adresse de ce premier élément. Et si vous avez quelques arguments à transmettre, ouvrez une parenthèse et déposez-y vos arguments, comme suggéré ci-dessous :

AmigaBasic

La première ligne (initialisation des arguments) est une quasi-nécessité. En effet, l'adresse d'un tableau dans la RAM de notre machine est encore moins stable que le cours du dollar en cruzeiros et à moins d'entretenir des relations privilégiées avec M. Guru, il ne faut définir aucune nouvelle variable entre le transfert de l'adresse de array%(0) dans routine& et son utilisation par l'instruction "CALL". A noter que vous pouvez aussi transmettre des nombres directement :

AmigaBasic

Fin de l'évangile selon Microsoft. Allez en paix

(petite voix étranglée)
- Mais comment ma routine va-t-elle récupérer ces arguments... ?

(le C... iel)
- Dans la pile, voyons. Comme il se doit, les derniers seront empilés les premiers, et comme d'habitude, les premiers ressortiront les premiers. Comment cela, ce n'est pas clair ? Tout d'abord, je ne l'avais pas dit, mais vous l'aviez subodoré, les entiers courts sont allongés - avec extension de signe s'il le faut - de manière à n'empiler que des longs. J'empile donc dans l'ordre le dernier argument, l'avant-dernier, le premier et bien sûr, l'adresse de retour, celle de l'instruction qui suit le "CALL" (entier long).

Le premier argument commence donc à 4 octets derrière le début de la pile, le 2e à 8, le 3e à 12 et ainsi dc suite. Il faudra donc écrire au début de la routine :

AmigaBasic

On n'utilisera que des "move.l". Si le premier argument est un entier court, le mot de poids fort recueilli dans d0 sera nul (ou -1 si l'argument est négatif). Et si on commence la routine par une sauvegarde générale des registres d0-d7/a0-a6, on empile 60 octets supplémentaires et tout recule de 60 :

AmigaBasic

Je récapitule

Vous voulez vérifier que 2+3=5. Vous pouvez envoyer 2 et 3 soit comme des nombres, soit comme le contenu de deux variables x et y. Par contre, si vous voulez récupérer le 5 de manière élégante, il faudra le mettre dans une variable AmigaBasic z%, au bon endroit, c'est-à-dire que vous placerez ce 5 à l'adresse (VARPTR) de cette variable. Première version, côté AmigaBasic :

AmigaBasic

Côté machine, 2 et 3 seront recueillis comme des entiers longs. Subséquemment :

AmigaBasic

Si vous voulez vraiment faire tourner cet exemple et ceux qui vont suivre, le code machine à mettre dans le tableau array% est indiqué à la fin de cet article.

Deuxième version, côté AmigaBasic :

AmigaBasic

Et côté machine :

AmigaBasic

On aura remarqué que cette deuxième version permet aussi de vérifier que 1+1=2 moyennant un effort modique côté AmigaBasic (par contre, essayez de voir si 40 000+30 000 font bien 70 000 et ne soyez pas surpris du résultat).

Enchaînons...

- Et ensuite, qu'est-ce que ça sera pour Monsieur ?
- Bien... et les chaînes de caractères ?
- Pour entrer dans la routine, facile ! L'AmigaBasic vous donne l'adresse du premier caractère par SADD(a$), et même la longueur par LEN(a$). Voici par exemple une petite routine qui trouve combien de fois une lettre figure dans un mot (très important pour jouer au pendu !) :

AmigaBasic

Côté machine :

AmigaBasic

- Bravo. Tout cela est fort intéressant. Mais est-ce qu'une routine peut aussi transmettre une chaîne de caractères à l'AmigaBasic ?
- Bien... si vous savez d'avance combien de caractères il vous faut, il suffit d'initialiser une variable chaîne avec le nombre de caractères requis, et d'envoyer son adresse (SADD) à la routine. Par exemple, si votre routine fabrique des mots de 10 caractères, vous pourrez écrire...

AmigaBasic

...à charge pour votre routine de mettre les 10 caractères à partir de l'adresse SADD(mot$).

- Et si je ne sais pas d'avance combien de caractères j'aurais besoin ?
- Aïe ! Aïe! Aïe ! Très embêtant ! J'ai bien peur qu'il ne faille aller "peeker" (en français dans le texte) votre chaîne octet par octet. Vous ne pouvez rien faire d'aussi facile que notre exemple précédent. Il y a bien une instruction VARPTR pour les chaînes, mais son emploi est... délicat.

Il y a cinq octets intéressants à l'adresse VARPTR(mot$) : les deux premiers forment un entier court qui est précisément LEN(mot$) et les trois derniers forment un entier long codé sur 24 bits qui est précisément SADD(mot$). Tentation : on pourrait tromper l'AmigaBasic en changeant tous ces octets. Par exemple, le petit programme AmigaBasic ci-dessous remplit un tableau toto%() avec neuf octets et va faire croire à la chaîne mot$, initialement vide, qu'elle contient neuf caractères qui commencent à l'adresse VARPTR(toto%(0)) :

AmigaBasic

Surprise ! (c'est ce qui doit apparaître à l'exécution). Mais ne triomphez pas trop vite. Tapez maintenant en mode direct :

x=0 (vous créez ainsi une nouvelle variable)
PRINT mot$

...et vous aurez droit à une nouvelle surprise. Non, ce n'est pas le Guru. Ces petits jeux sont nettement déconseillés par Microsoft.

- Mais c'est dramatique ! Que faire ?
- Du calme... En général, votre routine va créer cette chaîne dans quelque zone réservée de la mémoire et va mettre un octet nul au bout. Comme c'est vous qui avez écrit la routine machine, vous devez bien connaître l'adresse relative de cette zone par rapport au début de la routine. Vous irez "peeker" vos caractères un par un dans cette zone, jusqu'à ce que vous trouviez un octet nul. Par exemple, votre code machine ressemblera à :

AmigaBasic

Après assemblage, vous notez l'adresse relative (beuffeur-début). En AmigaBasic, cela va donner :

AmigaBasic

Voilà qui ne violera pas l'AmigaBasic et qui vous laissera une chaîne mot$ en bon état de marche.

La crise du relogement

Un détail qui a son importance : votre code machine devra être "relogeable", c'est-à-dire qu'il devra fonctionner quelle que soit l'adresse où il est implanté. En pratique, cela veut dire qu'il faudra se servir massivement de l'adressage indirect et de l'adressage relatif au compteur. A proscrire :

move.l toto,d0 ; (addressage absolu)

...et à remplacer par :

move.l toto(pc),d0 ; (addressage relatif au compteur)

Dans un cas aussi simple, l'adressage indirect paraît moins intéressant :

lea toto(pc),a0
move.l (a0),d0

Mais ce sera la seule solution pour transférer quelque chose dans "toto", parce que...

move.l d0,toto(pc) ; ????

...est interdit. Pas d'adressage relatif au compteur pour les destinations ! On écrira :

lea toto(pc),a0
move.l d0,(a0)

...et on n'oubliera pas que cette manoeuvre massacre le contenu de a0. C'est évident ? Bien sûr, mais il y a des évidences sur lesquelles on perd tant de temps en mise au point...

Le relogeage des structures

L'Amiga adore les structures. Si vous ouvrez une fenêtre avec des menus de votre cru, vous devrez remplir une (ou des) structure(s) de menu. Lors de la mise au point du programme, vous écrirez par exemple :

AmigaBasic

Tous les noms à droite des symboles dc.w ou dc.l doivent correspondre à des étiquettes figurant à un endroit ou un autre dans la colonne de gauche. Lors de l'assemblage, K-Seka porte le code machine dans le débogueur. Toutes les étiquettes deviennent des adresses absolues et ce sont ces adresses qui vont figurer dans les octets réservés par les instructions dc.l et dc.w. L'entier long figurant à l'adresse de "menu1:" est précisément l'adresse de "menu2:", mais ce ne sera plus vrai si on déplace tout le code machine dans la mémoire. Cet entier n'aura pas changé, et ce ne sera pas la nouvelle adresse de "menu2:". Et Guru viendra.

Il faudra donc commencer par rétablir tous les liens. Le plus simple serait de les rétablir un par un, structure par structure. Avec l'exemple précédent, on écrirait :

AmigaBasic

Si les structures ne sont pas trop compliquées, ce sera une solution acceptable. Si vous avez quatre menus, chacun avec dix sous-menus et chacun avec huit sous-sous-menus, cela va allonger démesurément le progrannne. Une meilleure technique sera d'établir des tables de saut. La première table indiquera les adresses à modifier, par rapport à une adresse de base, ici celle de "menu1:". La deuxième table donnerait les nombres à inscrire (après addition de l'adresse de menu1).

AmigaBasic

Ces deux tables s'emploient de la manière suivante :

AmigaBasic

Le hic va être de construire ces deux tables. La deuxième paraît assez évidente. Il suffit de ne rien oublier, et l'assembleur calculera tout seul les bonnes valeurs. Pour la première, n'essayez pas de compter les octets ! Introduisez plutôt des étiquettes supplémentaires aux endroits à contrôler et laissez faire K-Seka :

AmigaBasic

Et la table de saut sera :

AmigaBasic

Ainsi, pas de calcul mental. De la méticulosité, et un oeil de lynx. Les deux tables doivent avoir le même nombre d'éléments (plus un zéro pour finir la première). N'oubliez rien. Ne faites pas de faute de frappe. Et si vous avez oublié un saut quelque part, le Guru vous le rappellera rapidement. Cela dit, on peut obtenir ces tables de manière automatique et sans migraine, moyennant un peu de jonglage avec des fichiers intermédiaires. Si vous êtes intéressé, écrivez à l'éditeur, qui transmettra (pas de K-Seka, s'abstenir).

Le chargement du code machine

Le plus rapide est de constituer un fichier de pur code 68000. Avec K-Seka, cela se fait très vite au moyen de l'instruction "wi". Au préalable, vous aurez mis une étiquette "début:" au début de votre programme et une étiquette "fin:" tout à la fin, après de dernier RTS et le dernier octet de mémoire réservée et vous aurez assemblé. L'instruction "wi" permet d'envoyer tout ce qu'il y a entre deux adresses dans un fichier. On vous demande le nom du fichier, puis les adresses initiales et finales (ici, "début" et "fin"). En fait, K-Seka envoie les valeurs de ces deux adresses dans son débogueur, mais si le programme est bien relogeable, cela n'a pas d'importance. C'est tout.

Reste (enfin) à charger ce fichier dans le tableau array%(), dans le programme AmigaBasic. On se prend à regretter les temps héroïques de l'Apple II et de l'instruction "BLOAD". Mais tout est possible, avec l'Amiga. On va donc fabriquer une routine de chargement...

AmigaBasic

...qui va charger le fichier "routine.bcode" dans le tableau array%(). La taille du fichier (lue en CLI par "list") est "taille&". Enfin, "succès%" sera un indicateur de succès. Si on revient avec succès%=0, le chargement aura échoué et il vaudra mieux sortir illico du programme AmigaBasic. Le sous-programme BLoad peut s'écrire :

AmigaBasic

Bien entendu, les fonctions "xOpen&" et "xRead&" doivent avoir été déclarées (dos.library) et on doit avoir un fichier dos.bmap sous la main.

Résumons

1. Vous écrivez une première routine sous K-Seka, pour mettre au point le code assembleur. Proscrivez l'adressage absolu mais ne vous préoccupez pas encore des structures parce que celles pourront varier avant que votre programme ne marche. Évidemment, les arguments qui seront transmis par l'AmigaBasic ne doivent pas être tirés de la pile à ce stade : ce doit être des variables comme les autres. Votre routine doit tourner sous débogueur.

2. A ce point, vous écrivez les tables de saut (si vous en avez besoin) ainsi que le sous-programme associé. Vérification : vous remplacez les lignes du type "dc.l menu2,..." par "dc.l 0,...". Si le sous-programme rétablit correctement tous les liens, ça doit marcher comme avant sous débogueur. Sinon, cherchez l'oubli (et redémarrez, évidemment).

3. Écrivez enfin quelques lignes au début pour récupérer les arguments à partir de la pile. Ne mélangez pas pointeurs et variables pointées. Dernier assemblage et création du fichier de code machine.

4. C'est tout.

Ah ! J'oubliais ! Dernière formalité : lancez le programme AmigaBasic. Vous pouvez allumer un cierge ou sacrifier à Hermès le Dieu de l'esbrouffe, mais les gens bien informés estiment que cela ne changera pas grand-chose. Si vous avez l'impression que votre Amiga prend une grande liberté avec ce que vous croyez lui avoir dit de faire (et il arrive que même le Guru parfois n'ait pas le temps d'intervenir), vous redémarrez et vous reprenez le début de cet article. Courage !

Post-scriptum

Si vous voulez faire effectivement tourner les petits exemples de cet article, remplissez les tableaux array%() (après une déclaration de dimension correcte) avec les données ci-dessous :

2+3=z% :
DATA &H202F, 4, &HD0AF, 8, &H206F, 12, &H3080, &H4E75

x%+y%=z% :
DATA &H206F, 4, &H226F, 8, &H3010, &HD051, &H206F, 12, &H3080, &H4E75

Routine pour le pendu :
DATA &H206F, 4, &H202F, 8, &H226F, 12, &H7200, &H5380, &H1411, &HB418, &H6600, 4, &H5241, &H51C8, &HFFF6, &H206F, 16, &H3081, &H4E75


[Retour en haut] / [Retour aux articles]