_GRAPHICS PROGRAMMING COLUMN_ by Michael Abrash [LISTING ONE] typedef struct _ModelColor { unsigned char Red; /* 255 = max red, 0 = no red */ unsigned char Green; /* 255 = max green, 0 = no green */ unsigned char Blue; /* 255 = max blue, 0 = no blue */ } ModelColor; [LISTING TWO] /* Sets up the palette in mode X, to a 2-2-2 general R-G-B organization, with 64 separate levels each of pure red, green, and blue. This is very good for pure colors, but mediocre at best for mixes. ------------------------- |0 0 | Red|Green| Blue | ------------------------- 7 6 5 4 3 2 1 0 ------------------------- |0 1 | Red | ------------------------- 7 6 5 4 3 2 1 0 ------------------------- |1 0 | Green | ------------------------- 7 6 5 4 3 2 1 0 ------------------------- |1 1 | Blue | ------------------------- 7 6 5 4 3 2 1 0 Colors are gamma corrected for a gamma of 2.3 to provide approximately even intensity steps on the screen. */ #include #include "polygon.h" static unsigned char Gamma4Levels[] = { 0, 39, 53, 63 }; static unsigned char Gamma64Levels[] = { 0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, }; static unsigned char PaletteBlock[256][3]; /* 256 RGB entries */ void InitializePalette() { int Red, Green, Blue, Index; union REGS regset; struct SREGS sregset; for (Red=0; Red<4; Red++) { for (Green=0; Green<4; Green++) { for (Blue=0; Blue<4; Blue++) { Index = (Red<<4)+(Green<<2)+Blue; PaletteBlock[Index][0] = Gamma4Levels[Red]; PaletteBlock[Index][1] = Gamma4Levels[Green]; PaletteBlock[Index][2] = Gamma4Levels[Blue]; } } } for (Red=0; Red<64; Red++) { PaletteBlock[64+Red][0] = Gamma64Levels[Red]; PaletteBlock[64+Red][1] = 0; PaletteBlock[64+Red][2] = 0; } for (Green=0; Green<64; Green++) { PaletteBlock[128+Green][0] = 0; PaletteBlock[128+Green][1] = Gamma64Levels[Green]; PaletteBlock[128+Green][2] = 0; } for (Blue=0; Blue<64; Blue++) { PaletteBlock[192+Blue][0] = 0; PaletteBlock[192+Blue][1] = 0; PaletteBlock[192+Blue][2] = Gamma64Levels[Blue]; } /* Now set up the palette */ regset.x.ax = 0x1012; /* set block of DAC registers function */ regset.x.bx = 0; /* first DAC location to load */ regset.x.cx = 256; /* # of DAC locations to load */ regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which to load RGB settings */ sregset.es = _DS; /* segment of array from which to load settings */ int86x(0x10, ®set, ®set, &sregset); /* load the palette block */ } [LISTING THREE] /* Converts a model color (a color in the RGB color cube, in the current color model) to a color index for mode X. Pure primary colors are special-cased, and everything else is handled by a 2-2-2 model. */ int ModelColorToColorIndex(ModelColor * Color) { if (Color->Red == 0) { if (Color->Green == 0) { /* Pure blue */ return(192+(Color->Blue >> 2)); } else if (Color->Blue == 0) { /* Pure green */ return(128+(Color->Green >> 2)); } } else if ((Color->Green == 0) && (Color->Blue == 0)) { /* Pure red */ return(64+(Color->Red >> 2)); } /* Multi-color mix; look up the index with the two most significant bits of each color component */ return(((Color->Red & 0xC0) >> 2) | ((Color->Green & 0xC0) >> 4) | ((Color->Blue & 0xC0) >> 6)); } [LISTING FOUR] ; Demonstrates drawing solid text on the VGA, using The BitMan's write mode ; 3-based, one-pass technique. Tested with TASM 3.0 and MASM 5.1. CHAR_HEIGHT equ 8 ;# of scan lines per character (must be <256) SCREEN_HEIGHT equ 480 ;# of scan lines per screen SCREEN_SEGMENT equ 0a000h ;where screen memory is FG_COLOR equ 14 ;text color BG_COLOR equ 1 ;background box color GC_INDEX equ 3ceh ;Graphics Controller (GC) Index reg I/O port SET_RESET equ 0 ;Set/Reset register index in GC G_MODE equ 5 ;Graphics Mode register index in GC BIT_MASK equ 8 ;Bit Mask register index in GC .model small .stack 200h .data Line dw ? ;current line # CharHeight dw ? ;# of scan lines in each character (must be <256) MaxLines dw ? ;max # of scan lines of text that will fit on screen LineWidthBytes dw ? ;offset from one scan line to the next FontPtr dd ? ;pointer to font with which to draw SampleString label byte db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' db 'abcdefghijklmnopqrstuvwxyz' db '0123456789!@#$%^&*(),<.>/?;:',0 .code start: mov ax,@data mov ds,ax mov ax,12h int 10h ;select 640x480 16-color mode mov ah,11h ;BIOS character generator function mov al,30h ;BIOS get font pointer subfunction mov bh,3 ;get 8x8 ROM font subsubfunction int 10h ;get the pointer to the BIOS 8x8 font mov word ptr [FontPtr],bp mov word ptr [FontPtr+2],es mov bx,CHAR_HEIGHT mov [CharHeight],bx ;# of scan lines per character mov ax,SCREEN_HEIGHT sub dx,dx div bx mul bx ;max # of full scan lines of text that mov [MaxLines],ax ; will fit on the screen mov ah,0fh ;BIOS video status function int 10h ;get # of columns (bytes) per row mov al,ah ;convert byte columns variable in sub ah,ah ; AH to word in AX mov [LineWidthBytes],ax ;width of scan line in bytes ;now draw the text sub bx,bx mov [Line],bx ;start at scan line 0 LineLoop: sub ax,ax ;start at column 0; must be a multiple of 8 mov ch,FG_COLOR ;color in which to draw text mov cl,BG_COLOR ;color in which to draw background box mov si,offset SampleString ;text to draw call DrawTextString ;draw the sample text mov bx,[Line] add bx,[CharHeight] ;# of next scan line to draw on mov [Line],bx cmp bx,[MaxLines] ;done yet? jb LineLoop ;not yet mov ah,7 int 21h ;wait for a key press, without echo mov ax,03h int 10h ;back to text mode mov ah,4ch int 21h ;exit to DOS ; Draws a text string. ; Input: AX = X coordinate at which to draw upper left corner of first char ; BX = Y coordinate at which to draw upper left corner of first char ; CH = foreground (text) color ; CL = background (box) color ; DS:SI = pointer to string to draw, zero terminated ; CharHeight must be set to the height of each character ; FontPtr must be set to the font with which to draw ; LineWidthBytes must be set to the scan line width in bytes ; Don't count on any registers other than DS, SS, and SP being preserved. ; The X coordinate is truncated to a multiple of 8. Characters are ; assumed to be 8 pixels wide. align 2 DrawTextString proc near cld shr ax,1 ;byte address of starting X within scan line shr ax,1 shr ax,1 mov di,ax mov ax,[LineWidthBytes] mul bx ;start offset of initial scan line add di,ax ;start offset of initial byte mov ax,SCREEN_SEGMENT mov es,ax ;ES:DI = offset of initial character's ; first scan line ;set up the VGA's hardware so that we can ; fill the latches with the background color mov dx,GC_INDEX mov ax,(0ffh SHL 8) + BIT_MASK out dx,ax ;set Bit Mask register to 0xFF (that's the ; default, but I'm doing this just to make sure ; you understand that Bit Mask register and ; CPU data are ANDed in write mode 3) mov ax,(003h SHL 8) + G_MODE out dx,ax ;select write mode 3 mov ah,cl ;background color mov al,SET_RESET out dx,ax ;set the drawing color to background color mov byte ptr es:[0ffffh],0ffh ;write 8 pixels of the background ; color to unused offscreen memory mov cl,es:[0ffffh] ;read the background color back into the ; latches; the latches are now filled with ; the background color. The value in CL ; doesn't matter, we just needed a target ; for the read, so we could load the latches mov ah,ch ;foreground color out dx,ax ;set the Set/Reset (drawing) color to the ; foreground color ;we're ready to draw! DrawTextLoop: lodsb ;next character to draw and al,al ;end of string? jz DrawTextDone ;yes push ds ;remember string's segment push si ;remember offset of next character in string push di ;remember drawing offset ;load these variables before we wipe out DS mov dx,[LineWidthBytes] ;offset from one line to next dec dx ;compensate for STOSB mov cx,[CharHeight] ; mul cl ;offset of character in font table lds si,[FontPtr] ;point to font table add si,ax ;point to start of character to draw ;the following loop should be unrolled for ; maximum performance! DrawCharLoop: ;draw all lines of the character movsb ;get the next byte of the character and draw ; character; data is ANDed with Bit Mask ; register to become bit mask, and selects ; between latch (containing the background ; color) and Set/Reset register (containing ; foreground color) add di,dx ;point to next line of destination loop DrawCharLoop pop di ;retrieve initial drawing offset inc di ;drawing offset for next char pop si ;retrieve offset of next character in string pop ds ;retrieve string's segment jmp DrawTextLoop ;draw next character, if any align 2 DrawTextDone: ;restore the Graphics Mode register to its ; default state of write mode 0 mov dx,GC_INDEX mov ax,(000h SHL 8) + G_MODE out dx,ax ;select write mode 0 ret DrawTextString endp end start