(Incompleto) MINI CORSO DI ASSEMBLER 8086

(tratto dal sito:http://www.giobe2000.it/)

NOTE: DEVE ESSERE APPROFONDITO

TINY: E' il modello che consente la creazione di file .COM. Tutti i registri di segmento (CS, SS, DS ed ES) contengono lo stesso indirizzo, quello del Program Segment Prefix del programma. Quando il programma è un .COM, il registro IP è sempre inizializzato a 100h (256 decimale) e, dal momento che il PSP occupa proprio 256 byte, l'entry point del programma coincide col primo byte del file: i conti tornano.
Tanto nei file .COM che nei file .EXE, codice, dati e stack non possono superare i 64 Kb e tutti i puntatori sono, per default, near. La memoria è dunque gestita secondo una "mappa" analoga a quella presentata nella figura sottostante


.

Vale la pena di sottolineare che dati globali e statici, stack e heap condividono il medesimo segmento di memoria: un utilizzo "pesante" dell'allocazione dinamica della memoria riduce quindi lo spazio disponibile per le variabili locali e per i dati globali, e viceversa.

SMALL: Nel modello small il segmento del codice è separato da quello per i dati. I programmi generati con questo modello possono avere fino a 64 Kb di codice eseguibile, ed altri 64 Kb condivisi tra dati statici e globali, heap e stack. Come si vede nella figura sottostante, anche nei programmi compilati in modalità small lo spazio utilizzato dai dati globali riduce heap e stack, e viceversa, ma il valore iniziale di DS ed SS non coincide con quello di CS, in quanto viene stabilito in base ai parametri presenti nella relocation table, generata dal linker. E' inoltre disponibile il far heap, nel quale è possibile allocare memoria da gestire mediante puntatori far.


MEDIUM: Il modello medium è adatto ai programmi di grosse dimensioni che gestiscono piccole quantità di dati: infatti, i puntatori per il codice sono tutti a 32 bit (le chiamate a funzione sono tutte far), mentre i puntatori per i dati, per default, sono a 16 bit come nel modello small. Analogamente a quest'utlimo, perciò, il modello medium gestisce un segmento di 64 Kb per dati statici e globali, heap e stack separato dagli indirizzi del codice, che può invece raggiungere la dimensione (teorica) di 1 Mb.

Si noti che il codice eseguibile, qualora superi la dimensione di 64 Kb, deve essere "spezzato" in più moduli .OBJ, ognuno dei quali deve essere di dimensioni non superiori ai 64 Kb. La generazione di più moduli oggetto presuppone che il sorgente sia suddiviso in più file, ma è appena il caso di rimarcare che la dimensione di ogni singolo sorgente non ha alcuna importanza: i limiti accennati valgono per il codice già compilato. La figura 3 evidenzia che il registro CS è inizializzato per puntare ad uno dei moduli oggetto.


Nel modello medium, le funzioni dichiarate esplicitamente near sono richiamabili solo dall'interno dello stesso modulo oggetto nel quale esse sono definite, in quanto una chiamata near, gestita con un indirizzo a soli 16 bit, non può gestire "salti" inter­segmento.

COMPACT: Il modello compact può essere considerato il complementare del modello medium, in quanto genera per default chiamate near per le funzioni e indirizzamenti far per i dati: in pratica esso si addice a programmi piccoli, che gestiscono grandi moli di dati. Il codice non può superare i 64 Kb, come nel modello small, mentre per i dati può essere utilizzato fino ad 1 Mb (tale limite è teorico, in quanto ogni programma, in ambiente DOS, si scontra con l'infame "barriera" dei 640 Kb).

La figura successiva evidenzia che, a differenza di quanto avviene nei modelli tiny, small e medium, DS e SS sono inizializzati con valori differenti: il programma ha perciò un segmento di 64 Kb dedicato ai dati statici e globali, ed un altro, distinto, per la gestione dello stack. Lo heap (cioè l'area di RAM allocabile dinamicamente) occupa tutta la rimanente memoria disponibile ed è indirizzato per default con puntatori far. Proprio per questa caratteristica esso è definito heap e non far heap, come avviene invece nel modello small, nel quale è necessario dichiarare esplicitamente far i puntatori al far heap e si deve utilizzare farmalloc() per allocarvi memoria.


 



LARGE
: Il modello large genera per default indirizzamenti far sia al codice che ai dati e si rivela perciò
adatto a programmi di notevoli dimensioni che gestiscono grandi quantità di dati. Esso è, in pratica, un ibrido tra i modelli medium (per quanto riguarda la gestione del codice) e compact (per l'indirizzamento dei dati); codice e dati hanno quindi entrambi a disposizione (in teoria) 1 Mb.

Il modello large, per le sue  caratteristiche di indirizzamento, è probabilmente il più flessibile, anche se non il più efficiente. Le funzioni contenute in una libreria compilata per il modello large possono essere utilizzate senza problemi anche da programmi compilati per altri modelli: è sufficiente ricordarsi che tutti i puntatori parametri delle funzioni sono far e che le funzioni devono essere prototipizzate anch'esse come far: se questi non sono i default del modello di memoria utilizzato occorre agire di conseguenza. Esempio: abbiamo un sorgente, PIPPO.C, da compilare con il modello small, nel quale deve essere inserita una chiamata a funzStr() (che accetta un puntatore a carattere quale parametro e restituisce un puntatore a carattere) disponibile nella libreria LARGELIB.LIB, predisposta per il modello large. Alla libreria è associato uno header file, LARGELIB.H, che contiene il seguente prototipo di funzStr():

char *funz(char *string);

La funzione e i puntatori (il parametro e quello restituito) non sono dichiarati far, perché nel modello large tutti i puntatori e tutte le funzioni lo sono per default. Se non si provvede ad informare il compilatore che, pur essendo il modello di memoria small, funzStr(), i suoi parametri e il valore restituito sono far, si verificano alcuni drammatici problemi: in primo luogo, lo stack è gestito come se entrambi i puntatori fossero near. Ciò significa che a funzStr(), in luogo di un valore a 32 bit, ne viene passato uno a 16; il codice di funzStr(), però, lavora comunque su 32 bit, prelevando dallo stack 16 bit di "ignota provenienza" in luogo della vera parte segmento del puntatore. La funzStr(), inoltre, restituisce un valore a 32 bit utilizzando la coppia di registri DX:AX, ma la funzione chiamante, aspettandosi un puntatore a 16 bit, ne considera solo la parte in AX, cioè l'offset. Ma ancora non basta: la chiamata a funzStr() generata dal compilatore è near, secondo il default del modello small, perciò, a run­time, la CALL salva sullo stack solo il registro IP (e non la coppia CS:IP). Quando funzStr() termina, la RETF (far return) estrae dello stack 32 bit e ricarica con essi la coppia CS:IP; anche in questo caso, 16 di quei 32 bit sono di "ignota provenienza". Ce n'è quanto basta per bloccare la macchina alla prima chiamata. E' indispensabile correre ai ripari, modificando come segue il prototipo in LARGELIB.H


char far * far funzStr(char far *string);

e dichiarando esplicitamente far i puntatori coinvolti nella chiamata a funzStr():

....
char far *parmPtr, far *retPtr;
....
retPtr = funzStr(parmPtr);
....
A dire il vero, si può evitare di dichiarare parmPtr esplicitamente far, perché il compilatore, dall'esame del prototipo, è in grado di stabilire quale tipo di puntatore occorre passare a funzStr() e provvede da sé copiando sullo stack un puntatore far costruito come DS:parmPtr; la dichiarazione far, comunque, non guasta, purché ci si ricordi di avere a che fare con un puntatore a 32 bit anche laddove ciò non è richiesto.

Per facilitare l'uso dei puntatori far nei modelli di memoria tiny, small e medium sono state di recente aggiunte alle librerie standard nuove versioni (adatte a puntatori a 32 bit) di alcune funzioni molto usate: accanto a strcpy() troviamo perciò _fstrcpy(), e così via.


HUGE:


 

FLAT: Usando questo modello, un programma vede la memoria come un singolo e continuo spazio di indirizzi chiamato linear address space. Il modello base di flat nasconde i meccanismi di segmentazione dell'architettura anche al programmatore applicativo. Per implementare un modello di memoria flat di base devono essere creati almeno 2 segment descriptor, uno per referenziare un segmento codice, l'altro per un segmento dati. Entrambi questi segmenti comunque, sono mappati sull'intero Linear Address Space, ovvero entrambi i segmenti hanno lo stesso valore di base address (cioè 0) e lo stesso valore di limite di 4 Gb. Questo modello di memoria NON segmentato è disponibile su sistemi operativi a 32bit, diciamo, simile al modello Tiny per il fatto che tutto il codice e i dati stanno in un unico segmento di 32 bit. Questo spazio di indirizzi (fisico) non segmentato può essere mappato come memoria read-only, read-write e memory mapped I/O. Per usare il modello Flat prima di usare la direttiva .MODEL bisogna esplicitare .386 o .486 (indovinate un po', indica la modalità del processore che intendiamo usare nel nostro programma, è il set di direttive per selezionare il processore e il coprocessore). Il sistema operativo automaticamente inizializza i registri di segmento al momento del caricamento. CS, DS, ES ed SS occupano tutti il supergruppo FLAT.