Assembly Avanzato con NASM

Capitolo 11: La memoria video in modalità grafica VESA


Nel 1987, mentre la IBM studiava le possibili evoluzioni dell'adattatore VGA, una azienda di nome NEC Home Electronics suscitò grande clamore mettendo in commercio una potente scheda video per PC capace di supportare una risoluzione grafica da 800x600 pixel a 256 colori; per sottolineare il notevole balzo in avanti fatto rispetto al modo VGA, questa nuova modalità video venne denominata Super VGA o SVGA.
La IBM cercò di replicare a questa mossa imprevista e realizzò l'adattatore 8514/A capace di supportare risoluzioni grafiche sino a 1024x768 pixel a 256 colori; forse però a causa della quasi totale assenza di adeguata documentazione sulle specifiche hardware, questo nuovo adattatore ottenne uno scarso successo.
Stessa sorte toccò nel 1990 ad un ancora più potente adattatore denominato eXtended Graphics Array o XGA; questo adattatore era capace di supportare risoluzioni grafiche da 1024x768 pixel a 256 colori e da 640x480 pixel a ben 65536 colori!

Nel frattempo, l'iniziativa della NEC era stata seguita rapidamente da diversi altri produttori di hardware i quali misero in commercio schede video di classe SVGA con caratteristiche sempre più evolute; la fine del dominio IBM nel settore degli adattatori grafici era ormai iniziata in modo irreversibile!

Analizzando ciò che è diventato oggi il mondo delle schede video, possiamo affermare che la fine del dominio IBM ha avuto enormi conseguenze positive in quanto i produttori di hardware si sono potuti svincolare da standard spesso troppo restrittivi; in questo modo è stato possibile dare libero sfogo alla creatività dei progettisti determinando così una evoluzione vertiginosa del settore della grafica computerizzata.
In questa fase di evoluzione, una tappa fondamentale è stata la nascita dell'azienda 3Dfx, fondata nel 1994 da vari colossi della computer grafica come Digital Equipment Corporation, Silicon Graphics, MIPS Computer Systems e Pellucid; l'obiettivo era quello di portare anche nel mondo dei PC il concetto di accelerazione grafica, sino ad allora riservato solo ad ambienti professionali.
Per raggiungere tale obiettivo la 3Dfx mise in commercio schede video dotate di una novità rivoluzionaria rappresentata dalla GPU o Graphics Processing Unit; la GPU determina un enorme aumento delle prestazioni generali del sistema in quanto si fa carico di tutto il pesantissimo lavoro di elaborazione grafica che altrimenti andrebbe a gravare sulla CPU.

L'effetto negativo legato alla fine del dominio IBM è stato sicuramente il caos che si è venuto a creare in relazione alle specifiche hardware delle nuove schede video; in particolare, i vari produttori hanno abbandonato ogni convenzione in relazione alla codifica delle modalità video e degli indirizzi hardware dei registri.
Questa situazione ha avuto pesanti ripercussioni sui programmatori i quali si sono trovati costretti a destreggiarsi tra una miriade di codici e indirizzi che differivano da una scheda video all'altra; a titolo di esempio, la Figura 11.1 illustra i differenti codici utilizzati dai vari produttori di schede video per indicare la modalità 1024x768 a 16 colori. Da un lato, non avrebbe avuto senso imporre delle convenzioni univoche su aspetti legati all'hardware in quanto così facendo si sarebbe tornati indietro ai tempi del dominio IBM; d'altra parte, bisognava necessariamente trovare una qualche soluzione per venire incontro alle esigenze dei programmatori, senza limitare in alcun modo la creatività dei progettisti.
Fortunatamente, gli stessi produttori di schede video si resero conto della situazione e si riunirono per dare vita ad un consorzio denominato Video Electronics Standards Association o VESA; lo scopo che si prefiggeva tale consorzio era proprio quello di mettere a disposizione degli sviluppatori una interfaccia di programmazione standard capace di semplificare al massimo l'accesso a tutte le caratteristiche di qualsiasi scheda video dotata di supporto VESA.

11.1 Le specifiche VESA VGA BIOS Extension

Una delle attività più importanti svolte dal comitato VESA è la definizione e l'aggiornamento continuo delle specifiche VGA BIOS Extension o VBE; scopo di tali specifiche è quello di estendere i servizi video forniti dal BIOS attraverso la INT 10h in modo da supportare tutte le modalità SVGA disponibili con le schede video aderenti allo standard VESA. Nel seguito del capitolo ci occuperemo delle specifiche VBE versione 3.0 pubblicate nel 1998 e destinate alla modalità reale.

Una scheda video VESA compatibile supporta quindi tutti i servizi standard imposti a suo tempo dalla IBM e, in più, una ulteriore serie di servizi definiti dalle specifiche VBE; il programmatore accede a tutti questi servizi attraverso la normalissima procedura di chiamata della INT 10h.
I vecchi programmi grafici possono continuare a girare senza modifiche richiedendo i normali servizi standard IBM e ignorando completamente le specifiche VBE; i nuovi programmi grafici possono accedere a tutti i servizi VBE abbandonando del tutto i servizi standard IBM.
Le specifiche VBE includono anche i due vecchi servizi 00h (Set Video Mode) e 0Fh (Read Current Video State); in ogni caso, per i nuovi programmi che vogliono avvalersi delle specifiche VBE è raccomandato l'uso dei servizi alternativi 02h (Set Super VGA Video Mode) e 03h (Read Current Super VGA Video State).

Per distinguere i servizi VBE da quelli ordinari, viene utilizzato il valore 4Fh che deve essere caricato in AH; la chiamata di un servizio VBE assume quindi il seguente aspetto generale: Tutte le informazioni di stato (VBE Return Status) relative al servizio richiesto con il codice precedente vengono restituite in AL e AH; il contenuto di tali registri assume il seguente significato espresso con gli operatori booleani del linguaggio C: Le specifiche VBE raccomandano che un qualunque valore diverso da zero in AH venga trattato dal programmatore come una condizione generale di errore; è anche importante testare il valore restituito in AL in quanto determinati servizi potrebbero essere disponibili solo con le versioni più recenti delle specifiche VBE.

Per convenzione, tutte le modalità video VESA devono essere rappresentate da un codice a 16 bit il cui bit di posto 8 deve valere 1; ne consegue che tali codici partono dal valore minimo 0100h.
La modalità video è rappresentata quindi dai primi 9 bit del codice a 16 bit; i restanti 7 bit vengono utilizzati per abilitare o disabilitare determinate funzionalità.
I bit nelle posizioni 9 e 10 sono riservati e devono valere 0.
Il bit in posizione 11 serve per selezionare la modalità di controllo della frequenza di scansione verticale del monitor; se questo bit vale 0 si abilita la modalità predefinita controllata dal BIOS, mentre se questo bit vale 1 si lascia al programmatore il compito di effettuare le necessarie impostazioni.
I bit nelle posizioni 12 e 13 sono riservati e devono valere 0.
Il bit in posizione 14 serve per abilitare o disabilitare l'uso del frame buffer lineare per l'accesso alla VRAM; se questo bit vale 0 viene usato un normale frame buffer che permette di vedere la VRAM suddivisa in una serie di blocchi di dimensione predefinita (segmentazione della modalità reale), mentre se questo bit vale 1 viene usato un frame buffer lineare che permette di vedere la VRAM come un unico blocco (modalità protetta).
Il bit in posizione 15 serve per abilitare o disabilitare la pulizia della VRAM quando viene attivata una nuova modalità video VESA; se questo bit vale 0 il vecchio contenuto della VRAM viene cancellato, mentre se questo bit vale 1 il vecchio contenuto della VRAM viene preservato.

La Figura 11.2 illustra l'elenco delle principali modalità video grafiche ufficiali aggiornate allo standard VESA versione 3.0; la risoluzione è espressa in pixel. La Figura 11.3 illustra l'elenco delle principali modalità video testo ufficiali aggiornate allo standard VESA versione 3.0; la risoluzione è espressa in celle.

11.2 Accesso alla VRAM in modalità reale attraverso i servizi VBE

In modalità reale, l'accesso alla VRAM deve avvenire necessariamente in modo segmentato; come sappiamo, ciò è dovuto al fatto che in tale modalità la CPU vede qualunque tipo di memoria suddivisa in tanti blocchi (segmenti) ciascuno dei quali non può superare la dimensione di 65536 byte (in quanto i registri puntatori a 16 bit permettono di esprimere un offset compreso tra 0 e 65535).
Per venire incontro a questa situazione, le specifiche VBE implementano l'accesso alla VRAM in modalità reale attraverso una tecnica del tutto simile a quella illustrata nel capitolo dedicato allo standard EMS; la Figura 11.4 illustra tutti i dettagli. Osserviamo subito che la VRAM viene suddivisa in tanti blocchi consecutivi e contigui denominati memory banks (banchi di memoria), indicizzati a partire da 0; la dimensione fissa di ogni banco rappresenta la granularità della VRAM.
Il valore più usato per la granularità è pari a 64 KiB, ma non è raro trovare schede video con granularità 8 KiB, 16 KiB o 32 KiB; nell'esempio di Figura 11.4 la granularità è pari a 16 KiB.

Per l'accesso ai vari banchi di memoria il programmatore ha a disposizione due apposite finestre (in genere da 64 KiB ciascuna) denominate Window A e Window B, nelle quali possiamo mappare qualunque area della VRAM; la presenza di due finestre (anziché del singolo frame buffer dello standard EMS) permette di ottimizzare al massimo le operazioni di I/O con la VRAM grazie al fatto che si può usare, ad esempio, la Window A per la lettura dei dati e la Window B per la scrittura dei dati.
Questo però è un caso ideale in quanto la maggior parte delle schede video supporta una singola finestra da utilizzare quindi, sia per la lettura, sia per la scrittura dei dati; ovviamente, è compito dei programmi individuare l'esatta configurazione disponibile (granularità della VRAM, numero di finestre di I/O, dimensione delle finestre, etc).

Come è stato già spiegato, ogni finestra di I/O può essere usata per mappare a piacere qualunque area della VRAM; in Figura 11.4, ad esempio, vediamo che la Window A (da 64 KiB) sta mappando la VRAM a partire dal banco 2 in modo da permettere la lettura diretta dei dati dai banchi 2, 3, 4, 5, mentre la Window B (da 64 KiB) sta mappando la VRAM a partire dal banco 9 in modo da permettere la scrittura diretta dei dati nei banchi 9, 10, 11, 12.

11.3 Servizi VBE 3.0

Analizziamo ora i principali servizi messi a disposizione dalle specifiche VBE versione 3.0; come è stato già anticipato, prenderemo in considerazione solamente i servizi destinati alla modalità reale.

11.3.1 Servizio n. 4F00h: Return VBE Controller Information

Questo servizio restituisce una numerosa serie di informazioni relative alla versione VBE supportata dal Video BIOS (VBIOS) e alle caratteristiche hardware dell'adattatore video. Un programma che intende accedere correttamente ai servizi VBE supportati dal BIOS della scheda video, deve prima raccogliere una serie completa di informazioni relative alle capacità hardware dell'adattatore; a tale proposito, è disponibile proprio il servizio 4F00h.
Questo servizio richiede che la coppia ES:DI stia puntando ad un blocco di memoria appositamente allocato dal programmatore; tale blocco viene riempito dal servizio 4F00h con una numerosa serie di informazioni che, necessariamente, possono variare a seconda della versione VBE supportata dal VBIOS.
Tutte le informazioni vengono restituite dal servizio 4F00h in una struttura che prende il nome di VbeInfoBlock; tale struttura assume le caratteristiche illustrate in Figura 11.5. Il membro VbeSignature viene riempito dal servizio 4F00h con la stringa "VESA"; lo stesso membro può essere usato dal programmatore per richiedere esplicitamente informazioni avanzate disponibili solo a partire dalla versione 2.0 delle specifiche VBE.
Se questo membro non viene inizializzato dal programmatore con l'apposita stringa "VBE2", il servizio 4F00h riempie solo i primi 256 byte della struttura VbeInfoBlock (come previsto dalla versione 1.0 delle specifiche VBE); se questo membro viene inizializzato dal programmatore con l'apposita stringa "VBE2", il servizio 4F00h riempie tutti i 512 byte della struttura VbeInfoBlock (come previsto dalla versione 2.0 o superiore delle specifiche VBE).

Il membro VbeVersion indica la versione VBE, in formato BCD, implementata nel VBIOS; ad esempio, il valore 0102h indica la versione 1.2, dove 1 è il major number, mentre 2 è il minor number.

Il membro OemStringPtr è un puntatore FAR ad una stringa C contenente informazioni relative all'Original Equipment Manufacturer o OEM (fabbricante dell'hardware); questa stringa ha una lunghezza massima raccomandata di 256 byte e può trovarsi memorizzata nella RAM o nella ROM della scheda video (a partire dalle specifiche VBE 3.0 la stringa si trova nel membro OemData della struttura VbeInfoBlock).

Il membro Capabilities fornisce informazioni su particolari caratteristiche grafiche supportate dall'adattatore video; si tratta di un valore a 32 bit che assume la struttura illustrata in Figura 11.6. Il bit 0 indica la struttura del DAC per la rappresentazione dei colori primari RGB; la sigla DAC sta per Digital to Analog Converter ed indica il dispositivo hardware che converte una terna RGB (digitale) in un segnale analogico da inviare al monitor.
Se il bit 0 vale 0, il DAC usa 6 bit per ogni colore primario; se il bit 0 vale 1, il DAC può essere programmato per usare 8 (o più) bit per ogni colore primario.

Il bit 1 indica la compatibilità VGA dell'adattatore video; controllando tale bit il programmatore può sapere se, anche quando si opera in modalità VBE, possono essere utilizzate le funzionalità tipiche degli adattatori VGA (indirizzi delle porte hardware, tavolozze dei colori, etc).
Se il bit 1 vale 0, l'adattatore è VGA compatibile; se il bit 1 vale 1, l'adattatore non è VGA compatibile (cioè, supporta solo le funzionalità standard VBE, ma non quelle VGA).

Il bit 2 indica la modalità operativa del DAC; se si eccettua il caso delle vecchie schede video, normalmente tale bit viene posto a 0 per indicare la modalità operativa predefinita.
Le vecchie schede video, per evitare il fastidioso fenomeno dello sfarfallio, richiedevano che la programmazione del DAC avvenisse in modo sincrono con il refresh verticale; per queste vecchie schede video, il bit 2 deve essere posto a 1.

Il bit 3 indica se l'adattatore video supporta via hardware l'effetto "stereoscopico"; si tratta di una tecnica che consiste nel ricorso ad un particolare sistema di suddivisione e visualizzazione delle immagini in modo da creare un effetto stereoscopico tridimensionale.
Se il bit 3 vale 0, l'adattatore non supporta l'effetto stereoscopico; se il bit 3 vale 1, l'adattatore possiede l'hardware necessario per il supporto dell'effetto stereoscopico.

Se il servizio 4F00h pone a 1 il bit 3, allora il bit 4 indica se il segnale di sincronizzazione stereoscopica arriva attraverso un apposito connettore denominato EVC; se il bit 4 vale 1, è presente un connettore EVC, mentre in caso contrario è presente un generico connettore VESA.


Tornando alla struttura VbeInfoBlock di Figura 11.5, il membro VideoModePtr contiene un puntatore FAR ad una lista di modalità video VESA supportate dall'adattatore; tali modalità sono indicate dai valori a 16 bit di Figura 11.2 e Figura 11.3, mentre la fine della lista è individuata dal valore FFFFh. Il membro TotalMemory indica la quantità totale di VRAM (espressa in numero di blocchi da 64 KiB ciascuno) fisicamente disponibile e indirizzabile attraverso il frame buffer; si tenga presente che, a seconda della modalità video selezionata, potrebbe risultare disponibile tutta o solo un parte della VRAM totale.
Quando si utilizza una macchina virtuale o un emulatore DOS, il membro TotalMemory fornisce in genere una quantità totale di VRAM inferiore a quella effettivamente presente sulla scheda video; ovviamente, ciò è dovuto al fatto che una parte consistente della VRAM è riservata all'uso esclusivo del SO ospitante (host).

Tutti i successivi membri della struttura VbeInfoBlock sono disponibili solo in presenza del supporto VBE 2.0 o superiore e contengono informazioni che gli OEM utilizzano per identificare l'hardware da essi prodotto; tali informazioni sono rappresentate da numeri in formato BCD e da stringhe C.
A partire dalle specifiche VBE 2.0, le informazioni relative all'OEM possono trovarsi inserite, eventualmente, nei 256 byte del membro OemData.


In base alle considerazioni appena esposte, possiamo affermare quindi che il servizio 4F00h può essere usato innanzi tutto per verificare se un determinato VBIOS supporta i servizi VBE; ciò avviene quando lo stesso servizio 4F00h restituisce AL=4Fh e AH=00h.
Se in AL e AH si ottengono codici di errore, bisogna ovviamente terminare il programma segnalando, attraverso un apposito messaggio, il mancato supporto VBE; se, invece, il supporto VBE è presente, si può usare il contenuto della struttura VbeInfoBlock per ricavare tutte le informazioni sull'adattatore video.

Si ricordi che, per ottenere le informazioni estese sul supporto VBE, bisogna chiamare il servizio 4F00h dopo aver inizializzato il membro VbeSignature con la stringa "VBE2"; come sappiamo, tali informazioni sono disponibili solo in presenza di un VBIOS che supporta le specifiche VBE 2.0 o superiori (cosa che può essere verificata attraverso il membro VbeVersion). In ogni caso, per evitare spiacevoli sorprese conviene sempre allocare 512 byte di memoria da destinare alla struttura VbeInfoBlock!

11.3.2 Servizio n. 4F01h: Return VBE Mode Information

Questo servizio restituisce una numerosa serie di informazioni relative ad una specifica modalità video supportata dall'adattatore. Per ciascuna delle modalità video restituite nella lista puntata da VideoModePtr (Figura 11.5), è possibile richiedere una serie di ulteriori informazioni estremamente dettagliate; tali informazioni vengono restituite dal servizio 4F01h in una struttura denominata ModeInfoBlock.
La struttura ModeInfoBlock ha una dimensione pari a 256 byte che devono essere allocati dal programma che richiede il servizio 4F01h; la Figura 11.7 illustra tutti i dettagli. Il membro ModeAttributes è un valore a 16 bit che fornisce importanti caratteristiche hardware della modalità video selezionata; il significato dei vari bit è illustrato dalla Figura 11.8. Il bit 0 è molto importante in quanto ci permette di sapere se la modalità video VESA che abbiamo specificato è effettivamente attivabile sul computer in uso; come è stato spiegato in precedenza, può capitare che una determinata modalità video VESA, pur essendo supportata dalla scheda video, non possa essere attivata a causa delle limitazioni del monitor e/o della VRAM insufficiente.
Il bit 2 indica se il BIOS supporta le funzioni di emulazione del terminale; in caso affermativo, il programmatore può chiamare eventualmente la INT 10h per usufruire dei servizi standard 01h, 02h, 06h, 07h, 09h, 0Ah, 0Eh (vedere il documento VGABIOS.TXT disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito.
Il bit 3 indica se la modalità video selezionata è monocromatica o a colori; analogamente, il bit 4 indica se la modalità video selezionata è testuale o grafica.
Il bit 5 indica se la modalità video selezionata è "VGA compatibile"; in tal caso, il programmatore può eventualmente accedere a tutte le caratteristiche hardware standard degli adattatori VGA (porte di I/O, frame buffer, etc).
Il bit 6 (che deve essere combinato con il bit 7) indica se la modalità video selezionata permette l'accesso alla VRAM attraverso le apposite finestre (Window A e Window B) di I/O (accesso segmentato in modalità reale); in caso affermativo, come è stato già spiegato, la VRAM risulta suddivisa in tanti blocchi (generalmente da 64 KiB ciascuno) denominati memory banks (banchi di memoria).
Il bit 7 indica se la modalità video selezionata permette l'accesso alla VRAM attraverso un frame buffer lineare (modalità protetta); il bit 7 si combina con il bit 6 per formare un codice a 2 bit per il quale sono permessi solamente i tre valori 00b (solo accesso segmentato), 10b (accesso segmentato o lineare), 11b (solo accesso lineare).
Il bit 8 indica se la modalità video selezionata supporta il "double scan"; nelle modalità video "double scan" (con risoluzioni standard da 320x200, 320x240, 400x300) ogni scan line viene scandita due volte durante il refresh verticale.
Il bit 9 indica se la modalità video selezionata supporta la scansione interlacciata delle scan line; in caso affermativo, durante il vertical refresh, vengono scandite prima le scan line di indice pari e poi quelle di indice dispari.
Il bit 10 indica se la modalità video selezionata supporta il "triple buffering"; in caso affermativo i programmi possono usufruire di tre appositi buffer attraverso i quali si possono velocizzare notevolmente le operazioni di rendering sullo schermo.
Il bit 11 indica se la modalità video selezionata supporta lo "stereoscopic display"; come è stato già spiegato, si tratta di una tecnica che consiste nel ricorso ad un particolare sistema di suddivisione e visualizzazione delle immagini in modo da creare un effetto stereoscopico tridimensionale.
Il bit 12 indica se la modalità video selezionata supporta l'indirizzamento del "dual display"; in caso affermativo, l'effetto stereoscopico può essere creato con l'ausilio di due monitor.

Tornando alla struttura di Figura 11.7, i membri WinAAttributes e WinBAttributes forniscono le caratteristiche delle finestre per l'accesso segmentato alla VRAM; ciascuno dei due valori a 8 bit assume la struttura mostrata in Figura 11.9. Nella gran parte dei casi, risulta disponibile unicamente la Window A la quale sarà quindi leggibile e scrivibile; se nessuna delle due finestre è supportata, il programmatore può accedere alla VRAM solo attraverso un frame buffer lineare (modalità protetta).

Il membro WinGranularity indica la dimensione in KiB di ciascuno dei banchi di memoria visibili in Figura 11.4; si tratta quindi dell'incremento (decremento) in KiB necessario per posizionare la finestra di I/O sul banco di memoria successivo (precedente) a quello in cui ci troviamo.

Il membro WinSize indica la dimensione in KiB di ciascuna delle due finestre di I/O; il valore raccomandato dal comitato VESA è pari a 64 KiB (pari cioè alla dimensione di un segmento di memoria della modalità reale) e quindi, i programmi possono sempre assumere WinSize=64.

I membri WinASegment e WinBSegment hanno senso solo per la modalità reale e contengono l'indirizzo di memoria da cui parte la relativa finestra di I/O; come al solito, si tratta di un indirizzo allineato al paragrafo, per cui viene fornita la sola componente Seg a 16 bit (la componente Offset vale implicitamente 0000h).
Nel caso più frequente è presente una sola finestra che parte all'indirizzo A000h:0000h per le modalità grafiche, B800h:0000h per le modalità testo a colori e B000h:0000h per le modalità testo in bianco e nero; se una determinata finestra non è supportata, il relativo membro vale 0000h.

Il membro WinFuncPtr è un puntatore FAR da utilizzare per la chiamata di una apposita funzione attraverso la quale si può posizionare una finestra di I/O nell'area desiderata della VRAM; se tale funzione non è disponibile, il relativo membro vale 0000h:0000h (puntatore a NULL).
L'utilizzo della "window function" permette di velocizzare notevolmente le operazioni di posizionamento delle finestre di I/O; in assenza di tale funzione, il programmatore deve necessariamente ricorrere al meno efficiente servizio 4F05h della INT 10h (vedere più avanti).

Il membro BytesPerScanLine indica la dimensione in byte di ogni linea di scansione dello schermo; come abbiamo visto nel precedente capitolo, tale valore coincide con la larghezza in pixel dello schermo solo nelle modalità grafiche che utilizzano 1 byte per identificare il colore di ogni pixel.

I membri XResolution e YResolution indicano le dimensioni (larghezza e altezza) dello schermo nella modalità video selezionata; per le modalità grafiche si tratta di valori in pixel, mentre per le modalità testuali si tratta di valori in celle di carattere.

I membri XCharSize e YCharSize indicano le dimensioni in pixel di ogni cella di carattere nella modalità video selezionata.

Il membro NumberOfPlanes indica il numero di piani di bit utilizzati dalla modalità video che si sta usando; come abbiamo visto nel precedente capitolo, alcune particolari modalità video rappresentano la VRAM sotto forma di piani di bit sovrapposti.

Il membro BitsPerPixel indica il numero di bit utilizzato per rappresentare il colore di ciascun pixel nella modalità video che si sta usando; come sappiamo, spesso tale valore rappresenta un indice relativo agli elementi di una apposita tavolozza di colori.

Il membro MemoryModel codifica l'organizzazione della VRAM nella modalità video selezionata; si tratta di un codice a 8 bit che può assumere i valori illustrati in Figura 11.10. Il membro NumberOfBanks indica il numero di banchi di memoria nei quali si trovano raggruppate le scan line relative alla modalità grafica selezionata; per le modalità video che non utilizzano il raggruppamento in banchi delle scan line, il valore di questo membro è 1.
Dividendo l'indice n di una scan line per il valore NumberOfBanks si ottiene un quoziente che rappresenta l'indice b del banco che contiene la scan line n; il resto della divisione rappresenta, invece, l'indice i della scan line n relativo al banco b.

Il membro BankSize rappresenta la dimensione in KiB di ciascun banco utilizzato per raggruppare le scan line nella modalità video selezionata; per le modalità video che non utilizzano il raggruppamento in banchi delle scan line, il valore di questo membro è 0.

Il membro NumberOfImgPages rappresenta il numero totale, meno uno, di schermate complete che la VRAM è in grado di contenere contemporaneamente; se questo valore è maggiore di uno, è possibile gestire nella VRAM più schermate, ciascuna delle quali può essere poi trasferita velocemente sullo schermo.

I membri RedMaskSize, GreenMaskSize, BlueMaskSize e RsvdMaskSize rappresentano le dimensioni in bit delle tre componenti RGB (più una componente riservata) utilizzate per codificare i colori nelle modalità video che supportano tale caratteristica (Modello di memoria Direct Color o YUV); per le modalità video che non supportano le componenti di colore, questi quattro membri valgono 0.

I membri RedFieldPos, GreenFieldPos, BlueFieldPos e RsvdFieldPos rappresentano le posizioni del bit meno significativo di ciascuna delle tre componenti di colore (più una componente riservata) all'interno di una terna RGB; ad esempio, se le tre precedenti MaskSize sono 8:8:8, allora questi tre membri indicano le posizioni 16:8:0.
Per le modalità video che non supportano le componenti di colore, questi quattro membri valgono 0.

Il membro DirectColModeInfo contiene importanti informazioni relative alle modalità video che supportano le componenti di colore RGB; si tratta di un valore a 8 bit di cui solamente i primi due bit hanno significato.
Il bit in posizione 0 indica se la tavolozza dei colori è fissa (0) o programmabile (1); se tale bit vale 1, il programmatore può utilizzare il servizio 4F09h per caricare una tavolozza personalizzata.
Il bit in posizione 1 indica se i membri RsvdMaskSize e RsvdFieldPos sono usabili (1) o no (0); se tale bit vale 1, i colori RGB utilizzano anche una quarta componente (ad esempio, alcuni SO si servono di questa quarta componente per definire il grado di trasparenza del colore RGB di un pixel).

Tutti gli altri membri della struttura ModeInfoBlock hanno significato solo per la modalità protetta.

11.3.3 Servizio n. 4F02h: Set VBE Mode

Questo servizio permette di abilitare una modalità video VESA supportata dall'adattatore video. Il servizio 4F02h permette di abilitare una delle modalità video VESA supportate dall'hardware del computer che si sta utilizzando; il registro BX deve contenere l'opportuno codice di Figura 11.2.
Come è stato già spiegato in precedenza, il bit in posizione 11 di tale codice serve per selezionare la modalità di controllo della frequenza di scansione verticale del monitor; se questo bit vale 0 vengono utilizzate le impostazioni predefinite del BIOS, mentre se questo bit vale 1 vengono utilizzate le impostazioni personalizzate che il programmatore deve specificare in una struttura CRTCInfoBlock puntata da ES:DI.

A meno che si sappia esattamente ciò che si sta facendo, si consiglia vivamente di lasciare a 0 il bit in posizione 11; per maggiori dettagli sulla struttura CRTCInfoBlock si può consultare il documento VBE3.PDF disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito.

Un altro aspetto molto importante da ricordare è che una data modalità video, pur essendo supportata dal proprio adattatore, potrebbe non essere attivabile a causa della scarsità di VRAM o a causa delle limitazioni del monitor; proprio per questo motivo, prima di abilitare la modalità video desiderata conviene sempre consultare il bit in posizione 0 del membro ModeAttributes della struttura ModeInfoBlock.

11.3.4 Servizio n. 4F03h: Return Current VBE Mode

Questo servizio permette di ottenere informazioni sulla modalità video VESA attualmente abilitata. Il servizio 4F03h restituisce alcune sommarie informazioni sulla modalità video VESA corrente; tali informazioni risultano disponibili nel registro BX.
I bit nelle posizioni dalla 0 alla 13 contengono il codice della modalità video corrente (Figura 11.2). Il bit in posizione 14 indica se il modo video corrente supporta il frame buffer a finestre (0) o il frame buffer lineare (1); il bit in posizione 15 indica se, quando è stata attivata la modalità video corrente, la VRAM è stata cancellata (0) oppure no (1).

Si tenga presente che il servizio 4F03h restituisce le informazioni appena descritte solo quando la modalità video corrente è stata attivata con il servizio 4F02h; ciò può non accadere quando la modalità video corrente è stata attivata con il servizio standard 00h (Set Video Mode) della INT 10h.

11.3.5 Servizio n. 4F04h: Save/Restore Video State

Questo servizio permette di salvare o ripristinare lo stato hardware dell'adattatore video. Attraverso il servizio 4F04h è possibile salvare lo stato hardware corrente o ripristinare lo stato hardware precedente dell'adattatore video; la specifica operazione da eseguire deve essere indicata attraverso un codice nel registro DL. Il registro CX, attraverso i suoi 4 bit meno significativi, permette di specificare quale stato hardware si deve salvare (DL=01h) o ripristinare (DL=02h); il bit in posizione 0 si riferisce allo stato hardware del controller, il bit in posizione 1 si riferisce allo stato dei dati del BIOS, il bit in posizione 2 si riferisce allo stato hardware del DAC, il bit in posizione 3 si riferisce allo stato dei registri.
Naturalmente, è anche possibile combinare questi 4 bit; ponendo, ad esempio, CX=000Fh (00001111b), si indica che l'operazione di salvataggio o ripristino coinvolge tutte le quattro voci elencate in precedenza.

11.3.6 Servizio n. 4F05h: Display Window Control

Questo servizio permette di gestire l'accesso alla VRAM attraverso il frame buffer. Attraverso il servizio 4F05h è possibile controllare il posizionamento nella VRAM del frame buffer a finestre; l'operazione da svolgere viene specificata attraverso il registro BH, mentre la finestra coinvolta (A o B) viene specificata attraverso il registro BL. Il registro BL indica la finestra alla quale ci stiamo riferendo; il valore 0 indica la Window A, mentre il valore 1 indica la Window B.

Molti programmi fanno un uso piuttosto pesante del bank switching (commutazione di banco); in casi del genere, la chiamata del servizio 4F05h attraverso la INT 10h può provocare un sensibile calo delle prestazioni.
Per ovviare a questo inconveniente, il programmatore può anche servirsi della window function il cui indirizzo FAR, come già sappiamo, viene restituito nel membro WinFuncPtr della struttura ModeInfoBlock; è importante ricordare che, se tale funzione non è supportata, il membro WinFuncPtr restituisce l'indirizzo 0000h:0000h (puntatore FAR a NULL).

Se la window function è supportata, il suo utilizzo è vivamente raccomandato al posto della INT 10h; a tale proposito, dopo aver inizializzato i registri BX e DX richiesti dal servizio 4F05h, basta effettuare una chiamata FAR all'indirizzo WinFuncPtr. Ad esempio, se abbiamo creato una istanza mode_info della struttura ModeInfoBlock, possiamo scrivere: Ovviamente, se la window function non è supportata, dobbiamo sostituire la precedente chiamata FAR con una istruzione INT 10h (dopo aver posto AX=4F05h)!

11.3.7 Servizio n. 4F06h: Set/Get Logical Scan Line Length

Questo servizio permette di specificare la larghezza virtuale dello schermo. Attraverso il servizio 4F06h il programmatore può impostare una larghezza "virtuale" dello schermo maggiore di quella fisicamente visibile sul monitor; ad esempio, dopo aver attivato una modalità video da 640x480 pixel si può impostare una larghezza virtuale pari a 3*640=1920 pixel.

Il registro BL permette di specificare l'operazione richiesta; i valori disponibili sono: 00h (imposta la larghezza in pixel della scan line), 01h (restituisce la larghezza corrente della scan line), 02h (imposta la larghezza in byte della scan line), 03h (restituisce la massima larghezza possibile per una scan line).

Il registro CX permette di specificare la larghezza desiderata di una scan line; se BL=00h allora CX indica una larghezza in pixel, mentre se BL=02h allora CX indica una larghezza in byte (il contenuto di CX viene ignorato quando BL vale 01h o 03h).

In risposta alla chiamata del servizio 4F06h, vengono restituite una serie di informazioni nei registri BX, CX e DX; il registro BX restituisce la larghezza in byte della scan line, il registro CX restituisce la larghezza in pixel della scan line, mentre il registro DX restituisce il massimo numero di scan line disponibili.

Si presti molta attenzione al fatto che la richiesta di questo servizio può restituire un errore; come è stato spiegato in precedenza, ciò è dovuto ad eventuali limitazioni hardware (in questo caso, VRAM insufficiente).

11.3.8 Servizio n. 4F07h: Set/Get Display Start

Questo servizio permette di specificare quale pixel dello schermo virtuale verrà visualizzato nell'angolo in alto a sinistra del monitor. Attraverso il servizio 4F07h il programmatore può specificare la porzione dello schermo "virtuale" che verrà visualizzata sul monitor (cioè, il pixel dello schermo virtuale che comparirà nell'angolo in alto a sinistra del monitor); in tal modo è possibile creare un effetto di scorrimento dello schermo virtuale sul monitor.

Per effettuare tutte le necessarie impostazioni bisogna porre BL=00h; in tal caso, DX deve contenere l'indice della prima scan line da visualizzare, mentre CX deve contenere l'indice del pixel, di quella stessa scan line, da visualizzare per primo .
Per richiedere le impostazioni correnti bisogna porre BL=01h; in tal caso in DX viene restituito l'indice della prima scan line visualizzata, mentre in CX viene restituito il primo pixel visualizzato nella stessa prima scan line.

Lo standard VBE 3.0 introduce diverse nuove funzionalità per il servizio 4F07h; a tale proposito, si consiglia di consultare il documento VBE3.PDF disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito.

11.3.9 Servizio n. 4F08h: Set/Get DAC Palette Format

Questo servizio permette di specificare il numero di bit da utilizzare per rappresentare ciascuna delle 3 componenti RGB di un colore (Direct Color). Attraverso il servizio 4F08h il programmatore può specificare il numero di bit da utilizzare per rappresentare ciascuna delle 3 componenti primarie di un colore RGB (modello di memoria Direct Color); ciò è possibile solo quando il bit meno significativo del membro Capabilities della struttura VbeInfoBlock vale 1.

Per impostare il numero di bit per componente primaria bisogna porre BL=00h; in ogni caso (e quindi, anche in caso di errore), il registro BH restituisce il numero effettivo di bit assegnato ad ogni componente primaria.
Per richiedere le impostazioni correnti bisogna porre BL=01h; in tal caso in BH viene restituito il numero corrente di bit assegnato ad ogni componente primaria.

11.3.10 Servizio n. 4F09h: Set/Get DAC Palette Data

Questo servizio permette di gestire una tavolozza di colori RGB da utilizzare nelle modalità video con modello di memoria Direct Color. Attraverso il servizio 4F09h il programmatore può gestire la tavolozza dei colori da utilizzare nelle modalità video con modello di memoria Direct Color; questa funzionalità deve essere impiegata solo con gli adattatori video che non supportano l'accesso diretto alla tavolozza attraverso i servizi standard VGA (vedere la Figura 11.6).

Per impostare una nuova tavolozza bisogna porre BL=00h; in tal caso, il registro CX deve specificare il numero di elementi RGB da aggiornare nella tavolozza (max. 256), il registro DX deve specificare l'indice del primo elemento RGB da aggiornare nella tavolozza, mentre ES:DI deve puntare alla nuova tavolozza.
Per conoscere la tavolozza corrente bisogna porre BL=01h; anche in questo caso, il registro CX specifica il numero di elementi RGB da leggere (max. 256), il registro DX specifica l'indice del primo elemento RGB da leggere, mentre ES:DI deve puntare all'area di memoria dove salvare la tavolozza.

La tavolozza è un vettore di elementi RGB, ciascuno dei quali assume l'aspetto illustrato in Figura 11.11. Usualmente, il DAC utilizza 6 bit per ogni componente primaria di colore; per sapere se il DAC supporta le componenti a 8 bit è necessario consultare il bit in posizione 1 del membro Capabilities (vedere figura 11.6).

11.4 Supporto VESA per le vecchie schede video SVGA

Può capitare di imbattersi in vecchi PC dotati di schede video le quali, pur supportando diverse modalità SVGA, risultano prive delle estensioni VBE nel VBIOS essendo state fabbricate in un periodo precedente alla nascita dello standard VESA; molto spesso, questo problema può essere efficacemente risolto attraverso appositi programmi TSR (Terminate and Stay Resident) reperibili su Internet.
Una volta che il programmatore ha reperito il TSR adatto alla propria scheda video, procede alla relativa installazione ed attivazione al boot attraverso il file AUTOEXEC.BAT; ad esempio, nel caso di una scheda video Tseng Labs ET4000, il TSR prende il nome di TLIVESA.COM e, supponendo che tale file si trovi in C:\, può essere installato con la riga:
LH C:\TLIVESA.COM
(il comando LH significa Load High e permette di installare un programma nella upper memory).

Al riavvio del PC, il TSR si installa permanentemente in memoria ed effettua un lavoro molto importante che consiste nell'intercettare tutte le chiamate alla INT 10h; se il TSR si accorge che AH=4Fh, provvede a fornire il servizio VBE richiesto, mentre in caso contrario trasferisce la chiamata alla ISR predefinita per la INT 10h.

La Figura 11.12 illustra un elenco, in ordine alfabetico, dei principali produttori di (vecchie) schede video e dei relativi TSR reperibili su Internet; generalmente, ogni TSR è accompagnato da un documento che illustra le modalità di utilizzo e la versione VBE supportata. Naturalmente, è del tutto superfluo sottolineare che il programmatore deve munirsi del TSR adeguato per la propria scheda video SVGA; in caso contrario, si possono ottenere risultati del tutto imprevedibili!

11.5 Il bank switching

Come è stato ampiamente spiegato in precedenza, quando si programma in modalità VESA, uno degli aspetti fondamentali riguarda il cosiddetto bank switching (commutazione di banco); con questo termine si indica l'operazione di posizionamento della finestra (o delle finestre) nell'area desiderata della VRAM nella quale si intende leggere o scrivere.
Per svolgere correttamente tale lavoro, il programmatore deve prima raccogliere una serie completa di informazioni come quelle restituite dai servizi 4F00h e 4F01h; in particolare, le informazioni più importanti riguardano: la disponibilità del frame buffer a finestre, la dimensione delle finestre e la granularità della VRAM.
Tutte queste informazioni sono necessarie in quanto l'operazione di bank switching comporta lo svolgimento di una serie di calcoli; per chiarire questo aspetto, vediamo un esempio pratico illustrato dalla Figura 11.13. Nell'esempio di Figura 11.13 supponiamo di aver attivato una ipotetica modalità video 512x512 a 256 colori; come sappiamo, nelle modalità grafiche a 256 colori vengono utilizzati 8 bit per codificare il colore di ogni pixel.
La struttura ModeInfoBlock ci restituisce quindi le informazioni BitsPerPixel=8 e XResolution=512 (che coincide con BytesPerScanLine=512); supponiamo, inoltre, che sia presente il supporto per la sola WindowA con WinASegment=A000h, WinSize=64 KiB e WinGranularity=16 KiB.
La conseguenza di tutto ciò è che una schermata 512x512 pixel (che occupa quindi 262144 byte) risulterà suddivisa in 16 banchi di memoria (indicizzati in Figura 11.13 da 0 a 15) da 16 KiB ciascuno; la stessa Figura 11.13 mostra anche i 16 banchi di memoria suddivisi in 4 gruppi da 64 KiB ciascuno (e indicizzati da 0 a 3).
Un'altra conseguenza è che ogni banco di memoria risulterà contenere 32 scan line complete; come vedremo in seguito, questa è una situazione ideale che non sempre si verifica nella realtà.

Nell'esempio di Figura 11.13 risulta semplicissimo il calcolo dell'offset relativo ad un pixel di coordinate x, y; la formula generale è, infatti:
pixel_address = (y * XResolution) + x
Nel nostro caso, con x=158 e y=300, otteniamo:
pixel_address = (300 * 512) + 158 = 153758
Osservando che 512=29, possiamo velocizzare notevolmente il precedente calcolo eliminando la lentissima moltiplicazione e scrivendo simbolicamente:
pixel_address = (SHL y, 9) + x = 153600 + 158 = 153758
L'aspetto importantissimo da notare è che il risultato ottenuto può anche richiedere più di 16 bit per la sua rappresentazione; è fondamentale quindi utilizzare sempre ampiezze a 32 bit per questi particolari calcoli.

A questo punto dobbiamo determinare l'indice (win_bank) del banco di memoria che contiene il pixel P; questo calcolo è semplicissimo in quanto basta dividere pixel_address per WinGranularity.
Nel nostro caso, WinGranularity=16384 byte, per cui si ha (divisione intera):
win_bank = pixel_address / WinGranularity = 153758 / 16384 = 9
Anche in questo caso, osservando che 16384=214, possiamo eliminare la lentissima divisione e scrivere simbolicamente:
win_bank = SHR pixel_address, 14 = SHR 153758, 14 = 9
La Figura 11.13 conferma che il risultato del calcolo è corretto.

Dopo aver posizionato la WindowA nel banco 9, dobbiamo effettuare un ultimo calcolo che riguarda la determinazione dell'offset (a 16 bit) del pixel P all'interno dello stesso banco 9 da 16 KiB; anche questo calcolo è molto semplice in quanto basta sottrarre a pixel_address il valore win_bank*WinGranularity.
Considerando ancora il fatto che il valore WinGranularity è 16384=214, si può rendere tale operazione molto più efficiente in questo modo:
pixel_offset = pixel_address - (SHL win_bank, 14) = 153758 - 147456 = 6302
Osservando che 6302=189Eh, possiamo affermare che il nostro pixel P si troverà all'indirizzo logico A000h:189Eh della WindowA la quale, a sua volta, si trova già posizionata nel banco 9 della VRAM.

11.6 Codifica dei colori nelle modalità video VESA/SVGA

Nel precedente capitolo, parlando delle varie modalità video grafiche standard, abbiamo visto che in certi casi i bit della VRAM assegnati ad ogni pixel contengono un valore che non rappresenta direttamente il colore del pixel stesso, bensì l'indice relativo ad un vettore di colori; l'elemento associato a quell'indice codifica l'effettivo colore del pixel.
Questa stessa situazione si presenta anche quando si programmano le modalità SVGA attraverso lo standard VESA; la differenza fondamentale è data dal fatto che, nelle modalità SVGA, si ha a che fare con un numero di colori che può andare da 2 (modalità monocromatiche) a diversi milioni, per cui si rendono necessarie anche altre tecniche di codifica!

Abbiamo anche visto che, quando si lavora con le modalità video grafiche standard a bassa risoluzione (CGA, EGA, VGA e MCGA), le varie tecniche di codifica dei colori permettono di gestire in memoria una intera schermata senza mai sconfinare dal limite dei 64 KiB; in questo modo, il programmatore può lavorare sulla VRAM in modo estremamente efficiente.
Nella gestione della VRAM in modalità SVGA, attraverso lo standard VESA, si ha spesso a che fare con risoluzioni grafiche piuttosto alte che comportano la suddivisione di una schermata in numerosi blocchi da 64 KiB; ciò significa che, quando si deve accedere in I/O alla VRAM, si può rendere necessario un pesante lavoro di bank switching.

Per chiarire tutti questi aspetti, analizziamo i vari sistemi di codifica dei colori nelle modalità video grafiche VESA/SVGA.

11.6.1 Modalità video grafiche a 2 e a 4 colori

Nel precedente capitolo abbiamo visto che, nelle modalità video grafiche monocromatiche, ad ogni pixel viene assegnato un solo bit della VRAM; infatti, con 1 bit possiamo codificare la condizione 0 (pixel spento = colore nero) o 1 (pixel acceso).
Ogni byte della VRAM contiene le informazioni relative a 8/1=8 pixel; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution/8.
Nelle modalità video grafiche a 4 colori, ad ogni pixel vengono assegnati 2 bit della VRAM; infatti, con 2 bit possiamo codificare 22=4 colori differenti.
Ogni byte della VRAM contiene le informazioni relative a 8/2=4 pixel; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution/4.
La Figura 11.14 illustra quest'ultima situazione. Le specifiche VESA non prevedono alcun supporto per le modalità video grafiche a 2 o 4 colori; il programmatore deve quindi ricorrere ai servizi standard del BIOS illustrati nel precedente capitolo.

11.6.2 Modalità video grafiche a 16 colori

Nel precedente capitolo abbiamo visto che, nelle modalità video grafiche a 16 colori, ad ogni pixel vengono assegnati 4 bit della VRAM; infatti, con 4 bit possiamo codificare 24=16 colori differenti.
I 4 bit di ogni pixel risultano disposti in posizioni corrispondenti su 4 piani sovrapposti; la Figura 11.15 illustra questa situazione. Ogni piano di bit risulta composto da XResolution*YResolution bit; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution/8.

Le specifiche VESA forniscono il supporto per le modalità video grafiche a 16 colori con risoluzione maggiore o uguale a 800x600; le risoluzioni inferiori (ad esempio, 640x480) possono essere programmate con i servizi standard del BIOS illustrati nel precedente capitolo.
A seconda della risoluzione grafica (ad esempio, 1024x768) la quantità di memoria richiesta per gestire una schermata supera il limite dei 64 KiB (per ogni piano di bit) per cui entra in gioco anche il bank switching; ovviamente, questo aspetto vale per ciascuno dei 4 piani di bit.

Come sappiamo, nelle modalità video grafiche a 16 colori, i 4 bit assegnati ad ogni pixel rappresentano l'indice relativo ai primi 16 elementi di un vettore di 256 colori; ogni elemento è formato da 3 byte (componenti RGB) di ciascuno dei quali vengono utilizzati solo i primi 6 bit in modo da ottenere sino a 218=262144 colori.
Se il proprio adattatore video è dotato di compatibilità VGA (Figura 11.6 e Figura 11.8), tutti i registri relativi alla gestione dei piani di bit e della tavolozza dei colori possono essere programmati attraverso le tecniche illustrate nel precedente capitolo; in caso contrario, si veda il servizio 4F09h descritto in questo capitolo.

11.6.3 Modalità video grafiche a 256 colori

Nel precedente capitolo abbiamo visto che, nelle modalità video grafiche a 256 colori, ad ogni pixel vengono assegnati 8 bit della VRAM; infatti, con 8 bit possiamo codificare 28=256 colori differenti.
I vari gruppi di 8 bit risultano disposti in sequenza nella VRAM per cui la programmazione di queste modalità grafiche risulta semplicissima; la Figura 11.16 illustra questa situazione. L'unico piano di bit presente risulta composto da XResolution*YResolution byte; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution.

Come risulta dalla Figura 11.2, le specifiche VESA 3.0 forniscono il supporto per cinque differenti modalità video grafiche a 256 colori; le risoluzioni vanno dalla 640x400 alla 1280x1024.
A seconda della risoluzione grafica, la quantità di memoria richiesta per gestire una schermata supera il limite dei 64 KiB per cui entra in gioco anche il bank switching; ad esempio, una schermata da 640x480 pixel richiede 640x480=307200 byte di VRAM suddivisi in quasi 5 blocchi da 64 KiB.

Come sappiamo, nelle modalità video grafiche a 256 colori, gli 8 bit assegnati ad ogni pixel rappresentano l'indice relativo agli elementi di un vettore di 256 colori; ogni elemento è formato da 3 byte (componenti RGB) di ciascuno dei quali vengono utilizzati solo i primi 6 bit in modo da ottenere sino a 218=262144 colori.
Se il proprio adattatore video è dotato di compatibilità VGA (Figura 11.6 e Figura 11.8), tutti i registri relativi alla gestione della tavolozza dei colori possono essere programmati attraverso le tecniche illustrate nel precedente capitolo; in caso contrario, si veda il servizio 4F09h descritto in questo capitolo.

11.6.4 Modalità video grafiche High Color

La continua evoluzione tecnologica degli adattatori video ha portato anche ad un notevole aumento della quantità disponibile di VRAM e ciò ha reso possibile il supporto di modalità video grafiche capaci di gestire decine di migliaia di colori contemporaneamente; tali modalità video sono state definite High Color.

La codifica dei colori nelle modalità High Color si basa sempre sul concetto di terna RGB; a tale proposito, vengono utilizzati 2 byte (1 word) per ogni pixel secondo lo schema illustrato in Figura 11.17. In Figura 11.17a vediamo la tecnica di codifica di tipo 5:5:5 che consiste nell'utilizzare 5 bit per ogni componente primaria di colore; in questo modo, complessivamente, possiamo rappresentare:
25 * 25 * 25 = 25 + 5 + 5 = 215 = 32768
colori differenti.

Osservando che, nella WORD associata ad ogni pixel, rimane 1 bit libero, si può pensare ad una seconda tecnica di codifica di tipo 5:6:5 come descritto in Figura 11.17b; in questo modo, complessivamente, possiamo rappresentare:
25 * 26 * 25 = 25 + 6 + 5 = 216 = 65536
colori differenti.

La notevole quantità di VRAM presente negli adattatori video con supporto High Color permette di memorizzare i colori effettivi direttamente nella stessa VRAM; in sostanza, la memoria video nelle modalità High Color è strutturata come un vettore di WORD, ciascuna delle quali contiene direttamente il colore del relativo pixel.
Ogni schermata High Color risulta composta da (XResolution*2)*YResolution byte; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution*2.

11.6.5 Modalità video grafiche True Color

L'ulteriore evoluzione tecnologica degli adattatori video ha reso possibile il supporto di modalità video grafiche capaci di gestire milioni di colori contemporaneamente; tali modalità video sono state definite True Color.

La codifica dei colori nelle modalità True Color si basa sempre sul concetto di terna RGB; a tale proposito, vengono utilizzati 3 byte per ogni pixel secondo lo schema illustrato in Figura 11.18. La tecnica di codifica è di tipo 8:8:8 e consiste nell'utilizzare 8 bit per ogni componente primaria di colore; in questo modo, complessivamente, possiamo rappresentare:
28 * 28 * 28 = 28 + 8 + 8 = 224 = 16777216
colori differenti.

La notevole quantità di VRAM presente negli adattatori video con supporto True Color permette di memorizzare i colori effettivi direttamente nella stessa VRAM; in sostanza, la memoria video nelle modalità True Color è strutturata come un vettore di terne di BYTE, ciascuna delle quali contiene direttamente il colore del relativo pixel.
Ogni schermata True Color risulta composta da (XResolution*3)*YResolution byte; possiamo affermare quindi che il valore BytesPerScanLine è pari a XResolution*3.

Esistono anche modalità True Color che prevedono la rappresentazione del colore di un pixel attraverso 4 byte di cui i primi tre contengono la solita terna RGB di tipo 8:8:8, mentre il quarto byte è disponibile per altri usi; in questo caso, ogni schermata risulta composta da (XResolution*4)*YResolution byte con il valore BytesPerScanLine che è pari a XResolution*4.

11.7 Esempi pratici

Prima di analizzare alcuni esempi pratici è necessario sottolineare un aspetto importante; una macchina virtuale (come VirtualBox) o un emulatore DOS, possono fornire un proprio supporto VESA che potrebbe anche risultare differente da quello effettivamente presente nel VBIOS del computer host.
Generalmente, la differenza principale riguarda la versione VBE; non è raro allora che una macchina virtuale o un emulatore DOS dispongano di caratteristiche hardware "virtuali" superiori a quelle effettivamente disponibili.

11.7.1 Visualizzazione di informazioni dettagliate sul supporto VBE

Per programmare una scheda video in modo corretto ed efficiente attraverso le specifiche VESA, è necessario raccogliere innanzi tutto una serie dettagliata di informazioni relative all'hardware supportato; il programma di Figura 11.19 si prefigge proprio di eseguire tale compito. Il programma inizia chiamando il servizio 4F00h per riempire la struttura VbeInfoBlock; se la chiamata non restituisce un codice di errore, vengono visualizzate le principali informazioni contenute nella struttura stessa.

Successivamente, il programma si serve del puntatore VideoModePtr per attivare un loop dal quale si esce quando viene incontrato il valore FFFFh (fine lista delle modalità video); per ogni modalità video, vengono mostrate tutte le informazioni principali restituite nella struttura ModeInfoBlock dal servizio 4F01h.

11.7.2 Trasferimento dati in VRAM nei modi video a 256 colori

Vediamo ora un programma che utilizza REP STOSD per riempire con differenti colori i vari blocchi da 64 KiB che compongono una schermata grafica a 256 colori (modalità video standard con BitsPerPixel=8); la Figura 11.20 mostra il relativo listato. Inizialmente il programma effettua una serie di controlli per verificare che tutto sia a posto; in particolare, viene verificata la presenza del supporto VESA, il supporto hardware del modo video richiesto e la presenza del frame buffer a finestre.
Se tutti i controlli forniscono esito positivo, si procede con il calcolo di una serie di parametri da utilizzare nel seguito del programma; in particolare, viene determinato il numero di banchi che compongono una finestra, il numero di finestre in una schermata e il valore bank_shift di cui si è parlato in precedenza.
Successivamente, viene attivata la modalità video richiesta e si avvia un loop attraverso il quale si procede al riempimento dei vari blocchi da 64 KiB con differenti colori; si noti che, in generale, l'ultimo blocco ha una dimensione che è sempre minore o uguale a 64 KiB.

Osservando l'output che questo programma produce con certe risoluzioni grafiche, si nota chiaramente che ogni finestra da 64 KiB contiene un numero non intero di scan line; ciò accade quando la XResolution non è esprimibile come potenza intera di 2.
Ad esempio, per la risoluzione 640x480, ogni scan line occupa 640 byte, per cui il numero totale di scan line contenuto in una finestra da 64 KiB sarà:
65536 / 640 = 102.4
Un altro aspetto da notare nel listato del programma è la presenza di direttive %IFDEF, %ELSE e %ENDIF; grazie a tali direttive è possibile assemblare determinate porzioni di programma al posto di altre (assemblaggio condizionale).
In particolare, lo scopo di queste direttive nel listato di Figura 11.20 è legato alla necessità di impiegare preferibilmente la window function al posto della INT 10h per tutte le operazioni di bank switching (evitando l'uso pesante di istruzioni per il controllo del flusso); nel caso di hardware grafico che non supporta la window function, basta commentare la dichiarazione della costante simbolica WIN_FUNC all'inizio del programma per fare in modo che venga usata automaticamente la INT 10h.

11.7.3 Accesso ai pixel nelle modalità grafiche True Color

Vediamo ora un programma che ha lo scopo di evidenziare un importante aspetto relativo alle modalità video grafiche True Color con BitsPerPixel=24; la Figura 11.21 mostra il relativo listato. Il compito di questo programma è apparentemente molto semplice; si tratta, infatti, di riempire una intera schermata con XResolution*YResolution pixel.

Per ogni pixel di coordinate x,y dobbiamo calcolare innanzi tutto il valore pixel_address; osservando che, nelle modalità True Color, ogni pixel richiede 3 byte in VRAM, la formula generale sarà:
pixel_address = (y * BytesPerScanLine) + (x * 3)
Il blocco da 64 KiB in cui si trova il pixel si ottiene dalla formula:
win_bank = pixel_address >> 16
Dopo aver calcolato nel solito modo il valore bank_shift, possiamo determinare il banco in cui posizionare la finestra con la formula:
bank = win_bank << bank_shift
Dopo aver posizionato la finestra, calcoliamo l'offset del pixel con la formula:
pixel_offset = pixel_address AND FFFFh
A questo punto non dobbiamo fare altro che trasferire 3 byte di colore all'indirizzo appena calcolato; se però proviamo ad usare queste formule nel programma di Figura 11.21 otteniamo un sicuro crash!
Il perché di questo crash è legato proprio al fatto che, nelle modalità True Color, il colore di ogni pixel viene rappresentato con 3 byte, ma la dimensione di un banco di memoria è sempre multipla intera di 2 e non di 3; ciò significa che la terna di BYTE relativa a particolari pixel potrebbe trovarsi a cavallo tra un banco di memoria e quello successivo!
Questo problema non si presenta nelle modalità High Color dove ogni pixel occupa 2 byte in VRAM; questa coppia di BYTE relativa ad ogni pixel verrà quindi sempre a trovarsi all'interno dello stesso banco di memoria.

Il programma di Figura 11.21 mostra come affrontare la situazione nelle modalità True Color; in pratica, si distinguono i seguenti tre casi: Da notare l'importantissimo ruolo svolto dalla variabile curr_bank che memorizza l'indice del banco di memoria correntemente attivo; consultando tale variabile possiamo ridurre al minimo indispensabile le pesanti operazioni di bank switching!

L'esempio di Figura 11.21 si riferisce alla modalità VESA standard 0112h (risoluzione 640x480 e colori a 24 bit 8:8:8); il programma è totalmente parametrizzato per cui può essere testato anche con le altre modalità standard True Color (24 bit) tenendo però presente ciò che è stato spiegato per l'esempio di Figura 11.20.
Lo stesso esempio di Figura 11.21, inoltre, può essere facilmente adattato al caso BitsPerPixel=32 (dove i primi tre BYTE rappresentano una terna RGB di tipo 8:8:8, mentre il quarto BYTE è disponibile per altri usi); in tal caso la situazione si semplifica notevolmente in quanto WinGranularity è anche un multiplo intero di 4, per cui è impossibile che la quaterna di BYTE relativa ad un qualunque pixel venga a trovarsi a cavallo tra due banchi di memoria adiacenti.
Per esercizio si può provare a realizzare proprio l'adattamento appena descritto; in tal caso, si tenga presente che:
pixel_address = (y * BytesPerScanLine) + (x * 4)

11.8 Libreria VESALIB

Anziché riscrivere ogni volta centinaia di linee di codice da zero, conviene seguire il metodo già illustrato nei precedenti capitoli, che consiste nel crearsi una apposita libreria di procedure pronte per l'uso; in particolare, in relazione allo standard VBE, tali procedure devono fornire tutto il necessario per effettuare le varie verifiche e inizializzazioni.
In una libreria grafica è anche importante disporre di una procedura capace di svolgere un compito fondamentale, che consiste nella visualizzazione di un pixel sullo schermo; a partire da tale procedura, si possono poi affrontare casi più complessi, come la visualizzazione di stringhe, segmenti di retta, etc.

Nella sezione Librerie di supporto per il corso assembly dell’ Area Downloads di questo sito, è presente una libreria, denominata VESALIB, che può essere linkata ai programmi destinati alla generazione di eseguibili in formato EXE; all'interno del pacchetto vesalibexe.zip è presente la libreria VESALIB.ASM, l'include file VESALIBN.INC per il NASM e l'include file VESALIBM.INC per il MASM.

Per la creazione dell'object file di VESALIB.ASM è richiesto il NASM; l'assemblaggio consiste nel semplice comando:
nasm -f obj vesalib.asm
L'object file così ottenuto è perfettamente linkabile anche ai programmi scritti con MASM.

Analizziamo in dettaglio le procedure fornite dalla libreria VESALIB; ulteriori informazioni possono essere ottenute leggendo gli abbondanti commenti presenti nel file VESALIB.ASM. Questa procedura verifica la presenza del supporto VESA; se tale supporto è presente, viene riempita una struttura interna info_block di tipo VbeInfoBlock.
Non è richiesto alcun parametro in ingresso.
I parametri in uscita vengono restituiti in AL, AH, EBX e ECX. AL contiene il valore di ritorno vero e proprio; come sappiamo, AL=4Fh indica che il supporto VESA è presente. AH contiene un codice diagnostico; abbiamo visto che AH=00h indica che il servizio richiesto è stato eseguito correttamente. I due registri EBX e ECX restituiscono dei puntatori FAR a delle stringhe; tali stringhe descrivono il contenuto dei due registri AL e AH. Questa procedura restituisce i vari campi della struttura info_block riempita dalla procedura vesa_getcontrollerinfo; ovviamente, tali campi hanno senso solo se il supporto VESA è presente.
Come parametro in ingresso si deve inserire in BX l'identificatore del campo richiesto; a tale proposito, si veda l'include file della libreria VESALIB. Si può notare che gli identificatori non sono altro che gli offset dei vari campi della struttura info_block.
Il parametro in uscita consiste nel campo richiesto, il quale viene restituito in AX/EAX, a seconda dell'ampiezza; se in BX si passa l'identificatore VI_VBEINFOBLPTR, allora in EAX viene restituito un puntatore FAR alla struttura info_block. Questa procedura verifica la presenza del supporto VESA per una determinata modalità video; se tale supporto è presente, viene riempita una struttura interna mode_info di tipo ModeInfoBlock.
Come parametro in ingresso si deve inserire in CX l'identificatore del modo video richiesto; a tale proposito, si vedano le Figure 11.2 e 11.3.
I parametri in uscita vengono restituiti in AL, AH, EBX e ECX; il loro significato è identico a quello già descritto per la procedura vesa_getcontrollerinfo. Questa procedura restituisce i vari campi della struttura mode_info riempita dalla procedura vesa_getvbemodeinfo; ovviamente, tali campi hanno senso solo se il supporto VESA alla modalità video richiesta è presente.
Come parametro in ingresso si deve inserire in BX l'identificatore del campo richiesto; a tale proposito, si veda l'include file della libreria VESALIB. Anche in questo caso si può notare che gli identificatori non sono altro che gli offset dei vari campi della struttura mode_info; se il programmatore ne ha la necessità, può aggiungere nell'include file anche gli identificatori mancanti, che devono rappresentare in ogni caso gli offset dei relativi campi.
Il parametro in uscita consiste nel campo richiesto, il quale viene restituito in AL/AX/EAX, a seconda dell'ampiezza; se in BX si passa l'identificatore MI_MODEINFOBLPTR, allora in EAX viene restituito un puntatore FAR alla struttura mode_info. Questa procedura tenta di attivare il modo video VESA richiesto; se tale attivazione avviene correttamente, vengono effettuate una serie di inizializzazioni che, per le modalità grafiche, comprendono vari parametri per il bank switching, il puntatore al vettore dei caratteri nella ROM BIOS, etc. Prima di attivare il modo video richiesto, vengono effettuati tutti i necessari controlli; in particolare, viene anche testato il bit in posizione 0 del campo ModeAttributes (supporto hardware del modo video richiesto).
Come parametro in ingresso si deve inserire in BX l'identificatore del modo video richiesto; a tale proposito, si vedano le Figure 11.2 e 11.3.
I parametri in uscita vengono restituiti in AL, AH, EBX e ECX; il loro significato è identico a quello già descritto per la procedura vesa_getcontrollerinfo. Questa procedura riporta lo schermo in modalità video testo 80x25 celle a 16 colori; ovviamente, essa deve essere usata per uscire da una modalità grafica precedentemente attivata con vesa_setvbemode.
Non è previsto alcun parametro in ingresso.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è visualizzare un pixel in una determinata posizione (x, y), con il colore specificato. Le coordinate del pixel devono essere espresse come numeri interi con segno a 16 bit, riferiti ad un sistema di coordinate cartesiane la cui origine è nel centro dello schermo (vedere più avanti); come sappiamo, si tratta di numeri compresi tra -32768 e +32767.
I parametri in ingresso sono BX (ascissa del pixel), CX (ordinata del pixel) e DL (colore del pixel); il colore è un numero compreso tra 0 e 255 che, come sappiamo, rappresenta un indice nella tavolozza dei colori.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è leggere il colore del pixel presente in una determinata posizione (x, y). Per le coordinate del pixel valgono tutte le considerazioni esposte in precedenza per la procedura vesa_putpixel256.
I parametri in ingresso sono BX (ascissa del pixel) e CX (ordinata del pixel).
Il parametro in uscita viene restituito in AL e rappresenta il colore del pixel (indice nella tavolozza dei colori). Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è visualizzare un segmento orizzontale compreso tra gli estremi P1(x1, y) e P2(x2, y), con il colore specificato. Per le coordinate valgono tutte le considerazioni esposte in precedenza per la procedura vesa_putpixel256.
I parametri in ingresso sono AX (ascissa iniziale x1), BX (ascissa finale x2), CX (ordinata fissa y) e DL (colore del segmento); l'ascissa x1 può essere maggiore o minore di x2.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è visualizzare un segmento verticale compreso tra gli estremi P1(x, y1) e P2(x, y2), con il colore specificato. Per le coordinate valgono tutte le considerazioni esposte in precedenza per la procedura vesa_putpixel256.
I parametri in ingresso sono AX (ascissa fissa x), BX (ordinata iniziale y1), CX (ordinata finale y2) e DL (colore del segmento); l'ordinata y1 può essere maggiore o minore di y2.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è visualizzare una stringa C a partire da una determinata posizione (x, y), con il colore specificato. Per le coordinate valgono tutte le considerazioni esposte in precedenza per la procedura vesa_putpixel256.
I parametri in ingresso sono BX (ascissa iniziale), CX (ordinata iniziale), DL (colore della stringa) e DS:SI (puntatore FAR alla stringa C); le coordinate iniziali indicano il punto in alto a sinistra da cui parte la visualizzazione della stringa.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è visualizzare un sistema di assi cartesiani Oxy nel piano, la cui origine è posizionata al centro del monitor.
Non è previsto alcun parametro in ingresso.
Non è previsto alcun parametro in uscita. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è impostare una nuova tavolozza da 256 colori.
I parametri in ingresso sono CX (numero elementi da aggiornare), DX (indice primo elemento), ES:DI (puntatore alla nuova tavolozza).
I parametri in uscita vengono restituiti in AL, AH, EBX e ECX; il loro significato è identico a quello già descritto per la procedura vesa_getcontrollerinfo. Questa procedura deve essere utilizzata solo nelle modalità grafiche a 256 colori, i cui codici sono 0101h, 0103h, 0105h e 0107h (Figura 11.2); il suo scopo è leggere la tavolozza corrente da 256 colori.
I parametri in ingresso sono CX (numero elementi da leggere), DX (indice primo elemento), ES:DI (puntatore all'area di memoria che verrà riempita con la tavolozza).
I parametri in uscita vengono restituiti in AL, AH, EBX e ECX; il loro significato è identico a quello già descritto per la procedura vesa_getcontrollerinfo.

11.8.1 Dettagli sulla libreria VESALIB

Analizziamo alcuni importanti dettagli sulle procedure grafiche della libreria VESALIB, destinate alle modalità video a 256 colori; eventualmente, il programmatore può anche aggiungere analoghe procedure per il supporto delle modalità video High Color e True Color.

Come abbiamo visto in precedenza, per velocizzare il calcolo dell'offset assoluto di un pixel (pixel_address), conviene eliminare la moltiplicazione y*XResolution, sostituendola con degli shift a sinistra; a tale proposito, si può notare quanto segue: Avremo quindi, ad esempio:
y * 800 = y * (25 + 28 + 29) = (y * 25) + (y * 28) + (y * 29)
Di conseguenza:
y * 800 = (SHL y, 5) + (SHL y, 8) + (SHL y, 9)
Analogamente, la divisione pixel_address/WinGranularity, per determinare il banco di memoria in cui leggere o scrivere, può essere convertita in uno shift verso destra di pixel_address; non è casuale il fatto che WinGranularity sia sempre esprimibile come potenza intera di 2.

Tutti i parametri necessari per effettuare questi calcoli, vengono inizializzati dalla procedura vesa_setvbemode.

Le procedure grafiche della libreria VESALIB lavorano con coordinate dei pixel riferite ad un sistema di assi cartesiani Oxy, la cui origine O si trova al centro dello schermo; tali coordinate devono essere espresse da numeri interi con segno a 16 bit, compresi tra -32768 e +32767.
La libreria VESALIB provvede a convertire queste coordinate "relative" in coordinate "assolute", riferite ad un sistema di assi cartesiani O1x1y1, la cui origine O1 si trova nell'angolo in alto a sinistra dello schermo e il cui asse y1 è orientato verso il basso; la Figura 11.22 illustra tutti i dettagli. Il sistema di assi cartesiani O1x1y1 tiene conto del fatto che lo schermo grafico può essere immaginato come una matrice di m colonne per n righe di pixel; dato un pixel in posizione "assoluta" (x1,y1), si può notare che x1 è l'indice di colonna compreso tra 0 e m-1, a partire da sinistra, mentre y1 è l'indice di riga compreso tra 0 e n-1, a partire dall'alto.
La conversione di coordinate da Oxy a O1x1y1 per un punto P(x,y) (Figura 11.22) è semplicissima; infatti, se Ox e Oy sono le coordinate di O rispetto a O1x1y1, si ha: Come è stato appena spiegato, il programmatore non deve preoccuparsi di questi aspetti in quanto tutti i calcoli vengono effettuati dalla libreria VESALIB.

La visualizzazione delle stringhe avviene con il metodo già descritto nel precedente capitolo; a tale proposito, in fase di attivazione di una modalità video grafica, viene predisposto un puntatore FAR ad un vettore di bitmap dei caratteri da 8x16 pixel, presente nella ROM BIOS del computer. Si tenga presente che il vettore contiene solo le bitmap relative ai primi 128 simboli del set di codici ASCII; se si prova quindi a visualizzare una stringa contenente vocali accentate o simboli speciali internazionali, si possono ottenere risultati privi di senso.
Ogni bitmap occupa 16 byte (16 righe da 8 bit ciascuna); di conseguenza, l'offset nel vettore di un carattere c da visualizzare sarà dato da:
ASCII(c) * 16 = ASCII(c) * 24 = SHL ASCII(c), 4

11.8.2 Test della libreria VESALIB

Per illustrare il funzionamento della libreria VESALIB, scriviamo un semplice programma che effettua tutti i necessari controlli, mostra una serie di informazioni e attiva una modalità grafica a 256 colori; lo stesso programma visualizza poi il sistema di assi cartesiani Oxy, un pixel e alcune stringhe.
La Figura 11.23 mostra il listato del programma VLIBTEST.ASM. La Figura 11.24 mostra l'output di VLIBTEST.EXE in modalità video 640x480 a 256 colori. Il programma è interamente parametrizzato, per cui può essere eseguito con tutte le modalità video grafiche a 256 colori, identificate dai codici 0101h, 0103h, 0105h e 0107h; a tale proposito, si deve modificare la costante simbolica VESA_MODE in VLIBTEST.ASM.


Bibliografia

VESASP12.TXT - Super VGA BIOS Extension - Standard #VS911022 - VBE Version 1.2
(disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito - vesasp12.zip)

VBE3.PDF - VESA BIOS EXTENSION - Core Functions Standard
(disponibile nella sezione Documentazione tecnica di supporto al corso assembly dell’ Area Downloads di questo sito - vbe3.zip)