Assembly Avanzato con MASM

Capitolo 9: La memoria video in modalità testo standard


In seguito ad una serie di convenzioni stabilite a suo tempo dalla IBM, tutti i PC della famiglia 80x86 sono tenuti a fornire il supporto per le cosiddette modalità video standard; a tale proposito, è presente apposito hardware che, nel suo insieme, costituisce il video adapter (adattatore video) del PC.

Nei primi modelli di PC tutto l'hardware relativo al video adapter risulta integrato nella scheda madre e comprende, tra l'altro, una apposita memoria dedicata; per distinguere tale memoria dalla RAM centrale si utilizza la definizione di Video RAM o VRAM.
Nei moderni PC, tutto l'hardware relativo al supporto video risulta ormai disposto in apposite schede periferiche; in particolare, molte di tali schede sono dotate di enormi quantità di VRAM e di una o più GPU (Graphics Processing Unit) il cui scopo è quello di gestire autonomamente le elaborazioni grafiche le quali, altrimenti, finirebbero per gravare pesantemente sulla CPU.

Trattandosi di una memoria esterna, posta al di fuori dello spazio di indirizzamento della CPU, la VRAM viene resa accessibile attraverso un apposito frame buffer, esattamente come accade per la expanded memory o per il BIOS; in modalità reale, l'indirizzo del frame buffer e la relativa dimensione in byte variano a seconda della modalità video e assumono i valori convenzionali illustrati in Figura 9.1 (vedere anche la Figura 6.1 del Capitolo 6). Come si può vedere, la dimensione di ogni frame buffer non supera i 65536 byte (64 KiB); ancora una volta quindi si nota che, in modalità reale, la segmentazione a 64 KiB della RAM si ripercuote anche sulle memorie esterne (come la VRAM).

Per attivare le varie modalità video standard, il metodo preferito consiste nel servirsi della INT 10h del BIOS; tale vettore di interruzione fornisce anche una numerosa serie di servizi che saranno analizzati in questo e nei successivi capitoli (per ulteriori dettagli si consiglia di consultare i manuali disponibili sui siti dei vari produttori di BIOS).
La modalità video standard desiderata viene selezionata ponendo AH=0 (Set Video Mode) e caricando in AL un apposito codice a 8 bit; il relativo codice Assembly assume quindi un aspetto del tipo: Nel corso degli anni la IBM ha sviluppato una numerosa serie di adattatori video dotati di caratteristiche sempre più evolute; può essere interessante quindi ripercorrere brevemente l'evoluzione di questo particolare dispositivo hardware.

9.1 Breve storia degli adattatori video standard

Nell'estate del 1981 la IBM mette in commercio il primo PC di classe XT, bastato su CPU 8086 e 8088; il relativo adattatore video prende il nome di MDA o Monochrome Dispaly Adapter (adattatore video monocromatico).
Attraverso tale adattatore, lo schermo viene suddiviso in una matrice di 720x350 punti, denominati pixel; il prodotto 720x350 (pixel) indica la cosiddetta risoluzione dello schermo e, per convenzione, prevede che sia sempre specificato nel primo fattore il numero di colonne (larghezza dello schermo in pixel) e nel secondo fattore il numero di righe (altezza dello schermo in pixel).
A sua volta, la matrice 720x350 risulta suddivisa in 80x25 celle (cioè, 80 celle in larghezza per 25 celle in altezza) ciascuna delle quali sarà quindi formata da 720/80=9 pixel in orizzontale e 350/25=14 pixel in verticale; in ciascuna cella l'utente può visualizzare un qualunque simbolo appartenente al set di codici ASCII.
Una simile modalità video viene denominata alfanumerica (o modalità testo) proprio per indicare il fatto che l'utente può visualizzare sullo schermo solamente lettere dell'alfabeto, segni di punteggiatura, cifre numeriche e altri simboli del codice ASCII.
Il MDA non fornisce alcuna modalità video di tipo grafico e, inoltre, permette la visualizzazione del testo attraverso un unico colore su sfondo nero (per un totale di 2 colori); i PC dell'epoca, di conseguenza, vengono abbinati ai cosiddetti monitor monocromatici suddivisi in modelli a fosfori bianchi (bianco su nero), a fosfori verdi (verde su nero) e a fosfori ambra (ambra su nero).

La Figura 9.2 illustra le caratteristiche generali del supporto video offerto dal MDA (A/N = alfanumerico = modo testo); in particolare, la figura mostra il codice del BIOS da caricare in AL, l'indirizzo fisico del frame buffer, la memoria video totale fornita dall'adattatore e il numero totale di colori disponibili. Poco tempo dopo l'uscita dell'adattatore MDA, un'azienda di nome Hercules mette in commercio un nuovo adattatore video denominato HGC (Hercules Graphics Card); tale adattatore è del tutto analogo al MDA con la differenza però che, oltre alla modalità testo monocromatica da 80x25 celle, è anche presente una modalità grafica monocromatica con risoluzione di 720x350 pixel.
La IBM non sta a guardare e rende subito disponibile un adattatore denominato CGA o Color Graphics Adapter (adattatore grafico a colori); tale adattatore fornisce differenti modalità testo e grafica con la possibilità di utilizzare sino a 16 colori differenti.
Nelle modalità grafiche, ogni pixel risulta indirizzabile dai programmi e, proprio per questo motivo, tali modalità vengono indicate con l'acronimo APA che sta per All Points Addressable (tutti i pixel sono indirizzabili); la Figura 9.3 illustra le caratteristiche generali del supporto video offerto dal CGA. (Si noti che, ai tempi dell'adattatore CGA, il frame buffer per l'accesso alla VRAM si trovava sempre all'indirizzo fisico B8000h, sia per la modalità testo, sia per la modalità grafica).

Nel 1984, con l'avvento dei primi PC di classe AT, la IBM introduce un nuovo adattatore video denominato EGA o Enhanced Graphics Adapter (adattatore grafico avanzato); l'EGA apre una nuova era in quanto viene prodotto anche sotto forma di scheda periferica e comprende, tra le altre cose, la possibilità di espandere la VRAM.
Uno degli scopi principali dell'EGA è anche quello di garantire la compatibilità tra vecchie e nuove modalità video standard testo e grafiche; la Figura 9.4 illustra le caratteristiche generali del supporto video offerto dall'EGA. Dopo una sfortunata parentesi rappresentata dallo scarso successo ottenuto dal costosissimo adattatore PGA (Professional Dispaly Adapter), nel 1987 la IBM introduce nel mondo dei PC un nuovo standard denominato PS/2; questa nuova classe di PC si caratterizza per la presenza di differenti adattatori video tra i quali il 8514/A Display Adapter e il MCGA o MultiColor Graphics Adapter (adattatore grafico multicolore).
Il 8514/A Display Adapter è destinato esclusivamente all'architettura Micro Channel dei computer di classe PS/2 e fornisce diverse modalità grafiche con risoluzioni sino alla 1024x768 che, per l'epoca, erano considerate altissime; il MCGA, invece, è compatibile anche con l'architettura dei generici PC della famiglia 80x86.
In analogia all'EGA, anche il MCGA introduce nuove modalità video e garantisce la compatibilità con gli adattatori precedenti; la Figura 9.5 illustra le caratteristiche generali del supporto video offerto dal MCGA. Sempre nel 1987 la IBM annuncia che, oltre all'adattatore MCGA a bassa risoluzione, per i PC di classe PS/2 è disponibile anche un nuovo adattatore ad alta risoluzione denominato VGA o Video Graphics Array (matrice video grafica); questo nuovo adattatore ottiene un successo enorme ma, come vedremo nei capitoli successivi, segna anche la fine del dominio IBM nel settore degli adattatori video.
La caratteristica rivoluzionaria della VGA è la presenza di una interfaccia di programmazione incorporata nel Video ROM BIOS; i programmi che utilizzano tale interfaccia possono così garantire la totale indipendenza dall'hardware della scheda video.
Come nei casi precedenti, anche la VGA introduce nuove modalità video e garantisce la compatibilità con gli adattatori precedenti; la Figura 9.6 illustra le caratteristiche generali del supporto video offerto dalla VGA. In questo capitolo ci occuperemo delle modalità video testo standard; nei capitoli successivi, invece, parleremo delle modalità video grafiche standard.

9.2 Caratteristiche generali delle modalità video testo standard

Come sappiamo, all'accensione del PC il BIOS avvia una fase di diagnostica e inizializzazione dell'hardware; tra le varie inizializzazioni, una riguarda proprio la modalità video predefinita.
Nel caso più generale, tale modalità predefinita è quella indicata in Figura 9.6 dal codice 03h per gli adattatori video a colori e 07h per gli adattatori video monocromatici; si tratta quindi di una modalità video testo (o modalità alfanumerica) che, come è stato già spiegato, è così chiamata per indicare il fatto che sullo schermo è possibile rappresentare solo lettere dell'alfabeto, segni di punteggiatura, cifre numeriche e altri simboli appartenenti al set di codici ASCII.

9.2.1 Bitmap dei simboli per la modalità testo

Indipendentemente dall'aspetto esteriore, le modalità video sono in realtà tutte di tipo grafico; lo schermo viene cioè gestito in ogni caso sotto forma di matrice composta da m x n pixel.
La modalità testo viene simulata suddividendo la matrice di m x n pixel in tante celle, ciascuna delle quali è a sua volta una matrice di p x q pixel; nel caso della modalità 03h fornita dalla VGA, ad esempio, lo schermo è una matrice di 720x400 pixel suddivisa in 80x25 celle (cioè, 80 celle in larghezza per 25 celle in altezza).
Ogni cella viene identificata da un numero di colonna e un numero di riga; per la modalità 03h della VGA ricaviamo quindi le seguenti dimensioni p x q in pixel di ogni cella:
p = 720 / 80 = 9 pixel (larghezza)
q = 400 / 25 = 16 pixel (altezza)
La Figura 9.7a illustra la situazione appena descritta. Come è stato già spiegato, in ogni cella è possibile visualizzare uno dei 256 simboli appartenenti al set di codici ASCII; tali simboli si trovano memorizzati in apposite aree della PC ROM BIOS e della Video ROM BIOS (vedere la Figura 6.1 del Capitolo 6).
Nei vecchi PC i 256 simboli visualizzabili erano suddivisi in due gruppi o mappe: la mappa 1 con i simboli da 0 a 127 e la mappa 2 con i simboli da 128 a 256; successivamente, si è passati definitivamente ad una mappa unica contenente tutti i 256 simboli.
Ogni simbolo è memorizzato sotto forma di bitmap (mappa di bit); all'interno della mappa, un bit di valore 0 rappresenta un pixel spento sullo schermo, mentre un bit di valore 1 rappresenta un pixel acceso sullo schermo.
La Figura 9.7b mostra, ad esempio, una cella 9x16 riempita con la bitmap relativa alla lettera 'a' minuscola; i pixel bianchi rappresentano bit di valore 0, mentre i pixel rossi rappresentano bit di valore 1.
Le bitmap memorizzate nella ROM BIOS possono anche avere dimensioni differenti da quelle delle celle presenti sullo schermo; generalmente, i formati più usati sono quelli da 8x8, 8x14 e 8x16 pixel.
Nel caso, ad esempio, del formato 8x16, risulta quindi che ogni bitmap associata ad un simbolo ASCII occupa 16 byte; il vettore che contiene tutte le 256 bitmap occupa allora:
16 * 256 = 4096 byte
In modalità testo, il programmatore non deve assolutamente preoccuparsi di questi aspetti in quanto tutto viene gestito automaticamente via hardware; eventualmente (come vedremo in seguito), è possibile installare un proprio vettore di bitmap che sarà utilizzato al posto di quello predefinito.

Per i vecchi adattatori video monocromatici (con monitor a fosfori bianchi, verdi o ambra), la modalità video predefinita è la 07h; si tratta di una modalità testo che per le schede VGA prevede una matrice video di 720x400 pixel suddivisa in 80x25 celle da 9x16 pixel ciascuna.

9.2.2 Il BYTE degli attributi

Anche se lo schermo appare organizzato in forma matriciale, in realtà la VRAM (come la RAM) è gestita sotto forma di vettore di BYTE; in modalità testo, ad ogni cella come quella di Figura 9.7 vengono assegnati 2 byte: il primo byte contiene il codice ASCII del simbolo da visualizzare, mentre il secondo byte contiene il cosiddetto byte degli attributi.
Indicando allora con S il byte relativo al simbolo da visualizzare in una cella e con A il byte degli attributi della cella stessa, il vettore della VRAM in modo testo risulterà strutturato in questo modo:
S, A, S, A, S, A, S, A, S, A, ...
Nel caso degli adattatori monocromatici, il byte degli attributi permette di abilitare o disabilitare le funzionalità descritte in Figura 9.8; se un determinato bit vale 1, la relativa funzione risulta abilitata, mentre se lo stesso bit vale 0, la relativa funzione risulta disabilitata. Ad esempio, se il bit 1 nel byte degli attributi di una cella vale 1, il simbolo visualizzato nella cella stessa risulta sottolineato; viceversa, se tale bit vale 0, il simbolo visualizzato nella cella stessa risulta privo di sottolineatura.

Esistono anche altre particolari combinazioni del byte degli attributi per gli adattatori monocromatici; la Figura 9.9 illustra tutti i dettagli riferiti ad un monitor a fosfori bianchi (bianco su nero). Per gli adattatori a colori, il byte degli attributi permette di selezionare, principalmente, il colore di primo piano e il colore di sfondo; la Figura 9.10 illustra tutti i dettagli relativi alla modalità video 03h a 16 colori. Sia per il colore di sfondo, sia per il colore di primo piano, risultano disponibili 4 bit; uno dei bit permette di definire l'intensità (INT) di colore (0 = bassa, 1 = alta), mentre gli altri tre definiscono una cosiddetta terna RGB (da Red, Green, Blue = Rosso, Verde, Blu).
Come si sa dalla fisica ottica, i tre colori Rosso, Verde e Blu vengono definiti primari in quanto a partire da essi si può ottenere un qualsiasi altro colore; a tale proposito, basta assegnare i valori desiderati di intensità al Rosso, al Verde e al Blu e poi miscelare le tre componenti di colore così ottenute.
Nel caso della modalità testo 03h, ad ogni terna RGB possiamo assegnare solamente due valori dell'intensità (0 = bassa, 1 = alta); quindi, con i tre bit RGB possiamo formare 23=8 colori, ciascuno dei quali può avere una intensità alta o bassa, per un totale di 2*8=16 colori (sia per lo sfondo, sia per il primo piano).
Su alcuni BIOS (soprattutto quelli meno recenti) il bit in posizione 7 viene definito blinking bit (bit di lampeggiamento) in quanto permette di attivare (1) o disattivare (0) il lampeggiamento dello sfondo; in tal caso, per lo stesso sfondo si hanno a disposizione solo 23=8 colori a bassa intensità.

9.2.3 Le pagine video

Ad ogni modalità video viene riservata una apposita quantità di VRAM accessibile attraverso il frame buffer; ovviamente, tale quantità di VRAM deve essere sufficiente a contenere almeno una cosiddetta schermata (l'area visibile attraverso lo schermo).
Nel caso della modalità testo 07h, ad esempio, abbiamo visto che una schermata è formata da una matrice di 80x25=2000 celle; come sappiamo, a ciascuna cella vengono riservati 2 byte per cui sarà necessaria una VRAM formata da almeno 2*2000=4000 byte.
In effetti, come si vede in Figura 9.6, alla modalità testo 07h viene riservata una VRAM da 4096 byte (4 KiB) accessibile attraverso un frame buffer da 32 KiB che parte dall'indirizzo logico B000h:0000h; tale blocco da 4096 byte prende il nome di pagina video.
Le pagine video vengono numerate con gli indici 0, 1, 2, ... e, in un determinato momento, solo una di esse può essere attiva (cioè, visibile attraverso lo schermo); in Figura 9.6 si nota che, nel caso della modalità testo 07h, esiste una sola pagina video identificata dall'indice 0 (esistono anche varianti del MDA che forniscono una quantità maggiore di VRAM suddivisa in 2 o più pagine video da 4096 byte ciascuna).

Per la modalità testo 03h, che è la più diffusa, abbiamo a disposizione sino a 64 KiB di VRAM accessibili attraverso un frame buffer da 32 KiB che parte dall'indirizzo logico B800h:0000h; anche la modalità 03h richiede 4000 byte per ogni schermata 80x25.
Inizialmente, sullo schermo si trova visualizzata la pagina video 0, ma sono disponibili in totale sino a 8 pagine video, ciascuna delle quali occupa 4096 byte; osservando che 4096 in esadecimale si scrive 1000h, possiamo affermare che: e così via.

Se, in un determinato momento, risulta attiva la pagina video di indice m, allora tutto l'output inviato ad una pagina video di indice n (non attiva) risulterà invisibile sullo schermo; questa situazione permane finché il programmatore non effettua una commutazione rendendo la pagina video n attiva (con la pagina video m che diventa quindi invisibile)!

9.3 Principali servizi forniti dalla INT 10h per la modalità testo

Come è stato già anticipato, il vettore di interruzione n.10h del BIOS fornisce una numerosa serie di servizi destinati alla gestione del supporto video; analizziamo allora i principali di tali servizi che si rivolgono espressamente alle modalità testo.

9.3.1 Servizio n. 00h: Set Video Mode

Questo servizio permette di selezionare la modalità video desiderata. Il servizio n.00h permette di selezionare la modalità video desiderata; a tale proposito, bisogna caricare in AL uno dei codici visibili nelle figure dalla 9.2 alla 9.6.
Dopo la chiamata della INT 10h, nel registro AL viene restituito un codice che indica sommariamente la modalità video corrente; i valori possibili sono: 20h (indica che la modalità video selezionata è maggiore di 07h), 30h (indica che la modalità video selezionata è una tra 00h, 01h, 02h, 03h, 04h, 05h, 07h), 3Fh (indica che la modalità video selezionata è la 06h).
Quando si attiva una qualunque modalità video, l'intero contenuto dello schermo viene cancellato; se l'utente non vuole che ciò avvenga, deve porre a 1 il bit 7 di AL prima di chiamare il servizio n.00h.

9.3.2 Servizio n. 01h: Set Cursor Size

Questo servizio permette di impostare lo spessore del cursore sullo schermo. Il servizio 01h permette di impostare l'altezza in pixel del cursore sullo schermo; a tale proposito, bisogna tenere presente che ogni cella è suddivisa in tante linee orizzontali (da 1 pixel di spessore ciascuna) denominate scan lines (linee di scansione) e numerate con gli indici 0, 1, 2, ... a partire da quella più in alto.
Nel caso, ad esempio, della modalità video 03h, abbiamo visto che ogni cella ha un'altezza in pixel pari a 16, per cui la cella stessa risulterà composta da 16 linee di scansione numerate da 0 (quella più in alto) a 15; il programmatore può impostare lo spessore in pixel del cursore specificando la scan line iniziale (top scan line) e quella finale (bottom scan line) tra le quali il cursore stesso risulterà compreso.
La top scan line deve essere specificata nei bit 0, 1, 2, 3, 4 di CH, mentre la bottom scan line deve essere specificata nei bit 0, 1, 2, 3, 4 di CL; i bit 5, 6, 7 di CL vengono ignorati.
Il bit 7 di CH deve valere 0, mentre i bit 5, 6 di CH permettono di impostare la modalità di lampeggiamento del cursore; i valori possibili sono: 00h (normale), 01h (invisibile), 10h (erratico), 11h (lento).

9.3.3 Servizio n. 02h: Set Cursor Position

Questo servizio permette di modificare la posizione del cursore sullo schermo. Attraverso questo servizio il programmatore può posizionare il cursore lampeggiante in una qualsiasi cella dello schermo; a tale proposito, le nuove coordinate devono comprendere la riga e la colonna della cella in cui il cursore deve essere posizionato.
Il registro DH indica la nuova riga, mentre il registro DL indica la nuova colonna; ad esempio, nella modalità video 03h da 80x25 celle, le righe sono comprese tra 0 (quella più in alto) e 24, mentre le colonne sono comprese tra 0 (quella più a sinistra) e 79.
Il registro BH indica la pagina video in cui visualizzare il cursore; per le modalità con una sola pagina video, BH deve valere sempre 00h.

9.3.4 Servizio n. 03h: Get Cursor Position and Size

Questo servizio restituisce una serie di informazioni sul cursore lampeggiante. Questo servizio restituisce una serie di informazioni relative allo stato corrente del cursore lampeggiante; tali informazioni comprendono lo spessore del cursore in pixel e la sua posizione (coordinate riga, colonna) sullo schermo.

9.3.5 Servizio n. 05h: Select Active Display Page

Questo servizio permette di impostare la pagina video corrente. Attraverso questo servizio, il programmatore può impostare la nuova pagina video attiva (cioè, visibile sullo schermo); subito dopo l'esecuzione di tale servizio, la precedente pagina video diventa invisibile.

9.3.6 Servizio n. 08h: Read Character and Attribute at Cursor Position

Questo servizio permette di ottenere il simbolo e l'attributo relativo alla cella in cui si trova il cursore. Chiamando questo servizio, possiamo ottenere l'attributo e il codice ASCII relativi alla cella in cui si trova in quel momento il cursore dello schermo; è anche possibile ottenere le stesse informazioni accedendo direttamente alla memoria video.

9.3.7 Servizio n. 09h: Write Character and Attribute at Cursor Position

Questo servizio permette di modificare il simbolo e l'attributo presenti nella cella in cui si trova il cursore. Utilizzando questo servizio è possibile impostare dei nuovi valori per il codice ASCII e l'attributo relativi alla cella in cui si trova in quel momento il cursore dello schermo; il simbolo da visualizzare viene ripetuto per un numero di volte specificato in CX.
Si può ottenere lo stesso risultato accedendo direttamente alla memoria video.

9.3.8 Servizio n. 0Fh: Get Current Video Mode

Questo servizio restituisce informazioni relative alla modalità video correntemente selezionata. Attraverso questo servizio il programmatore può ottenere tutte le informazioni relative alla modalità video correntemente selezionata; il valore restituito in AL è uno di quelli visibili nelle figure dalla 9.2 alla 9.6.

9.3.9 Servizio n. 11h: Text Mode Character Generator

Questo servizio permette di selezionare un vettore di bitmap per i simboli da visualizzare sullo schermo. Attraverso questo servizio, possiamo impostare il vettore contenente le bitmap dei simboli da visualizzare sullo schermo; è possibile selezionare uno dei vettori memorizzati nella ROM, oppure un vettore di bitmap personalizzate.

Se AL vale 00h o 10h, allora questo servizio permette di caricare un vettore di bitmap personalizzate predisposto dal programmatore; in tal caso, la coppia ES:BP deve puntare al vettore, CX deve contenere il numero di elementi del vettore, BH deve contenere l'ampiezza in byte di ogni bitmap, BL deve contenere il blocco da caricare nella mappa 2, DX deve contenere l'offset delle bitmap all'interno del blocco nella mappa 2.
I valori da caricare in DX e BL possono essere ricavati dal servizio 1Bh illustrato più avanti; i valori che ci interessano si trovano agli offset 2Bh e 2Ch della tabella di informazioni restituita dal servizio stesso.

Se AL contiene un valore diverso da 00h o 10h, allora questo servizio permette di selezionare uno dei vettori di bitmap presenti nella ROM; in particolare: In tutti questi casi BL deve valere 00h.

9.3.10 Servizio n. 12h: Select Vertical Resolution (VGA)

Questo servizio permette di impostare il numero di linee di scansione per la modalità testo degli adattatori VGA. Attraverso questo servizio, possiamo modificare l'altezza in pixel dello schermo (numero di linee di scansione dello schermo) in modalità testo; ciò è possibile solo in presenza di un adattatore VGA.
Il nuovo numero di line di scansione deve essere specificato attraverso il registro AL; i valori permessi sono: 00h (200 linee di scansione), 01h (350 linee di scansione), 02h (400 linee di scansione).

Sfruttando questo servizio e quello n.11h presentato in precedenza, si può ottenere un interessante effetto che consiste nell'impostare una modalità testo 03h da 80x50 celle in combinazione con un vettore di bitmap da 8x8 pixel ciascuna; il procedimento da utilizzare è il seguente: In pratica, selezioniamo una normale modalità video testo 03h con risoluzione di schermo da 720x400 pixel (400 scan lines); l'aspetto particolare sta nel fatto che ci serviamo di font da 8x8 pixel che ci permettono di sfruttare 400/8=50 righe di celle (al posto delle 400/16=25 righe di celle che si hanno con l'utilizzo dei normali font da 8x16 pixel).

Per tornare alla normale visualizzazione da 80x25 celle, basta semplicemente eseguire il codice:

9.3.11 Servizio n. 13h: Write String

Questo servizio permette di visualizzare una stringa sullo schermo. Il servizio 13h permette di visualizzare una stringa sullo schermo a partire dalla cella che si trova alle coordinate DH (riga), DL (colonna); la stringa deve essere puntata dalla coppia ES:BP e deve terminare con il simbolo '$'.
Il registro AL permette di selezionare alcune opzioni supplementari attraverso i bit in posizione 0 e 1 (i bit dal 2 al 7 sono riservati); il bit 0 permette di stabilire se la posizione del cursore deve essere aggiornata (1) o meno (0) dopo la scrittura, mentre il bit 1 indica se la stringa è formata solo da simboli (0) o da una alternanza di simboli e attributi (1).
Il registro BL permette di impostare l'attributo comune di tutte le celle occupate dalla stringa; ovviamente, BL viene preso in considerazione solo se il bit 1 di AL vale 0.

9.3.12 Servizio n. 1Bh: Functionality/State Information

Questo servizio permette di ottenere una dettagliata lista di informazioni sull'adattatore video presente nel computer. Il servizio 1Bh riempie un vettore da 64 byte, puntato da ES:DI, con un elenco dettagliato di informazioni sull'adattatore video presente nel computer; il blocco da 64 byte necessario per contenere il vettore deve essere allocato dal programmatore.
Per maggiori dettagli su questo servizio si può consultare la documentazione presente nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito. (vedere la bibliografia in fondo alla pagina).

9.4 Esempi pratici

I numerosi servizi offerti dalla INT 10h, pur essendo molto semplici e comodi da usare, presentano la caratteristica negativa di una relativa lentezza; come sappiamo, ciò è dovuto al fatto che spesso le ISR eseguono prudenzialmente una notevole serie di controlli che finiscono per appesantire notevolmente il codice.
Questa situazione diventa particolarmente problematica quando si ha a che fare con dispositivi (come la memoria video) che comportano trasferimenti di grosse quantità di dati alla massima velocità possibile; proprio per questo motivo, gli esempi che seguono fanno ricorso ad un metodo molto più efficiente che consiste nell'accesso diretto alla VRAM.

9.4.1 Accesso alle 8 pagine video della modalità 03h da 80x25 celle

Partiamo subito con un programma che mostra come gestire le 8 pagine video disponibili nella modalità standard 03h da 80x25 celle; la Figura 9.11 illustra il listato del programma VIDPAGES.ASM. Questo programma riempie le 8 pagine video disponibili nella modalità testo standard 03h da 80x25 celle; successivamente, le informazioni memorizzate in VRAM vengono mostrate in sequenza sullo schermo.

La pagina video corrente è la numero 0 e viene riempita con 80x25=4000 coppie [simbolo, attributo]; il simbolo iniziale è il codice ASCII (41h) della lettera 'A', mentre l'attributo iniziale è 12h=00010010b e cioè: sfondo blu scuro e testo verde scuro.
Per accelerare al massimo il trasferimento dati nella VRAM, mettiamo due di queste coppie (12411241h) in EAX; a questo punto trasferiamo 2000 coppie (4000 byte) in VRAM attraverso REP STOSD.
Siccome stiamo scrivendo nella pagina video corrente (la numero 0), tutti i dati appena trasferiti in VRAM saranno visualizzati istantaneamente sul monitor.

Ad ogni loop incrementiamo di 01h il codice ASCII del simbolo da visualizzare, mentre il byte degli attributi viene incrementato di 12h (per influenzare lo sfondo e il primo piano); nel caso, ad esempio, del primo loop, otteniamo:
EAX = EAX + 12011201h = 12411241h + 12011201h = 24422442h
Osserviamo che dopo l'esecuzione dell'istruzione REP STOSD con CX=1000, il registro DI risulterà contenere il valore 4000; per far puntare DI all'offset della successiva pagina video (da 4096 byte) dobbiamo quindi incrementare lo stesso DI di 96 byte.

Il loop delimitato dall'etichetta show_vpages non fa altro che visualizzare in sequenza le altre 7 pagine video che abbiamo appena riempito (la n.0 è già visibile); a tale proposito, utilizziamo il servizio n.05h della INT 10h.

Prima di terminare, il programma provvede a ripristinare lo stato dell'adattatore video in modo che la pagina video n.0 torni ad essere quella attiva. Come si può facilmente intuire, la struttura del programma di Figura 9.11 si presta perfettamente per l'uso dei segmenti di programma con attributo ABSOLUTE; in base allora a quanto è stato esposto nel Capitolo 12 della sezione Assembly Base, possiamo definire un segmento di programma del tipo: In questo modo, MASM crea un segmento di programma che parte dall'indirizzo logico assoluto B800h:0000h, coincidente proprio con l'inizio del frame buffer per la modalità video testo a colori; ponendo ora ES=VIDBUFFER, possiamo facilmente effettuare operazioni di I/O sulla VRAM utilizzando un registro puntatore (ad esempio, DI) in combinazione con lo stesso ES!

9.4.2 Accesso ad una qualunque cella dello schermo

Se vogliamo accedere in I/O ad una qualunque cella dello schermo, non dobbiamo fare altro che applicare semplicissime formule di geometria; a tale proposito, basta ricordare che lo schermo ci appare sotto forma di matrice di celle, mentre la VRAM è, in realtà, un vettore di BYTE.
Nel caso, ad esempio, della modalità testo standard 03h da 80x25 celle, abbiamo 80 colonne numerate da 0 a 79 e 25 righe numerate da 0 a 24; ad ogni cella viene assegnata una WORD per cui, se vogliamo sapere a quale WORD della VRAM corrisponde la cella di coordinate x, y (colonna, riga) della pagina video di indice i, basta calcolare:
(i * 4096) + (y * (2 * 80)) + (x * 2)
Ad esempio, la cella di coordinate 13, 8 della pagina video 0 risulta associata alla WORD di indice:
(0 * 4096) + (8 * (2 * 80)) + (13 * 2) = 0 + 1280 + 26 = 1306 = 051Ah
del vettore della VRAM; di conseguenza, il simbolo da visualizzare nella cella si trova all'indirizzo B800h:051Ah, mentre l'attributo si trova all'indirizzo B800h:051Bh.

Per velocizzare le moltiplicazioni si può notare che 4096=212; possiamo affermare allora che, simbolicamente:
(i * 4096) = i * 212 = SHL i, 12
Analogamente:
(x * 2) = x * 21 = SHL x, 1
Il valore 2 * 80 = 160, invece, non può essere espresso sotto forma di potenza intera di 2 o somma di potenze intere di 2; in un caso del genere siamo costretti a svolgere la moltiplicazione ordinaria.

9.4.3 Visualizzazione di numeri interi sullo schermo

In base a quanto è stato esposto in questo capitolo, abbiamo appurato che sullo schermo è possibile visualizzare in modo diretto solamente stringhe; ciò vale, sia per la modalità testo, sia per la modalità grafica.
Tutto ciò significa che non è possibile visualizzare numeri direttamente sullo schermo; per fare una cosa del genere, è necessario prima convertire i numeri stessi in quelle che, nella sezione Assembly Base, abbiamo definito stringhe numeriche.

Partiamo dal caso più semplice rappresentato dai numeri binari che, come sappiamo, sono composti da sequenze dei due soli simboli '0' e '1'; nell'ipotesi di voler operare sui numeri binari a 8 bit, definiamoci allora le due stringhe: Utilizzando ora DS per gestire il segmento dati contenente le due stringhe, possiamo scrivere: Il registro AL contiene il numero binario a 8 bit da convertire in una stringa; il registro DI contiene l'offset dell'ottavo BYTE (da sinistra) di str_bin8 (ricordiamoci che le stringhe vengono visualizzate da sinistra a destra mentre, nel sistema posizionale, il peso delle cifre di un numero cresce procedendo da destra verso sinistra).
Ripetiamo ora per 8 volte il loop delimitato dall'etichetta bin8_to_str; ad ogni iterazione, trasferiamo in BL il contenuto di AL ed isoliamo il bit meno significativo dello stesso BL.
A questo punto, tenendo presente che BH=0, si vede subito che, se BL=0, allora str_cifre+BX punta al simbolo '0'; analogamente, se BL=1, allora str_cifre+BX punta al simbolo '1'.
Tale simbolo, che rappresenta il codice ASCII di '0' o di '1', viene trasferito in DL; a sua volta, il contenuto di DL viene trasferito in str_bin8 attraverso il puntatore DI.
Prima di ripetere il loop spostiamo di un posto verso destra i bit di AL in modo che il prossimo bit da convertire si venga a trovare in posizione 0 nello stesso registro AL; il registro DI viene decrementato di 1 in modo che punti ad un nuovo BYTE di str_bin8.
Terminate le 8 iterazioni, abbiamo ottenuto in str_bin8 una stringa che riproduce simbolicamente gli 8 bit del valore numero_binario.

Nel caso di numeri binari a 16 o più bit, il procedimento è assolutamente analogo; a tale proposito, basta modificare opportunamente il numero di iterazioni da effettuare ricordandosi anche di applicare SHR ad AX per i numeri a 16 bit e ad EAX per i numeri a 32 bit.

Passiamo ora al caso altrettanto semplice dei numeri esadecimali che, come sappiamo, sono composti da sequenze dei simboli da '0' a '9' e da 'A' a 'F'; anche in questo caso, il procedimento è del tutto analogo a quello seguito per i numeri binari.
Osserviamo subito che ogni cifra esadecimale è composta da 4 bit e rappresenta un valore compreso tra 0 e 15; nell'ipotesi allora di voler operare sui numeri esadecimali a 16 bit, definiamoci le due stringhe: Utilizzando ora DS per gestire il segmento dati contenente le due stringhe, possiamo scrivere: Come si può notare, l'unica differenza rispetto ai numeri binari sta nel fatto che operiamo sui nibble anziché sui singoli bit; tenendo presente che BH=0, ogni nibble isolato in BL è tale che BX conterrà un valore compreso tra 0 e 15 per cui str_cifre+BX punterà al corrispondente simbolo esadecimale.

Il caso più impegnativo è sicuramente quello dei numeri interi in base 10; come sappiamo, le difficoltà sono legate al fatto che 10 non può essere espresso sotto forma di potenza intera di 2.
Consideriamo, ad esempio, il numero 27952; questo numero richiede almeno 16 bit, ma non avrebbe alcun senso pensare di suddividerlo in gruppi di bit in modo da ricavare una delle cifre decimali da ciascuno di tali gruppi!

Un altro aspetto di notevole importanza è dato dal fatto che in presenza di numeri interi relativi in base 10, dobbiamo occuparci anche della gestione del segno; questo problema non si pone per i numeri relativi binari o esadecimali in quanto il segno è codificato nel numero stesso (bit di segno).
La gestione del segno avviene in modo molto semplice sfruttando il concetto di complemento a 2 illustrato nel Capitolo 4 della sezione Assembly Base; per analizzare in pratica il metodo da seguire, supponiamo di operare sui numeri interi relativi a 16 bit.
Come sappiamo, in questo caso si parla di "codifica dei numeri interi relativi in complemento a 2 modulo 65536; il nostro insieme sarà allora rappresentato da tutti i numeri interi relativi compresi tra -32768 e +32767.

Per convertire un numero intero decimale in una stringa numerica, si può utilizzare un procedimento che già conosciamo e che consiste nel sottoporre il numero stesso ad una serie di divisioni per 10 (cioè, per la base); in questo modo si ottiene una sequenza di resti che, nel loro insieme, rappresentano le cifre del nostro numero (a partire da quella meno significativa).
Vediamo un esempio relativo al numero 23272; dividendo ripetutamente per 10 otteniamo: Come si può notare, il procedimento termina quando si ottiene un quoziente pari a 0; unendo tutti i resti ottenuti si ricava proprio 23272.

Nell'ipotesi allora di voler operare sui numeri interi relativi a 16 bit, definiamoci le due stringhe: Utilizzando ora DS per gestire il segmento dati contenente le due stringhe, possiamo scrivere: Prima di tutto, se il numero è positivo lo lasciamo così com'è; se, invece, è negativo, gli mettiamo un segno - davanti e lo neghiamo (cioè, lo convertiamo nel suo equivalente positivo).
A questo punto possiamo applicare il metodo delle divisioni successive; ogni cifra ottenuta (dal resto) viene usata per leggere il corrispondente simbolo da str_cifre.

Bibliografia

Cristopher, Feigenbaum, Saliga - MS-DOS MANUALE DI PROGRAMMAZIONE - Mc Graw Hill

VGABIOS.TXT - Elenco servizi Video BIOS della INT 10h
(disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito - vgabios.zip)