La
mémoire XMS
eXtended Memory System
par Haypo
- 24 juin 2001
Sommaire :
Introduction :
La mémoire XMS est gérée par le pilote HIMEM.SYS chargé automatiquement
par Windows (et très souvent par DOS). Ce pilote permet d'accéder à toute
la mémoire RAM disponible sur l'ordinateur (contrairement à Turbo Pascal,
programme 16 bits, qui est limité aux 640 Ko).
Côté technique :
Toutes les fonctions XMS passent par l'interruption 2Fh (interruption
qui regroupe plusieurs extensions comme le pilote de CD-Rom MSCDEX par
exemple) et la fonction 43h, oui, faut faire de l'assembleur. Si une erreur
se produit (AX=0), BL contient le code d'erreur. Sinon AX est différent
de 0. Je ne vais pas détailler toutes les fonctions car premièrement je
ne les connais pas, et deuxièmement j'ai une unité qui fait ça très bien.
J'ai menti (j'avoue), l'unité n'est pas entièrement de moi, je me suis
beaucoup basé sur une autre unité (dont la source n'était pas indiquée),
ce qui veut dire que je ne suis pas un expert en mémoire XMS. L'unité
s'appelle "XMS.PAS" (elle est dans le pack de toutes mes unités,
disponnible sur ce site).
Vérifier la présence
du pilote XMS :
Le pilote pour la mémoire XMS n'est pas toujours disponnible (il arrive
que HIMEM.SYS ne soit pas chargé). Il est donc OBLIGATOIRE (sous peine
de plantage ou d'erreurs inexpliquées) d'appeler : " VerifiePresenceXms"
au démarrage de votre programme. Celui-ci afficher un message d'erreur
standart si le pilote n'est pas présent. Dans la version 0.4 de l'unité,
ça donne :
Le driver XMS n'est pas
installé !
[ Sous DOS ]
-> Vérifiez que vous avez bien la ligne
'DEVICE=C:\WINDOWS\HIMEM.SYS' dans le fichier Config.sys
[ Sous Windows 95/98 ]
-> 'Cliquez avec le bouton droit sur
le programme, choisissez 'Propriétés'. Dans l'onglet 'Mémoire'', sous
''Mémoire XMS'', choisissez 'Auto'.
Allouer de la mémoire
:
Pour allouer de la mémoire, utilisez la fonction " AlloueMemXms"
(j'ai essayé de donner des noms parlant !). La taille du "bloc" demandé
est donné en Kilo-octets. Malgré l'appellation "Kilo", 1 Kilo octet =
1024 octets. Pour convertir les octets en kilo-octets, il faut diviser
par 1024 et arrondir par défaut (car écrire hors du bloc XMS provoque
une erreur!), pour celà tapez : " Taille := Taille div 1024 +1;" ou plus rapidement "
Taille := Taille shr 10 +1;". "shr d" est un décalage binaire de d bits vers la droite,
R = Right = Droite en anglais. Donc shr n = div (2^n) et 2^10 = 1024 !
Il faut taper {$G+} au début de votre programme pour pouvoir utiliser
cette instruction (= utilise les instructions du 80286, dire qu'on en
est au 686 !). Chaque "bloc" de mémoire alloué est désigné par un "handle",
c'est un nombre de type Word (valeur entre 0 et 65535), mais sa valeur
ne peut pas être 0 (on verra plus tard). Fonction AlloueMemXms : " Function AlloueMemXms (Taille: word) : word;".
Rendre la mémoire :
C'est tout simple : "RendMemXms". Il faut juste donner comme paramètre
le handle renvoyé par AlloueMemXms. "Procedure RendMemXms
(Handle: word);". Exemple :
uses Xms;
var MaMem: Word; Taille: LongInt;
begin
VerifiePresenceXms;
Taille := 81530;
MaMem := AlloueMemXms (Taille shr 10 +1);
Writeln ("Ma mémoire est allouée");
RendMemXms (MaMem);
.
Créer un tampon
de transfert :
Voilà, on a de la mémoire disponnible, voyons comment y accéder maintenant.
La solution la plus pratique est de "vérouiller" un bloc mémoire pour
qu'il soit accessible via un pointeur : " Function VerrouilleBlocXms (Handle : word) : pointer;". Le
problème est que ça ne fonctionne pas toujours ... Je n'ai donc jamais
utilisé cette méthode. Peut-être que je m'y prends mal, à vous de tester.
Personellement je crée un tampon de 64 Ko (taille maximale avec Turbo
Pascal, car c'est un programme 16 bits : 2^16 = 65536 octets !) avec GetMem
:
uses StopProg;
type
TamponMax = Array[0..$FFFE] of Byte;
PtrTampon = ^TamponMax;
var
Tampon: PtrTampon;
txt: String;
begin
{ Assez de mémoire libre ? }
if MemAvail<$FFFF then
begin
Str ($FFFF-MemAvail,txt);
StopProgramInfo ('allocation du tampon de transfert','Mémoire libre
insuffisante','manque '+txt+'octets');
end;
GetMem (Tampon,$FFFF); { $FFFF = 65535 octets }
Tampon^[0] := 0;
fillchar (Tampon^,65535,23);
FreeMem (Tampon,$FFFF);
end.
Copier de la mémoire
:
Maintenant que nous avons de la mémoire XMS et un tampon de transfert,
voyons comment passer de l'un à l'autre. Le coeur de l'accès à la mémoire
XMS est "CopieXms", c'est une variable de l'unité XMS du type "StructCopieXms".
Voyons ce type :
| La structure |
| StructCopieXms = record |
| Taille : LongInt; |
Taille des données à copier. Ce qui est embêtant, c'est qu'il
faut que c'est la taille soit paire. On peut arrondir par excès
avec "Taille := Taille or 1; ", mais
il faut faire attention à ne pas sortir de notre bloc de mémoire
XMS, ni du tampon de transfert.
Remarque: Windows permet des tailles impaires, mais pas DOS, alors
utilisez toujours des tailles paires. |
| SrcHdl : word; |
Handle de la source. Si un Handle est égal à zéro, alors SrcOfs
est une adresse dans la mémoire 16 bits (les 640 Ko). |
| SrcOfs : LongInt; |
- Si SrcHdl<>0 : Offset dans le bloc XMS dont le handle
est SrcHdl (Attention à ce que SrcOfs+Taille soit inférieur
ou égal à la taille du bloc désigné, sous peine d'erreur)
- Si SrcHdl=0,
- Vous pouvez taper directement une adresse. Pour la mémoire
vidéo en mode texte couleur (mode 03h), l'adresse est B800:0000h,
tapez "$B8000000".
- Dans le cas de notre tampon de transfert : utilisez la
fonction "Ptr2Long" qui convertit un pointeur en
LongInt. Exemple :
CopieXms.SrcOfs := Ptr2Long (Tampon);
- Sinon pour n'importe quelle variable, utilisez "Ptr2Long".
Le pointeur sera l'adresse de vos données. Exemple :
CopieXms.SrcOfs := Ptr2Long (Ptr(Seg(txt),Ofs(txt)));
ou (beaucoup) plus simplement :
CopieXms.SrcOfs := Ptr2Long (@txt);
car "@var" renvoie l'adresse
de la variable var.
|
| DstHdl : word; |
Même remarque que pour SrcHdl, sauf qu'ici c'est la destination. |
| DstOfs : LongInt; |
Même remarque que pour SrcOfs, sauf qu'ici c'est la destination. |
| End; |
Après avoir initiliser la structure CopieXms, il faut appeler la
procédure "CopieMemXms" avec comme paramètre CopieXms (ou un autre
StructCopieXms de votre choix). "Procedure CopieMemXms
(var StructCopie: StructCopieXms);".
Retour au sommaire
Gestion des erreurs :
Il faut vérifier après chaque appel de fonction ou procédure qu'aucune
erreur ne se soit produit. Pour celà, pas de procédure standart, je vous
ai laisssé programmer la votre. Cela vous permettra de choisir d'afficher
les erreurs en mode texte, ou en mode graphique, et d'afficher ce que
vous voulez. Je vous offre quand même ma procédure standart que j'intègre
dans tous mes programmes :
(fonctionne en mode texte couleur uniquement)
(nécessite les unités EcranTxt, Clavier et Convert)
procedure TestErreurXms (Quand:
String); begin
if ErreurXms=0 then
exit;
EffaceEcranTxt;
EcriTcr ('Erreur lors de '+Quand+' (n°'+Hex8z(ErreurXms)+') :',12);
Ecrit ('=> '); EcriTcr (TxtErreurXms+' !',15);
SauteLigne;
EcriTcr ('[ Info pour le débogage ]',15+3*16);
with CopieXms do begin
Ecrit ('Taille = '); EcriTncr (Taille,10);
Ecrit ('SrcHdl = '); EcriTncr (SrcHdl,10);
Ecrit ('SrcOfs = '); EcriTnc (SrcOfs,10);
Ecrit (' ('); EcriTc (Hex16z(SrcOfs shr
16)+':'+Hex16z(SrcOfs and $FFFF),10); EcriTr
(')');
Ecrit ('DstOfs = '); EcriTnc (DstOfs,10);
Ecrit (' ('); EcriTc (Hex16z(DstOfs shr
16)+':'+Hex16z(DstOfs and $FFFF),10); EcriTr
(')');
Ecrit ('Mémoire XMS libre = '); EcriTnc (MemXmsLibre,10); EcriTr ('
Ko');
end;
SauteLigne;
Ecrit ('Pressez une touche pour finir :'); LitTouche; EffaceLigneTxt;
RendMemXms (TamponTexte);
Halt (8);
end;
Cette procédure doit être appelée après chaque appel à une fonction
de l'unite XMS avec comme paramètre un texte expliquant l'opération que
l'on voulait exécuter (qui sera précédé de "Erreur lors de "). Exemple
: pour allouer de la mémoire, on tapera : " TestErreurXms ('l''allocation d''un tampon mémoire');".
Autre point : rendre la mémoire allouée en cas d'erreur. La solution
la plus simple est de créer une procédure (nommée "LibereMemAllouee" par
exemple) qui sera exécutée en cas d'erreur avant d'arrêter l'exécution
du programme (appel de "Halt"). Si vous utilisez
mon unité StopProg, vous pouvez mettre cette procédure en appel far (par
la commande "far;" écrite juste avant begin
ou var), puis la rentrer dans la variable
"ProcAppeleeStop" (procédure appelée lorsque le programme stoppe son exécution
:-). Exemple :
(uses StopProg;)
procedure LibereMemAllouee;
far; { Adressage long}
var ...
begin
Dispose (Tampon);
RendMemXms (MonHandle);
end;
begin
...
ProcAppeleeStop := LibereMemAllouee;
...
LibereMemAllouee;
end.
Retour au sommaire
- Retour aux tutoriels
Par Haypo
|