Introduzione alla programmazione della VGA


Tratteremo in questo tutorial un argomento tra i più interessanti nella programmazione.
Non riuscirò certo a coprire l'intero argomento, mi propongo solo di dare una infarinatura generale lasciando poi a voi il compito di approfondire, in rete si trovano molte informazioni sulla programmazione della scheda video..
Il modo grafico che considererò qui è il 13h, quello della VGA "Standard" che lavora a 320x200 con 256 colori.
La prima cosa che un programma assembly (ma anche in ogni altro linguaggio) deve fare è impostare la modalità bene, questo può essere fatto utilizzando l'int 10h tramite la funzione 00h. Quindi:
	Set_VGA	PROC 
		mov	ax,0013h     ;ah=00  al=13h (modalità 320x200x256)
		int	10h
		RET
	Set_VGA	ENDP
Una volta terminato il programma si deve reimpostare la modalità testo, anche questo può essere fatto tramite l'int 10h:
	Set_TXT	PROC
		mov	ax,0003h     ;ah=00 al=03h  (modalita testo 80x25)
		int	10h
		RET
	Set_TXT	ENDP
Ora che siamo riusciti ad impostare la modalità grafica voluta vediamo come si può mettere un Pixel sullo schermo.
Anche per questa operazione si può ricorrere all'int 10h (funzione 0Ch)
	Put_Pixel PROC
		mov	ah,0Ch
		mov	al,Color	;colore del pixel
		mov	cx,X		;coordinata X
		mov	dx,Y		;coordinata Y
		mov	bx,1		;numero di pagina (per le modalità
					;                  con più pagine)
		int	10h
		RET
	Put_Pixel ENDP
Un problema di questo modo di posizionare un pixel sul video è dato dalla lentezza veramente insopportabile in alcune applicazioni, perciò invece di usare la funzione del BIOS (int 10h) possiamo accedere direttamente alla memoria video.
Il segmento in cui questa risiede è all'indirizzo A000, quindi il punto di coordinate 0,0 sarà allindirizzo A000:0000 quello di coordinate 319,199 a A000:F9FF
(infatti 320*200-1=63999=F9FFh
 oppure: 319+ (199*320)=63999=F9FFh)
E il punto di coordinate 145,76??
Bene per trovare l'indirizzo di un generico punto x,y in memoria si può usare la formula:
		ind = x + ( Y * 320)
Chiaro?
OK ma cosa ci mettiamo all'indirizzo calcolato??
Bene a quell'indirizzo ci va l'indice del colore del pixel.
Per capire bene questo dobbiamo fare una parentesi sulla palette.

LA PALETTE
La palette è la tavolozza dei colori da usare per realizzare il nostro disegno. Si tratta di un array bidimensionale di 256 elementi ognuno dei quali contiene 3 valori che corrispono al livello di Rosso, Verde, Blue (R G B).

	            Palette
		     -------
		    | r|g|b |
		   0|__|_|__|
		    | r|g|b |
		   1|__|_|__|
		    |r |g|b | 
		   2|__|_|__|
		    |  | |  |

Come voi saprete mescolando in modo opportuno il Rosso, Verde e Blue possiamo ottenere tutti i colori che vogliamo. Le gradazioni di R G e B vanno da 0 a 63.

Per leggere il valore di R G e B di un colore dalla palette procedo nel modo seguente:

get_pal	      PROC
	      mov    dx,3c7h  ;porta in cui va indicato l'indice del colore
	      mov    al,col   ;col è l'indice del colore da leggere (0..255)
	      out    dx,al    
              add    dx,2     ;3c9h è la porta da cui si leggono i 3 valori
              in     al,dx    
	      mov    R,al  
	      in     al,dx    
	      mov    G,al  
	      in     al,dx    
	      mov    B,al  	; in R, G e B ho i valori
	      RET
get_pal  ENDP
La procedura inversa, quella per impostare i valori di R G e B nella palette è la seguente:
set_pal       PROC
	      mov    dx,3c8h   ;porta in cui va indicato l'indice del colore
			       ;da scrivere (NB è diverso dal precedente!!)
	      mov    al,col
   	      out    dx,al	;scrivo sulla porta il colore
   	      inc    dx	    	;3c9 è la porta su cui scrivere
				;i 3 valori R G B 
   	      mov    al,R
   	      out    dx,al
   	      mov    al,G
   	      out    dx,al
   	      mov    al,B
   	      out    dx,al	;ora nella palette c'è il nuovo colore
   	      RET
set_pal	 ENDP
Spero di aver chiarito cos'è e come funziona la palette.

Tornando al discorso della put_pixel che agisce direttamente in memoria si diceva che all'indirizzo calcolato dalla formula si doveva scrivere l'indice del colore, beh ora è chiaro no? Metto l'indice che il colore ha nella palette.

Per concludere il discorso vi lascio un esempio che utilizza un po' tutte le funzioni viste. Si tratta di un pgm che simula l'effetto neve che si vede sui televisori rotti o non sintonizzati.
Questo è un programma "random", infatti utilizza un generatore di numeri (pseudo)casuali.


SNOW.COM SNOW.ASM ;SNOW.ASM - By b0nu$, 1997 .286 SEG_A SEGMENT ASSUME CS:SEG_A, DS:SEG_A ORG 100H SNOW PROC FAR INIT: JMP START rgb db 3 dup(?) ; Array che contiene i valori di ; R G B del colore num_rnd_val dw 12345 ; A caso num_rnd_mul dw 9421 ; A caso (diverso da 0 e pari) ; (vedi nota finale) START: mov ax,0013h int 10h ;imposto modalità 320x200 xor ax,ax loop_pal: mov rgb[0],al mov rgb[1],al mov rgb[2],al CALL Set_Pal ;imposto la palette con tutte le ;tonalità di grigio nel quale si ha R=G=B. inc al jne loop_pal xor bx,bx ;x=0 Ciclo_x: xor dx,dx ;y=0 ;parto da 0,0 Ciclo_y: push dx ;questo ciclo posiziona a caso ;pixel sullo schermo push dx CALL Random pop dx CALL Put_Pixel pop dx inc dx cmp dx,0C8h ;200 jne Ciclo_y inc bx cmp bx,0140h ;320 jne Ciclo_x mov ax,0003h int 10h ;imposto modalità txt RETN SNOW ENDP ; stampa un pixel alle coordinate y*256 + x*64 ; (se c'Š overflow vale l'implementazione circolare) Put_Pixel PROC ;dx=y bx=x ah=col push ax ;usa l'accesso diretto in memoria video mov ax,0A000h mov es,ax push bx mov dh,dl xor dl,dl shl bx,6 add dx,bx pop bx add bx,dx mov di,bx ;xor al,al pop ax ;prelevo il colore mov es:[di],ah RET Put_Pixel ENDP Set_Pal PROC ;al=colore mov dx,3c8h ;mov al,[col] out dx,al inc dx mov al,rgb[0] ;red out dx,al mov al,rgb[1] ;green out dx,al mov al,rgb[2] ;blue out dx,al RET Set_Pal ENDP Random PROC ;Genera un numero casuale con l'algoritmo ; di D.Lehmer mov ax,num_rnd_val mul num_rnd_mul inc ax mov num_rnd_val,ax RET Random ENDP SEG_A ENDS END INIT
NOTE SUL PRG:
All'inizio, avevo scritto il ciclo così:
		xor	ax,ax
		
loop_pal:	mov	rgb[0],al
		mov	rgb[1],al
		mov	rgb[2],al
		CALL 	Set_Pal	; imposto la palette con tutte le tonalità
				; di grigio nel quale si ha R=G=B.
		inc	al
		cmp	al,0FFh
		jne	loop_pal		
ecco cosa fa: imposta i colori della tavolozza tutti grigi. Siccome i grigi sono 64 e i colori della palette sono 256, questo ciclo intelligentemente ripete la serie dei grigi per 256/64=4 volte. Solo che c'è un errore:
il 256-esimo colore (quello numero 255) non viene impostato; se ad es. un altro programma via aveva lasciato un colore rosso, allora nella neve si vedranno alcuni puntini rossi!
Incredibile ma vero, per risolvere il problema basta TOGLIERE l'istruzione
		cmp	al,0FFh
senza cambiare il tipo di salto (cioè ci rimane jne loop_pal). Infatti quando al ritorna a 0, dopo che il colore numero 255 è stato impostato, la inc imposta il flag di zero per la prima volta! Oppure avrei dovuto riscrivere il ciclo in maniera differente.

L'algoritmo di D.Lehmer dà una serie nulla (dà tutti 0) se num_rnd_mul è 0, percui se uno lo cambia a 0, quando esegue SNOW si vedrà lo schermo completamente nero.

Mi sono divertito a cambiare i numeri:

num_rnd_val	dw	12345		; A caso
num_rnd_mul	dw	9421		; A caso
ho messo al posto di num_rnd_mul un numero pari e al posto di num_rnd_val vari valori ed ho ottenuto che molto rapidamente lo schermo diventatava di un solo colore. Preciso quindi che alcuni valori, forse tutti i valori pari di num_rnd_mul (ci vuole un matematico qui!) non danno l'effetto neve desiderato.

Un'altra cosa da precisare: Put_Pixel non è una procedura general-purpose, (provate a scriverla voi una Put_Pixel "normale"), ma stampa il pixel alle coordinate y*256 + x*64; questa espressione, che può dare overflow, è propria di questo algoritmo (cioè di questa simulazione dell'effetto neve).


Naturalmente non pretendo certo di aver coperto l'intero l'argomento, prendetelo solo come un'introduzione, un punto di partenza da cui spiccare il salto verso la grafica avanzata.

Qui spero solo di avervi fatto capire alcuni meccanismi che stanno alla base della programmazione della scheda video VGA.


Assembly Page di Antonio
<< Indice >>