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