[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 gli esempi relativi al linguaggio Clipper per un 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 in un paragrafo dedicato

[14] Il puntatore mcb non è dichiarato nearfar: il suo tipo dipende perciò dal modello di memoria scelto per la compilazione; 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). 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 in un paragrafo dedicato

[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 A0...A31) 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 il paragrafo dedicato

[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, in un paragrafo dedicato

[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. 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).