200 lines
11 KiB
Markdown
200 lines
11 KiB
Markdown
Chapter 49\
|
|
Mode X 256-Color Animation {#Heading1}
|
|
---------------------------
|
|
|
|
### How to Make the VGA Really Get up and Dance {#Heading2}
|
|
|
|
Okay—no amusing stories or informative anecdotes to kick off this
|
|
chapter; lotta ground to cover, gotta hurry—you're impatient, I can
|
|
smell it. I won't talk about the time a friend made the mistake of
|
|
loudly saying "\$100 bill" during an animated discussion while walking
|
|
among the bums on Market Street in San Francisco one night, thereby
|
|
graphically illustrating that context is everything. I can't spare a
|
|
word about how my daughter thinks my 11-year-old floppy-disk-based CP/M
|
|
machine is more powerful than my 386 with its 100-MB hard disk because
|
|
the CP/M machine's word processor loads and runs twice as fast as the
|
|
386's Windows-based word processor, demonstrating that progress is not
|
|
the neat exponential curve we'd like to think it is, and that features
|
|
and performance are often conflicting notions. And, lord knows, I can't
|
|
take the time to discuss the habits of small white dogs, notwithstanding
|
|
that such dogs seem to be relevant to just about every aspect of
|
|
computing, as Jeff Duntemann's writings make manifest. No lighthearted
|
|
fluff for us; we have real work to do, for today we animate with 256
|
|
colors in Mode X.
|
|
|
|
### Masked Copying {#Heading3}
|
|
|
|
Over the past two chapters, we've put together most of the tools needed
|
|
to implement animation in the VGA's undocumented 320x240 256-color Mode
|
|
X. We now have mode set code, solid and 4x4 pattern fills, system
|
|
memory-to-display memory block copies, and display memory-to-display
|
|
memory block copies. The final piece of the puzzle is the ability to
|
|
copy a nonrectangular image to display memory. I call this *masked
|
|
copying*.
|
|
|
|
Masked copying is sort of like drawing through a stencil, in that only
|
|
certain pixels within the destination rectangle are drawn. The objective
|
|
is to fit the image seamlessly into the background, without the
|
|
rectangular fringe that results when nonrectangular images are drawn by
|
|
block copying their bounding rectangle. This is accomplished by using a
|
|
second rectangular bitmap, separate from the image but corresponding to
|
|
it on a pixel-by-pixel basis, to control which destination pixels are
|
|
set from the source and which are left unchanged. With a masked copy,
|
|
only those pixels properly belonging to an image are drawn, and the
|
|
image fits perfectly into the background, with no rectangular border. In
|
|
fact, masked copying even makes it possible to have transparent areas
|
|
within images.
|
|
|
|
Note that another way to achieve this effect is to implement copying
|
|
code that supports a transparent color; that is, a color that doesn't
|
|
get copied but rather leaves the destination unchanged. Transparent
|
|
copying makes for more compact images, because no separate mask is
|
|
needed, and is generally faster in a software-only implementation.
|
|
However, Mode X supports masked copying but not transparent copying in
|
|
hardware, so we'll use masked copying in this chapter.
|
|
|
|
The system memory to display memory masked copy routine in Listing 49.1
|
|
implements masked copying in a straightforward fashion. In the main
|
|
drawing loop, the corresponding mask byte is consulted as each image
|
|
pixel is encountered, and the image pixel is copied only if the mask
|
|
byte is nonzero. As with most of the system-to-display code I've
|
|
presented, Listing 49.1 is not heavily optimized, because it's
|
|
inherently slow; there's a better way to go when performance matters,
|
|
and that's to use the VGA's hardware.
|
|
|
|
**LISTING 49.1 L49-1.ASM**
|
|
|
|
; Mode X (320x240, 256 colors) system memory-to-display memory masked copy
|
|
; routine. Not particularly fast; images for which performance is critical
|
|
; should be stored in off-screen memory and copied to screen via latches. Works
|
|
; on all VGAs. Copies up to but not including column at SourceEndX and row at
|
|
; SourceEndY. No clipping is performed. Mask and source image are both byte-
|
|
; per-pixel, and must be of same widths and reside at same coordinates in their
|
|
; respective bitmaps. Assembly code tested with TASM C near-callable as:
|
|
;
|
|
; void CopySystemToScreenMaskedX(int SourceStartX,
|
|
; int SourceStartY, int SourceEndX, int SourceEndY,
|
|
; int DestStartX, int DestStartY, char * SourcePtr,
|
|
; unsigned int DestPageBase, int SourceBitmapWidth,
|
|
; int DestBitmapWidth, char * MaskPtr);
|
|
|
|
SC_INDEX equ 03c4h ;Sequence Controller Index register port
|
|
MAP_MASK equ 02h ;index in SC of Map Mask register
|
|
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
|
|
|
|
parms struc
|
|
dw 2 dup (?) ;pushed BP and return address
|
|
SourceStartX dw ? ;X coordinate of upper left corner of source
|
|
; (source is in system memory)
|
|
SourceStartY dw ? ;Y coordinate of upper left corner of source
|
|
SourceEndX dw ? ;X coordinate of lower right corner of source
|
|
; (the column at EndX is not copied)
|
|
SourceEndY dw ? ;Y coordinate of lower right corner of source
|
|
; (the row at EndY is not copied)
|
|
DestStartX dw ? ;X coordinate of upper left corner of dest
|
|
; (destination is in display memory)
|
|
DestStartY dw ? ;Y coordinate of upper left corner of dest
|
|
SourcePtr dw ? ;pointer in DS to start of bitmap which source resides
|
|
DestPageBase dw ? ;base offset in display memory of page in
|
|
; which dest resides
|
|
SourceBitmapWidth dw ? ;# of pixels across source bitmap (also must
|
|
; be width across the mask)
|
|
DestBitmapWidth dw ? ;# of pixels across dest bitmap (must be multiple of 4)
|
|
MaskPtr dw ? ;pointer in DS to start of bitmap in which mask
|
|
; resides (byte-per-pixel format, just like the source
|
|
; image; 0-bytes mean don't copy corresponding source
|
|
; pixel, 1-bytes mean do copy)
|
|
parms ends
|
|
|
|
RectWidth equ -2 ;local storage for width of rectangle
|
|
RectHeight equ -4 ;local storage for height of rectangle
|
|
LeftMask equ -6 ;local storage for left rect edge plane mask
|
|
STACK_FRAME_SIZE equ 6
|
|
.model small
|
|
.code
|
|
public _CopySystemToScreenMaskedX
|
|
_CopySystemToScreenMaskedX proc near
|
|
push bp ;preserve caller's stack frame
|
|
mov bp,sp ;point to local stack frame
|
|
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
|
|
push si ;preserve caller's register variables
|
|
push di
|
|
|
|
mov ax,SCREEN_SEG ;point ES to display memory
|
|
mov es,ax
|
|
mov ax,[bp+SourceBitmapWidth]
|
|
mul [bp+SourceStartY] ;top source rect scan line
|
|
add ax,[bp+SourceStartX]
|
|
mov bx,ax
|
|
add ax,[bp+SourcePtr] ;offset of first source rect pixel
|
|
mov si,ax ; in DS
|
|
add bx,[bp+MaskPtr] ;offset of first mask pixel in DS
|
|
|
|
mov ax,[bp+DestBitmapWidth]
|
|
shr ax,1 ;convert to width in addresses
|
|
shr ax,1
|
|
mov [bp+DestBitmapWidth],ax ;remember address width
|
|
mul [bp+DestStartY] ;top dest rect scan line
|
|
mov di,[bp+DestStartX]
|
|
mov cx,di
|
|
shr di,1 ;X/4 = offset of first dest rect pixel in
|
|
shr di,1 ; scan line
|
|
add di,ax ;offset of first dest rect pixel in page
|
|
add di,[bp+DestPageBase] ;offset of first dest rect pixel
|
|
; in display memory
|
|
and cl,011b ;CL = first dest pixel's plane
|
|
mov al,11h ;upper nibble comes into play when plane wraps
|
|
; from 3 back to 0
|
|
shl al,cl ;set the bit for the first dest pixel's plane
|
|
mov [bp+LeftMask],al ; in each nibble to 1
|
|
|
|
mov ax,[bp+SourceEndX] ;calculate # of pixels across
|
|
sub ax,[bp+SourceStartX] ; rect
|
|
jle CopyDone ;skip if 0 or negative width
|
|
mov [bp+RectWidth],ax
|
|
sub word ptr [bp+SourceBitmapWidth],ax
|
|
;distance from end of one source scan line
|
|
to start of next
|
|
mov ax,[bp+SourceEndY]
|
|
sub ax,[bp+SourceStartY] ;height of rectangle
|
|
jle CopyDone ;skip if 0 or negative height
|
|
mov [bp+RectHeight],ax
|
|
mov dx,SC_INDEX ;point to SC Index register
|
|
mov al,MAP_MASK
|
|
out dx,al ;point SC Index reg to the Map Mask
|
|
inc dx ;point DX to SC Data reg
|
|
CopyRowsLoop:
|
|
mov al,[bp+LeftMask]
|
|
mov cx,[bp+RectWidth]
|
|
push di ;remember the start offset in the dest
|
|
CopyScanLineLoop:
|
|
cmp byte ptr [bx],0 ;is this pixel mask-enabled?
|
|
jz MaskOff ;no, so don't draw it
|
|
;yes, draw the pixel
|
|
out dx,al ;set the plane for this pixel
|
|
mov ah,[si] ;get the pixel from the source
|
|
mov es:[di],ah ;copy the pixel to the screen
|
|
MaskOff:
|
|
inc bx ;advance the mask pointer
|
|
inc si ;advance the source pointer
|
|
rol al,1 ;set mask for next pixel's plane
|
|
adc di,0 ;advance destination address only when
|
|
;wrapping from plane 3 to plane 0
|
|
loop CopyScanLineLoop
|
|
pop di ;retrieve the dest start offset
|
|
add di,[bp+DestBitmapWidth];point to the start of the
|
|
; next scan line of the dest
|
|
add si,[bp+SourceBitmapWidth] ;point to the start of the
|
|
;next scan line of the source
|
|
add bx,[bp+SourceBitmapWidth] ;point to the start of the
|
|
;next scan line of the mask
|
|
dec word ptr [bp+RectHeight] ;count down scan lines
|
|
jnz CopyRowsLoop
|
|
CopyDone:
|
|
pop di ;restore caller's register variables
|
|
pop si
|
|
mov sp,bp ;discard storage for local variables
|
|
pop bp ;restore caller's stack frame
|
|
ret
|
|
_CopySystemToScreenMaskedX endp
|
|
end
|