Graphics-mode memory-mapped programming for IBM PC—compatible adapters is considerably more complicated than text-mode programming. Each bit or group of bits in the regen buffer corresponds to an addressable point, or pixel, on the screen. The mapping of bits to pixels differs for each of the available graphics modes, with their differences in resolution and number of supported colors. The newer adapters (EGA, MCGA, and VGA) also use the concept of bit planes, where bits of a pixel are segregated into multiple banks of memory mapped at the same address; you must manipulate these bit planes by a combination of memory-mapped I/O and port addressing.
IBM-video-systems graphics programming is a subject large enough for a book of its own, but we can use the 640-by-200, 2-color graphics display mode of the CGA (which is also supported by all subsequent IBM text/graphics adapters) to illustrate a few of the techniques involved. This mode is simple to deal with because each pixel is represented by a single bit. The pixels are assigned (x,y) coordinates in the range (0,0) through (639,199), where x is the horizontal displacement, y is the vertical displacement, and the home position (0,0) is the upper left corner of the display. (See Figure 6-7.)
Figure 6-7. Point addressing for 640-by-200, 2-color graphics modes on the CGA, EGA, MCGA, and VGA (IBM ROM BIOS mode 6).
Please refer to the printed book for this figure.
Each successive group of 80 bytes (640 bits) represents one horizontal scan line. Within each byte, the bits map one-for-one onto pixels, with the most significant bit corresponding to the leftmost displayed pixel of a set of eight pixels and the least significant bit corresponding to the rightmost displayed pixel of the set. The memory map is set up so that all the even y coordinates are scanned as a set and all the odd y coordinates are scanned as a set; this mapping is referred to as the memory interlace.
To find the regen buffer offset for a particular (x,y) coordinate, you would use the following formula:
offset = ((y AND 1) * 2000H) + (y/2 * 50H) + (x/8)
The assembly-language implementation of this formula is as follows:
; assume AX = Y, BX = X
shr bx,1 ; divide X by 8
shr bx,1
shr bx,1
push ax ; save copy of Y
shr ax,1 ; find (Y/2) * 50h
mov cx,50h ; with product in DX:AX
mul cx
add bx,ax ; add product to X/8
pop ax ; add (Y AND 1) * 2000h
and ax,1
jz label1
add bx,2000h
label1: ; now BX = offset into
; video buffer
After calculating the correct byte address, you can use the following formula to calculate the bit position for a given pixel coordinate:
bit = 7 - (x MOD 8)
where bit 7 is the most significant bit and bit 0 is the least significant bit. It is easiest to build an 8-byte table, or array of bit masks, and use the operation X AND 7 to extract the appropriate entry from the table:
(X AND 7) Bit mask (X AND 7) Bit mask
0 80H 4 08H
1 40H 5 04H
2 20H 6 02H
3 10H 7 01H
The assembly-language implementation of this second calculation is as follows:
table db 80h ; X AND 7 = offset 0
db 40h ; X AND 7 = offset 1
db 20h ; X AND 7 = offset 2
db 10h ; X AND 7 = offset 3
db 08h ; X AND 7 = offset 4
db 04h ; X AND 7 = offset 5
db 02h ; X AND 7 = offset 6
db 01h ; X AND 7 = offset 7
.
.
.
; assume BX = X coordinate
and bx,7 ; isolate 0—7 offset
mov al,[bx+table]
; now AL = mask from table
.
.
.
The program can then use the mask, together with the byte offset previously calculated, to set or clear the appropriate bit in the video controller's regen buffer.