Direct programming of the IBM PC—compatible video adapters in their text display modes (sometimes also called alphanumeric display modes) is straightforward. The character set is the same for all, and the cursor home position——(x,y) = (0,0)——is defined to be the upper left corner of the screen (Figure 6-3). The MDA uses 4 KB of memory starting at segment B000H as a regen buffer, and the various adapters with both text and graphics capabilities (CGA, EGA, MCGA, and VGA) use 16 KB of memory starting at segment B800H. (See Figure 6-1.) In the latter case, the 16 KB is divided into "pages" that can be independently updated and displayed.
Figure 6-3. Cursor addressing for 80-by-25 text display modes (IBM ROM BIOS modes 2, 3, and 7).
Please refer to the printed book for this figure.
Each character-display position is allotted 2 bytes in the regen buffer. The first byte (even address) contains the ASCII code of the character, which is translated by a special hardware character generator into a dot-matrix pattern for the screen. The second byte (odd address) is the attribute byte. Several bit fields in this byte control such features as blinking, intensity (highlighting), and reverse video, depending on the adapter type and display mode (Figures 6-4 and 6-5). Figure 6-6 shows a hex and ASCII dump of part of the video map for the MDA.
Display Background Foreground
No display (black) 000 000
No display (white)(1) 111 111
Underline 000 001
Normal video 000 111
Reverse video 111 000
(1)VGA only
Figure 6-4. Attribute byte for 80-by-25 monochrome text display mode on the MDA, Hercules cards, EGA, and VGA (IBM ROM BIOS mode 7).
Value Color
0 Black
1 Blue
2 Green
3 Cyan
4 Red
5 Magenta
6 Brown
7 White
8 Gray
9 Light blue
10 Light green
11 Light cyan
12 Light red
13 Light magenta
14 Yellow
15 Intense white
Figure 6-5. Attribute byte for the 40-by-25 and 80-by-25 text display modes on the CGA, EGA, MCGA, and VGA (IBM ROM BIOS modes 0—3). The table of color values assumes default palette programming and that the B or I bit controls intensity.
B000:0000 3e 07 73 07 65 07 6c 07 65 07 63 07 74 07 20 07
B000:0010 74 07 65 07 6d 07 70 07 20 07 20 07 20 07 20 07
B000:0020 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0030 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0040 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0050 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0060 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0070 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0080 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
B000:0090 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
Figure 6-6. Example dump of the first 160 bytes of the MDA's regen buffer. These bytes correspond to the first visible line on the screen. Note that ASCII character codes are stored in even bytes and their respective character attributes in odd bytes; all the characters in this example line have the attribute normal video.
You can calculate the memory offset of any character on the display as the line number (y coordinate) times 80 characters per line times 2 bytes per character, plus the column number (x coordinate) times 2 bytes per character, plus (for the text/graphics adapters) the page number times the size of the page (4 KB per page in 80-by-25 modes; 2 KB per page in 40-by-25 modes). In short, the formula for the offset of the character-attribute pair for a given screen position (x,y) in 80-by-25 text modes is
offset = ((y * 50H + x) * 2) + (page * 1000H)
In 40-by-25 text modes, the formula is
offset = ((y * 50H + x) * 2) + (page * 0800H)
Of course, the segment register being used to address the video buffer must be set appropriately, depending on the type of display adapter.
As a simple example, assume that the character to be displayed is in the AL register, the desired attribute byte for the character is in the AH register, the x coordinate (column) is in the BX register, and the y coordinate (row) is in the CX register. The following code stores the character and attribute byte into the MDA's video refresh buffer at the proper location:
push ax ; save char and attribute
mov ax,160
mul cx ; DX:AX = Y * 160
shl bx,1 ; multiply X by 2
add bx,ax ; BX = (Y*160) + (X*2)
mov ax,0b000h ; ES = segment of monochrome
mov es,ax ; adapter refresh buffer
pop ax ; restore char and attribute
mov es:[bx],ax ; write them to video buffer
More frequently, we wish to move entire strings into the refresh buffer, starting at a given coordinate. In the next example, assume that the DS:SI registers point to the source string, the ES:DI registers point to the starting position in the video buffer (calculated as shown in the previous example), the AH register contains the attribute byte to be assigned to every character in the string, and the CX register contains the length of the string. The following code moves the entire string into the refresh buffer:
xfer: lodsb ; fetch next character
stosw ; store char + attribute
loop xfer ; until all chars moved
Of course, the video drivers written for actual application programs must take into account many additional factors, such as checking for special control codes (linefeeds, carriage returns, tabs), line wrap, and scrolling.
Programs that write characters directly to the CGA regen buffer in text modes must deal with an additional complicating factor——they must examine the video controller's status port and access the refresh buffer only during the horizontal retrace or vertical retrace intervals. (A retrace interval is the period when the electron beam that illuminates the screen phosphors is being repositioned to the start of a new scan line.) Otherwise, the contention for memory between the CPU and the video controller is manifest as unsightly "snow" on the display. (If you are writing programs for any of the other IBM PC—compatible video adapters, such as the MDA, EGA, MCGA, or VGA, you can ignore the retrace intervals; snow is not a problem with these video controllers.)
A program can detect the occurrence of a retrace interval by monitoring certain bits in the video controller's status register. For example, assume that the offset for the desired character position has been calculated as in the preceding example and placed in the BX register, the segment for the CGA's refresh buffer is in the ES register, and an ASCII character code to be displayed is in the CL register. The following code waits for the beginning of a new horizontal retrace interval and then writes the character into the buffer:
mov dx,03dah ; DX = video controller's
; status port address
cli ; disable interrupts
; if retrace is already
; in progress, wait for
; it to end...
wait1: in al,dx ; read status port
and al,1 ; check if retrace bit on
jnz wait1 ; yes, wait
; wait for new retrace
; interval to start...
wait2: in al,dx ; read status port
and al,1 ; retrace bit on yet?
jz wait2 ; jump if not yet on
mov es:[bx],cl ; write character to
; the regen buffer
sti ; enable interrupts again
The first wait loop "synchronizes" the code to the beginning of a horizontal retrace interval. If only the second wait loop were used (that is, if a character were written when a retrace interval was already in progress), the write would occasionally begin so close to the end of a horizontal retrace "window" that it would partially miss the retrace, resulting in scattered snow at the left edge of the display. Notice that the code also disables interrupts during accesses to the video buffer, so that service of a hardware interrupt won't disrupt the synchronization process.
Because of the retrace-interval constraints just outlined, the rate at which you can update the CGA in text modes is severely limited when the updating is done one character at a time. You can obtain better results by calculating all the relevant addresses and setting up the appropriate registers, disabling the video controller by writing to register 3D8H, moving the entire string to the buffer with a REP MOVSW operation, and then reenabling the video controller. If the string is of reasonable length, the user won't even notice a flicker in the display. Of course, this procedure introduces additional hardware dependence into your code because it requires much greater knowledge of the 6845 controller. Luckily, snow is not a problem in CGA graphics modes.