Suivez-nous sur X

|
|
|
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,
ALL
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - exemple de programme avec Exec (CheckMem)
(Article écrit par Gilles Soulet et extrait d'Amiga News - mars 1993)
|
|
Après une courte interruption pour cause d'actualité très chargée, nous reprenons ce mois-ci la
suite de notre grande saga des bibliothèques. Aujourd'hui, nous allons aborder concrètement
l'utilisation d'Exec au travers d'un petit programme en assembleur. Ceci va nous permettre
d'introduire progressivement une autre bibliothèque fondamentale de l'Amiga : Intuition.
Un petit exemple...
Nous avons vu la dernière fois certains des rôles fondamentaux d'Exec :
gestion des listes, de la mémoire et du multitâche. Je vous propose aujourd'hui de mettre en application tous ces
concepts au travers d'un exemple simple. Nous allons programmer un petit utilitaire qui espionnera les
zones mémoire des sept vecteurs d'interruption 68000 et des trois vecteurs Kickstart
de la structure ExecBase (KickMemPtr, KickTagPtr et KickCheckSum) de façon à ce qu'à la moindre
modification d'un des vecteurs, l'utilisateur soit averti. L'intérêt de ce type de programme est
évident : modifier les vecteurs 68000 est extrêmement dangereux pour le système, pour ne pas
dire carrément illégal ! Si un programme quelconque s'amuse à taper sauvagement dans cette
zone, vous serez aussitôt averti. De plus, la plupart des virus détournent un ou plusieurs vecteurs
Kickstart du système, de façon à résister à une réinitialisation.
L'utilisateur prévenu pourra alors choisir de laisser le vecteur modifié ou de le remettre
à son ancienne valeur. La programmation de ce petit utilitaire va nous permettre d'aborder
plusieurs concepts très importants. En tout premier lieu, notre programme se doit d'être rapide.
Il consulte les vecteurs en permanence, donc il doit utiliser un minimum de temps machine.
La meilleure façon d'implémenter ceci est d'utiliser une interruption. Ce concept n'a pas encore
été abordé, mais ne vous inquiétez pas, il existe dans Exec des fonctions très simples et
très faciles à utiliser : AddIntServer() et RemIntServer().
Notre programme comporte deux parties : une première initialisation qui implémente une
petite interruption, puis l'interruption elle-même qui est "relogeable", c'est-à-dire
qui peut fonctionner à n'importe quelle adresse mémoire. Ceci va nous permettre d'utiliser
les fonctions d'allocation/libération de la mémoire : AllocMem() et FreeMem(). De plus, le
programme est volontairement très court (pour l'instant). Il se présente sous forme d'un
petit fichier exécutable, avec un minimum de fioritures : pas d'interface,
pas de fenêtre, peu de dialogues. Il fonctionne comme une "bascule" :
au premier appel, le programme installe l'interruption, et si on le relance, il enlève
l'interruption. Ceci va être réalisé au moyen d'une fonction de recherche d'un noeud dans
une liste : FindName().
Comment ça marche ?
Le programme est lancé normalement depuis le CLI, mais il peut aussi être placé
dans votre startup-sequence. Il ne "bloque" pas la fenêtre du CLI et il n'a pas besoin d'être lancé
avec un "run".
Il commence par rechercher le canal de sortie standard (celui du CLI donc) au
moyen de la fonction Output() de la bibliothèque DOS, ceci afin d'afficher un petit
message grâce à la fonction Write(). Ensuite, il recherche si son interruption est
déjà dans le système. Cette interruption s'appelle "VBL/CheckMem_It", donc il
peut la retrouver facilement en utilisant la fonction FindName() qui recherche un noeud
dans une liste, en utilisant son nom. En effet, Exec gère un système appelé "serveur
d'interruptions" qui consiste à mettre toutes les interruptions de même niveau dans une même
liste. Chaque interruption est en fait une structure particulière dont le début est une
structure de type noeud :
struct Interrupt
{
struct Node is_Node;
APTR is_Data;
void (*is_Code)();
}
|
Le champ (*is_Code)() doit être initialisé avec l'adresse du point d'entrée de
la routine aux interruptions. Le champ is_Data contient l'adresse d'une zone ou
peuvent éventuellement être stockées des données nécessaires à cette routine.
Le registre A1 sera automatiquement initialisé avec cette adresse lorsque la
routine d'interruption sera invoquée par le serveur. Enfin, cette routine
doit préserver les registres qu'elle utilise, et doit se terminer par RTS
(et non RTE).
L'interruption est donc avant tout un noeud, avec un type, une priorité et un nom. Le type d'un
noeud d'interruption "hard" est toujours 02. La priorité permet de privilégier une interruption
par rapport à une autre de même niveau. Le niveau d'interruption, que l'on passe à la
fonction AddIntServer(), correspond à une valeur prise dans l'échelle des pseudos-priorités
matérielles de l'Amiga (de 0 à 13). Nous le fixerons à 5, ce qui correspond à l'interruption du
rafraîchissement vertical (ou raster), c'est-à-dire le moment où le spot vidéo remonte pour
recommencer à tracer l'image.
Ainsi, les vecteurs seront consultés 50 fois par seconde.
Enfin, le nom choisi est "VBL/CheckMem_It", mais rien ne vous empêche de le changer.
L'essentiel, c'est que le programme soit capable de retrouver son interruption.
Au lancement du programme, si l'interruption est déjà présente, on doit alors
l'enlever du système (le programme fonctionne comme un interrupteur), en utilisant la fonction
RemIntServer(). Si par contre l'interruption "CheckMem It" ne se trouve pas dans le système,
on va l'installer au moyen de AddIntServer() (après l'installation, on pourra vérifier que
l'interruption "tourne" avec X-Oper ou ARTM).
Dans les deux cas, le programme se termine sans attendre par un RTS. Inutile donc de le
lancer avec un "run". Par contre, il ne faut pas oublier un détail très important :
lorsqu'une commande est lancée depuis le CLI, elle est d'abord chargée par la
fonction LoadSeg() qui alloue de la mémoire et y place les segments de la commande, en
utilisant une table de relocation pour toutes les adresses absolues du programme.
Ensuite, le CLI effectue un JSR vers le point d'entrée du programme, qui est donc
considéré comme un véritable sous-programme du CLI !
Mais lorsque le programme se termine (RTS), le CLI reprend le contrôle et libère
les zones mémoire allouées pour la commande. Et c'est dans ces zones mémoire
que se trouvent la structure Interrupt, ainsi que son code et ses données.
Ces zones étant libérées, elles peuvent être ensuite réutilisées et écrasées par
un autre programme, ce qui planterait gravement le système. C'est pourquoi avant
d'installer l'interruption, notre programme doit allouer de la mémoire pour la structure Interrupt,
pour le code elles données, puis y recopier ces zones.
Inversement, lorsqu'on enlève l'interruption, il ne faut pas oublier de libérer les zones
déjà allouées, car la fonction RemIntServer() ne libère pas de mémoire. Elle se
contente d'extraire le noeud d'interruption de la liste IntrList (listing du programme,
début voir ci-dessous).
Remarques
Ce programme n'en est qu'à sa première version. Il comporte quelques défauts,
dont le plus évident est son absence totale d'interface. Si un vecteur est
modifié, l'utilisateur ne voit qu'un joli dégradé de couleurs !
Il serait préférable d'expliquer la situation, en indiquant clairement quel vecteur a été
modifié, et qu'est-ce qu'on attend de l'utilisateur...
Mais cet aspect rustique est volontaire : nous n'avons utilisé qu'une petite interruption
pour faire le travail. Or, pendant une interruption, beaucoup de choses simples sont
interdites, par exemple allouer de la mémoire. Par conséquent, il est impossible d'ouvrir
une fenêtre ou d'afficher une alerte pendant une interruption. Il est clair que si nous voulons
dialoguer avec l'utilisateur, il va falloir trouver un moyen de "sortir" de
l'interruption.
Tout ceci fera l'objet de l'article du mois prochain. Nous verrons comment on peut améliorer
ce programme en rajoutant une tâche dans le système, puis en réveillant cette tâche depuis
l'interruption ! Ce sera alors l'occasion pour nous de pénétrer dans le monde merveilleux d'Intuition...
|