; 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);
;
; Tested with TASM 4.0 by Jim Mischel 12/16/94.

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
