Text Mode

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.