home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chapter35 / l35-3.asm < prev    next >
Assembly Source File  |  1997-06-18  |  13KB  |  374 lines

  1. ;
  2. ; *** Listing 14.3 ***
  3. ;
  4. ; Fast assembler implementation of Bresenham's line drawing algorithm
  5. ; for the EGA and VGA. Works in modes 0Eh, 0Fh, 10h, and 12h.
  6. ; C near-callable.
  7. ; Bit mask accumulation technique when |DeltaX| >= |DeltaY|
  8. ;  suggested by Jim Mackraz.
  9. ;
  10. ; Assembled with TASM 4.0
  11. ; Checked by Jim Mischel 11/21/94
  12. ;
  13. ; By Michael Abrash.
  14. ;
  15. ;****************************************************************
  16. ; C-compatible line-drawing entry point at _EVGALine.           *
  17. ; Near C-callable as:                                           *
  18. ;       EVGALine(X0, Y0, X1, Y1, Color);                        *
  19. ;****************************************************************
  20. ;
  21.  
  22.     model small
  23.     .code
  24.  
  25. ;
  26. ; Equates.
  27. ;
  28. EVGA_SCREEN_WIDTH_IN_BYTES equ  80      ;memory offset from start of
  29.                                         ; one row to start of next
  30.                                         ; in display memory
  31. EVGA_SCREEN_SEGMENT     equ     0a000h  ;display memory segment
  32. GC_INDEX                equ     3ceh    ;Graphics Controller
  33.                                         ; Index register port
  34. SET_RESET_INDEX         equ     0       ;indexes of needed
  35. ENABLE_SET_RESET_INDEX  equ     1       ; Graphics Controller
  36. BIT_MASK_INDEX          equ     8       ; registers
  37.  
  38. ;
  39. ; Stack frame.
  40. ;
  41. EVGALineParms   struc
  42.         dw      ?               ;pushed BP
  43.         dw      ?               ;pushed return address (make double
  44.                                 ; word for far call)
  45. X0      dw      ?               ;starting X coordinate of line
  46. Y0      dw      ?               ;starting Y coordinate of line
  47. X1      dw      ?               ;ending X coordinate of line
  48. Y1      dw      ?               ;ending Y coordinate of line
  49. Color   db      ?               ;color of line
  50.         db      ?               ;dummy to pad to word size
  51. EVGALineParms   ends
  52.  
  53. ;****************************************************************
  54. ; Line drawing macros.                                          *
  55. ;****************************************************************
  56.  
  57. ;
  58. ; Macro to loop through length of line, drawing each pixel in turn.
  59. ; Used for case of |DeltaX| >= |DeltaY|.
  60. ; Input:                                
  61. ;       MOVE_LEFT: 1 if DeltaX < 0, 0 else
  62. ;       AL: pixel mask for initial pixel
  63. ;       BX: |DeltaX|
  64. ;       DX: address of GC data register, with index register set to
  65. ;               index of Bit Mask register
  66. ;       SI: DeltaY
  67. ;       ES:DI: display memory address of byte containing initial
  68. ;               pixel
  69. ;
  70. LINE1   macro   MOVE_LEFT
  71.         local   LineLoop, MoveXCoord, NextPixel, Line1End
  72.         local   MoveToNextByte, ResetBitMaskAccumulator
  73.         mov     cx,bx           ;# of pixels in line
  74.         jcxz    Line1End     ;done if there are no more pixels
  75.                                 ; (there's always at least the one pixel
  76.                                 ; at the start location)
  77.         shl     si,1            ;DeltaY * 2
  78.         mov     bp,si           ;error term
  79.         sub     bp,bx           ;error term starts at DeltaY * 2 - DeltaX
  80.         shl     bx,1            ;DeltaX * 2
  81.         sub     si,bx           ;DeltaY * 2 - DeltaX * 2 (used in loop)
  82.         add     bx,si           ;DeltaY * 2 (used in loop)
  83.         mov     ah,al           ;set aside pixel mask for initial pixel
  84.                                 ; with AL (the pixel mask accumulator) set
  85.                                 ; for the initial pixel
  86. LineLoop:
  87. ;
  88. ; See if it's time to advance the Y coordinate yet.
  89. ;
  90.         and     bp,bp               ;see if error term is negative
  91.         js      MoveXCoord          ;yes, stay at the same Y coordinate
  92. ;
  93. ; Advance the Y coordinate, first writing all pixels in the current
  94. ; byte, then move the pixel mask either left or right, depending
  95. ; on MOVE_LEFT.
  96. ;
  97.         out     dx,al           ;set up bit mask for pixels in this byte
  98.         xchg    byte ptr [di],al
  99.                                 ;load latches and write pixels, with bit mask
  100.                                 ; preserving other latched bits. Because
  101.                                 ; set/reset is enabled for all planes, the
  102.                                 ; value written actually doesn't matter
  103.         add     di,EVGA_SCREEN_WIDTH_IN_BYTES   ;increment Y coordinate
  104.         add     bp,si           ;adjust error term back down
  105. ;
  106. ; Move pixel mask one pixel (either right or left, depending
  107. ; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
  108. ;
  109. if MOVE_LEFT
  110.         rol     ah,1            ;move pixel mask 1 pixel to the left
  111. else
  112.         ror     ah,1            ;move pixel mask 1 pixel to the right
  113. endif
  114.         jnc     ResetBitMaskAccumulator ;didn't wrap to next byte
  115.         jmp     short MoveToNextByte    ;did wrap to next byte
  116. ;
  117. ; Move pixel mask one pixel (either right or left, depending
  118. ; on MOVE_LEFT), adjusting display memory address and writing pixels
  119. ; in this byte when pixel mask wraps.
  120. ;
  121. MoveXCoord:
  122.         add     bp,bx           ;increment error term & keep same
  123. if MOVE_LEFT
  124.         rol     ah,1            ;move pixel mask 1 pixel to the left
  125. else
  126.         ror     ah,1            ;move pixel mask 1 pixel to the right
  127. endif
  128.         jnc     NextPixel     ;if still in same byte, no need to
  129.                                 ; modify display memory yet
  130.         out     dx,al           ;set up bit mask for pixels in this byte.
  131.         xchg    byte ptr [di],al
  132.                                 ;load latches and write pixels, with bit mask
  133.                                 ; preserving other latched bits. Because
  134.                                 ; set/reset is enabled for all planes, the
  135.                                 ; value written actually doesn't matter
  136. MoveToNextByte:
  137. if MOVE_LEFT
  138.         dec     di              ;next pixel is in byte to left
  139. else
  140.         inc     di              ;next pixel is in byte to right
  141. endif
  142. ResetBitMaskAccumulator:
  143.         sub     al,al           ;reset pixel mask accumulator   
  144. NextPixel:
  145.         or      al,ah           ;add the next pixel to the pixel mask
  146.                                 ; accumulator
  147.         loop    LineLoop
  148. ;
  149. ; Write the pixels in the final byte.
  150. ;
  151. Line1End:
  152.         out     dx,al           ;set up bit mask for pixels in this byte.
  153.         xchg    byte ptr [di],al
  154.                                 ;load latches and write pixels, with bit mask
  155.                                 ; preserving other latched bits. Because
  156.                                 ; set/reset is enabled for all planes, the
  157.                                 ; value written actually doesn't matter
  158.         endm
  159.  
  160. ;
  161. ; Macro to loop through length of line, drawing each pixel in turn.
  162. ; Used for case of DeltaX < DeltaY.
  163. ; Input:                                
  164. ;       MOVE_LEFT: 1 if DeltaX < 0, 0 else
  165. ;       AL: pixel mask for initial pixel
  166. ;       BX: |DeltaX|
  167. ;       DX: address of GC data register, with index register set to
  168. ;               index of Bit Mask register
  169. ;       SI: DeltaY
  170. ;       ES:DI: display memory address of byte containing initial
  171. ;               pixel
  172. ;
  173. LINE2   macro   MOVE_LEFT
  174.         local   LineLoop, MoveYCoord, ETermAction, Line2End
  175.         mov     cx,si           ;# of pixels in line
  176.         jcxz    Line2End     ;done if there are no more pixels
  177.         shl     bx,1            ;DeltaX * 2
  178.         mov     bp,bx           ;error term
  179.         sub     bp,si           ;error term starts at DeltaX * 2 - DeltaY
  180.         shl     si,1            ;DeltaY * 2
  181.         sub     bx,si           ;DeltaX * 2 - DeltaY * 2 (used in loop)
  182.         add     si,bx           ;DeltaX * 2 (used in loop)
  183. ;
  184. ; Set up initial bit mask & write initial pixel.
  185. ;
  186.         out     dx,al
  187.         xchg    byte ptr [di],ah
  188.                                 ;load latches and write pixel, with bit mask
  189.                                 ; preserving other latched bits. Because
  190.                                 ; set/reset is enabled for all planes, the
  191.                                 ; value written actually doesn't matter
  192. LineLoop:
  193. ;
  194. ; See if it's time to advance the X coordinate yet.
  195. ;
  196.         and     bp,bp               ;see if error term is negative
  197.         jns     ETermAction         ;no, advance X coordinate
  198.         add     bp,si               ;increment error term & keep same
  199.         jmp     short MoveYCoord     ; X coordinate
  200. ETermAction:
  201. ;
  202. ; Move pixel mask one pixel (either right or left, depending
  203. ; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
  204. ;
  205. if MOVE_LEFT
  206.         rol     al,1
  207.         sbb     di,0
  208. else
  209.         ror     al,1
  210.         adc     di,0
  211. endif
  212.         out     dx,al           ;set new bit mask
  213.         add     bp,bx           ;adjust error term back down
  214. ;
  215. ; Advance Y coordinate.
  216. ;
  217. MoveYCoord:
  218.         add     di,EVGA_SCREEN_WIDTH_IN_BYTES
  219. ;
  220. ; Write the next pixel.
  221. ;
  222.         xchg    byte ptr [di],ah
  223.                                 ;load latches and write pixel, with bit mask
  224.                                 ; preserving other latched bits. Because
  225.                                 ; set/reset is enabled for all planes, the
  226.                                 ; value written actually doesn't matter
  227. ;
  228.         loop    LineLoop
  229. Line2End:
  230.         endm
  231.  
  232. ;****************************************************************
  233. ; Line drawing routine.                                         *
  234. ;****************************************************************
  235.  
  236.         public  _EVGALine
  237. _EVGALine       proc    near
  238.         push    bp
  239.         mov     bp,sp
  240.         push    si              ;preserve register variables
  241.         push    di
  242.         push    ds
  243. ;
  244. ; Point DS to display memory.
  245. ;
  246.         mov     ax,EVGA_SCREEN_SEGMENT
  247.         mov     ds,ax
  248. ;
  249. ; Set the Set/Reset and Set/Reset Enable registers for
  250. ; the selected color.
  251. ;
  252.         mov     dx,GC_INDEX
  253.         mov     al,SET_RESET_INDEX
  254.         out     dx,al
  255.         inc     dx
  256.         mov     al,[bp+Color]
  257.         out     dx,al
  258.         dec     dx
  259.         mov     al,ENABLE_SET_RESET_INDEX
  260.         out     dx,al
  261.         inc     dx
  262.         mov     al,0ffh
  263.         out     dx,al
  264. ;
  265. ; Get DeltaY.
  266. ;
  267.         mov     si,[bp+Y1]          ;line Y start
  268.         mov     ax,[bp+Y0]          ;line Y end, used later in
  269.                                     ;calculating the start address
  270.         sub     si,ax               ;calculate DeltaY
  271.         jns     CalcStartAddress     ;if positive, we're set
  272. ;
  273. ; DeltaY is negative -- swap coordinates so we're always working
  274. ; with a positive DeltaY.
  275. ;
  276.         mov     ax,[bp+Y1]          ;set line start to Y1, for use
  277.                                     ; in calculating the start address
  278.         mov     dx,[bp+X0]
  279.         xchg    dx,[bp+X1]
  280.         mov     [bp+X0],dx          ;swap X coordinates
  281.         neg     si                  ;convert to positive DeltaY
  282. ;
  283. ; Calculate the starting address in display memory of the line.
  284. ; Hardwired for a screen width of 80 bytes.
  285. ;
  286. CalcStartAddress:
  287.         shl     ax,1    ;Y0 * 2     ;Y0 is already in AX
  288.         shl     ax,1    ;Y0 * 4
  289.         shl     ax,1    ;Y0 * 8
  290.         shl     ax,1    ;Y0 * 16
  291.         mov     di,ax
  292.         shl     ax,1    ;Y0 * 32
  293.         shl     ax,1    ;Y0 * 64
  294.         add     di,ax   ;Y0 * 80
  295.         mov     dx,[bp+X0]
  296.         mov     cl,dl           ;set aside lower 3 bits of column for
  297.         and     cl,7            ; pixel masking
  298.         shr     dx,1
  299.         shr     dx,1
  300.         shr     dx,1            ;get byte address of column (X0/8)
  301.         add     di,dx           ;offset of line start in display segment
  302. ;
  303. ; Set up GC Index register to point to the Bit Mask register.
  304. ;
  305.         mov     dx,GC_INDEX
  306.         mov     al,BIT_MASK_INDEX
  307.         out     dx,al
  308.         inc     dx              ;leave DX pointing to the GC Data register
  309. ;
  310. ; Set up pixel mask (in-byte pixel address).
  311. ;
  312.         mov     al,80h
  313.         shr     al,cl
  314. ;
  315. ; Calculate DeltaX.
  316. ;
  317.         mov     bx,[bp+X1]
  318.         sub     bx,[bp+X0]
  319. ;
  320. ; Handle correct one of four octants.
  321. ;
  322.         js      NegDeltaX
  323.         cmp     bx,si
  324.         jb      Octant1
  325. ;
  326. ; DeltaX >= DeltaY >= 0.
  327. ;
  328.         LINE1   0
  329.         jmp     EVGALineDone
  330. ;
  331. ; DeltaY > DeltaX >= 0.
  332. ;
  333. Octant1:
  334.         LINE2   0
  335.         jmp     short EVGALineDone
  336. ;
  337. NegDeltaX:
  338.         neg     bx      ;|DeltaX|
  339.         cmp     bx,si
  340.         jb      Octant2
  341. ;
  342. ; |DeltaX| >= DeltaY and DeltaX < 0.
  343. ;
  344.         LINE1   1
  345.         jmp     short EVGALineDone
  346. ;
  347. ; |DeltaX| < DeltaY and DeltaX < 0.
  348. ;
  349. Octant2:
  350.         LINE2   1
  351. ;
  352. EVGALineDone:
  353. ;
  354. ; Restore EVGA state.
  355. ;
  356.         mov     al,0ffh
  357.         out     dx,al           ;set Bit Mask register to 0ffh
  358.         dec     dx
  359.         mov     al,ENABLE_SET_RESET_INDEX
  360.         out     dx,al
  361.         inc     dx
  362.         sub     al,al
  363.         out     dx,al           ;set Enable Set/Reset register to 0
  364. ;
  365.         pop     ds
  366.         pop     di
  367.         pop     si
  368.         pop     bp
  369.         ret
  370. _EVGALine       endp
  371.  
  372.         end
  373.  
  374.