Previous Table of Contents Next


LISTING 50.1 L50-1.ASM

; Draws all pixels in the list of horizontal lines passed in, in
; Mode X, the VGA’s undocumented 320x240 256-color mode. Clips to
; the rectangle specified by (ClipMinX,ClipMinY),(ClipMaxX,ClipMaxY).
; Draws to the page specified by CurrentPageBase.
; C near-callable as:
;
;     void DrawHorizontalLineList(struct HLineList * HLineListPtr,
;          int Color);
;
; All assembly code tested with TASM and MASM

SCREEN_WIDTH    equ   320
SCREEN_SEGMENT  equ   0a000h
SC_INDEX        equ   03c4h        ;Sequence Controller Index
MAP_MASK        equ   2            ;Map Mask register index in SC

HLine   struc
XStart          dw      ?          ;X coordinate of leftmost pixel in line
XEnd            dw      ?          ;X coordinate of rightmost pixel in line
HLine   ends

HLineList struc
Lngth           dw      ?          ;# of horizontal lines
YStart          dw      ?          ;Y coordinate of topmost line
HLinePtr        dw      ?          ;pointer to list of horz lines
HLineList ends

Parms   struc
                dw      2 dup(?)   ;return address & pushed BP
HLineListPtr    dw      ?          ;pointer to HLineList structure
Color           dw      ?          ;color with which to fill
Parms   ends
        .model small
        .data
        extrn   _CurrentPageBase:word,_ClipMinX:word
        extrn   _ClipMinY:word,_ClipMaxX:word,_ClipMaxY:word
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask       db      00fh,00eh,00ch,008h
RightClipPlaneMask      db      001h,003h,007h,00fh
        .code
        align   2
ToFillDone:
        jmp     FillDone
        public _DrawHorizontalLineList
        align   2
_DrawHorizontalLineList proc
        push    bp                 ;preserve caller’s stack frame
        mov     bp,sp              ;point to our stack frame
        push    si                 ;preserve caller’s register variables
        push    di
        cld                        ;make string instructions inc pointers
        mov     dx,SC_INDEX
        mov     al,MAP_MASK
        out     dx,al              ;point SC Index to the Map Mask
        mov     ax,SCREEN_SEGMENT
        mov     es,ax              ;point ES to display memory for REP STOS
        mov     si,[bp+HLineListPtr] ;point to the line list
        mov     bx,[si+HLinePtr]     ;point to the XStart/XEnd descriptor
                                   ; for the first (top) horizontal line
        mov     cx,[si+YStart] ;first scan line to draw
        mov     si,[si+Lngth]  ;# of scan lines to draw
        cmp     si,0               ;are there any lines to draw?
        jle     ToFillDone         ;no, so we’re done
        cmp     cx,[_ClipMinY]     ;clipped at top?
        jge     MinYNotClipped     ;no
        neg     cx                 ;yes, discard however many lines are
        add     cx,[_ClipMinY]     ; clipped
        sub     si,cx              ;that many fewer lines to draw
        jle     ToFillDone         ;no lines left to draw
        shl     cx,1               ;lines to skip*2
        shl     cx,1               ;lines to skip*4
        add     bx,cx              ;advance through the line list
        mov     cx,[_ClipMinY]     ;start at the top clip line
MinYNotClipped:
        mov     dx,si
        add     dx,cx              ;bottom row to draw + 1
        cmp     dx,[_ClipMaxY]     ;clipped at bottom?
        jle     MaxYNotClipped     ;no
        sub     dx,[_ClipMaxY]     ;# of lines to clip off the bottom
        sub     si,dx              ;# of lines left to draw
        jle     ToFillDone         ;all lines are clipped
MaxYNotClipped:
        mov     ax,SCREEN_WIDTH/4  ;point to the start of the first
        mul     cx                 ; scan line on which to draw
        add     ax,[_CurrentPageBase] ;offset of first line
        mov     dx,ax              ;ES:DX points to first scan line to draw
        mov     ah,byte ptr [bp+Color] ;color with which to fill
FillLoop:
        push    bx                 ;remember line list location
        push    dx                 ;remember offset of start of line
        push    si                 ;remember # of lines to draw
        mov     di,[bx+XStart] ;left edge of fill on this line
        cmp     di,[_ClipMinX]     ;clipped to left edge?
        jge     MinXNotClipped     ;no
        mov     di,[_ClipMinX]     ;yes, clip to the left edge
MinXNotClipped:
        mov     si,di
        mov     cx,[bx+XEnd]   ;right edge of fill
        cmp     cx,[_ClipMaxX]     ;clipped to right edge?
        jl      MaxXNotClipped     ;no
        mov     cx,[_ClipMaxX]     ;yes, clip to the right edge
        dec     cx
MaxXNotClipped:
        cmp     cx,di
        jl      LineFillDone       ;skip if negative width
        shr     di,1    ;X/4 = offset of first rect pixel in scan
        shr     di,1    ; line
        add     di,dx   ;offset of first rect pixel in display mem
        mov     dx,si   ;XStart
        and     si,0003h    ;look up left-edge plane mask
        mov     bh,LeftClipPlaneMask[si] ; to clip & put in BH
        mov     si,cx
        and     si,0003h           ;look up right-edge plane
        mov     bl,RightClipPlaneMask[si] ; mask to clip & put in BL
        and     dx,not 011b        ;calculate # of addresses across rect
        sub     cx,dx
        shr     cx,1
        shr     cx,1               ;# of addresses across rectangle to fill - 1
        jnz     MasksSet           ;there’s more than one byte to draw
        and     bh,bl              ;there’s only one byte, so combine the left
                                   ; and right edge clip masks
MasksSet:
        mov     dx,SC_INDEX+1  ;already points to the Map Mask reg
FillRowsLoop:
        mov     al,bh              ;put left-edge clip mask in AL
        out     dx,al              ;set the left-edge plane (clip) mask
        mov     al,ah              ;put color in AL
        stosb                      ;draw the left edge
        dec     cx                 ;count off left edge byte
        js      FillLoopBottom     ;that’s the only byte
        jz      DoRightEdge        ;there are only two bytes
        mov     al,00fh            ;middle addresses are drawn 4 pixels at a pop
        out     dx,al              ;set the middle pixel mask to no clip
        mov     al,ah              ;put color in AL
        rep     stosb              ;draw the middle addresses four pixels apiece
DoRightEdge:
        mov     al,bl              ;put right-edge clip mask in AL
        out     dx,al              ;set the right-edge plane (clip) mask
        mov     al,ah              ;put color in AL
        stosb                      ;draw the right edge
FillLoopBottom:
LineFillDone:
        pop     si                 ;retrieve # of lines to draw
        pop     dx                 ;retrieve offset of start of line
        pop     bx                 ;retrieve line list location
        add     dx,SCREEN_WIDTH/4  ;point to start of next line
        add     bx,size HLine      ;point to the next line descriptor
        dec     si                 ;count down lines
        jnz     FillLoop
FillDone:
        pop     di                 ;restore caller’s register variables
        pop     si
        pop     bp                 ;restore caller’s stack frame
        ret
_DrawHorizontalLineList endp
        end


Previous Table of Contents Next

Graphics Programming Black Book © 2001 Michael Abrash