Previous | Table of Contents | Next

LISTING 57.2 L57-2.ASM

    ; Draws all pixels in the specified scan line, with the pixel colors
    ; taken from the specified texture map.  Uses approach of pre-stepping
    ; 1/2 pixel into the source image and rounding to the nearest source
    ; pixel at each step, so that texture maps will appear reasonably similar
    ; at all angles.  This routine is specific to 320-pixel-wide planar
    ; (non-chain4) 256-color modes, such as mode X, which is a planar
    ; (non-chain4) 256-color mode with a resolution of 320x240.
    ; C near-callable as:
    ;   void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge);
    ; Tested with TASM 3.0.

    SC-INDEX     equ    03c4h     ;Sequence Controller Index
    MAP-MASK     equ    02h       ;index in SC of Map Mask register
    SCREEN-SEG   equ    0a000h    ;segment of display memory in mode X
    SCREEN-WIDTH equ    80        ;width of screen in bytes from one scan line
                                  ; to the next

            .model  small
            .data
            extrn   -TexMapBits:word, -TexMapWidth:word, -DestY:word
            extrn   -CurrentPageBase:word, -ClipMinX:word
            extrn   -ClipMinY:word, -ClipMaxX:word, -ClipMaxY:word

    ; Describes the current location and stepping, in both the source and
    ; the destination, of an edge. Mirrors structure in DRAWTEXP.C.
    EdgeScan struc
    Direction      dw    ?        ;through edge list; 1 for a right edge (forward
                                  ; through vertex list), -1 for a left edge (backward
                                  ; through vertex list)
    RemainingScans dw    ?        ;height left to scan out in dest
    CurrentEnd     dw    ?        ;vertex # of end of current edge
    SourceX        dd    ?        ;X location in source for this edge
    SourceY        dd    ?        ;Y location in source for this edge
    SourceStepX    dd    ?        ;X step in source for Y step in dest of 1
    SourceStepY    dd    ?        ;Y step in source for Y step in dest of 1
                                  ;variables used for all-integer Bresenham’s-type
                                  ; X stepping through the dest, needed for precise
                                  ; pixel placement to avoid gaps
    DestX          dw    ?        ;current X location in dest for this edge
    DestXIntStep   dw    ?        ;whole part of dest X step per scan-line Y step
    DestXDirection dw    ?        ;-1 or 1 to indicate which way X steps (left/right)
    DestXErrTerm   dw    ?        ;current error term for dest X stepping
    DestXAdjUp     dw    ?        ;amount to add to error term per scan line move
    DestXAdjDown   dw    ?        ;amount to subtract from error term when the
                                  ; error term turns over
    EdgeScan ends

    Parms   struc
                   dw    2 dup(?) ;return address & pushed BP
    LeftEdge       dw    ?        ;pointer to EdgeScan structure for left edge
    RightEdge      dw    ?        ;pointer to EdgeScan structure for right edge
    Parms   ends

    ;Offsets from BP in stack frame of local variables.
    lSourceX       equ   -4       ;current X coordinate in source image
    lSourceY       equ   -8       ;current Y coordinate in source image
    lSourceStepX   equ   -12      ;X step in source image for X dest step of 1
    lSourceStepY   equ   -16      ;Y step in source image for X dest step of 1
    lXAdvanceByOne equ   -18      ;used to step source pointer 1 pixel
                                  ; incrementally in X
    lXBaseAdvance  equ   -20      ;use to step source pointer minimum number of
                                  ; pixels incrementally in X
    lYAdvanceByOne equ   -22      ;used to step source pointer 1 pixel
                                  ; incrementally in Y
    lYBaseAdvance  equ   -24      ;use to step source pointer minimum number of
                                  ; pixels incrementally in Y
    LOCAL-SIZE     equ    24      ;total size of local variables
            .code
            extrn   -FixedMul:near, -FixedDiv:near
            align   2
    ToScanDone:
            jmp     ScanDone
            public  -ScanOutLine
            align   2
    -ScanOutLine    proc    near
            push    bp              ;preserve caller’s stack frame
            mov     bp,sp           ;point to our stack frame
            sub     sp,LOCAL-SIZE   ;allocate space for local variables
            push    si              ;preserve caller’s register variables
            push    di
    ; Nothing to do if destination is fully X clipped.
            mov     di,[bp].RightEdge
            mov     si,[di].DestX
            cmp     si,[-ClipMinX]
            jle     ToScanDone      ;right edge is to left of clip rect, so done
            mov     bx,[bp].LeftEdge
            mov     dx,[bx].DestX
            cmp     dx,[-ClipMaxX]
            jge     ToScanDone      ;left edge is to right of clip rect, so done
            sub     si,dx           ;destination fill width
            jle     ToScanDone      ;null or negative full width, so done

            mov     ax,word ptr [bx].SourceX        ;initial source X coordinate
            mov     word ptr [bp].lSourceX,ax
            mov     ax,word ptr [bx].SourceX+2
            mov     word ptr [bp].lSourceX+2,ax

            mov     ax,word ptr [bx].SourceY        ;initial source Y coordinate
            mov     word ptr [bp].lSourceY,ax
            mov     ax,word ptr [bx].SourceY+2
            mov     word ptr [bp].lSourceY+2,ax
    ; Calculate source steps that correspond to each 1-pixel destination X step
    ; (across the destination scan line).
            push    si              ;push dest X width, in fixedpoint form
            sub     ax,ax
            push    ax              ;push 0 as fractional part of dest X width
            mov     ax,word ptr [di].SourceX
            sub     ax,word ptr [bp].lSourceX       ;low word of source X width
            mov     dx,word ptr [di].SourceX+2
            sbb     dx,word ptr [bp].lSourceX+2     ;high word of source X width
            push    dx              ;push source X width, in fixedpoint form
            push    ax
            call    -FixedDiv       ;scale source X width to dest X width
            add     sp,8            ;clear parameters from stack
            mov     word ptr [bp].lSourceStepX,ax   ;remember source X step for
            mov     word ptr [bp].lSourceStepX+2,dx ; 1-pixel destination X step
            mov     cx,1            ;assume source X advances non-negative
            and     dx,dx           ;which way does source X advance?
            jns     SourceXNonNeg   ;non-negative
            neg     cx              ;negative
            cmp     ax,0            ;is the whole step exactly an integer?
            jz      SourceXNonNeg   ;yes
            inc     dx              ;no, truncate to integer in the direction of
                                    ; 0, because otherwise we’ll end up with a
                                    ; whole step of 1-too-large magnitude
    SourceXNonNeg:
            mov     [bp].lXAdvanceByOne,cx  ;amount to add to source pointer to
                                            ; move by one in X
            mov     [bp].lXBaseAdvance,dx   ;minimum amount to add to source
                                            ; pointer to advance in X each time
                                            ; the dest advances one in X
            push    si              ;push dest Y height, in fixedpoint form
            sub     ax,ax
            push    ax              ;push 0 as fractional part of dest Y height
            mov     ax,word ptr [di].SourceY
            sub     ax,word ptr [bp].lSourceY       ;low word of source Y height
            mov     dx,word ptr [di].SourceY+2
            sbb     dx,word ptr [bp].lSourceY+2     ;high word of source Y height
            push    dx              ;push source Y height, in fixedpoint form
            push    ax
            call    -FixedDiv       ;scale source Y height to dest X width
            add     sp,8            ;clear parameters from stack
            mov     word ptr [bp].lSourceStepY,ax   ;remember source Y step for
            mov     word ptr [bp].lSourceStepY+2,dx ; 1-pixel destination X step
            mov     cx,[-TexMapWidth] ;assume source Y advances non-negative
            and     dx,dx           ;which way does source Y advance?
            jns     SourceYNonNeg   ;non-negative
            neg     cx              ;negative
            cmp     ax,0            ;is the whole step exactly an integer?
            jz      SourceYNonNeg   ;yes
            inc     dx              ;no, truncate to integer in the direction of
                                    ; 0, because otherwise we’ll end up with a
                                    ; whole step of 1-too-large magnitude
    SourceYNonNeg:
            mov     [bp].lYAdvanceByOne,cx  ;amount to add to source pointer to
                                            ; move by one in Y
            mov     ax,[-TexMapWidth]       ;minimum distance skipped in source
            imul    dx                      ; image bitmap when Y steps (ignoring
            mov     [bp].lYBaseAdvance,ax   ; carry from the fractional part)
    ; Advance 1/2 step in the stepping direction, to space scanned pixels evenly
    ; between the left and right edges. (There’s a slight inaccuracy in dividing
    ; negative numbers by 2 by shifting rather than dividing, but the inaccuracy
    ; is in the least significant bit, and we’ll just live with it.)
            mov     ax,word ptr [bp].lSourceStepX
            mov     dx,word ptr [bp].lSourceStepX+2
            sar     dx,1
            rcr     ax,1
            add     word ptr [bp].lSourceX,ax
            adc     word ptr [bp].lSourceX+2,dx

            mov     ax,word ptr [bp].lSourceStepY
            mov     dx,word ptr [bp].lSourceStepY+2
            sar     dx,1
            rcr     ax,1
            add     word ptr [bp].lSourceY,ax
            adc     word ptr [bp].lSourceY+2,dx
    ; Clip right edge if necessary.
            mov     si,[di].DestX
            cmp     si,[-ClipMaxX]
            jl      RightEdgeClipped
            mov     si,[-ClipMaxX]
    RightEdgeClipped:
    ; Clip left edge if necssary
            mov     bx,[bp].LeftEdge
            mov     di,[bx].DestX
            cmp     di,[-ClipMinX]
            jge     LeftEdgeClipped
    ; Left clipping is necessary; advance the source accordingly
            neg     di
            add     di,[-ClipMinX]  ;ClipMinX - DestX
                                    ;first, advance the source in X
            push    di              ;push ClipMinX - DestX, in fixedpoint form
            sub     ax,ax
            push    ax              ;push 0 as fractional part of ClipMinX-DestX
            push    word ptr [bp].lSourceStepX+2
            push    word ptr [bp].lSourceStepX
            call    -FixedMul       ;total source X stepping in clipped area
            add     sp,8            ;clear parameters from stack
            add     word ptr [bp].lSourceX,ax;step the source X past clipping
            adc     word ptr [bp].lSourceX+2,dx
                                    ;now advance the source in Y
            push    di              ;push ClipMinX - DestX, in fixedpoint form
            sub     ax,ax
            push    ax              ;push 0 as fractional part of ClipMinX-DestX
            push    word ptr [bp].lSourceStepY+2
            push    word ptr [bp].lSourceStepY
            call    -FixedMul       ;total source Y stepping in clipped area
            add     sp,8            ;clear parameters from stack
            add     word ptr [bp].lSourceY,ax;step the source Y past clipping
            adc     word ptr [bp].lSourceY+2,dx
            mov     di,[-ClipMinX]  ;start X coordinate in dest after clipping
    LeftEdgeClipped:
    ; Calculate actual clipped destination drawing width.
            sub     si,di
    ; Scan across the destination scan line, updating the source image position
    ; accordingly.
    ; Point to the initial source image pixel, adding 0.5 to both X and Y so that
    ; we can truncate to integers from now on but effectively get rounding.
            add     word ptr [bp].lSourceY,8000h;add 0.5
            mov     ax,word ptr [bp].lSourceY+2
            adc     ax,0
            mul     [-TexMapWidth]   ;initial scan line in source image
            add     word ptr [bp].lSourceX,8000h;add 0.5
            mov     bx,word ptr [bp].lSourceX+2 ;offset into source scan line
            adc     bx,ax            ;initial source offset in source image
            add     bx,[-TexMapBits] ;DS:BX points to the initial image pixel
    ; Point to initial destination pixel.
            mov     ax,SCREEN-SEG
            mov     es,ax
            mov     ax,SCREEN-WIDTH
            mul     [-DestY] ;offset of initial dest scan line
            mov     cx,di   ;initial destination X
            shr     di,1
            shr     di,1    ;X/4 = offset of pixel in scan line
            add     di,ax   ;offset of pixel in page
            add     di,[-CurrentPageBase] ;offset of pixel in display memory
    ;ES:DI now points to the first destination pixel

            and     cl,011b ;CL = pixel’s plane
            mov     al,MAP-MASK
            mov     dx,SC-INDEX
            out     dx,al   ;point the SC Index register to the Map Mask
            mov     al,11h  ;one plane bit in each nibble, so we’ll get carry
                            ; automatically when going from plane 3 to plane 0
            shl     al,cl   ;set the bit for the first pixel’s plane to 1
    ; If source X step is negative, change over to working with non-negative
    ; values.
            cmp     word ptr [bp].lXAdvanceByOne,0
            jge     SXStepSet
            neg     word ptr [bp].lSourceStepX
            not     word ptr [bp].lSourceX
    SXStepSet:
    ; If source Y step is negative, change over to working with non-negative
    ; values.
            cmp     word ptr [bp].lYAdvanceByOne,0
            jge     SYStepSet
            neg     word ptr [bp].lSourceStepY
            not     word ptr [bp].lSourceY
    SYStepSet:
    ; At this point:
    ;       AL = initial pixel’s plane mask
    ;       BX = pointer to initial image pixel
    ;       SI = # of pixels to fill
    ;       DI = pointer to initial destination pixel
            mov     dx,SC-INDEX+1   ;point to SC Data; Index points to Map Mask
    TexScanLoop:
    ; Set the Map Mask for this pixel’s plane, then draw the pixel.
            out     dx,al
            mov     ah,[bx]         ;get image pixel
            mov     es:[di],ah      ;set image pixel
    ; Point to the next source pixel.
            add     bx,[bp].lXBaseAdvance   ;advance the minimum # of pixels in X
            mov     cx,word ptr [bp].lSourceStepX
            add     word ptr [bp].lSourceX,cx;step the source X fractional part
            jnc     NoExtraXAdvance          ;didn’t turn over; no extra advance
            add     bx,[bp].lXAdvanceByOne   ;did turn over; advance X one extra
    NoExtraXAdvance:
            add     bx,[bp].lYBaseAdvance;advance the minimum # of pixels in Y
            mov     cx,word ptr [bp].lSourceStepY
            add     word ptr [bp].lSourceY,cx;step the source Y fractional part
            jnc     NoExtraYAdvance;didn’t turn over; no extra advance
            add     bx,[bp].lYAdvanceByOne;did turn over; advance Y one extra
    NoExtraYAdvance:
    ; Point to the next destination pixel, by cycling to the next plane, and
    ; advancing to the next address if the plane wraps from 3 to 0.
            rol     al,1
            adc     di,0
    ; Continue if there are any more dest pixels to draw.
            dec     si
            jnz     TexScanLoop
    ScanDone:
            pop     di              ;restore caller’s register variables
            pop     si
            mov     sp,bp           ;deallocate local variables
            pop     bp              ;restore caller’s stack frame
            ret
    -ScanOutLine    endp
            end

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash