Appunti per Scuola e Università
humanisticheUmanistiche
Appunti e tesine di tutte le materie per gli studenti delle scuole medie riguardanti le materie umanistiche: dall'italiano alla storia riguardanti le materie umanistiche: dall'italiano alla storia 
sceintificheScientifiche
Appunti, analisi, compresione per le scuole medie suddivisi per materie scientifiche, per ognuna troverai appunti, dispense, esercitazioni, tesi e riassunti in download.
tecnicheTecniche
Gli appunti, le tesine e riassunti di tecnica amministrativa, ingegneria tecnico, costruzione. Tutti gli appunti di AppuntiMania.com gratis!
Appunti
informatica
CComputerDatabaseInternetJava
Linux unixReti


AppuntiMania.com » Informatica » Appunti di c » Gestione a basso livello della memoria

Gestione a basso livello della memoria




Visite: 1833Gradito:apreciate 4-stela [ Grande appunti ]
Leggi anche appunti:

Problemi di cooperazione nel modello a memoria comune


Problemi di cooperazione nel modello a memoria comune IV) Scrivere una applicazione

Contenuto del floppy disk


Contenuto del floppy disk Il floppy disk allegato costituisce una raccolta di

Gli operatori


Gli operatori Come tutti i linguaggi di programmazione, il C dispone di un insieme
immagine di categoria

Scarica gratis Gestione a basso livello della memoria

Gestione a basso livello della memoria

Il presente capitolo non ha la pretesa di analizzare dal punto di vista tecnico il comportamento del DOS o delle funzioni di allocazione dinamica presenti nella libreria C: esso si propone, piuttosto, di fornire qualche spunto su particolarità non sempre evidenti . Alcuni cenni di carattere tecnico sono, tuttavia, indispensabili.

Il compilatore C

La libreria C comprende diverse funzioni dedicate all'allocazione dinamica della RAM: esse possono essere suddivise, forse un poco grossolanamente, in due gruppi.

Da un lato vi sono quelle che gestiscono la memoria secondo modalità, per così dire, tipiche della libreria C: malloc() realloc() free() e, in sostanza, tutte le funzioni dichiarate nel file ALLOC.H (o MALLOC.H : se ne parla a pagina 

Dall'altro lato troviamo le funzioni basate sui servizi di allocazione della memoria resi disponibili dall'int 21h allocmem() setblock() e freemem(), dichiarate in DOS.H. Ecco la descrizione dei servizi testè citati:

Int 21h, serv. 48h: Alloca un blocco memoria

Input

AH

BX

48h

Numero di paragrafi da allocare

Output

AH

Indirizzo di segmento dell'area allocata, oppure il codice di errore se CarryFlag = 1. In questo caso BX contiene il massimo numero di paragrafi disponibili per l'allocazione.

Note


Se la funzione è eseguita con successo, AX:0000 punta all'area allocata. Invocare la funzione con BX = FFFFh è un metodo per conoscere la quantità di memoria libera.

Int 21h, serv. 49h: Dealloca un blocco di memoria

Input

AH

ES

49h

Segmento dell'indirizzo dell'area da liberare

Output

AX

Codice di errore, se il CarryFlag 

Note


Questo servizio restituisce al DOS un'area allocata mediante il servizio 48h. ES contiene il valore da questo restituito in AX

Int 21h, serv. 4Ah: Modifica l'ampiezza del blocco di memoria allocato

Input

AH

BX

ES

4Ah

Nuova dimensione in paragrafi del blocco

Segmento dell'indirizzo del blocco da modifcare

Output

AX

Codice di errore, se CarryFlag  . In questo caso, se il blocco doveva essere espanso, BX contiene il massimo numero di paragrafi disponibili.

Note


Questa funzione è utilizzata per espandere o contrarre un blocco precedentemente allocato via servizio 48h.

E' evidente che un programma il quale intenda interagire con il DOS nell'allocazione della RAM deve necessariamente utilizzare le funzioni appartenenti al secondo gruppo oppure ricorrere direttamente all'int 21h.

Memoria convenzionale

La memoria convenzionale è costituita dai primi 640 Kb (o meno di 640) di RAM installati sulla macchina: essi sono compresi tra gli indirizzi  9FFF:000F . Essi sono l'unica parte di RAM che il DOS è in grado di utilizzare senza artifici per l'esecuzione di se stesso e dei programmi applicativi. L'uso della memoria convenzionale è descritto graficamente in figura 

Il primo Kilobyte, dall'indirizzo  (origine) a 003F:000F, è occupato dalla tavola dei vettori (vedere pag.  ). I successivi 256 byte costituiscono un'area a disposizione del BIOS per la gestione di dati come il modo video attuale, il timer, etc.; essi sono seguiti da un'area di 512 byte usata in modo analogo dal DOS. A  è caricato, in fase di bootstrap , il primo dei due file nascosti di sistema, di solito chiamato, a seconda della versione di DOS e del suo produttore, IBMBIO.COM o MSDOS.SYS . Tutti i restanti oggetti evidenziati in figura  (a partire dal secondo file nascosto, IBMDOS.COM o IO.SYS ) sono caricati ad indirizzi variabili che dipendono dalla versione del sistema operativo e dalla configurazione della macchina (dal tipo e dal numero di device driver caricati, per fare un esempio). Il confine tra le aree 'Programmi Applicativi' e 'COMMAND.COM : parte transiente' è tratteggiato, in quanto la parte transiente dell'interprete dei comandi può essere sovrascritta in qualsiasi momento dai programmi di volta in volta eseguiti: essa è caricata nella parte alta della memoria convenzionale, ma lo spazio occupato non viene considerato un'area protetta . L'allocazione della memoria per tutti gli oggetti caricati in RAM successivamente a IO.SYS (o IBMDOS.COM) è gestita mediante i Memory Control Block (MCB): ciascuno di essi contiene le informazioni necessarie alla gestione dell'area di memoria della quale costituisce l'intestazione

Fig.  : Utilizzo, da parte del DOS, della memoria convenzionale.

Facciamo un esempio: dopo il bootstrap vi è una porzione (solitamente ampia) di RAM libera, disponibile per l'esecuzione dei programmi. In testa a tale area vi è un MCB. Quando viene caricato ed eseguito un programma, il DOS gli assegna due aree: la prima, di norma piccola, contiene una copia delle variabili d'ambiente (l'environment); la seconda è riservata al programma stesso. L'area libera è stata così suddivisa in tre parti: le prime due appartengono al programma, mentre la terza è libera. Ciascuna delle tre ha il proprio MCB: da ogni MCB è possibile risalire al successivo, ricostruendo così tutta la catena (e quindi la mappa dell'utilizzo della RAM). Supponiamo che il programma non utilizzi il proprio environment, e quindi restituisca al DOS la memoria da quello occupata: le aree sono sempre tre, ma solo la seconda è allocata, mentre la prima e la terza sono libere. Quando, infine, il programma termina, la RAM da esso occupata torna ad essere libera: per evitare inutili frazionamenti della RAM il DOS riunisce tutte le aree libere contigue. Si ritorna perciò alla situazione di partenza: un'unica area, libera, con un unico MCB.

A questo punto è indispensabile analizzare gli MCB con maggiore dettaglio: la figura  ne descrive la struttura.

Come si vede, ciascuno di essi ha ampiezza pari a un paragrafo (16 byte) ed è suddiviso in campi.

Fig.  : La struttura dei Memory Control Block.

Il campo POS, di un solo byte, indica la posizione del MCB: se questo è l'ultimo (l'area di RAM che esso controlla è quella che inizia al maggiore indirizzo) il campo contiene il carattere 'Z', altrimenti il carattere 'M

Il campo PSP, una word (unsigned int, dal punto di vista del C), indica l'indirizzo di segmento del Program Segment Prefix del programma a cui appartiene l'area di memoria . Nell'esempio precedente, i campi PSP del MCB del programma e del suo environment hanno il medesimo contenuto. Il campo PSP del MCB di un'area libera assume valore zero. I valori  indicano che l'area è riservata al DOS; in particolare,  è il valore che assume il campo PSP del MCB dell'area allocata ai device driver. Detto MCB è, tra l'altro, il primo della catena

Il campo DIM, una word, esprime la dimensione, in paragrafi, dell'area di memoria (escluso il MCB medesimo). Incrementando di uno la somma tra l'indirizzo di segmento di un MCB e il suo campo DIM si ottiene l'indirizzo di segmento del successivo MCB. Se il calcolo è effettuato con riferimento all'ultimo MCB della catena, il valore ottenuto è il cosiddetto Top Of Memory (A000h nel caso di 640 Kb installati)

I 3 byte del campo RESERVED attualmente non sono utilizzati.

Il campo NAME, di 8 byte, a partire dal DOS 4.0 contiene il nome del programma a cui l'area è assegnata (se questa contiene il Program Segment Prefix del programma: rifacendosi ancora all'esempio riportato, il nome non appare nel MCB dell'environment). Se il nome non occupa tutti gli 8 byte disponibili, quelli restanti sono riempiti con spazi (ASCII  , esadecimale  ). Se l'area è riservata al DOS, il nome, quando presente, è solitamente una stringa significativa per il solo DOS, e il carattere tappo può essere l'ASCII  FFh

Qui giunti, conosciamo quanto basta (con un po' di ottimismo e di fortuna) per lavorare alla pari con il DOS. Si tratta, ora, di illustrare alcuni metodi (gran parte dei quali non documentati ufficialmente) per individuare gli indirizzi degli oggetti di cui abbiamo discusso.

Cominciamo dal secondo file nascosto. Il servizio 34h dell'int 21h restituisce l'indirizzo, nel segmento di memoria allocato al DOS, dell'InDOS flag

Int 21h, serv. 34h: Indirizzo dell'InDOS flag

Input

AH

34h

Output

ES:BX

Indirizzo (seg:off) dell'InDOS flag

La parte segmento dell'indirizzo dell'InDOS flag (restituita in ES) è l'indirizzo di segmento al quale è caricato il secondo file nascosto; esso si trova, in altri termini, a ES:0000. La funzione getdosseg() è un esempio di come è possibile procedere per ottenere detto indirizzo.



BARNINGA_Z! - 1991


DOSSEG.C - getdosseg()


unsigned cdecl getdosseg(void);

Restituisce: l'indirizzo di segmento di IO.SYS o IBMDOS.COM


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx dosseg.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <dos.h>


unsigned cdecl getdosseg(void)


Circa la parola chiave cdecl vedere pag.  . Una versione più efficiente della getdosseg() è listata di seguito:



BARNINGA_Z! - 1991


DOSSEG.C - getdosseg()


unsigned cdecl getdosseg(void);

Restituisce: l'indirizzo di segmento di IO.SYS o IBMDOS.COM


COMPILABILE CON BORLAND C++ 2.0


bcc -O -d -c -k- -mx dosaddr.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline

#pragma option -k- // per maggiore efficienza


unsigned cdecl getdosseg(void)


Passiamo ai Memory Control Block. Come si è detto, la prima area gestita tramite MCB è quella dei device driver. Sfortunatamente, anche in questo caso non esistono metodi ufficiali per conoscerne l'indirizzo. Esiste, però, un servizio non documentato dell'int 21h, la funzione 52h, detto 'GetDosListOfLists ', che restituisce l'indirizzo di una tavola di parametri ad uso interno del sistema. La word che precede questa tavola è l'indirizzo (segmento) del primo MCB.

Int 21h, serv. 52h: Indirizzo della lista delle liste

Input

AH

52h

Output

ES:BX

Indirizzo (segmento:offset) della lista delle liste.

Di seguito riportiamo un esempio di funzione che restituisce l'indirizzo di segmento del primo MCB.



BARNINGA_Z! - 1991


FIRSTMCB.C - getfirstmcb()


unsigned cdecl getfirstmcb(void);

Restituisce: l'indirizzo di segmento del primo MCB


COMPILABILE CON BORLAND C++ 2.0


bcc -O -d -c -mx firstmcb.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <dos.h>


unsigned cdecl getfirstmcb(void)


La macro MK_FP() è descritta a pag.  . Anche in questo caso riportiamo la versione basata sullo inline assembly:



BARNINGA_Z! - 1991


FIRSTMCB.C - getfirstmcb()


unsigned cdecl getfirstmcb(void);

Restituisce: l'indirizzo di segmento del primo MCB


COMPILABILE CON BORLAND C++ 2.0


bcc -O -d -c -k- -mx firstmcb.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline

#pragma option -k- // per maggiore efficienza


unsigned cdecl getfirstmcb(void)


A scopo di chiarezza, ripetiamo che getfirstmcb() non restituisce l'indirizzo della prima area di RAM controllata da MCB, bensì quello del primo MCB. L'indirizzo (di segmento) dell'area si ottiene, ovviamente, sommando uno al valore restituito.

Ora che sappiamo dove trovarli, i MCB possono essere comodamente manipolati con l'aiuto di una struttura:

struct MCB

Attenzione: il campo name della struttura di tipo MCB non è una vera e propria stringa, in quanto privo del NULL finale. Ecco, ora, il listato di una funzione in grado di copiare un Memory Control Block in un buffer appositamente predisposto.



BARNINGA_Z! - 1991


PARSEMCB.C - parsemcb()


unsigned cdecl parsemcb(struct MCB *mcb,unsigned mcbseg);

struct MCB *mcb; puntatore ad una struttura di tipo MCB: deve

essere gia' allocata

unsigned ncbseg; indirizzo (segmento) del MCB da copiare


Restituisce: l'indirizzo (segmento) dell'area di RAM controllata dal

MCB dopo avere copiato il contenuto del MCB nella

struttura mcb.


COMPILABILE CON BORLAND C++ 2.0


bcc -O -d -c -mx parsemcb.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


#include <dos.h>


unsigned cdecl parsemcb(struct MCB *mcb,unsigned mcbseg)


Lo inline assembly rende il codice compatto e veloce ; la funzione potrebbe comunque essere realizzata facilmente in C puro. La parsemcb() copia i dati di un MCB in una struttura di template MCB e restituisce l'indirizzo del Memory Control Block incrementato di uno, cioè l'indirizzo (segmento) dell'area di memoria controllata da quel MCB.

Abbiamo tutto ciò che occorre per ricostruire la mappa della memoria convenzionale: basta collegare i vari frammenti in modo opportuno.



BARNINGA_Z! - 1991


MCBCHAIN.C - getmcbchain()


struct MCB * cdecl getmcbchain(unsigned basemcb);

unsigned basemcb; indirizzo (segmento) del primo MCB della catena.

Restituisce: un puntatore ad un array di strutture di tipo MCB.


COMPILABILE CON BORLAND C++ 2.0


bcc -O -d -c -mx mcbchain.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <stdio.h>            // per NULL

#include <alloc.h>                              // per malloc() e realloc()


struct MCB * cdecl getmcbchain(unsigned basemcb)

while(mcb[i++].pos != 'Z');    // i e' incrementata dopo il confronto

return(mcb);


La getmcbchain() prende come parametro l'indirizzo di segmento del primo MCB della catena, facilmente ottenibile mediante la getfirstmcb(): come si può vedere, nulla di complicato. Per avere una mappa completa della memoria convenzionale basta ricavare l'indirizzo del secondo file nascosto con una chiamata alla getdosseg(). La mappa può poi essere arricchita individuando la strategia utilizzata dal DOS nell'allocazione della memoria, cioè l'algoritmo con il quale il DOS ricerca un blocco libero di dimensioni sufficienti. Le strategie possibili sono tre: la prima, detta FirstFit , consiste nel ricercare il blocco a partire dall'origine della RAM; la seconda, BestFit, nell'allocare il blocco nella minore area libera disponibile; la terza, LastFit, si esplica nell'allocare la parte alta dell'ultimo blocco libero. La strategia di allocazione è gestita dal servizio 58h dell'int 21h.

Int 21h, serv. 58h: Gestione della strategia di allocazione

Input

AH

AL

BX

58h

00h: ottiene la strategia di allocazione
01h: determina la strategia di allocazione

solo per AL 01h (set strategy):

: FirstFit

: BestFit

>= : LastFit

Output

AX

codice di errore se CarryFlag ; altrimenti:

solo per AL 00h (get strategy):

: FirstFit

: BestFit

>= : LastFit




BARNINGA_Z! - 1992


ALCSTRAT.C - getallocstrategy()


int cdecl getallocstrategy(void);

Restituisce: la strategia di allocazione DOS. In caso di errore

restituisce -1.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx alcstrat.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline

#pragma option -k- // maggiore efficienza


int cdecl getallocstrategy(void)


Per un esempio di modifica della strategia DOS di allocazione vedere pag. 

Upper memory

Il metodo di puntamento basato sulla coppia segmento:offset consente al DOS di indirizzare, su macchine a 16 bit, un megabyte di RAM . I 384 Kb compresi tra i primi 640 Kb e il Mb sono, di norma, utilizzati come indirizzi per il ROM‑BIOS o sue estensioni, per la gestione del video, etc.: uno schema è riprodotto in figura 

Fig.  : Utilizzo degli indirizzi di memoria tra i 640 Kb e il megabyte.

Gli indirizzi compresi tra C000:0 e EFFF:000F sono disponibili per le estensioni ROM‑BIOS: possono, cioè, essere utilizzati dalle schede di supporto per il networking o per particolari periferiche (fax, scanner, etc.). Ad esempio, l'intervallo che si estende da da C000:0C7FF:000F è di norma occupato dal BIOS delle schede VGA; inoltre molti calcolatori tipo notebook o laptop dispongono di estensioni ROM‑BIOS, spesso dedicate al controllo del video LCD, nel range da E000:0EFFF:000F

Gli indirizzi non occupati possono essere impiegati per simulare l'esistenza di aree di memoria che il DOS gestisce in modo analogo a quelle presenti nella memoria convenzionale: a tal fine è indispensabile un driver in grado di rimappare gli indirizzi tra A000:0FFFF:000F alla memoria fisicamente presente, in quanto a detti indirizzi non corrisponde RAM installata. Tali driver utilizzano, per effettuare il remapping, memoria espansa: ne consegue la necessità che sulla macchina ne sia installata una quantità sufficiente (almeno pari all'ampiezza totale delle aree da simulare) . Il DOS, a partire dalla versione 5.0, include il software necessario alla gestione, su macchine 80386 e superiori, di aree di RAM tra i 640 Kb e il Mb, dette Upper Memory Block. Inoltre, sono reperibili in commercio diversi prodotti mediante i quali è possibile ottenere prestazioni analoghe o migliori (per efficienza e flessibilità) sia su macchine 80386/80486 che 80286, con o senza DOS 5.0.

La tecnica di gestione degli Upper Memory Block (UMB), analogamente a quanto avviene per la memoria convenzionale, si basa sui Memory Control Block ; sfortunatamente, i driver DOS e quelli di produttori indipendenti definiscono le aree e comunicano con il sistema (programmi, etc.) mediante tecniche differenti: insomma, il caos regna sovrano. Gli esempi che seguono intendono fornire gli elementi minimi necessari a ricavare una mappa della Upper Memory: essi vanno comunque 'presi con le pinze', dal momento che si basano esclusivamente sui risultati di una ostinata sperimentazione.

Il DOS 5.0 crea (tramite HIMEM.SYS e EMM386.EXE ) la catena di MCB per gli Upper Memory Block restringendo l'ultima area di memoria convenzionale di 16 byte, nei quali definisce il primo MCB della nuova catena: nel caso di 640 Kb di RAM convenzionale, esso si trova a 9FFF:0. Il suo campo POS contiene il carattere 'M'; il campo PSP è valorizzato a  ; il campo NAME contiene la stringa 'SC', seguita da sei NULL. Sommando il valore contenuto nel campo DIM all'indirizzo del MCB si ottiene la parte segmento dell'indirizzo di un successivo MCB , il cui campo NAME contiene la stringa 'UMB' seguita da 5 blanks. Il campo PSP è pari all'indirizzo del MCB stesso, incrementato di uno; il campo DIM esprime la dimensione, in paragrafi, dell'Upper Memory Block. All'interno di questo vi sono i MCB necessari per la definizione delle aree allocate e libere . Il campo POS vale 'Z' se vi è questo UMB soltanto, 'M' altrimenti: in questo caso, sommando il campo DIM incrementato di uno all'indirizzo dell'attuale UMB si ottiene l'indirizzo del successivo MCB. Questo, a sua volta, potrebbe avere lo scopo di 'proteggere' un'area non rimappabile . La catena continua sino ad esaurimento degli indirizzi liberi.

Basandosi su queste caratteristiche (lo ripetiamo: individuate empiricamente) è possibile realizzare una funzione in grado di determinare se nel sistema è disponibile Upper Memory gestita dal DOS 5.0:



BARNINGA_Z! - 1991


UMBDOS.C - testforDOS()


unsigned cdecl testforDOS(void);

Restituisce: l'indirizzo (segmento) del MCB corrispondente al primo

UMB (area di RAM sopra i 640 Kb). Restituisce NULL se

non riesce ad individuare la catena di UMB.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx umbdos.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <stdio.h>            // per NULL

#include <string.h>      // per strncmp()


unsigned cdecl testforDOS(void)


return(NULL);


La testforDOS() ipotizza che gli utlimi 16 byte di memoria convenzionale siano un MCB: se il presunto campo POS vale 'M' e il presunto PSP , allora il controllo prosegue secondo le linee indicate in precedenza. Se i 16 byte all'indirizzo segbase+umb.dim sono un MCB il cui campo PSP è pari al proprio indirizzo incrementato di uno e il cui campo NAME contiene 'UMB     ', allora testforDOS() presume che gli ultimi 16 byte di memoria convenzionale siano realmente il primo MCB della catena che gestisce gli Upper Memory Block e ne restituisce l'indirizzo, altrimenti restituisce NULL (a significare che non vi è Upper Memory disponibile, o non è stato possibile individuarne la presenza). La mappa della Upper Memory può essere ottenuta semplicemente passando alla getmcbchain() di pag.  proprio l'indirizzo restituito dalla testforDOS()

Per quanto riguarda i driver reperibili in commercio (non facenti parte del pacchetto DOS) l'esempio che segue fa riferimento al QEMM386.SYS , prodotto dalla Quarterdeck Office Systems, il quale, su macchine dotate di processore 80386 o superiore, fornisce supporto per la memoria estesa ed espansa, nonché per gli Upper Memory Block. Il metodo utilizzato per la loro gestione differisce significativamente da quello implementato dal DOS. In primo luogo, non esistono UMB contenitori di aree di RAM: ogni UMB rappresenta un'area a se stante, dotata di un proprio MCB, in modo del tutto analogo alle aree definite entro i primi 640 Kb. Inoltre, il primo UMB si trova al primo indirizzo disponibile sopra la memoria convezionale (e non negli ultimi 16 byte di questa); gli indirizzi non disponibili sono protetti con un UMB il cui MCB presenta nel campo PSP il memdesimo valore del campo PSP del MCB dell'area allocata, tra i device driver, a QEMM386.SYS . Questo, infine, incorpora un gestore per l'interrupt 2Fh, tramite il quale comunica con il sistema. Invocando l'int 2Fh dopo avere caricato con opportuni valori i registri è possibile desumere, dai valori in essi restituiti, se QEMM386.SYS è attivo e qual è l'indirizzo del primo UMB.



BARNINGA_Z! - 1991


UMBQEMM.C - testforQEMM386()


unsigned cdecl testforQEMM386(void);

Restituisce: l'indirizzo (segmento) del MCB corrispondente al primo

UMB (area di RAM sopra i 640 Kb). Restituisce NULL se

non riesce ad individuare la catena di UMB.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx umbqemm.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma  inline

#pragma  option -k- // non indispensabile, ma aggiunge efficienza


#include <stdio.h>            // per NULL

#include <string.h>      // per strncmp()


unsigned cdecl testforQEMM386(void)


NOTQEMM386:

asm xor cx,cx;

EXITFUNC:

return(_CX);


La testforQEMM386() invoca due volte l'int 2Fh. Nella prima richiede il servizio 0 (AL ; può essere una richiesta di conferma dell'avvenuta installazione): AH BX CX e DX contengono, presumibilmente, valori aventi funzione di 'parola d'ordine' (vedere, per alcuni dettagli circa l'int 2Fh, pag.  ). Se QEMM386.SYS è installato ed attivo AX BX CX e DX contengono valori prestabiliti, controllati dalla funzione. La seconda chiamata all'int 2Fh richiede il servizio 1 (AL ; appare essere la richiesta dell'indirizzo del primo MCB per UMB): anche in questo caso AH BX CX e DX sono caricati con valori costanti. QEMM386.SYS restituisce in AX e BX ancora valori prestabiliti, ed in CX la parte segmento dell'indirizzo del primo MCB di controllo per gli Upper Memory Block. La testforQEMM386() restituisce NULL se non ha individuato la presenza di QEMM386.SYS. Anche in questo caso il valore restituito, se diverso da NULL, può essere parametro attuale della getmcbchain() per ricavare la mappa della Upper Memory.

Gli UMB (pag.  ) sono definiti dalla XMS (eXtended Memory Services) Specification, a cui si rimanda per la descrizione dei servizi che consentono la loro allocazione e deallocazione (pag. 

Memoria espansa

Fig.  : Schema di mapping EMM tra pagine fisiche (EMM page frame) e pagine logiche. La figura ipotizza che la
Page Frame sia gestita nella Upper Memory.

La memoria espansa consiste in pagine di RAM che possono essere copiate dentro e fuori lo spazio fisico di indirizzamento oltre il limite dei 640 Kb. E', in sostanza, un metodo per superare il limite dei 640 Kb tramite un driver in grado di gestire lo scambio di dati tra la RAM direttamente indirizzabile dal DOS e la memoria presente oltre il primo megabyte, attraverso la page frame, un'area definita entro il primo Mb stesso (memoria convenzionale o upper memory) e utilizzata come buffer di 'transito'. Secondo le specifiche LIM 4.0 la page frame ha dimensione pari a 64 Kb (4 pagine fisiche), e può essere utilizzata per gestire fino a 32 Mb di RAM, suddivisa in pagine logiche (in genere di 16 Kb). Ad ogni gruppo di pagine logiche è associato uno handle (concetto analogo ma non coincidente con quello di pag.  ), che lo identifica in modo univoco: il driver si occupa di creare una corrispondenza trasparente tra pagine fisiche e pagine logiche associate ad un certo handle (figura  ); il funzionamento del meccanismo sarà sperimentato nel corso del paragrafo. Su macchine 80386 o superiori la memoria espansa può essere emulata (mediante appositi driver) utilizzando la memoria estesa (vedere pag. 

Il driver che gestisce la memoria espansa (detta memoria EMS) installa un proprio gestore dell'int 67h e rende disponibile un device che ha il nome convenzionale EMMXXXX0 . Esso è detto EMM (EMS Manager). Di seguito sono presentati i listati di alcune funzioni basate sui servizi dell'int 67h.

Prima di effettuare qualsiasi operazione mediante il driver EMS si deve stabilire se esso è effettivamente installato. Il metodo raccomandato dalle specifiche LIM consiste nel tentare l'apertura del device EMMXXXX0



BARNINGA_Z! - 1991


EMMTEST.C - testEMM()


int cdecl testEMM(void);

Restituisce: 1 se il driver EMM e' installato

0 altrimenti


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmtest.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma warn -pia


#include <stdio.h>            // per NULL

#include <dos.h>

#include <io.h>

#include <fcntl.h>


int cdecl testEMM(void)                     /* uso della memoria espansa */


close(handle);

return(retcode);


La testEMM() apre il device EMMXXXX0 (nome di default dell'Expanded Memory Manager) e, mediante la subfunzione 0 del servizio 44h dell'int 21h controlla che esso sia effettivamente un device (bit 7 di DX ) e non un file . Tramite la subfunzione 7 del medesimo servizio, getEMMusage() controlla che il device sia in stato di ready (AL FFh); in caso affermativo può essere comunicato (retcode = 1) alla funzione chiamante che il driver è installato e pronto ad eseguire i servizi richiesti via int 67h.

E' possibile utilizzare un metodo alternativo per controllare la presenza del driver EMM, basato sulle specifiche Microsoft per la struttura dei device driver (vedere pag.  ). Vediamo una seconda versione di testEMM(), più snella della precedente

#include <dos.h>

#include <string.h>


#define EMMNAME 'EMMXXXX0'


int cdecl testEMM2(void)


La testEMM2() costruisce un puntatore far a carattere la cui parte segmento è data dalla parte segmento del vettore dell'int 67h (ottenuto tramite il servizio 35h dell'int 21h) e la cui parte offset equivale a 10 (ad offset 10 del blocco di memoria allocato ad un device driver si trova il nome dello stesso); la funzione _fstrncmp() è utilizzata per vedere se la sequenza di caratteri che si trova a quell'indirizzo è proprio EMMXXXX0. Se testEMM2() è compilata con uno dei modelli di memoria tiny, small o medium (pag.  ) l'indirizzo della costante manifesta EMMNAME near, ma il compilatore provvede, in base al prototipo di _fstrncmp() dichiarato in STRING.H, a convertirlo opportunamente in puntatore far. L'uso di _fstrncmp() in luogo di _fstrcmp() è imposto dal fatto che il nome dei device driver non è gestito, nell'area di RAM loro allocata, come una stringa C (in altre parole, non è seguito dal NULL). La _fstrncmp() restituisce  se le stringhe confrontate sono uguali, cioè nel caso in cui il driver sia installato: l'operatore di not logico '; vedere pag.  ) 'capovolge' il risultato, perciò anche questa versione di testEMM() restituisce un valore non nullo se il driver è presente e  se non lo è (circa MK_FP() vedere pag. 

La versione del gestore EMM installato è ottenibile via int 67h, servizio 46h, sul quale si basa la getEMMversion()

Int 67h, serv. 46h: Versione EMM

Input

AH

46h

Output

AH

AL

Stato dell'operazione (errore se ; vedere pag. 

Versione e revisione del gestore EMM. La versione è nei bit 4‑7, la revisione nei bit 0-3.



BARNINGA_Z! - 1991


EMMVER.C - getEMMversion()


unsigned cdecl getEMMversion(void);

Restituisce: la versione del driver EMM (versione nel byte meno

significativo, revisione nel byte piu' significativo:

4.0 e' restituito come 0x0004).

Se si e' verificato un errore restituisce un numero

negativo (il codice d'errore EMS cambiato di segno).


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx -k- emmver.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline

#pragma option -k-


unsigned cdecl getEMMversion(void)            /* ottiene la versione LIM */


SETVER:

asm

EXITFUNC:

return(_AX);


La getEMMversion() restituisce un unsigned integer, il cui byte meno significativo rappresenta la versione, e quello più significativo la revisione del gestore EMM . In caso di errore è restituito un valore negativo (il codice di errore cambiato di segno).

La funzione che segue, getEMMframeAddr(), restituisce l'indirizzo della page frame , ottenuto invocando servizio 41h dell'int 67h.

Int 67h, serv. 41h: Indirizzo della page frame

Input

AH

41h

Output

AH

BX

Stato dell'operazione (errore se ; pag.

Indirizzo (segmento) della page frame.



BARNINGA_Z! - 1991


EMMFRAME.C - getEMMframeAddr()


unsigned cdecl getEMMframeAddr(void);

Restituisce: l'indirizzo (segmento) della Page Frame.

Se si e' verificato un errore restituisce un numero

negativo (il codice d'errore EMS cambiato di segno).


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx -k- emmframe.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline

#pragma option -k- /* non indispensabile; accresce l'efficienza */


unsigned cdecl getEMMframeAddr(void)


EXITFUNC:

return(_BX);


Ancora, attraverso i servizi dell'int 67h, è possibile conoscere lo stato della memoria espansa (numero di pagine totali e libere, stato degli handle , etc.).

Int 67h, serv. 42h: Numero di pagine

Input

AH

42h

Output

AH

BX

DX

Stato dell'operazione (errore se ; pag.

Numero di pagine non allocate.

Numero totale di pagine EMS.



BARNINGA_Z! - 1991


EMMTOTP.C - getEMMtotPages()


int cdecl getEMMtotPages(void);

Restituisce: il numero di pagine totali EMS

Se < 0 si e' verificato un errore; il valore cambiato di segno

e' il codice di errore EMS


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmtotp.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <dos.h>


int cdecl getEMMtotPages(void)


La getEMMtotPages() restituisce il numero di pagine logiche EMS disponibili nel sistema: se il valore restituito è negativo rappresenta, cambiato di segno, il codice di errore EMS (l'operazione non è stata eseguita correttamente); inoltre il dato restituito può evidenziare una quantità di memoria EMS maggiore della quantità di memoria fisica installata sulla macchina, quando sia attivo un ambiente in grado di creare memoria virtuale . Analoghe considerazioni valgono per la getEMMfreePages(), che restituisce il numero di pagine logiche EMS non ancora allocate (e quindi disponibili per i programmi).



BARNINGA_Z! - 1991


EMMFREEP.C - getEMMfreePages()


int cdecl getEMMfreePages(void);

Restituisce: il numero di pagine libere EMS

Se < 0 si e' verificato un errore; il valore cambiato di segno

e' il codice di errore EMS


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmfreep.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <dos.h>


int cdecl getEMMfreePages(void)


Int 67h, serv. 4Bh: Numero di handle EMM aperti

Input

AH

4Bh

Output

AH

BX

Stato dell'operazione (errore se != ; pag.

Numero di handle aperti.

Int 67h, serv. 4Dh: Pagine allocate agli handle

Input

AH

ES:DI

4Dh

Buffer costituito da tante coppie di word quanti sono gli handle aperti.

Output

AH

BX

Stato dell'operazione (errore se ; pag.

Numero di handle attivi.

Le words dell'array in ES:DI sono riempite, alternativamente, con un numero di handle e il numero delle pagine allocate a quello handle.



BARNINGA_Z! - 1991


EMMOHNDL.C - getEMMopenHandles()


int cdecl getEMMopenHandles(void);

Restituisce: il numero di handles EMS aperti

Se < 0 si e' verificato un errore; il valore cambiato di segno

e' il codice di errore EMS


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmohndl.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#include <dos.h>


int cdecl getEMMopenHandles(void)


Anche la getEMMopenHandles() in caso di errore restituisce il codice d'errore EMS cambiato di segno (negativo); un valore maggiore o uguale a  esprime invece il numero di handle EMS aperti, cioè utilizzati nel sistema. La getEMMpagesPerHandle() utilizza invece il servizio 4Dh dell'int 67h per conoscere il numero di pagine allocate a ciascuno handle aperto e memorizza i dati nell'array di strutture di tipo EMMhnd, il cui indirizzo le è passato come parametro.

struct EMMhnd ;

L'array di strutture deve essere allocato a cura del programmatore, ad esempio con una chiamata a malloc()

struct EMMhnd *emmHnd;

unsigned oHnd;

.

if((oHnd = getEMMopenHandles()) < 0)

else

if(!(emmHnd = (struct EMMhnd *)malloc(oHnd*sizeof(struct EMMhnd))))

In assenza di errori può essere invocata la getEMMpagesPerHandle(), listata di seguito, passandole come parametro il puntatore emmHnd



BARNINGA_Z! - 1991


EMMPPH.C - getEMMpagesPerHandle()


int cdecl getEMMpagesPerHandle(struct EMMhnd *emmHnd);

struct EMMhnd *emmHnd; puntatore ad array di strutture EMMhnd, gia' allocato

con tanti elementi quanti sono gli handles aperti.

Restituisce: >= 0 se l'operazione e' stata eseguita correttamente. Il valore

rappresenta il numero di handles EMS attivi.

Se < 0 si e' verificato un errore; il valore cambiato di segno

e' il codice di errore EMS


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmpph.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma warn -pia


#include <dos.h>

#include <errno.h>


int cdecl getEMMpagesPerHandle(struct EMMhnd *emmHnd)  // uso della memoria espansa


Circa l'uso di DS nella funzione vedere pag.  ; le macro FP_SEG() e FP_OFF() sono descritte a pag. 

Ad ogni handle può essere associato un nome, mediante la subfunzione 1 del servizio 53h dell'int 67h. I nomi associati agli handle aperti possono essere conosciuti tramite la subfunzione 0 del medesimo servizio.

Int 67h, serv. 53h: Nome dello handle EMS

Input

AH

AL

53h

00h: richiede il nome di uno handle

DX = Numero dello handle EMM.

ES:DI = Indirizzo di un buffer ampio almeno 8 byte, in cui è copiato il nome dello handle.

01h: assegna il nome ad uno handle

DX = Numero dello handle EMM

DS:SI = Indirizzo di un buffer ampio almeno 8 byte, contenente il nome da assegnare allo handle (ASCIIZ

Output

AH

Stato dell'operazione (errore se ; pag.



BARNINGA_Z! - 1991


EMMGHNAM.C - getEMMhandleName()


int cdecl getEMMhandleName(unsigned handle,char *EMMhname);

int EMMhandle; handle EMM di cui si vuole conoscere il nome.

char *EMMhname; buffer di 9 bytes in cui memorizzare il nome.

Restituisce: lo stato dell'operazione. Se < 0, il valore, cambiato di segno,

e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmghnam.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


#include <string.h>


int cdecl getEMMhandleName(int EMMhandle,char *EMMhname)


La getEMMhandleName() utilizza il servizio descritto (subfunzione 0) per conoscere il nome associato allo handle EMMhandle. Il buffer EMMhname deve comprendere 9 byte : nei primi 8 è copiato il nome, mentre l'ultimo è valorizzato a NULL per costruire una normale stringa (circa l'uso di DS vedere pag.  ). In caso di errore è restituito un valore negativo che, cambiato di segno, rappresenta il codice dell'errore EMS: la funzione non potrebbe segnalare l'errore semplicemente copiando in EMMhname una stringa vuota, dal momento che questa è un nome valido. La differente gestione dei puntatori nei diversi modelli di memoria rende necessaria, come già in getEMMusage(), la compilazione condizionale delle istruzioni relative al caricamento dei registri ES:DI con l'indirizzo del buffer.

Del tutto analoga appare la setEMMhandleName(), che assegna un nome ad uno handle tramite la subfunzione 1 del solito int 67h, servizio 53h.



BARNINGA_Z! - 1991


EMMSHNAM.C - setEMMhandleName()


int cdecl setEMMhandleName(unsigned handle,char *EMMhname);

int EMMhandle; handle EMM a cui si vuole assegnare il nome.

char *EMMhname; buffer contenente il nome (stringa chiusa da NULL).

Restituisce: lo stato dell'operazione. Se < 0, il valore, cambiato di segno,

e' il codice di errore EMS. Se e' 0, l'operazione e' stata

eseguita correttamente.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmshnam.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


#include <string.h>


int cdecl setEMMhandleName(int EMMhandle,char *EMMhname)


Le funzioni sin qui presentate (eccetto setEMMhandleName()) consentono esclusivamente di analizzare l'attuale utilizzo della expanded memory: si tratta ora di definire (in breve!) un algoritmo di utilizzo della medesima, per dotarci degli strumenti che ci consentano di sfruttarla attivamente nei nostri programmi. In primo luogo è necessario allocare un certo numero di pagine logiche ad uno handle; in altre parole dobbiamo stabilire quanta memoria espansa ci occorre, tenendo presente che essa è solitamente allocata, per compatitbilità con le prime versioni delle specifiche LIM EMS, in blocchi multipli di 16 Kb (le pagine). Così, se dobbiamo disporre di 40 Kb di expanded memory, è indispensabile allocare 3 pagine logiche. Il driver EMM assegna al gruppo di pagine logiche allocate un numero identificativo (lo handle) al quale è necessario riferirsi per tutte le operazione successivamente effettuate su di esse.

Allocare pagine logiche significa destinarle ad uso esclusivo del programma. L'area di memoria espansa è quindi identificata da due 'coordinate': lo handle e il numero di pagina logica all'interno dell'area stessa . L'allocazione delle pagine logiche è effettuata dal servizio 43h dell'int 67h:

Int 67h, serv. 43h: Alloca pagine logiche nella memoria espansa

Input

AH

BX

43h

numero di pagine logiche che si desidera allocare

Output

AH

DX

Stato dell'operazione (errore se ; pag.

Handle (se AX



BARNINGA_Z! - 1991


EMMALLOC.C - allocEMMpages()


int cdecl allocEMMpages(int pages);

int pages; numero di pagine da allocare.

Restituisce: un intero. Se e' maggiore di zero e' lo handle associato

alle pagine allocate. Se e' = zero si e' verificato un errore

non identificato. Se < 0, il valore, cambiato di segno,

e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmalloc.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl allocEMMpages(int pages)


La allocEMMpages() restituisce lo handle associato alle pagine allocate: si tratta di un numero maggiore di  . Se viene restituito un numero pari o inferiore a zero, si è verificato un errore e le pagine non sono state allocate: il valore zero non è un codice di errore vero e proprio, ma indica una situazione 'strana', in quanto lo handle  è riservato al sistema operativo. Un valore minore di zero, cambiato di segno, rappresenta invece un normale codice di errore EMS. Una tabella dei codici di errore è presentata a pag. 

Come utilizzare le pagine disponibili? Il driver è in grado di effettuare un mapping trasparente delle pagine logiche con le pagine fisiche. Ciò significa che tutte le operazioni effettuate su queste ultime (definite all'interno del primo Mb, dunque indirizzabili mediante comuni puntatori di tipo far o huge) vengono trasferite, senza che il programmatore debba preoccuparsene, sulle pagine logiche poste in corrispondenza (cioè mappate) con esse. Effettuare il mapping di una pagina logica con una pagina fisica significa, in pratica, porre la prima in corrispondenza biunivoca con la seconda: è il driver a riportare nella pagina logica tutte le modifiche apportate dal programma al contenuto della pagina fisica. Vediamo una possibile implementazione del servizio 44h dell'int 67h.

Int 67h, serv. 44h: Effettua il mapping di pagine logiche a pagine fisiche

Input

AH

AL

BX

DX

44h

numero della pagina fisica su cui mappare la pagina logica

numero della pagina logica da mappare su quella fisica

handle associato alla pagina logica (o al gruppo di pagine logiche di cui questa fa parte)

Output

AH

Stato dell'operazione (errore se ; pag.



BARNINGA_Z! - 1991


EMMPGMAP.C - mapEMMpages()


int cdecl mapEMMpages(int physicalPage,int logicalPage,int EMMhandle);

int physicalPage; numero della pag. fisica su cui mappare la pag. logica

int logicalPage; numero della pag. logica da mappare su quella fisica

int EMMhandle; handle associato alla pagina logica

Restituisce: 0 se l'operazione e' stata eseguita correttamente

Se < 0, il valore, cambiato di segno, e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmpgmap.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl mapEMMpages(int physicalPage,int logicalPage,int EMMhandle)


Va ancora precisato che una medesima pagina fisica può essere utilizzata, senza difficoltà operative, per effettuare il mapping di più pagine logiche, purché in momenti diversi. Per fare un esempio concreto, possiamo mappare alla pagina fisica  la pagina logica  associata ad un certo handle e memorizzare in quella pagina fisica i nostri dati, secondo necessità. Successivamente è possibile mappare alla stessa pagina fisica  un'altra pagina logica, associata o no al medesimo handle. I dati che erano presenti nella pagina fisica  non vengono persi, perché il driver, in modo trasparente, riflette le operazioni sulla pagina logica associata, e quindi essi sono memorizzati in quest'ultima. E' facile constatare che effettuando nuovamente il mapping della prima pagina logica dell'esempio alla solita pagina fisica  ritroviamo in quest'ultima i dati originariamente memorizzati.

Confusi? Niente paura, un programmino ci aiuterà a capire tuttavia, dobbiamo ancora discutere un dettaglio: la deallocazione della memoria espansa. E' importante ricordare che i programmi, prima di terminare, devono sempre disallocare esplicitamente la memoria espansa allocata. Il DOS, infatti, non si immischia affatto nella gestione EMS (e, del resto, il driver EMS e il sistema operativo non interagiscono se non nella fase del caricamento del primo da parte del secondo durante bootstrap): tutta la memoria espansa allocata da un programma e dal medesimo non rilasciata è inutilizzabile per tutti gli altri programmi (e per il DOS medesimo) fino al successivo bootstrap della macchina.

Int 67h, serv. 45h: Dealloca le pagine associate ad uno handle

Input

AH

DX

45h

handle associato alla pagina logica (o gruppo di pagine logiche) da deallocare (sono sempre disallocate tutte le pagine logiche associate allo handle)

Output

AH

Stato dell'operazione (errore se ; pag.



BARNINGA_Z! - 1991


EMMFREEH.C - freeEMMhandle()


int cdecl freeEMMhandle(int EMMhandle);

int EMMhandle; handle associato alle pagine logiche da rilasciare.

Restituisce: 0 se l'operazione e' stata eseguita correttamente

Se < 0, il valore, cambiato di segno, e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmfreeh.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl freeEMMhandle(int EMMhandle)


Tutte la pagine associate allo handle passato a freeEMMhandle() sono disallocate e lo handle rilasciato: questo non è più utilizzabile per alcuna operazione EMS. Anche la freeEMMhandle() restituisce se l'operazione è stata eseguita correttamente e, in caso di errore, un valore minore di zero che, cambiato di segno, rappresenta il codice di errore.

E' tempo di affrontare la realtà: di seguito è listato il programma che utilizza alcune delle funzioni presentate sin qui per effettuare operazioni di allocazione, utilizzo e deallocazione della memoria espansa.



EMS.C - Barninga Z! - 1994


Programma di test per alcune funzioni di servizio EMS.

Il sorgente non include i listati delle funzioni.


Compilato con Borland C++ 3.1


bcc ems.c



#pragma warn -pia


#include <stdio.h>

#include <dos.h>

#include <string.h>


int      allocEMMpages(int pages);

int      freeEMMhandle(int EMMhandle);

unsigned getEMMframeAddr(void);

int      getEMMhandleName(int EMMhandle,char *EMMhname);

int      mapEMMpages(int physicalPage,int logicalPage,int EMMhandle);

int      setEMMhandleName(int EMMhandle,char *EMMhname);


void main(void)



frameSeg = getEMMframeAddr();

physicalPage0 = (char far *)MK_FP(frameSeg,0);

printf('Page Frame Address: %Fpn',physicalPage0);

for(i = 0; i < 2; i++)

if(retcode = setEMMhandleName(EMMhandles[i],EMMhandleNames[i]))

}

if(i < 2)

for(i = 0; i < 2; i++)

if(retcode = getEMMhandleName(EMMhandles[i],strBuf))

printf('Error %X getting name of handle %d.n',-retcode,EMMhandles[i]);

else

printf('Handle %d name: %sn',EMMhandles[i],buffer);

if(retcode = mapEMMpages(0,0,EMMhandles[0]))

printf('Error %X mapping Log. page %d of handle %d to Phys. page %d.n',

-retcode,0,EMMhandles[0],0);

_fstrcpy(physicalPage0,'@@@ A String For Logical Page 0 @@@');

if(retcode = mapEMMpages(0,1,EMMhandles[1]))

printf('Error %X mapping Log. page %d of handle %d to Phys. page %d.n',

-retcode,1,EMMhandles[1],0);

_fstrcpy(physicalPage0,'XXXXXXXXXXXXXXXXXXXXX');

if(retcode = mapEMMpages(0,0,EMMhandles[0]))

printf('Error %X mapping Log. page %d of handle %d to Phys. page %d.n',

-retcode,0,EMMhandles[0],0);

_fstrcpy(strBuf,physicalPage0);

printf('Logical Page 0 content: %sn',strBuf);

for(i = 0; i < 2; i++)

if(retcode = freeEMMhandle(EMMhandles[i]))

printf('Error %X deallocating EMM pages of handle %d.n',-retcode,

EMMhandles[i]);


In testa al programma sono dichiarati i prototipi delle funzioni descritte in precedenza (si presume che esse si trovino, già compilate, in un object file o una libreria da specificare in fase di compilazione e linking). La prima operazione svolta da main() consiste nel chiamare la getEMMframeAddr() per conoscere l'indirizzo di segmento della page frame. Dato che le quattro pagine fisiche in cui essa è suddivisa sono numerate in ordine crescente a partire dalla sua 'origine', l'indirizzo della page frame coincide con quello della pagina fisica  . Si tratta, ovviamente, di un indirizzo del tipo seg:off (vedere pag.  ), che viene ottenuto 'accostando', mediante la macro MK_FP() definita in DOS.H (pag.  ), un offset pari a zero al segmento restituito da getEMMframeAddr()

Il ciclo for successivo gestisce l'allocazione di due blocchi di memoria espansa, ciascuno costituito di  pagine logiche;  è infatti il parametro passato a allocEMMpages(), che, ad ogni chiamata, restituisce lo handle associato al singolo blocco di pagine logiche. Gli handle sono memorizzati negli elementi dell'array di interi EMMhandles. Se il valore restituito è minore o uguale a  si è verificato un errore: viene visualizzato un apposito messaggio e il ciclo di allocazione è interrotto (break). Nello stesso ciclo ad ogni handle allocato è assegnato un nome, mediante la setEMMhandleName(): i nomi (normali stringhe) per gli handle sono contenuti nell'array EMMhandleNames; anche in questo caso la restituzione di un codice di errore determina l'interruzione del ciclo. Se in uscita dal ciclo i è minore di  , significa che esso è stato interrotto (da un errore): il programma termina, ma prima vengono rilasciati gli handle eventualmente allocati (di fatto, se vi è stato un errore, gli handle allocati sono uno solo, o nessuno).

Il flusso elaborativo prosegue con un altro ciclo for, all'interno del quale sono visualizzati, ricavandoli tramite la getEMMhandleNames(), i nomi precedentemente assegnati agli handle; va inoltre precisato che non è affatto necessario assegnare un nome ad ogni handle per utilizzare le pagine logiche ad esso associate. Il nostro programma non perde alcuna delle sue (strabilianti) funzionalità, pur eliminando le chiamate a setEMMhandleNames() e getEMMhandleNames(). Va ancora precisato che una chiamata a getEMMhandleNames() per richiedere il nome di uno handle che ne è privo non determina un errore, ma semplicemente l'inizializzazione a stringa vuota del buffer il cui indirizzo le è passato come parametro (EMMhname

Siamo così giunti alla parte più interessante del listato, quella, cioè, in cui vengono finalmente effettuati il mapping delle pagine logiche ed il loro utilizzo effettivo.

La prima chiamata a mapEMMpages() mappa la pagina logica  sulla pagina fisica  . Da questo momento tutte le operazioni effettuate sulla pagina fisica  (la prima delle quattro in cui è suddivisa la page frame) si riflettono automaticamente su quella pagina logica. Scopo del programma è proprio verificare quanto appena affermato: si tratta di scrivere qualcosa nella pagina fisica  , mappare ad essa un'altra pagina logica, in cui scrivere altri dati, e poi mappare nuovamente la pagina logica attualmente associata, per verificare se i dati originariamente scritti sono ancora lì. In effetti, quelle descritte sono proprio le operazioni che il programma esegue.

La prima chiamata a _fstrcpy() copia nella pagina fisica  la stringa '@@@ A String For Logical Page 0 @@@'. Immediatamente dopo, la seconda chiamata a mapEMMpages() mappa alla solita pagina fisica  la seconda ( ) delle due ( ) pagine logiche associate al secondo handle (EMMhandles[1]) e la _fstrcpy() scrive nella pagina fisica  la stringa 'XXXXXXXXXXXXXXXXXXXXX', che viene in realtà scritta nella pagina logica appena mappata. Il dubbio che questa operazione di scrittura possa sovrascrivere la stringa precedente (copiata allo stesso indirizzo fisico, ma non allo stesso indirizzo logico) è fugato dalle operazioni immediatamente seguenti.

Il mapping effettuato dalla terza chiamata a mapEMMpages() associa nuovamente alla pagina fisica  la prima pagina logica ( ) delle due ( ) allocate al primo handle (EMMhandles[0]). Questa volta _fstrcpy() copia nel buffer strBuf, che è near, la stringa che si trova all'inizio della pagina fisica  . L'operazione è necessaria perché printf(), nell'ipotesi di compilare il programma con modello di memoria tiny, small o medium (pag.  ) non può accettare come parametro l'indirizzo far della page frame. Il momento della verità è giunto: printf() visualizza '@@@ A String For Logical Page 0 @@@', proprio come previsto.

Il programma si chiude con un ciclo che chiama freeEMMhandle(), per rilasciare tutta la memoria espansa allocata, al fine di renderla nuovamente disponibile ai processi eseguiti successivamente.

Non crediate di cavarvela così facilmente: i guai, con la memoria EMS, non finiscono qui. Il programma presentato poco fa lavora nella presunzione, peraltro fondata, che nessuno gli mescoli le carte in tavola: in altre parole assume che la situazione di mapping non venga modificata da eventi esterni. In realtà ciò può avvenire in almeno due situazioni: se il programma lancia un'altra applicazione che utilizza memoria EMS oppure se è un TSR (pag.  ) a farne un uso poco corretto . Va da sé che nel caso di un TSR o un device driver 'maleducato' c'è poco da fare, ma se sappiamo in partenza che un evento generato dal nostro programma può modificare la mappatura delle pagine EMS è meglio correre ai ripari, salvando lo stato della mappatura delle pagine logiche su quelle fisiche (page map ). Niente di tremendo: l'int 67h mette a disposizione un servizio progettato proprio per questo.

Int 67h, serv. 4Eh: Salva e ripristina la page map

Input

AH

AL


4Eh

00h salva la page map

    ES:DI = punta a un buffer in cui salvare la page map

01h rispristina la page map

    DS:SI = punta a un buffer da cui caricare la page map

02h salva page map e ne carica una salvata precedentemente

    DS:SI = punta a un buffer da cui caricare la page map

    ES:DI = punta a un buffer in cui salvare la page map

03h restituisce la dimensione del buffer per la page map

Output

AH

AL

Stato dell'operazione (errore se ; pag.

Solo per subfunzione 03h: dimensione in byte del buffer necessario a contenere la page map

Ecco alcuni esempi di funzioni basate sul servizio 4Eh dell'int 67h:



BARNINGA_Z! - 1991


EMMGPMD.C - getEMMpageMapDim()


int cdecl getEMMpageMapDim(void);

Restituisce: Se > 0 è la dimensione in bytes dell'array necessario a

contenere la page map

Se < 0, il valore, cambiato di segno, e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmgpmd.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl getEMMpageMapDim(void)


La getEMMpageMapDim() deve essere chiamata per conoscere la dimensione del buffer che deve contenere la page map; questo può essere allocato, ad esempio, tramite malloc(), prima di chiamare la saveEMMpageMap(), listata di seguito (circa l'uso di DS vedere pag. 



BARNINGA_Z! - 1991


EMMSPM.C - saveEMMpageMap(unsigned char *destBuf)


int cdecl saveEMMpageMap(unsigend char *destBuf);

unsigned char *destBuf; buffer che conterra' la page map.

Restituisce: Se 0 l'operazione è stata eseguita correttamente

Se < 0, il valore, cambiato di segno, e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmspm.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl saveEMMpageMap(unsigned char *destBuf)


Dopo avere salvato la page map il programma può lanciare il child process , senza preoccuparsi di come esso utilizza la memoria EMS. Al rientro è sufficiente chiamare la restoreEMMpageMap() per riportare lo stato del driver alla situazione salvata.



BARNINGA_Z! - 1991


EMMRPM.C - restoreEMMpageMap(unsigned char *sourceBuf)


int cdecl saveEMMpageMap(unsigned char *sourceBuf);

unsigned char *sourceBuf; buffer che contiene la page map da riattivare.

Restituisce: Se 0 l'operazione è stata eseguita correttamente

Se < 0, il valore, cambiato di segno, e' il codice di errore EMS.


COMPILABILE CON TURBO C++ 2.0


tcc -O -d -c -mx emmrpm.c


dove -mx puo' essere -mt -ms -mc -mm -ml -mh



#pragma inline


int cdecl restoreEMMpageMap(unsigned char *sourceBuf)


Per un esempio di funzione basata sulla subfunzione 02h, vedere pag. 

Con la versione 4.0 dello standard LIM sono state introdotte interessanti funzionalità di trasferimento dati tra memoria espansa e aree di memoria convenzionale, rendendo così possibile 'scavalcare' la page frame. Il servizio che implementa tale funzionalità è il 57h; la subfunzione 00h consente di trasferire dati direttamente dalla memoria convenzionale alla memoria EMS e richiede che i registri DS:SI siano caricati con l'indirizzo di un buffer contenente le informazioni necessarie per effettuare il trasferimento. La subfunzione 01h del medesimo servizio permette invece di scambiare il contenuto di due aree di memoria, che possono essere indifferentemente situate in memoria convenzionale o espansa.

Int 67h, serv. 57h: Trasferisce da mem. convenz. a mem. EMS e viceversa

Input

AH

AL


57h

00h trasferisce da memoria convenzionale a memoria EMS

    DS:SI = punta al buffer di info per il trasferimento

01h scambia il contenuto di due aree di memoria

    DS:SI = punta al buffer di info per lo scambio

Output

AH

Stato dell'operazione (errore se ; pag.

Il formato del buffer richiesto dal servizio analizzato è il seguente:

Formato del buffer utilizzato dall'int 67h, serv. 57h

OFFSET

byte

DESCRIZIONE

00h


Lunghezza in byte dell'area di memoria da trasferire

04h


Tipo della memoria sorgente (  = convenzionale;  = EMS)

05h


Handle EMS sorgente ( se memoria convenzionale)

07h


Offset dell'area sorgente (rispetto al segmento se memoria convenzionale; rispetto alla pagina logica se memoria EMS)

09h


Segmento dell'area sorgente (se memoria convenzionale) o pagina logica (se memoria EMS)

0Bh


Tipo della memoria destinazione (  = convenzionale;  = EMS)

0Ch


Handle EMS destinazione ( se memoria convenzionale)

0Eh


Offset dell'area destinazione (rispetto al segmento se memoria convenzionale; rispetto alla pagina logica se memoria EMS)

10h


Segmento dell'area destinazione (se memoria convenzionale) o pagina logica (se memoria EMS)

Il buffer può essere facilmente rappresentato con una struttura:

struct EMMmove ;

L'esempio seguente copia direttamente 32 Kb dal buffer video colore (B800:0000) ad offset  nella terza pagina del blocco EMS allocato allo handle 09h

#include <dos.h>

.

struct REGPACK r;

struct EMMmove EMMinfoBuf;

.

EMMinfoBuf.length = 32*1024; // 32 Kb

EMMinfoBuf.sourceType = 0; // sorgente: memoria convenzionale

EMMinfoBuf.sourceHandle = 0; // handle sempre 0 per mem. convenz.

EMMinfoBuf.sourceOffset = 0; // segmento:offset

EMMinfoBuf.sourceSegment = 0xB800; // B800:0000

EMMinfoBuf.destType = 1; // destinazione: memoria EMS

EMMinfoBuf.destHandle = 0x09; // handle (da int 67h, servizio 43h)

EMMinfoBuf.destOffset = 256; // offset all'interno della pag.logica

EMMinfoBuf.destSegment = 2; // terza pag.logica

r.r_ax = 0x5700;

r.r_ds = FP_SEG((struct EMMmove far *)&EMMinfoBuf);

r.r_si = (unsigned)&EMMinfoBuf;

intr(0x67,&r);

if(r.r_ax & 0xFF00)

La EMMmoveMem() riceve due parametri: il primo è il puntatore alla struttura EMMmove; il secondo è un intero che, a seconda del valore assunto, stabilisce se debba essere effettuata un'operazione di spostamento del contenuto di un'area di memoria (operation ) o di scambio del contenuto di due aree (operation ). La funzione restituisce  se non si è verificato alcun errore, mentre è restituito  se il parametro operation contiene un valore illecito; la restituzione di un valore maggiore di  indica che si è verificato un errore EMS (del quale il valore stesso rappresenta il codice).

Le specifiche LIM 4.0 hanno introdotto un altro servizio degno di nota: il mapping di pagine multiple. Il servizio 44h dell'int 67h, descritto poco sopra, si limita ad effettuare il mapping di una sola pagina logica su una pagina fisica. Se il programma deve agire su una quantità di dati superiore ai 16 Kb è necessario richiamare più volte il servizio stesso, ad esempio all'interno di un ciclo, per mappare tutte le pagine necessarie (al massimo 4). La subfunzione 00h del servizio 50h supera detta limitazione e consente di mappare in un'unica operazione fino a 4 pagine logiche, non necessariamente consecutive, su 4 diverse pagine fisiche, anch'esse non consecutive. La subfunzione 01h consente di effettuare il mapping delle pagine logiche direttamente su aree di memoria convenzionale, senza necessità di utilizzare la page frame (pagine fisiche).

Int 67h, serv. 50h: Mapping multiplo e su memoria convenzionale

Input

AH

AL

CX

DX

50h

00h mapping di più pagine logiche su più pagine fisiche

    DS:SI = punta al buffer di info per il mapping

01h mapping di più pagine logiche su memoria convenzionale

    DS:SI = punta al buffer di info per il mapping

Numero di pagine da mappare

Handle delle pagine logiche

Output

AH

Stato dell'operazione (errore se != 0; pag. 169).

Il buffer di informazioni è una sequenza di 4 coppie di word (unsigned int). Per la subfunzione 00h, in ogni coppia di word il primo unsigned rappresenta il numero della pagina logica da mappare, mentre il secondo contiene quello della pagina fisica su cui deve avvenire il mapping. Esempio:

#include <dos.h>

.

    struct REGPACK r;

    unsigned info[8];

    .

    info[0] = 4;                                // mappare la pag. logica 4

    info[1] = 0;                                     // sulla pag. fisica 0

    info[2] = 6;                                // mappare la pag. logica 6

    info[3] = 3;                                     // sulla pag. fisica 3

    info[4] = 7;                                // mappare la pag. logica 7

    info[5] = 1;                                     // sulla pag. fisica 1

    r.r_ax = 0x5000;                       // servizio 50h, subfunzione 00h

    r.r_cx = 3;                                        // 3 pagine in tutto

    r.r_dx = 09h;               // handle delle pag. logiche (da serv. 43h)

    r.r_ds = FP_SEG((int far *)info);

    r.r_si = (unsigned)info;

    intr(0x67,&r);

    if(r.r_ax & 0xFF00)

La getXMMaddress() restituisce un puntatore a funzione void, che rappresenta l'entry point del driver XMM.

A differenza dei servizi EMM, gestiti tramite un interrupt (l'int 67h), quelli resi disponibili dal driver XMM sono accessibili invocando direttamente la routine che si trova all'entry point del driver, dopo avere opportunamente caricato i registri. In termini di assembler si tratta di eseguire una CALL; chi preferisce il C può utilizzare l'indirezione del puntatore all'entry point stesso. Alcuni esempi serviranno a chiarire le idee.

Servizio XMS 00h: Versione del driver XMM e esistenza DELLA HMA

Input

AH

00h

Output

AH

AL

BH

BL

DX

Versione XMS supportata.

Revisione XMS supportata.

Versione del driver XMM.

Revisione del driver XMM.

1 se esiste HMA, 0 altrimenti

Note

Se in uscita AX = 0, si è verificato un errore. In tal caso BL contiene il numero di errore XMS (vedere pag. 169).

/********************

    BARNINGA_Z! - 1992

    XMMVERS.C - getXMMversion()

    int cdecl getXMMversion(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver.

    Restituisce: un intero il cui byte meno significativo rappresenta la versione

                 XMS supportata e quello piu' significativo la revisione;

                 Se < 0 e' il codice di errore XMS cambiato di segno.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmmvers.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl getXMMversion(void (far *XMMdriver)(void))

La getXMMversion() restituisce un unsigned integer, il cui byte meno significativo rappresenta la versione, e quello più significativo la revisione[39] della specifica XMS supportata. Se il valore restituito è minore di 0 si è verificato un errore: detto valore è il codice di errore XMS cambiato di segno. L'indirezione del puntatore all'entry point del driver trasferisce ad esso il controllo: dal momento che il parametro XMMdriver rappresenta proprio quell'indirizzo, l'istruzione C

    (*XMMdriver)();

è equivalente allo inline assembly

    asm call dword ptr XMMdriver;

Circa i puntatori a funzione vedere pag. 95.

/********************

    BARNINGA_Z! - 1992

    XMMDVERS.C - getXMMdrvVersion()

    int cdecl getXMMdrvVersion(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver.

    Restituisce: un intero il cui byte meno significativo rappresenta la versione

                 del driver XMM e quello piu' significativo la revisione;

                 Se < 0 e' il codice di errore XMS cambiato di segno.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmmdvers.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl getXMMdrvVersion(void (far *XMMdriver)(void))

Anche la getXMMdrvVersion() restituisce un unsigned int, il cui byte meno significativo rappresenta la versione, e quello più significativo la revisione[40] del gestore XMM. Se il valore restituito è minore di 0 si è verificato un errore: detto valore è il codice di errore XMS cambiato di segno.

Il servizio XMS 08h consente di conoscere la quantità di memoria XMS libera.

Servizio XMS 08h: Quantità di memoria XMS disponibile

Input

AH

08h

Output

AX

DX

La dimensione in Kb del maggiore blocco libero.

La quantità totale, in Kb, di memoria XMS libera.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

La quantità di memoria XMS libera non include mai la HMA, neppure nel caso in cui questa non sia allocata.

/********************

    BARNINGA_Z! - 1992

    XMSFREEM.C - getXMSfreeMem()

    long getXMSfreeMem(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    Restituisce: La quantità di memoria XMS libera, in Kilobytes.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmsfreem.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

long cdecl getXMSfreeMem(void (far *XMMdriver)(void))

/********************

    BARNINGA_Z! - 1992

    XMSFREEB.C - getXMSfreeBlock()

    long getXMSfreeBlock(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    Restituisce: la dimensione in Kb del maggiore blocco XMS disponibile;

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmsfreeb.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

long cdecl getXMSfreeBlock(void (far *XMMdriver)(void))

Non esiste un servizio XMS per determinare la quantità totale di memoria estesa installata sulla macchina; essa è sempre almeno pari ai Kb liberi ai quali vanno sommati, se la HMA esiste, altri 64 Kb. La dimensione della memoria XMS può essere maggiore di quella fisicamente presente sulla macchina nel caso in cui sia attivo un ambiente in grado di creare memoria virtuale (vedere pag. 218).

La quantità di memoria estesa fisica (la memoria realmente installata sulla macchina) è la word memorizzata nel CMOS[41] ad offset 17h: ecco un suggerimento per conoscerla.

/********************

    BARNINGA_Z! - 1992

    EXTINST.C - getEXTinstalled()

    unsigned getEXTinstalled(void);

    Restituisce: il numero di Kb di memoria estesa fisica installati.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- extinst.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

unsigned cdecl getEXTinstalled(void)

    return(_AX);

}

La quantità di memoria estesa installata e non gestita da un driver XMM è conoscibile attraverso l'int 15h[42].

Int 15h, Serv. 88h: Memoria estesa (non XMS) disponibile

Input

AH

88h

Output

AX

La quantità in Kb di memoria estesa installata non gestita da un driver XMM.

Note

In caso di errore CarryFlag = 1; il valore in AX non è significativo.

/********************

    BARNINGA_Z! - 1992

    EXTFREE.C - getEXTfree()

    unsigned getEXTfree(void);

    Restituisce: il numero di Kb di memoria estesa installati e attualmente

                 non sotto il controllo di un driver XMM.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- extfree.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

unsigned cdecl getEXTfree(void)

Il valore restituito da getEXTfree() è sempre minore o uguale di quello restituito da getEXTinstalled(), salvo il caso in cui il sistema utilizzi memoria virtuale.

Quando si conosca la quantità di memoria XMS libera, l'operazione preliminare al suo utilizzo è l'allocazione di un Extended Memory Block (EMB), il quale altro non è che un'area di memoria XMS, della dimensione desiderata, alla quale il driver associa uno handle di riferimento: l'analogia con i servizi EMS (pag. 169) è evidente.

Servizio XMS 09h: Alloca un blocco di memoria XMS

Input

AH

DX

09h

La dimensione del blocco, in Kilobyte

Output

AX

DX

1 se l'allocazione è riuscita correttamente.

Lo handle associato al blocco.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Vediamo un esempio di funzione basata sul servizio 09h.

/********************

    BARNINGA_Z! - 1992

    EMBALLOC.C - allocEMB()

    int allocEMB(void (far *XMMdriver)(void),unsigned EMBkb);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned EMBkb;                dimensione in Kb del blocco richiesto.

    Restituisce: Lo handle associato al blocco allocato.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- emballoc.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl allocEMB(void (far *XMMdriver)(void),unsigned EMBkb)

La allocEMB() tenta di allocare un Extended Memory Block della dimensione (in Kb) specificata con il parametro EMBkb. Se l'operazione riesce viene restituito lo handle che il driver XMS ha associato al blocco, altrimenti il valore restituito, cambiato di segno, è il codice di errore XMS.

Per copiare dati dalla memoria convenzionale ad un EMB o viceversa è disponibile il servizio XMS 0Bh, che può essere utilizzato anche per copiare dati da un EMB ad un secondo EMB, nonché tra due indirizzi in memoria convenzionale: l'operazione desiderata è infatti descritta al driver tramite un buffer composto di 5 campi, il primo dei quali indica la lunghezza in byte dell'area da copiare; i campi successivi possono essere suddivisi in due coppie (una per l'area sorgente ed una per l'area destinazione), in ciascuna delle quali il significato del secondo campo dipende dal valore contenuto nel primo. In particolare, se il primo campo contiene un valore diverso da 0, questo è interpretato come handle di un EMB, e dunque il secondo campo della coppia indica l'offset lineare a 32 bit all'interno dell'EMB. Se, al contrario, il primo campo è 0, allora il secondo è interpretato come un normale indirizzo far a 32 bit in memoria convenzionale. Valorizzando opportunamente i campi è possibile richiedere operazioni di copia in qualsiasi direzione.

Servizio XMS 0Bh: Copia tra aree di memoria convenzionale o XMS

Input

AH

DS:SI

0Bh

Buffer descrittivo dell'operazione (vedere tabella seguente)

Output

AX

1 se l'operazione di copia è riuscita correttamente.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Il formato del buffer richiesto dal servizio è il seguente:

Formato del buffer utilizzato dal servizio XMS 0Bh

OFFSET

byte

DESCRIZIONE

00h

4

Lunghezza in byte dell'area di memoria da trasferire (deve essere un numero pari)

04h

2

Handle XMS sorgente (0 se memoria convenzionale)

06h

4

Se il campo precedente è 0 questo campo è un puntatore far ad un'area di memoria convenzionale; se invece il campo predecente è diverso da 0, questo campo rappresenta un offset lineare a 32 bit nel blocco di memoria estesa associato allo handle. In entrambi i casi l'indirizzo è inteso come sorgente.

0Ah

2

Handle XMS destinazione (0 se memoria convenzionale)

0Ch

4

Se il campo precedente è 0 questo campo è un puntatore far ad un'area di memoria convenzionale; se invece il campo predecente è diverso da 0, questo campo rappresenta un offset lineare a 32 bit nel blocco di memoria estesa associato allo handle. In entrambi i casi l'indirizzo è inteso come destinazione.

Il buffer può essere rappresentato da una struttura:

struct EMBmove ;

La struttura EMBmove è il secondo parametro della funzione XMSmoveMem():

/********************

    BARNINGA_Z! - 1992

    XMSMOVM.C - XMSmoveMem()

    int XMSmoveMem(void (far *XMMdriver)(void),struct EMBmove *EMBbuffer);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    struct EMBmove *EMBbuffer;     puntatore al buffer che descrive l'operazione.

    Restituisce: 0 se l'operazione e' stata eseguita correttamente.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmsmovm.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl XMSmoveMem(void (far *XMMdriver)(void),struct EMBmove *EMBbuffer)

L'esempio che segue utilizza la XMSmoveMem() per copiare 96 Kb da memoria convenzionale a memoria estesa. I dati sono copiati ad offset 10000h nell'EMB.

    .

    unsigned char far *convMemPtr;

    void (far *XMMdriver)(void);    // puntatore all'entry point del driver XMM

    struct EMBmove EMBbuffer;

    unsigned EMBhandle;

    .

    EMBbuffer.length = 96 * 1024;        // lunghezza in bytes dell'area da copiare

    EMBbuffer.srcHandle = 0;              // sorgente memoria convenzionale

    EMBbuffer.srcOffset = (long)convMemPtr;   // indirizzo sorgente in mem. conv.

    EMBbuffer.dstHandle = EMBhandle;            // restituito da allocEMB()

    EMBbuffer.dstOffset = 0x10000; // offset nell'EMB associato a EMBhandle

    if(XMSmoveMem(XMMdriver,&EMBbuffer) < 0)

        .                                         // gestione errore XMS

Si tenga presente che il servizio XMS 0Bh non garantisce che la copia sia effettuata correttamente se l'area sorgente e l'area destinazione si sovrappongono anche parzialmente e l'indirizzo della prima è maggiore dell'indirizzo della seconda. Inoltre non è necessario abilitare la A20 line per utilizzare il servizio.

Prima di terminare i programmi devono esplicitamente liberare gli EMB eventualmente allocati e la HMA se utilizzata, al fine di renderli nuovamente disponibili agli altri processi. Il DOS non interagisce con il driver XMM nella gestione della memoria estesa, pertanto in uscita dai programmi non effettua alcuna operazione di cleanup riguardante gli EMB e la HMA: se non rilasciati dal processo che termina, questi risultano inutilizzabili da qualsiasi altro programma sino al reset della macchina (la situazione è analoga a quella descritta con riferimento ai servizi EMS: vedere pag. 169).

Servizio XMS 0Ah: Dealloca uno handle XMS

Input

AH

DX

0Ah

Handle XMS da deallocare

Output

AX

1 se la disallocazione è riuscita correttamente. Tutta la memoria estesa a cui è associato lo handle è liberata.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Segue, come sempre, un esempio di funzione basata sul servizio appena descritto.

/********************

    BARNINGA_Z! - 1992

    EMBFREE.C - freeEMB()

    int freeEMB(void (far *XMMdriver)(void),unsigned EMBhandle);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned EMBhandle;            handle EMB da deallocare.

    Restituisce: 0 se l'operazione è stata eseguita con successo.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- embfree.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl freeEMB(void (far *XMMdriver)(void),unsigned EMBhandle)

Anche in questo caso è restituito 0 se l'operazione è stata eseguita correttamente, mentre un valore minore di 0 segnala che si è verificato un errore XMS e ne rappresenta, al tempo stesso, il codice cambiato di segno.

Vale ancora la pena di aggiungere che è possibile richiedere la modifica della dimensione di un EMB mediante il servizio XMS 0Fh:

Servizio XMS 0Fh: Modifica la dimensione di un EMB

Input

AH

BX

DX

0Fh

Nuova dimensione richiesta in Kilobyte

Handle XMS associato all'EMB

Output

AX

1 se la disallocazione è riuscita correttamente. Tutta la memoria estesa a cui è associato lo handle è liberata.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Segue esempio:

/********************

    BARNINGA_Z! - 1992

    EMBRESIZ.C - resizeEMB()

    int resizeEMB(void (far *XMMdriver)(void),unsigned EMBhandle,unsigned newKb);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned EMBhandle;            handle XMS associato all'EMB da modificare.

    unsigned newKb;                nuova dimensione desiderata, in Kilobytes.

    Restituisce: 0 se l'operazione è stata eseguita con successo.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- embresiz.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl resizeEMB(void (far *XMMdriver)(void),unsigned EMBhandle,unsigned newKb)

La resizeEMB() restituisce un valore secondo criteri coerenti con quelli adottati dalle funzioni sopra listate[43].

I servizi XMS per la HMA

Una parte dei servizi XMS è implementa la gestione della High Memory Area. Vediamo i listati di alcune funzioni.

/********************

    BARNINGA_Z! - 1992

    XMMISHMA.C - isHMA()

    int cdecl isHMA(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver.

    Restituisce: 1  HMA esistente

                 0  HMA non esistente

                 Se < 0 e' il codice di errore XMS cambiato di segno.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- xmmishma.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl isHMA(void (far *XMMdriver)(void))

La isHMA() restituisce 1 se la HMA è presente, 0 in caso contrario. Un valore minore di 0 rappresenta il codice di errore XMS.

In caso di esistenza della HMA è possibile determinare se essa è allocata o libera tentando di allocarla: se l'operazione ha successo la HMA era disponibile (occorre allora disallocarla per ripristinare la situazione iniziale).

Servizio XMS 01h: Alloca la HMA (se disponibile)

Input

AH

DX

01h

Byte necessari al programma nella HMA (valori possibili 0‑FFF0).

Output

AX

BL

1 se la HMA è stata allocata con successo. In caso di fallimento, AX è 0 e BL contiene il codice di errore: se BL = 91h, la HMA è già allocata (vedere pag. 169).

Note

Il numero di byte specificato in DX viene confrontato con il valore attribuito al parametro /HMAMIN sulla riga di comando del driver XMM: se è inferiore a quest'ultimo la HMA non viene allocata neppure se libera, in caso contrario è allocata tutta la HMA.

Circa il servizio XMS 01h, va precisato che, secondo la documentazione ufficiale, non è possibile dividere la HMA in subaree: essa è allocata interamente, anche se sono richiesti meno di 64Kb. Esiste però un servizio non documentato[44] dell'int 2Fh, supportato dal driver HIMEM.SYS del DOS, che dispone di due subfunzioni utili per allocare la parte libera di HMA quando questa sia già allocata:

Int 2Fh, serv. 4Ah: Gestisce la porzione libera di una HMA già allocata

Input

AH

AL

4Ah

01h  richiede quanti byte sono ancora liberi nella HMA

02h  alloca spazio nella HMA

     BX  numero di byte richiesti

Output

ES:DI

BX

Indirizzo del primo byte libero nella HMA (FFFF:FFFF in caso di errore).

Numero di byte liberi in HMA (solo subfunzione 01h).

Note

Questo servizio è disponibile solo se il DOS è caricato in HMA: in caso contrario viene restituito 0 in BX e FFFF:FFFF in ES:DI.

Servizio XMS 02h: Dealloca la HMA

Input

AH

02h

Output

AX

BL

1 se la HMA è stata liberata con successo. In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

codice di errore

Note

Il programma che alloca la HMA deve disallocarla prima di restituire il controllo al sistema; in caso contrario la HMA rimane inutilizzabile fino al primo bootstrap.

/********************

    BARNINGA_Z! - 1992

    ISHMAFRE.C - isHMAfree()

    int isHMAfree(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    Restituisce: 1 se la HMA e' libera;

                 0 se gia' allocata;

                 Se < 0 e' il codice di errore XMS cambiato di segno.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- ishmafre.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl isHMAfree(void (far *XMMdriver)(void))

La isHMAfree() restituisce 0 se la HMA è già allocata o, al contrario, 1 se è libera. In caso di errore è restituito un valore negativo che rappresenta, cambiato di segno, il codice di errore XMS.

E' facile, 'spezzando' la isHMAfree(), realizzare due funzioni in grado di allocare la HMA (HMAalloc()) e, rispettivamente, disallocarla (HMAdealloc()).

/********************

    BARNINGA_Z! - 1992

    HMAALLOC.C - HMAalloc()

    int *HMAalloc(void (far *XMMdriver)(void),unsigned nBytes);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned nBytes;               numero di bytes da allocare.

    Restituisce: 0 se l'allocazione e' stata effettuata con successo;

                 Se < 0 e' il codice di errore XMS

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- hmaalloc.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl HMAalloc(void (far *XMMdriver)(void),unsigned nBytes)

/********************

    BARNINGA_Z! - 1992

    HMADEALL.C - HMAdealloc()

    int HMAdealloc(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    Restituisce: 0 se la HMA e' stata deallocata con successo;

                 Se < 0 e' il codice di errore XMS cambiato di segno.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- hmadeall.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl HMAdealloc(void (far *XMMdriver)(void))

Lo stato della A20 Line, attraverso la quale è possibile indirizzare la HMA, può essere indagato tramite il servizio 07h del driver XMM.

Servizio XMS 07h: Stato della A20 Line

Input

AH

07h

Output

AX

BL

1 se la A20 Line è abilitata, 0 se non lo è (in questo caso anche BL = 0). In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

codice di errore

/********************

    BARNINGA_Z! - 1992

    ISA20ON.C - isA20enabled()

    int cdecl isA20enabled(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);     puntatore all'entry point del driver XMM.

    Restituisce: 1 se la A20 line e' abilitata;

                 0 se la A20 line e' disabilitata;

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- isa20on.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl isA20enabled(void (far *XMMdriver)(void))

La isA20enabled() restituisce 1 se la A20 line è abilitata, 0 se non lo è. In caso di errore è restituito un valore negativo che, cambiato di segno rappresenta il codice di errore XMS.

Per utilizzare la HMA occorre che la linea A20 sia abilitata; i servizi XMS 03h e 04h la abilitano e disabilitano specificamente per consentire l'accesso alla HMA[45].

Servizio XMS 03h: Abilita la A20 Line

Input

AH

03h

Output

AX

1 se la A20 Line è stata attivata correttamente.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Questo servizio deve essere utilizzato solo se il programma ha allocato con successo la HMA mediante il servizio 01h.

Servizio XMS 04h: Disabilita la A20 Line

Input

AH

04h

Output

AX

1 se la A20 Line è stata disattivata correttamente.

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Questo servizio deve essere utilizzato solo se il programma ha allocato con successo la HMA mediante il servizio 01h.

I due esempi di funzione che seguono utilizzano i servizi testè descritti.

/********************

    BARNINGA_Z! - 1992

    A20ENABL.C - enableA20()

    int cdecl enableA20(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);     puntatore all'entry point del driver XMM.

    Restituisce: 0 se la A20 line e' stata abilitata con successo;

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- a20enabl.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl enableA20(void (far *XMMdriver)(void))

/********************

    BARNINGA_Z! - 1992

    A20DISAB.C - disableA20()

    int cdecl disableA20(void (far *XMMdriver)(void));

    void (far *XMMdriver)(void);     puntatore all'entry point del driver XMM.

    Restituisce: 0 se la A20 line e' stata disabilitata con successo;

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- a20disab.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl disableA20(void (far *XMMdriver)(void))

Le funzioni enableA20() e disableA20() restituiscono 0 se hanno eseguito correttamente il loro compito; in caso di errore è restituito un valore negativo che rappresenta, cambiato di segno, il codice di errore XMS.

I servizi XMS per gli UMB

Riportiamo la descrizione dei servizi XMS (e gli esempi relativi) che consentono di allocare e deallocare gli Upper Memory Block. Per ulteriori e più approfonditi particolari circa gli UMB vedere pag. 169 e seguenti.

Servizio XMS 0Fh: Alloca un UMB

Input

AH

DX

10h

Dimensione richiesta in paragrafi (blocchi di 16 byte)

Output

AX

BX

DX

1 se la disallocazione è riuscita correttamente.

Indirizzo di segmento dell'UMB allocato

Dimensione reale in paragrafi

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169), mentre DX contiene la dimensione in paragrafi del massimo UMB disponibile.

Segue esempio:

/********************

    BARNINGA_Z! - 1992

    UMBALLOC.C - allocUMB()

    int allocUMB(void (far *XMMdriver)(void),unsigned UMBKb,unsigned *UMBseg);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned UMBKb;                dimensione desiderata, in paragrafi.

    unsigned *UMBseg;              usata per restituire l'indirizzo di segmento

                                   dell'UMB allocato. In caso di errore contiene la

                                   dimensione del massimo UMB disponibile.

    Restituisce: 0 se l'operazione e' stata eseguita con successo. All'indirizzo

                 UMBseg e' memorizzato l'indirizzo di segmento dell'UMB allocato;

                 Se < 0 e' il codice di errore XMS; All'indirizzo UMBseg e'

                 memorizzata la dimensione del massimo UMB disponibile.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- umballoc.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl allocUMB(void (far *XMMdriver)(void),unsigned UMBkb,unsigned *UMBseg)

Servizio XMS 11h: Dealloca un un UMB

Input

AH

DX

11h

Indirizzo di segmento dell'UMB

Output

AX

1 se la disallocazione è riuscita correttamente. L'UMB è nuovamente libero

Note

In caso di fallimento, AX è 0 e BL contiene il codice di errore (vedere pag. 169).

Segue esempio:

/********************

    BARNINGA_Z! - 1992

    UMBFREE.C - freeUMB()

    int freeUMB(void (far *XMMdriver)(void),unsigned UMBseg);

    void (far *XMMdriver)(void);   puntatore all'entry point del driver XMM.

    unsigned UMBseg;               indirizzo di segmento dell'UMB da liberare.

    Restituisce: 0 se l'operazione è stata eseguita con successo.

                 Se < 0 e' il codice di errore XMS.

    COMPILABILE CON TURBO C++ 2.0

        tcc -O -d -c -mx -k- umbfree.c

    dove -mx puo' essere -mt -ms -mc -mm -ml -mh

********************/

#pragma  inline

int cdecl freeUMB(void (far *XMMdriver)(void),unsigned UMBseg)

Un UMB allocato con allocUMB() può essere referenziato mediante un puntatore far o huge, costruito con la macro MK_FP() (pag. 25):

#include <dos.h>                                             // per MK_FP()

    .

    unsigned umbSeg;

    char far *umbPtr;

    .

    if(allocUMB(XMMdriver,10000,&umbSeg) < 0)

    else

    }

La tabella che segue riporta i codici di errore XMS.

Codici di errore XMS

Codice

Descrizione

80h

Funzione non valida

81h

E' installato VDISK

82h

Errore nella A20 line

8Eh

Errore interno al driver

8Fh

Errore irrecuperabile del driver

90h

La HMA non esiste

91h

La HMA è già in uso

92h

Richiesta allocazione in HMA di un numero di byte minore del parametro associato a /HMAMIN sulla riga di comando del driver XMM

93h

La HMA è libera

94h

Non è stato possibile disattivare la A20 line

A0h

Tutta la memoria estesa è già allocata

A1h

Tutti gli handles disponibili per la memoria estesa sono già utilizzati

A2h

Handle non valido

A3h

Handle sorgente non valido

A4h

Offset sorgente non valido

A5h

Handle destinazione non valido

A6h

Offset destinazione non valido

A7h

La lunghezza del blocco non è valida

A8h

Le aree sorgente e destinazione si sovrappongono

A9h

Errore di parità

AAh

Il blocco non è locked

ABh

Il blocco è locked

ACh

Overflow nel conteggio dei lock del blocco

ADh

Operazione di lock fallita

B0h

E' disponibile un UMB di dimensione minore a quella richiesta

B1h

Non sono disponibili UMB

B2h

Segmento UMB non valido




[1] Che cosa vi aspettavate? Questa non è una guida di riferimento tecnico per il sistema operativo, né un supplemento alla manualistica dei compilatori C. Questa è beh chissà.

[2] Il C Borland include ALLOC.H; il C Microsoft MALLOC.H.

[3] Si noti che i servizi DOS gestiscono la memoria in unità minime di 16 byte, dette paragrafi. Inoltre, ogni blocco allocato dal DOS si trova sempre ad un indirizzo allineato a paragrafo (divisibile, cioè, per 16), esprimibile con un'espressione del tipo segmento:0000.

[4] Forse è opportuno ricordare che gli indirizzi segmento:offset sono una rappresentazione (coerente con i registri a 16 bit della CPU) di un indirizzo a 20 bit; 9FFF:000F equivale a 9FFFF.

[5] Tutte le versioni di DOS, inclusa la 6.2, sembrano caricare il primo file nascosto proprio all'indirizzo 0070:0000.

[6] Il DOS non distingue le due aree: dopo il bootstrap, tutta la RAM al di sopra dell'environment di COMMAND.COM è considerata un'unica area, libera, a disposizione dei programmi. La parte transiente di COMMAND.COM, se sovrascritta, viene ricaricata da disco all'occorrenza.

[7] In sostanza, il DOS gestisce la RAM per aree (che possono essere allocate ad un programma oppure libere), in testa ad ognuna delle quali crea un MCB. Un po' di pazienza, tra breve analizzeremo i MCB in dettaglio.

[8] Questa è la regola generale. A partire dal DOS 4.0, però, l'area di RAM allocata ai device driver ha un MCB regolare, recante la lettera 'M' nel campo POS, ma è a sua volta suddivisa in tante sub‑aree quanti sono i driver installati, ciascuna dotata, in testa, di un proprio MCB. In tali Memory Control Block il campo POS indica il tipo di driver; il suo contenuto può essere: 'D' blocco device driver (installato dal comando DEVICE in CONFIG.SYS), 'F' blocco FILES, 'X' blocco FCBS, 'B' blocco BUFFERS, 'C' blocco buffer EMS, 'I' blocco IFS, 'L' blocco LASTDRIVE, 'S' blocco STACKS, 'E' blocco device driver appendage.

[9] Il PSP è un record di 256 byte che il DOS prepara in testa al codice del programma eseguito. Per ogni programma caricato in memoria si ha dunque un MCB, immediatamente seguito dal PSP, a sua volta seguito dal codice del programma stesso. L'indirizzo di segmento del PSP di un programma è pertanto pari a quello del suo MCB, incrementato di uno.

[10] In effetti, l'area dei device driver è quella che immediatamente segue la RAM riservata al secondo file nascosto, come evidenziato in figura 1.

[11] Tutte le aree di RAM gestite mediante servizi DOS hanno dimensione (in byte) divisibile per 16 (multiple per paragrafi: non è più una novità). Anche il loro indirizzo è divisibile per 16 (cioè allineato a paragrafo) ed è esprimibile mediante la sola parte segmento (seg:0000). Ne segue che anche i MCB sono allineati a paragrafo, dal momento che occupano il paragrafo immediatamente precedente l'area allocata. Vale infine la pena di sottolineare che i servizi 48h, 49h e 4Ah dell'int 21h restituiscono e/o richiedono in input l'indirizzo (sotto forma di word, la sola parte segmento) dell'area e non quello del MCB (ricavabile decrementando di uno quello dell'area).

[12] Le versioni di DOS anteriori alla 4.0 non utilizzano questo campo; con esse il solo metodo per conoscere il nome del programma è andare a curiosare in coda all'environment di questo (vedere pag. 169 per un esempio di metodo valido, comunque, anche con DOS 4 e successivi). Qui il nome è memorizzato completo di drive e pathname; tuttavia esso scompare se la RAM allocata all'environment viene liberata.

[13] Alcune interessanti particolarità relative all'InDOS Flag sono discusse alle pagine 297 e seguenti.

[14] Il puntatore mcb non è dichiarato nearfar (pag. 22): il suo tipo dipende perciò dal modello di memoria scelto per la compilazione (pag. 151); esso, in particolare, è near nei modelli tiny, small e medium. All'interno della funzione non è possibile sapere se la struttura a cui mcb punta è stata allocata nello heap con una chiamata a malloc() (con indirizzo relativo a DS), nell'area dati statici e globali (indirizzo ancora relativo a DS) o nello stack come variabile automatica (indirizzo relativo a SS). In tutti gli esempi di funzione presentati nel testo, in casi come quello analizzato, si assume che nei modelli small e medium DS e SS coincidano (e si utilizza dunque DS per ricavare la parte segmento dell'indirizzo), in quanto questo è il default di comportamento del compilatore. Solo con particolari e pericolose opzioni della riga di comando è infatti possibile richiedere che DS e SS, in detti modelli di memoria, non siano necessariamente uguali.

[15] Per la precisione: un megabyte e 64 Kb meno 16 byte (FFFF:FFFF). I (circa) 64 Kb eccedenti il Mb sono denominati HMA (High Memory Area; vedere pag. 230 e seguenti). Le macchine a 32 bit (80386, 80486, etc.) possono indirizzare linearmente grandi quantità di RAM, ma il limite descritto permane in ambiente DOS.

[16] Quanto detto è vero per le macchine 80286. Le macchine basate su processore 80386 o 80486 (comprese le versioni SX) possono utilizzare anche memoria estesa, se al bootstrap è installato un driver in grado di emulare la memoria espansa attraverso quella estesa.

[17] Forse vale la pena di ricordare che la parte segmento di un indirizzo equivale alla word più significativa di un puntatore C far o huge.

[18] Il primo MCB ha lo scopo di escludere dal remapping il buffer video EGA/VGA (A000:0‑AFFF:000F). La dimensione del MCB può variare a seconda delle opzioni presenti sulla riga di comando del driver che gestisce la Upper Memory.

[19] La logica è analoga a quella descritta circa il caricamento dei device driver in memoria convenzionale.

[20] Riprendendo l'esempio precedente: a 9FFF:0 vi è il primo MCB dell'Upper Memory; la formula indirizzo+DIM+1 fornisce B001h. Se sulla macchina è installato un video a colori, l'intervallo B001:0‑B7FF:0 costituisce il primo UMB (a B000:0 vi è il suo proprio MCB), che può contenere uno o più MCB. A B7FF:0 si trova un MCB che ha lo scopo di proteggere l'intervallo B800:0‑C7FF:000F (nell'ipotesi di scheda VGA presente): la formula indirizzo+DIM+1 fornisce C801h. A C801:0 vi è un altro UMB (il suo proprio MCB è a C000:0), che può contenere diversi MCB, e così via.

[21] La word a 0:0413 contiene i Kb di memoria convenzionale installati.

[22] Esempio: se sulla macchina è installato un video a colori, a B000:0 vi è il primo MCB dell'Upper Memory (l'area UMB è a B001:0); la formula indirizzo+DIM+1 fornisce l'indirizzo del successivo MCB. A B7FF:0 si trova un MCB che ha lo scopo di proteggere l'intervallo B800:0‑C7FF:000F (nell'ipotesi di scheda VGA presente): la formula indirizzo+DIM+1 fornisce C800h. Qui vi è un altro MCB (l'area UMB è a C001:0), per il quale la formula indirizzo+DIM+1 fornisce l'indirizzo del successivo MCB, e così via.

[23] Gli esempi su allocazione e disallocazione degli UMB presumono la conoscenza della modalità di chiamata dei servizi XMS, descritta proprio nel capitolo dedicato alla memoria estesa.

[24] Una pagina equivale a 16 Kb.

[25] Lo standard industriale di specifiche per la gestione della memoria espansa definito da Lotus, Intel e Microsoft.

[26] Parte delle funzioni è scritta in C puro, parte, a scopo esemplificativo, si basa sull'inline assembly.

[27] Se il bit 7 di DX è 0, allora esiste nella directory corrente un file avente nome EMMXXXX0: la open() ha aperto detto file (e non il device EMM). Quando si dice la sfortuna

[28] Tra l'altro questo algoritmo è facilmente implementabile anche all'interno di gestori di interrupt.

[29] Il valore restituito dall'int 67h è 'risistemato' in modo coerente con il servizio 30h dell'int 21h, che restituisce in AL la versione e in AH la revisione del DOS.

[30] In grado, cioè, di utilizzare porzioni dello spazio libero su disco come RAM aggiuntiva. E' il caso, ad esempio, di Microsoft Windows 3.x su macchine 80386 o superiori, se attivo in modalità '80386 Avanzata'.

[31] Se la sua dimensione supera i 9 byte, sono comunque utilizzati solamente i primi 9.

[32] Se, ad esempio, si richiede al driver di allocare un gruppo di 3 pagine logiche, queste saranno numerate da 0 a 2. Ciascuna di esse è identificabile univocamente mediante lo handle e il proprio numero. Nonostante il paragone sia un po' azzardato, si può pensare ad un blocco di pagine logiche come ad un array: lo handle identifica l'array stesso, ed ogni pagina ne è un elemento, che può essere referenziato tramite il proprio numero (l'indice).

[33] Mappare. orrendo!

[34] Alcune notizie relative all'utilizzo della memoria EMS nei TSR sono date a pag. 169.

[35] I programmi DOS possono operare in modo protetto solo su macchine dotate di processore 80286 o superiore. La modalità standard di lavoro in ambiente DOS, possibile su tutte le macchine, è la cosiddetta reale (real mode).

[36] XMS è acronimo di eXtended Memory Specification, standard industriale per la gestione della memoria estesa. La XMS include anche regole per la gestione degli UMB.

[37] Ne segue che la memoria estesa in senso stretto si trova oltre i primi 1088 Kb (FFFF:FFFF).

[38] Forse è il caso di spendere due parole di chiarimento circa la HMA. Questa è l'unica parte di memoria estesa indirizzabile da un processore 80286 o superiore senza necessità di lavorare in modalità protetta. Infatti l'indirizzo F000:FFFF punta all'ultimo byte di memoria entro il primo Mb: detto indirizzo è normalizzato in FFFF:000F. Incrementandolo di uno si ottiene FFFF:0010, cioè l'indirizzo del primo byte di memoria estesa, equivalente all'indirizzo lineare 100000h, esprimibile mediante 21 bit. Le macchine 8086 dispongono di sole 20 linee di indirizzamento della RAM e possono quindi gestire indirizzi 'contenuti' in 20 bit. Per questo motivo l'indirizzo FFFF:0010 subisce su di esse il cosiddetto address wrapping e diviene 0000:0000. Al contrario, le macchine 80286 dispongono di 24 bit per l'indirizzamento della memoria; quelle basate sul chip 80386 ne hanno 32. La A20 line è la linea hardware (sono convenzionalmente indicate con A0A31) di indirizzamento della RAM corrispondente al ventunesimo bit: essa consente, se attiva (i servizi XMS lo consentono anche in modalità reale), di indirizzare i primi FFF0h byte (65520) al di là del primo Mb, che rappresentano, appunto, la HMA. Per ulteriori notizie circa l'indirizzamento della RAM vedere pag. 16.

[39] Il valore restituito dall'int 67h è 'risistemato' in modo coerente con il servizio 30h dell'int 21h, che restituisce in AL la versione e in AH la revisione del DOS.

[40] Il valore restituito dall'int 67h è 'risistemato' in modo coerente con il servizio 30h dell'int 21h, che restituisce in AL la versione e in AH la revisione del DOS.

[41] Del CMOS si parla, con maggiore dettaglio, a pagina 555.

[42] L'uso dell'int 15h rappresenta uno standard (precedente al rilascio delle specifiche XMS) in base al quale il programma che intende allocare memoria estesa chiama l'int 15h e memorizza la quantità di memoria estesa esistente: tale valore rappresenta anche l'indirizzo dell'ultimo byte di memoria fisicamente installata. Il programma stesso deve poi installare un proprio gestore dell'int 15h, che restituisce al successivo chiamante un valore inferiore: la differenza rappresenta proprio la quantità di memoria estesa che il programma intende riservare a sé. Un altro standard, anch'esso precedente a quello XMS, è il cosiddetto sistema 'VDISK', dal nome del driver Microsoft per ram‑disk che lo implementò per primo. L'algoritmo è analogo a quello dell'int 15h, ma la memoria estesa è allocata al programma a partire 'dal basso', cioè dall'indirizzo 100000h (con lo standard dell'int 15h la memoria è, evidentemente, allocata a partire dall'alto). I tre sistemi (int 15h, VDISK e XMS) sono beatamente incompatibili tra loro c'era da dubitarne?

[43] Non è davvero il caso di ripetere sempre la medesima litania.

[44] E, pertanto, pericoloso. Non sembrano inoltre essere disponibili servizi per la disallocazione delle porzioni di HMA allocate mediante la subfunzione 02h del servizio 4Ah.

[45] Quando la A20 line è attiva, una coppia di registri a 16 bit può esprimere indirizzi lineari a 21 bit, fino a FFFF:FFFF (non è più una novità). Ne segue che è possibile referenziare direttamente indirizzi all'interno della HMA tramite normali puntatori far o huge (pag. 22). Ad esempio l'istruzione

#include <dos.h>                                                     // per MK_FP()

.

    register i;

    unsigned char *cPtr;

    for(i = 0x10; i < 0x20; i++)

        *(unsigned char far *)MK_FP(0xFFFF,i) = *cPtr++;

copia 16 byte da un indirizzo in memoria convenzionale (referenziato da un puntatore near) all'inizio della HMA. I servizi XMS 05h e 06h, del tutto analoghi ai servizi 03h e 04h, consentono di attivare e, rispettivamente, disattivare la A20 line per indirizzare direttamente, mediante puntatori lineari a 32 bit, circa 1 Mb di memoria estesa (al di fuori della HMA).

Scarica gratis Gestione a basso livello della memoria
Appunti su:



Scarica 100% gratis e , tesine, riassunti



Registrati ora

Password dimenticata?
  • Appunti superiori
  • In questa sezione troverai sunti esame, dispense, appunti universitari, esercitazioni e tesi, suddivisi per le principali facoltà.
  • Università
  • Appunti, dispense, esercitazioni, riassunti direttamente dalla tua aula Universitaria
  • all'Informatica
  • Introduzione all'Informatica, Information and Comunication Tecnology, componenti del computer, software, hardware ...