Assembly Avanzato con NASM
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:
- la pagina video 0 parte dall'indirizzo logico B800h:0000h
- la pagina video 1 parte dall'indirizzo logico B800h:1000h
- la pagina video 2 parte dall'indirizzo logico B800h:2000h
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:
- se AL=01h o AL=11h, viene selezionato un vettore di bitmap 8x14
per adattatori monocromatici
- se AL=02h o AL=12h, viene selezionato un vettore di bitmap 8x8
- se AL=04h o AL=14h, viene selezionato un vettore di bitmap 8x16
per adattatori VGA
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:
SEGMENT VIDBUFFER ABSOLUTE=0B800h
In questo modo, NASM 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)