**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