home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / xsharp22 / drawtex.asm < prev    next >
Assembly Source File  |  1997-06-18  |  23KB  |  502 lines

  1. ; Draws all pixels in the specified column, with the pixel colors
  2. ; taken from the specified texture map.  Uses approach of pre-stepping
  3. ; 1/2 pixel into the source image and rounding to the nearest source
  4. ; pixel at each step, so that texture maps will appear reasonably similar
  5. ; at all angles.  This routine is specific to 320-pixel-wide planar
  6. ; (non-Chain4) 256-color modes, such as mode X, which is a planar
  7. ; (non-chain4) 256-color mode with a resolution of 320x240.
  8. ;
  9. ; SS must == DS so that the texture map can be accessed via SS:BP.
  10. ;
  11. ; C near-callable as:
  12. ;     void ScanOutLine(EdgeScan * TopEdge, EdgeScan * BottomEdge);
  13. ;
  14. ; Tested with TASM 4.0.
  15. ;
  16.  
  17. SC_INDEX equ    03c4h   ;Sequence Controller Index
  18. MAP_MASK equ    02h     ;index in SC of Map Mask register
  19. SCREEN_SEG equ  0a000h  ;segment of display memory in mode X
  20. SCREEN_WIDTH equ 80     ;width of screen in bytes from one scan line
  21.                         ; to the next
  22.  
  23.         .model  small
  24.         .386
  25.         .data
  26.         extrn   _TexMapBits:word, _TexMapWidth:word, _DestX:word
  27.         extrn   _CurrentPageBase:word, _ClipMinX:word
  28.         extrn   _ClipMinY:word, _ClipMaxX:word, _ClipMaxY:word
  29.  
  30. ; Describes the current location and stepping, in both the source and
  31. ; the destination, of an edge. Mirrors structure in DRAWTEXP.C.
  32. EdgeScan struc
  33. Direction dw    ?       ;through edge list; 1 for a right edge (forward
  34.                         ; through vertex list), -1 for a left edge (backward
  35.                         ; through vertex list)
  36. RemainingScans dw ?     ;height left to scan out in dest
  37. CurrentEnd dw   ?       ;vertex # of end of current edge
  38. SourceX dd      ?       ;X location in source for this edge
  39. SourceY dd      ?       ;Y location in source for this edge
  40. SourceStepX dd  ?       ;X step in source for Y step in dest of 1
  41. SourceStepY dd  ?       ;Y step in source for Y step in dest of 1
  42.                         ;variables used for all-integer Bresenham's-type
  43.                         ; X stepping through the dest, needed for precise
  44.                         ; pixel placement to avoid gaps
  45. DestY   dw      ?       ;current Y location in dest for this edge
  46. DestYIntStep dw ?       ;whole part of dest Y step per column X step
  47. DestYDirection dw ?     ;-1 or 1 to indicate which way Y steps (left/right)
  48. DestYErrTerm dw ?       ;current error term for dest Y stepping
  49. DestYAdjUp dw   ?       ;amount to add to error term per scan line move
  50. DestYAdjDown dw ?       ;amount to subtract from error term when the
  51.                         ; error term turns over
  52. EdgeScan ends
  53.  
  54. Parms   struc
  55.           dw      2 dup(?) ;return address & pushed BP
  56. TopEdge  dw      ?        ;pointer to EdgeScan structure for left edge
  57. BottomEdge dw      ?        ;pointer to EdgeScan structure for right edge
  58. Parms   ends
  59.  
  60. ;Offsets from BP in stack frame of local variables.
  61. lSourceX        equ     -4      ;current X coordinate in source image
  62. lSourceY        equ     -8      ;current Y coordinate in source image
  63. lSourceStepX    equ     -12     ;X step in source image for X dest step of 1
  64. lSourceStepY    equ     -16     ;Y step in source image for X dest step of 1
  65. lXAdvanceByOne  equ     -18     ;used to step source pointer 1 pixel
  66.                                 ; incrementally in X
  67. lBaseAdvance   equ      -20     ;use to step source pointer minimum number of
  68.                                 ; pixels incrementally in X+Y simultaneously
  69. lYAdvanceByOne  equ     -22     ;used to step source pointer 1 pixel
  70.                                 ; incrementally in Y
  71. LOCAL_SIZE      equ     22      ;total size of local variables
  72.  
  73.         .code
  74.         extrn   _FixedMul:near, _FixedDiv:near
  75.  
  76.         align   2
  77. ToScanDone:
  78.         jmp     ScanDone
  79.  
  80.         public  _ScanOutLine
  81.         align   2
  82. _ScanOutLine    proc    near
  83.         push    bp              ;preserve caller's stack frame
  84.         mov     bp,sp           ;point to our stack frame
  85.         sub     sp,LOCAL_SIZE   ;allocate space for local variables
  86.         push    si              ;preserve caller's register variables
  87.         push    di
  88.  
  89. ; Nothing to do if destination is fully Y clipped.
  90.  
  91.         mov     di,[bp].BottomEdge
  92.         mov     si,[di].DestY
  93.         cmp     si,[_ClipMinY]
  94.         jle     short ToScanDone ;bottom edge is to left of clip rect, so done
  95.         mov     bx,[bp].TopEdge
  96.         mov     dx,[bx].DestY
  97.         cmp     dx,[_ClipMaxY]
  98.         jge     short ToScanDone ;top edge is to right of clip rect, so done
  99.         sub     si,dx            ;destination fill height
  100.         jle     short ToScanDone ;null or negative full height, so done
  101.  
  102.         mov     ax,word ptr [bx].SourceX        ;initial source X coordinate
  103.         mov     word ptr [bp].lSourceX,ax
  104.         mov     ax,word ptr [bx].SourceX+2
  105.         mov     word ptr [bp].lSourceX+2,ax
  106.  
  107.         mov     ax,word ptr [bx].SourceY        ;initial source Y coordinate
  108.         mov     word ptr [bp].lSourceY,ax
  109.         mov     ax,word ptr [bx].SourceY+2
  110.         mov     word ptr [bp].lSourceY+2,ax
  111.  
  112. ; Calculate source steps that correspond to each 1-pixel destination X step
  113. ; (across the destination scan line).
  114.  
  115.         push    si              ;push dest Y height, in fixedpoint form
  116.         sub     ax,ax
  117.         push    ax              ;push 0 as fractional part of dest Y height
  118.         mov     ax,word ptr [di].SourceX
  119.         sub     ax,word ptr [bp].lSourceX       ;low word of source X width
  120.         mov     dx,word ptr [di].SourceX+2
  121.         sbb     dx,word ptr [bp].lSourceX+2     ;high word of source X width
  122.         push    dx              ;push source X width, in fixedpoint form
  123.         push    ax
  124.         call    _FixedDiv       ;scale source X width to dest Y height
  125.         add     sp,8            ;clear parameters from stack
  126.         mov     word ptr [bp].lSourceStepX,ax   ;remember source X step for
  127.         mov     word ptr [bp].lSourceStepX+2,dx ; 1-pixel destination Y height
  128.         mov     cx,1            ;assume source X advances non-negative
  129.         and     dx,dx           ;which way does source X advance?
  130.         jns     short SourceXNonNeg ;non-negative
  131.         neg     cx              ;negative
  132.         cmp     ax,0            ;is the whole step exactly an integer?
  133.         jz      short SourceXNonNeg ;yes
  134.         inc     dx              ;no, truncate to integer in the direction of
  135.                                 ; 0, because otherwise we'll end up with a
  136.                                 ; whole step of 1-too-large magnitude
  137. SourceXNonNeg:
  138.         mov     [bp].lXAdvanceByOne,cx  ;amount to add to source pointer to
  139.                                         ; move by one in X
  140.         mov     [bp].lBaseAdvance,dx    ;minimum amount to add to source
  141.                                         ; pointer to advance in X each time
  142.                                         ; the dest advances one in Y
  143.  
  144.         push    si              ;push dest Y height, in fixedpoint form
  145.         sub     ax,ax
  146.         push    ax              ;push 0 as fractional part of dest Y height
  147.         mov     ax,word ptr [di].SourceY
  148.         sub     ax,word ptr [bp].lSourceY       ;low word of source Y height
  149.         mov     dx,word ptr [di].SourceY+2
  150.         sbb     dx,word ptr [bp].lSourceY+2     ;high word of source Y height
  151.         push    dx              ;push source Y height, in fixedpoint form
  152.         push    ax
  153.         call    _FixedDiv       ;scale source Y height to dest Y height
  154.         add     sp,8            ;clear parameters from stack
  155.         mov     word ptr [bp].lSourceStepY,ax   ;remember source Y step for
  156.         mov     word ptr [bp].lSourceStepY+2,dx ; 1-pixel destination X step
  157.         mov     cx,[_TexMapWidth] ;assume source Y advances non-negative
  158.         and     dx,dx           ;which way does source Y advance?
  159.         jns     short SourceYNonNeg ;non-negative
  160.         neg     cx              ;negative
  161.         cmp     ax,0            ;is the whole step exactly an integer?
  162.         jz      short SourceYNonNeg ;yes
  163.         inc     dx              ;no, truncate to integer in the direction of
  164.                                 ; 0, because otherwise we'll end up with a
  165.                                 ; whole step of 1-too-large magnitude
  166. SourceYNonNeg:
  167.         mov     [bp].lYAdvanceByOne,cx  ;amount to add to source pointer to
  168.                                         ; move by one in Y
  169.         mov     ax,[_TexMapWidth]       ;minimum distance skipped in source
  170.         imul    dx                      ; image bitmap when Y steps (ignoring
  171.         add     [bp].lBaseAdvance,ax    ; carry from the fractional part);
  172.                                         ; add into total minimum source
  173.                                         ; advance amount
  174.  
  175. ; Advance 1/2 step in the stepping direction, to space scanned pixels evenly
  176. ; between the left and right edges. (There's a slight inaccuracy in dividing
  177. ; negative numbers by 2 by shifting rather than dividing, but the inaccuracy
  178. ; is in the least significant bit, and we'll just live with it.)
  179.  
  180.         mov     ax,word ptr [bp].lSourceStepX
  181.         mov     dx,word ptr [bp].lSourceStepX+2
  182.         sar     dx,1
  183.         rcr     ax,1
  184.         add     word ptr [bp].lSourceX,ax
  185.         adc     word ptr [bp].lSourceX+2,dx
  186.  
  187.         mov     ax,word ptr [bp].lSourceStepY
  188.         mov     dx,word ptr [bp].lSourceStepY+2
  189.         sar     dx,1
  190.         rcr     ax,1
  191.         add     word ptr [bp].lSourceY,ax
  192.         adc     word ptr [bp].lSourceY+2,dx
  193.  
  194. ; Clip bottom edge if necessary.
  195.  
  196.         mov     bx,[di].DestY
  197.         cmp     bx,[_ClipMaxY]
  198.         jl      short BottomEdgeClipped
  199.         mov     bx,[_ClipMaxY]
  200. BottomEdgeClipped:
  201.  
  202. ; Clip top edge if necssary
  203.  
  204.         mov     si,[bp].TopEdge
  205.         mov     di,[si].DestY
  206.         cmp     di,[_ClipMinY]
  207.         jge     short TopEdgeClipped
  208.  
  209. ; Top clipping is necessary; advance the source accordingly
  210.  
  211.         neg     di
  212.         add     di,[_ClipMinY]  ;ClipMinY - DestY
  213.                                 ;first, advance the source in X
  214.         push    di              ;push ClipMinX - DestX, in fixedpoint form
  215.         sub     ax,ax
  216.         push    ax              ;push 0 as fractional part of ClipMinX-DestX
  217.         push    word ptr [bp].lSourceStepX+2
  218.         push    word ptr [bp].lSourceStepX
  219.         call    _FixedMul       ;total source X stepping in clipped area
  220.         add     sp,8            ;clear parameters from stack
  221.         add     word ptr [bp].lSourceX,ax   ;step the source X past clipping
  222.         adc     word ptr [bp].lSourceX+2,dx
  223.                                 ;now advance the source in Y
  224.         push    di              ;push ClipMinX - DestX, in fixedpoint form
  225.         sub     ax,ax
  226.         push    ax              ;push 0 as fractional part of ClipMinX-DestX
  227.         push    word ptr [bp].lSourceStepY+2
  228.         push    word ptr [bp].lSourceStepY
  229.         call    _FixedMul       ;total source Y stepping in clipped area
  230.         add     sp,8            ;clear parameters from stack
  231.         add     word ptr [bp].lSourceY,ax   ;step the source Y past clipping
  232.         adc     word ptr [bp].lSourceY+2,dx
  233.  
  234.         mov     di,[_ClipMinY]  ;start Y coordinate in dest after clipping
  235. TopEdgeClipped:
  236.  
  237. ; Calculate actual clipped destination drawing height.
  238.  
  239.         sub     bx,di
  240.  
  241. ; Scan down the destination column, updating the source image position
  242. ; accordingly.
  243.  
  244. ; Point to the initial source image pixel, adding 0.5 to both X and Y so that
  245. ; we can truncate to integers from now on but effectively get rounding.
  246.  
  247.         add     word ptr [bp].lSourceY,8000h    ;add 0.5
  248.         mov     ax,word ptr [bp].lSourceY+2
  249.         adc     ax,0
  250.         mul     [_TexMapWidth]   ;initial scan line in source image
  251.         add     word ptr [bp].lSourceX,8000h    ;add 0.5
  252.         mov     si,word ptr [bp].lSourceX+2 ;offset into source scan line
  253.         adc     si,ax            ;initial source offset in source image
  254.         add     si,[_TexMapBits] ;SI points to the initial image pixel
  255.  
  256. ; Point to initial destination pixel.
  257.  
  258.         mov     ax,SCREEN_WIDTH
  259.         mul     di      ;offset of initial dest scan line
  260.         mov     di,[_DestX]
  261.         mov     cx,di   ;initial destination X
  262.         shr     di,1
  263.         shr     di,1    ;X/4 = offset of pixel in scan line
  264.         add     di,ax   ;offset of pixel in page
  265.         add     di,[_CurrentPageBase] ;offset of pixel in display memory
  266.                         ;DI now points to the first destination pixel
  267.  
  268.         and     cl,011b ;CL = pixel's plane
  269.         mov     al,MAP_MASK
  270.         mov     dx,SC_INDEX
  271.         mov     ah,01h  ;one plane bit in each nibble, so we'll get carry
  272.                         ; automatically when going from plane 3 to plane 0
  273.         shl     ah,cl   ;set the bit for the first pixel's plane to 1
  274.         out     dx,ax
  275.  
  276. ; If source Y step is negative, change over to working with non-negative
  277. ; values.
  278.  
  279.         cmp     word ptr [bp].lYAdvanceByOne,0
  280.         jge     short SYStepSet
  281.         neg     word ptr [bp].lSourceStepY
  282.         not     word ptr [bp].lSourceY
  283. SYStepSet:
  284.  
  285. ; If source X step is negative, handle with a separate inner loop.
  286.  
  287.         cmp     word ptr [bp].lXAdvanceByOne,0
  288.         jl      short TexScanRToL
  289.  
  290. ; Handles edges that go left to right through the source texture.
  291.  
  292.         mov     cx,word ptr [bp].lSourceStepX
  293.         shl     ecx,16          ;upper word of ECX is fractional X advance
  294.         mov     cx,word ptr [bp].lSourceStepY
  295.         shr     cx,1            ;lower 15 bits of ECX are fractional Y
  296.                                 ; advance, bit 15 is 0
  297.         mov     dx,[bp].lSourceX
  298.         shl     edx,16
  299.         mov     dx,[bp].lSourceY
  300.         shr     dx,1            ;upper 16 bits are X fractional coord, bit 15
  301.                                 ; is 0, lower 15 bits are Y fractional coord
  302.         push    ds              ;preserve normal data segment
  303.         mov     ax,SCREEN_SEG
  304.         mov     ds,ax           ;DS:DI->initial destination pixel
  305.         mov     ax,[bp].lYAdvanceByOne
  306.         push    bp              ;preserve stack frame pointer
  307.                                 ;***stack frame not available***
  308.         mov     bp,[bp].lBaseAdvance
  309.         xchg    bp,si           ;SS:BP->initial source texture pixel
  310.                                 ;SI = minimum source advance per destination
  311.                                 ; step
  312.         inc     bx              ;round pixel-pair count up
  313.         shr     bx,1            ;# of pixel-pairs to texture map
  314.         jc      short TexScanLToRLoop ;even # of pixels
  315.         push    bx              ;preserve pixel-pair count
  316.         sub     di,SCREEN_WIDTH ;preadjust back to compensate for
  317.                                 ; built-in offset in odd-pixel code
  318.                                 ; being jumped to
  319.         jmp     short TexScanLToRLoopOddEntry ;odd # of pixels
  320.  
  321. ; Inner loop to draw a single texture-mapped vertical column,
  322. ; rather than a horizontal scanline. Maxed-out 16-bit version.
  323. ;
  324. ; At this point:
  325. ;       AX = source pointer increment to advance one in Y
  326. ;       BX = # of pixel-pairs to draw
  327. ;       ECX = fractional Y advance in lower 15 bits of CX,
  328. ;             fractional X advance in high word of ECX, bit
  329. ;             15 set to 0
  330. ;       EDX = fractional source texture Y coordinate in lower
  331. ;             15 bits of CX, fractional source texture X coord
  332. ;             in high word of ECX, bit 15 set to 0
  333. ;       SI = sum of integral X & Y source pointer advances
  334. ;       DS:DI = initial destination pointer
  335. ;       SS:BP = initial source texture pointer
  336. ;       Plane mask set to point to the plane containing this scan
  337.  
  338.         align   2
  339. TexScanLToRLoop:
  340.         push    bx      ;remember pixel-pair count
  341.  
  342.         mov     bl,ss:[bp]      ;get texture pixel
  343.         mov     ds:[di],bl      ;set screen pixel
  344.  
  345.         add     edx,ecx ;advance frac Y in DX,
  346.                         ; frac X in high word of EDX
  347.         adc     bp,si   ;advance source pointer by integral
  348.                         ; X & Y amount, also accounting for
  349.                         ; carry from X fractional addition
  350.         test    dh,80h  ;carry from Y fractional addition?
  351.         jz      short TSLToRL1 ;no
  352.         add     bp,ax   ;yes, advance Y by one
  353.         and     dh,not 80h ;reset the Y fractional carry bit
  354. TSLToRL1:
  355.  
  356. TexScanLToRLoopOddEntry:
  357.  
  358.         mov     bl,ss:[bp]              ;get texture pixel
  359.         mov     ds:[di+SCREEN_WIDTH],bl ;set screen pixel
  360.  
  361.         add     edx,ecx ;advance frac Y in DX,
  362.                         ; frac X in high word of EDX
  363.         adc     bp,si   ;advance source pointer by integral
  364.                         ; X & Y amount, also accounting for
  365.                         ; carry from X fractional addition
  366.         test    dh,80h  ;carry from Y fractional addition?
  367.         jz      short TSLToRL2 ;no
  368.         add     bp,ax   ;yes, advance Y by one
  369.         and     dh,not 80h ;reset the Y fractional carry bit
  370. TSLToRL2:
  371.  
  372. ; Point to the next destination pixel.
  373.  
  374.         add     di,SCREEN_WIDTH*2
  375.  
  376. ; Continue if there are any more dest pixels to draw.
  377.  
  378.         pop     bx
  379.         dec     bx
  380.         jnz     short TexScanLToRLoop
  381.  
  382.         pop     bp              ;restore stack frame pointer
  383.                                 ;***stack frame available***
  384.         pop     ds              ;restore normal data segment
  385.         jmp     short ScanDone
  386.  
  387. ; Handles edges that go right to left through the source texture.
  388.         align   2
  389. TexScanRToL:
  390.         mov     cx,word ptr [bp].lSourceStepX
  391.         neg     cx              ;make fractional X advance positive, so we can
  392.                                 ; add both X and Y simultaneously
  393.         shl     ecx,16          ;upper word of ECX is fractional X advance
  394.         mov     cx,word ptr [bp].lSourceStepY
  395.         shr     cx,1            ;lower 15 bits of ECX are fractional Y
  396.                                 ; advance, bit 15 is 0
  397.         mov     dx,[bp].lSourceX
  398.         not     dx              ;convert fraction X advance to counting-up
  399.                                 ; mode, so we can add both X and Y
  400.                                 ; simultaneously (since X goes right to left,
  401.                                 ; fractional X would normally be advanced
  402.                                 ; with subtraction)
  403.         shl     edx,16
  404.         mov     dx,[bp].lSourceY
  405.         shr     dx,1            ;upper 16 bits are X fractional coord, bit 15
  406.                                 ; is 0, lower 15 bits are Y fractional coord
  407.         push    ds              ;preserve normal data segment
  408.         mov     ax,SCREEN_SEG
  409.         mov     ds,ax           ;DS:DI->initial destination pixel
  410.         mov     ax,[bp].lYAdvanceByOne
  411.         push    bp              ;preserve stack frame pointer
  412.                                 ;***stack frame not available***
  413.         mov     bp,[bp].lBaseAdvance
  414.         xchg    bp,si           ;SS:BP->initial source texture pixel
  415.         neg     si              ;SI = minimum source advance per destination
  416.                                 ; step, negated so we can use SBB in the inner
  417.                                 ; loop
  418.         inc     bx              ;round pixel-pair count up
  419.         shr     bx,1            ;# of pixel-pairs to texture map
  420.         jc      short TexScanRToLLoop ;even # of pixels
  421.         push    bx              ;preserve pixel-pair count
  422.         sub     di,SCREEN_WIDTH ;preadjust back to compensate for
  423.                                 ; built-in offset in odd-pixel code
  424.                                 ; being jumped to
  425.         jmp     short TexScanRToLLoopOddEntry ;odd # of pixels
  426.  
  427. ; Inner loop to draw a single texture-mapped vertical column,
  428. ; rather than a horizontal scanline. Maxed-out 16-bit version.
  429. ;
  430. ; At this point:
  431. ;       AX = source pointer increment to advance one in Y
  432. ;       BX = # of pixel-pairs to draw
  433. ;       ECX = fractional Y advance in lower 15 bits of CX,
  434. ;             fractional X advance in high word of ECX, bit
  435. ;             15 set to 0
  436. ;       EDX = fractional source texture Y coordinate in lower
  437. ;             15 bits of CX, fractional source texture X coord
  438. ;             in high word of ECX, bit 15 set to 0
  439. ;       SI = sum of integral X & Y source pointer advances
  440. ;       DS:DI = initial destination pointer
  441. ;       SS:BP = initial source texture pointer
  442. ;       Plane mask set to point to the plane containing this scan
  443.  
  444.         align   2
  445. TexScanRToLLoop:
  446.         push    bx      ;remember pixel-pair count
  447.  
  448.         mov     bl,ss:[bp]      ;get texture pixel
  449.         mov     ds:[di],bl      ;set screen pixel
  450.  
  451.         add     edx,ecx ;advance frac Y in DX,
  452.                         ; frac X in high word of EDX
  453.         sbb     bp,si   ;advance source pointer by integral
  454.                         ; X & Y amount, also accounting for
  455.                         ; carry from X fractional addition
  456.         test    dh,80h  ;carry from Y fractional addition?
  457.         jz      short TSRToLL1 ;no
  458.         add     bp,ax   ;yes, advance Y by one
  459.         and     dh,not 80h ;reset the Y fractional carry bit
  460. TSRToLL1:
  461.  
  462. TexScanRToLLoopOddEntry:
  463.  
  464.         mov     bl,ss:[bp]              ;get texture pixel
  465.         mov     ds:[di+SCREEN_WIDTH],bl ;set screen pixel
  466.  
  467.         add     edx,ecx ;advance frac Y in DX,
  468.                         ; frac X in high word of EDX
  469.         sbb     bp,si   ;advance source pointer by integral
  470.                         ; X & Y amount, also accounting for
  471.                         ; carry from X fractional addition
  472.         test    dh,80h  ;carry from Y fractional addition?
  473.         jz      short TSRToLL2 ;no
  474.         add     bp,ax   ;yes, advance Y by one
  475.         and     dh,not 80h ;reset the Y fractional carry bit
  476. TSRToLL2:
  477.  
  478. ; Point to the next destination pixel.
  479.  
  480.         add     di,SCREEN_WIDTH*2
  481.  
  482. ; Continue if there are any more dest pixels to draw.
  483.  
  484.         pop     bx
  485.         dec     bx
  486.         jnz     short TexScanRToLLoop
  487.  
  488.         pop     bp              ;restore stack frame pointer
  489.                                 ;***stack frame available***
  490.         pop     ds              ;restore normal data segment
  491.  
  492. ScanDone:
  493.         pop     di              ;restore caller's register variables
  494.         pop     si
  495.         mov     sp,bp           ;deallocate local variables
  496.         pop     bp              ;restore caller's stack frame
  497.         ret
  498. _ScanOutLine    endp
  499.  
  500.         end
  501.  
  502.