abrash-black-book/chapter-25.md
2014-01-06 23:29:31 +11:00

865 lines
32 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Michael Abrash's Graphics Programming Black Book, Special Edition
author: Michael Abrash
date: '1997-07-01'
identifier:
- scheme: ISBN
text: 1576101746
publisher: The Coriolis Group
category: 'Web and Software Development: Game Development,Web and Software Development:
Graphics and Multimedia Development'
chapter: '25'
pages: 461-479
---
## Chapter 25 -- VGA Data Machinery
### The Barrel Shifter, Bit Mask, and Set/Reset Mechanisms
In the last chapter, we examined a simplified model of data flow within
the GC portion of the VGA, featuring the latches and ALUs. Now we're
ready to expand that model to include the barrel shifter, bit mask, and
the set/reset capabilities, leaving only the write modes to be explored
over the next few chapters.
### VGA Data Rotation
Figure 25.1 shows an expanded model of GC data flow, featuring the
barrel shifter and bit mask circuitry. Let's look at the barrel shifter
first. A barrel shifter is circuitry capable of shifting—or rotating, in
the VGA's case—data an arbitrary number of bits in a single operation,
as opposed to being able to shift only one bit position at a time. The
barrel shifter in the VGA can rotate incoming CPU data up to seven bits
to the right (toward the least significant bit), with bit 0 wrapping
back to bit 7, after which the VGA continues processing the rotated byte
just as it normally processes unrotated CPU data. Thanks to the nature
of barrel shifters, this rotation requires no extra processing time over
unrotated VGA operations. The number of bits by which CPU data is
shifted is controlled by bits 2-0 of GC register 3, the Data Rotate
register, which also contains the ALU function select bits (data
unmodified, AND, OR, and XOR) that we looked at in the last chapter.
![**Figure 25.1**  *Data flow through the Graphics Controller.*](images/25-01.jpg)
The barrel shifter is powerful, but (as sometimes happens in this
business) it sounds more useful than it really is. This is because the
GC can only rotate CPU data, a task that the CPU itself is perfectly
capable of performing. Two `OUT`s are needed to select a given
rotation: one to set the GC Index register, and one to set the Data
Rotate register. However, with careful programming it's sometimes
possible to leave the GC Index always pointing to the Data Rotate
register, so only one `OUT` is needed. Even so, it's often easier
and/or faster to simply have the CPU rotate the data of interest CL
times than to set the Data Rotate register. (Bear in mind that a single
`OUT` takes from 11 to 31 cycles on a 486—and longer if the VGA is
sluggish at responding to OUTs, as many VGAs are.) If only the VGA could
rotate *latched* data, then there would be all sorts of useful
applications for rotation, but, sadly, only CPU data can be rotated.
The drawing of bit-mapped text is one use for the barrel shifter, and
I'll demonstrate that application below. In general, though, don't knock
yourself out trying to figure out how to work data rotation into your
programs—it just isn't all that useful in most cases.
### The Bit Mask
The VGA has bit mask circuitry for each of the four memory planes. The
four bit masks operate in parallel and are all driven by the same mask
data for each operation, so they're generally referred to in the
singular, as "the bit mask." Figure 25.2 illustrates the operation of
one bit of the bit mask for one plane. This circuitry occurs eight times
in the bit mask for a given plane, once for each bit of the byte written
to display memory. Briefly, the bit mask determines on a bit-by-bit
basis whether the source for each byte written to display memory is the
ALU for that plane or the latch for that plane.
![**Figure 25.2**  *Bit mask operation.*](images/25-02.jpg)
The bit mask is controlled by GC register 8, the Bit Mask register. If a
given bit of the Bit Mask register is 1, then the corresponding bit of
data from the ALUs is written to display memory for all four planes,
while if that bit is 0, then the corresponding bit of data from the
latches for the four planes is written to display memory unchanged. (In
write mode 3, the actual bit mask that's applied to data written to
display memory is the logical AND of the contents of the Bit Mask
register and the data written by the CPU, as we'll see in Chapter 26.)
The most common use of the bit mask is to allow updating of selected
bits within a display memory byte. This works as follows: The display
memory byte of interest is latched; the bit mask is set to preserve all
but the bit or bits to be changed; the CPU writes to display memory,
with the bit mask preserving the indicated latched bits and allowing ALU
data through to change the other bits. Remember, though, that it is not
possible to alter selected bits in a display memory byte *directly;* the
byte must first be latched by a CPU read, and then the bit mask can keep
selected bits of the latched byte unchanged.
Listing 25.1 shows a program that uses the bit mask data rotation
capabilities of the GC to draw bitmapped text at any screen location.
The BIOS only draws characters on character boundaries; in 640x480
graphics mode the default font is drawn on byte boundaries horizontally
and every 16 scan lines vertically. However, with direct bitmapped text
drawing of the sort used in Listing 25.1, it's possible to draw any font
of any size anywhere on the screen (and a lot faster than via DOS or the
BIOS, as well).
**LISTING 25.1 L25-1.ASM**
```nasm
; Program to illustrate operation of data rotate and bit mask
; features of Graphics Controller. Draws 8x8 character at
; specified location, using VGA's 8x8 ROM font. Designed
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
; By Michael Abrash.
;
stack segment para stack STACK'
db 512 dup(?)
stack ends
;
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
;
; VGA register equates.
;
GC_INDEX equ 3ceh ;GC index register
GC_ROTATE equ 3 ;GC data rotate/logical function
; register index
GC_BIT_MASK equ 8 ;GC bit mask register index
;
dseg segment para common DATA'
TEST_TEXT_ROW equ 69 ;row to display test text at
TEST_TEXT_COL equ 17 ;column to display test text at
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
TestString label byte
db Hello, world!',0 ;test string to print.
FontPointer dd ? ;font offset
dseg ends
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov ax,(SETTING SHL 8) OR INDEX
out dx,ax
endm
;
cseg segment para public CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
; Set driver to use the 8x8 font.
;
mov ah,11h ;VGA BIOS character generator function,
mov al,30h ; return info subfunction
mov bh,3;get 8x8 font pointer
int 10h
call SelectFont
;
; Print the test string.
;
mov si,offset TestString
mov bx,TEST_TEXT_ROW
mov cx,TEST_TEXT_COL
StringOutLoop:
lodsb
and al,al
jz StringOutDone
call DrawChar
add cx,TEST_TEXT_WIDTH
jmp StringOutLoop
StringOutDone:
;
; Reset the data rotate and bit mask registers.
;
SETGC GC_ROTATE, 0
SETGC GC_BIT_MASK, 0ffh
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Return to text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
Start endp
;
; Subroutine to draw a text character in a linear graphics mode
; (0Dh, 0Eh, 0Fh, 010h, 012h).
; Font used should be pointed to by FontPointer.
;
; Input:
; AL = character to draw
; BX = row to draw text character at
; CX = column to draw text character at
;
; Forces ALU function to "move".
;
DrawChar proc near
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
;
; Set DS:SI to point to font and ES to point to display memory.
;
lds si,[FontPointer] ;point to font
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
;
; Calculate screen address of byte character starts in.
;
push ds ;point to BIOS data segment
sub dx,dx
mov ds,dx
xchg ax,bx
mov di,ds:[SCREEN_WIDTH_IN_BYTES] ;retrieve BIOS
; screen width
pop ds
mul di ;calculate offset of start of row
push di ;set aside screen width
mov di,cx ;set aside the column
and cl,0111b ;keep only the column in-byte address
shr di,1
shr di,1
shr di,1 ;divide column by 8 to make a byte address
add di,ax ;and point to byte
;
; Calculate font address of character.
;
sub bh,bh
shl bx,1 ;assumes 8 bytes per character; use
shl bx,1 ; a multiply otherwise
shl bx,1 ;offset in font of character
add si,bx ;offset in font segment of character
;
; Set up the GC rotation.
;
mov dx,GC_INDEX
mov al,GC_ROTATE
mov ah,cl
out dx,ax
;
; Set up BH as bit mask for left half,
; BL as rotation for right half.
;
mov bx,0ffffh
shr bh,cl
neg cl
add cl,8
shl bl,cl
;
; Draw the character, left half first, then right half in the
; succeeding byte, using the data rotation to position the character
; across the byte boundary and then using the bit mask to get the
; proper portion of the character into each byte.
; Does not check for case where character is byte-aligned and
; no rotation and only one write is required.
;
mov bp,FONT_CHARACTER_SIZE
mov dx,GC_INDEX
pop cx ;get back screen width
dec cx
dec cx ; -2 because do two bytes for each char
CharacterLoop:
;
; Set the bit mask for the left half of the character.
;
mov al,GC_BIT_MASK
mov ah,bh
out dx,ax
;
; Get the next character byte & write it to display memory.
; (Left half of character.)
;
mov al,[si] ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Set the bit mask for the right half of the character.
;
mov al,GC_BIT_MASK
mov ah,bl
out dx,ax
;
; Get the character byte again & write it to display memory.
; (Right half of character.)
;
lodsb ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Point to next line of character in display memory.
;
add di,cx
;
dec bp
jnz CharacterLoop
;
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
DrawChar endp
;
; Set the pointer to the font to draw from to ES:BP.
;
SelectFont proc near
mov word ptr [FontPointer],bp ;save pointer
mov word ptr [FontPointer+2],es
ret
SelectFont endp
;
cseg ends
end start
```
The bit mask can be used for much more than bit-aligned fonts. For
example, the bit mask is useful for fast pixel drawing, such as that
performed when drawing lines, as we'll see in Chapter 35. It's also
useful for drawing the edges of primitives, such as filled polygons,
that potentially involve modifying some but not all of the pixels
controlled by a single byte of display memory.
Basically, the bit mask is handy whenever only *some* of the eight
pixels in a byte of display memory need to be changed, because it allows
full use of the VGA's four-way parallel processing capabilities for the
pixels that are to be drawn, without interfering with the pixels that
are to be left unchanged. The alternative would be plane-by-plane
processing, which from a performance perspective would be undesirable
indeed.
It's worth pointing out again that the bit mask operates on the data in
the latches, not on the data in display memory. This makes the bit mask
a flexible resource that with a little imagination can be used for some
interesting purposes. For example, you could fill the latches with a
solid background color (by writing the color somewhere in display
memory, then reading that location to load the latches), and then use
the Bit Mask register (or write mode 3, as we'll see later) as a mask
through which to draw a foreground color stencilled into the background
color *without* reading display memory first. This only works for
writing whole bytes at a time (clipped bytes require the use of the bit
mask; unfortunately, we're already using it for stencilling in this
case), but it completely eliminates reading display memory and does
foreground-plus-background drawing in one blurry-fast pass.
> ![](images/i.jpg)
> This last-described example is a good illustration of how I'd suggest
> you approach the VGA: As a rich collection of hardware resources that
> can profitably be combined in some non-obvious ways. Don't let yourself
> be limited by the obvious applications for the latches, bit mask, write
> modes, read modes, map mask, ALUs, and set/reset circuitry. Instead, try
> to imagine how they could work together to perform whatever task you
> happen to need done at any given time. I've made my code as much as four
> times faster by doing this, as the discussion of Mode X in Chapters
> 47-49 demonstrates.
The example code in Listing 25.1 is designed to illustrate the use of
the Data Rotate and Bit Mask registers, and is not as fast or as
complete as it might be. The case where text *is* byte-aligned could be
detected and performed much faster, without the use of the Bit Mask or
Data Rotate registers and with only one display memory access per font
byte (to write the font byte), rather than four (to read display memory
and write the font byte to each of the two bytes the character spans).
Likewise, non-aligned text drawing could be streamlined to one display
memory access per byte by having the CPU rotate and combine the font
data directly, rather than setting up the VGA's hardware to do it.
(Listing 25.1 was designed to illustrate VGA data rotation and bit
masking rather than the fastest way to draw text. We'll see faster
text-drawing code soon.) One excellent rule of thumb is to minimize
display memory accesses of all types, especially reads, which tend to be
considerably slower than writes. Also, in Listing 25.1 it would be
faster to use a table lookup to calculate the bit masks for the two
halves of each character rather than the shifts used in the example.
For another (and more complex) example of drawing bit-mapped text on the
VGA, see John Cockerham's article, "Pixel Alignment of EGA Fonts," *PC
Tech Journal*, January, 1987. Parenthetically, I'd like to pass along
John's comment about the VGA: "When programming the VGA, *everything* is
complex."
He's got a point there.
### The VGA's Set/Reset Circuitry
At last we come to the final aspect of data flow through the GC on write
mode 0 writes: the set/reset circuitry. Figure 25.3 shows data flow on a
write mode 0 write. The only difference between this figure and Figure
25.1 is that on its way to each plane potentially the rotated CPU data
passes through the set/reset circuitry, which may or may not replace the
CPU data with set/reset data. Briefly put, the set/reset circuitry
enables the programmer to elect to independently replace the CPU data
for each plane with either 00 or 0FFH.
What is the use of such a feature? Well, the standard way to control
color is to set the Map Mask register to enable writes to only those
planes that need to be set to produce the desired color. For example,
the Map Mask register would be set to 09H to draw in high-intensity
blue; here, bits 0 and 3 are set to 1, so only the blue plane (plane 0)
and the intensity plane (plane 3) are written to.
![**Figure 25.3**  *Data flow during a write mode 0 write operation.*](images/25-03.jpg)
Remember, though, that planes that are disabled by the Map Mask register
are not written to or modified in any way. This means that the above
approach works only if the memory being written to is zeroed; if,
however, the memory already contains non-zero data, that data will
remain in the planes disabled by the Map Mask, and the end result will
be that some planes contain the data just written and other planes
contain old data. In short, color control using the Map Mask does not
force all planes to contain the desired color. In particular, it is not
possible to force some planes to zero and other planes to one in a
single write with the Map Mask register.
The program in Listing 25.2 illustrates this problem. A green pattern
(plane 1 set to 1, planes 0, 2, and 3 set to 0) is first written to
display memory. Display memory is then filled with blue (only plane 0
set to 1), with a Map Mask setting of 01H. Where the blue crosses the
green, cyan is produced, rather than blue, because the Map Mask register
setting of 01H that produces blue leaves the green plane (plane 1)
unchanged. In order to generate blue unconditionally, it would be
necessary to set the Map Mask register to 0FH, clear memory, and then
set the Map Mask register to 01H and fill with blue.
**LISTING 25.2 L25-2.ASM**
```nasm
; Program to illustrate operation of Map Mask register when drawing
; to memory that already contains data.
; By Michael Abrash.
;
stack segment para stack STACK'
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public CODE#146;
assume cs:cseg
start proc near
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h ;map mask setting enables only
; plane 1, the green plane
sub di,di ;start at beginning of video memory
mov al,0ffh
mov bp,24 ;# bars to draw
HorzBarLoop:
mov cx,80*10 ;# bytes per horizontal bar
rep stosb ;draw bar
add di,80*10 ;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with blue, using Map Mask register to enable writes
; to blue plane only.
;
SETSC SC_MAP_MASK,01h ;map mask setting enables only
; plane 0, the blue plane
sub di,di
mov cx,80*480 ;# bytes per screen
mov al,0ffh
rep stosb ;perform fill (affects only
; plane 0, the blue plane)
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start
```
#### Setting All Planes to a Single Color
The set/reset circuitry can be used to force some planes to 0-bits and
others to 1-bits during a single write, while letting CPU data go to
still other planes, and so provides an efficient way to set all planes
to a desired color. The set/reset circuitry works as follows:
For each of the bits 0-3 in the Enable Set/Reset register (Graphics
Controller register 1) that is 1, the corresponding bit in the Set/Reset
register (GC register 0) is extended to a byte (0 or 0FFH) and replaces
the CPU data for the corresponding plane. For each of the bits in the
Enable Set/Reset register that is 0, the CPU data is used unchanged for
that plane (normal operation). For example, if the Enable Set/Reset
register is set to 01H and the Set/Reset register is set to 05H, then
the CPU data is replaced for plane 0 only (the blue plane), and the
value it is replaced with is 0FFH (bit 0 of the Set/Reset register
extended to a byte). Note that in this case, bits 1-3 of the Set/Reset
register have no effect.
It is important to understand that the set/reset circuitry directly
replaces CPU data in Graphics Controller data flow. Refer back to Figure
25.3 to see that the output of the set/reset circuitry passes through
(and may be transformed by) the ALU and the bit mask before being
written to memory, and even then the Map Mask register must enable the
write. When using set/reset, it is generally desirable to set the Map
Mask register to enable all planes the set/reset circuitry is
controlling, since those memory planes which are disabled by the Map
Mask register cannot be modified, and the purpose of enabling set/reset
for a plane is to force that plane to be set by the set/reset circuitry.
Listing 25.3 illustrates the use of set/reset to force a specific color
to be written. This program is the same as that of Listing 25.2, except
that set/reset rather than the Map Mask register is used to control
color. The preexisting pattern is completely overwritten this time,
because the set/reset circuitry writes 0-bytes to planes that must be
off as well as 0FFH-bytes to planes that must be on.
**LISTING 25.3 L25-3.ASM**
```nasm
; Program to illustrate operation of set/reset circuitry to force
; setting of memory that already contains data.
; By Michael Abrash.
;
stack segment para stack STACK#146;
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public CODE#146;
assume cs:cseg
start proc near
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h ;map mask setting enables only
; plane 1, the green plane
sub di,di ;start at beginning of video memory
mov al,0ffh
mov bp,24 ;# bars to draw
HorzBarLoop:
mov cx,80*10 ;# bytes per horizontal bar
rep stosb ;draw bar
add di,80*10 ;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with blue, using set/reset to force plane 0 to 1#146;s and all
; other plane to 0#146;s.
;
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
; planes, so set/reset values can
; be written to memory
SETGC GC_ENABLE_SET_RESET,0fh ;CPU data to all planes will be
; replaced by set/reset value
SETGC GC_SET_RESET,01h ;set/reset value is 0ffh for plane 0
; (the blue plane) and 0 for other
; planes
sub di,di
mov cx,80*480 ;# bytes per screen
mov al,0ffh ;since set/reset is enabled for all
; planes, the CPU data is ignored-
; only the act of writing is
; important
rep stosb ;perform fill (affects all planes)
;
; Turn off set/reset.
;
SETGC GC_ENABLE_SET_RESET,0
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start
```
#### Manipulating Planes Individually
Listing 25.4 illustrates the use of set/reset to control only some,
rather than all, planes. Here, the set/reset circuitry forces plane 2 to
1 and planes 0 and 3 to 0. Because bit 1 of the Enable Set/Reset
register is 0, however, set/reset does not affect plane 1; the CPU data
goes unchanged to the plane 1 ALU. Consequently, the CPU data can be
used to control the value written to plane 1. Given the settings of the
other three planes, this means that each bit of CPU data that is 1
generates a brown pixel, and each bit that is 0 generates a red pixel.
Writing alternating bytes of 07H and 0E0H, then, creates a vertically
striped pattern of brown and red.
In Listing 25.4, note that the vertical bars are 10 and 6 bytes wide,
and do not start on byte boundaries. Although set/reset replaces an
entire byte of CPU data for a plane, the combination of set/reset for
some planes and CPU data for other planes, as in the example above, can
be used to control individual pixels.
**LISTING 25.4 L25-4.ASM**
```nasm
; Program to illustrate operation of set/reset circuitry in conjunction
; with CPU data to modify setting of memory that already contains data.
; By Michael Abrash.
;
stack segment para stack STACK#146;
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public CODE#146;
assume cs:cseg
start proc near
;
; Select 640x350 graphics mode.
;
mov ax,010h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 18 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h;map mask setting enables only
; plane 1, the green plane
sub di,di;start at beginning of video memory
mov al,0ffh
mov bp,18;# bars to draw
HorzBarLoop:
mov cx,80*10;# bytes per horizontal bar
rep stosb;draw bar
add di,80*10;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with alternating bars of red and brown, using CPU data
; to set plane 1 and set/reset to set planes 0, 2 & 3.
;
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
; planes, so set/reset values can
; be written to planes 0, 2 & 3
; and CPU data can be written to
; plane 1 (the green plane)
SETGC GC_ENABLE_SET_RESET,0dh ;CPU data to planes 0, 2 & 3 will be
; replaced by set/reset value
SETGC GC_SET_RESET,04h ;set/reset value is 0ffh for plane 2
; (the red plane) and 0 for other
; planes
sub di,di
mov cx,80*350/2 ;# words per screen
mov ax,07e0h ;CPU data controls only plane 1;
; set/reset controls other planes
rep stosw ;perform fill (affects all planes)
;
; Turn off set/reset.
;
SETGC GC_ENABLE_SET_RESET,0
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start
```
There is no clearly defined role for the set/reset circuitry, as there
is for, say, the bit mask. In many cases, set/reset is largely
interchangeable with CPU data, particularly with CPU data written in
write mode 2 (write mode 2 operates similarly to the set/reset
circuitry, as we'll see in Chapter 27). The most powerful use of
set/reset, in my experience, is in applications such as the example of
Listing 25.4, where it is used to force the value written to certain
planes while the CPU data is written to other planes. In general,
though, think of set/reset as one more tool you have at your disposal in
getting the VGA to do what you need done, in this case a tool that lets
you force all bits in each plane to either zero or one, or pass CPU data
through unchanged, on each write to display memory. As tools go,
set/reset is a handy one, and it'll pop up often in this book.
### Notes on Set/Reset
The set/reset circuitry is not active in write modes 1 or 2. The Enable
Set/Reset register is inactive in write mode 3, but the Set/Reset
register provides the primary drawing color in write mode 3, as
discussed in the next chapter.
> ![](images/i.jpg)
> Be aware that because set/reset directly replaces CPU data, it does not
> necessarily have to force an entire display memory byte to 0 or 0FFH,
> even when set/reset is replacing CPU data for all planes. For example,
> if the Bit Mask register is set to 80H, the set/reset circuitry can only
> modify bit 7 of the destination byte in each plane, since the other
> seven bits will come from the latches for each plane. Similarly, the
> set/reset value for each plane can be modified by that plane's ALU. Once
> again, this illustrates that set/reset merely replaces the CPU data for
> selected planes; the set/reset value is then processed in exactly the
> same way that CPU data normally is.
### A Brief Note on Word OUTs
In the early days of the EGA and VGA, there was considerable debate
about whether it was safe to do word `OUT`s (`OUT DX,AX`) to set
Index/Data register pairs in a single instruction. Long ago, there were
a few computers with buses that weren't quite PC-compatatible, in that
the two bytes in each word `OUT` went to the VGA in the wrong order:
Data register first, then Index register, with predictably disastrous
results. Consequently, I generally wrote my code in those days to use
two 8-bit `OUT`s to set indexed registers. Later on, I made it a habit
to use macros that could do either one 16-bit `OUT` or two 8-bit
`OUT`s, depending on how I chose to assemble the code, and in fact
you'll find both ways of dealing with `OUT`s sprinkled through the
code in this part of the book. Using macros for word OUTs is still not a
bad idea in that it does no harm, but in my opinion it's no longer
necessary. Word `OUT`s are standard now, and it's been a long time
since I've heard of them causing any problems.