Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1331 lines
43 KiB

  1. ;---------------------------Module-Header------------------------------;
  2. ; Module Name: fastline.asm
  3. ;
  4. ; This module draws solid lines using the line hardware of the S3.
  5. ; It handles entirely unclipped lines, or lines clipped to a single
  6. ; rectangle by using the S3's hardware clipping.
  7. ;
  8. ; All lines that have integer end-points are drawn directly using the
  9. ; line hardware, and any lines having fractional end-points and are
  10. ; less than 256 pels long are also drawn directly by the line hardware.
  11. ; (Fractional end-point lines require 4 more bits of precision from the
  12. ; DDA hardware than do integer end-point lines, and the S3 has but 13
  13. ; bits to use. Lines longer than 255 pels are punted to the general
  14. ; purpose strip routines, which won't overflow the hardware).
  15. ;
  16. ; There are a number of ways unclipped lines can be sped up on the S3:
  17. ;
  18. ; 1) Optimize NT's GDI. This isn't an option for everybody.
  19. ;
  20. ; 2) Use memory-mapped I/O.
  21. ;
  22. ; 3) If you can't use memory-mapped I/O, you can at least minimize
  23. ; outs and ins because they are so painfully expensive. One
  24. ; way to do this is to amortize the cost of the in used for checking
  25. ; the FIFO: do one in to make sure a large number of entries on
  26. ; the FIFO are free (perhaps 6), then do the next 6 outs without
  27. ; having to check the FIFO.
  28. ;
  29. ; This will be a win on figures not requiring many outs (such as when
  30. ; radial lines are drawn as with a rectangle), assuming the hardware
  31. ; isn't always being overdriven.
  32. ;
  33. ; 4) In conjunction with 3) above, since short integer lines occur so
  34. ; frequently with CAD programs, it would be beneficial to use short-
  35. ; stroke vectors to do those lines, as they could reduce the number
  36. ; of outs required. Perhaps any line 10 pels or shorter could be
  37. ; output by using an array lookup to determine the short stroke vectors
  38. ; needed to represent that line.
  39. ;
  40. ; 5) Numerous other things I haven't thought of.
  41. ;
  42. ; Other improvements that should be done to make lines faster in general:
  43. ;
  44. ; 1) Simple clipping should be improved. Currently, we simply set the
  45. ; clip rectangle and let the hardware do everything. We should at least
  46. ; perform a trivial clip test to see if there's no chance the line
  47. ; intersects the clip rectangle.
  48. ;
  49. ; Also, for single lines it's expensive to do the 4 outs required to
  50. ; set the S3's clip rectangle, draw the line, then do 4 outs to reset
  51. ; the clip rectangle again. In those cases, it would be faster to do the
  52. ; clipping entirely in software.
  53. ;
  54. ; 2) This code can be enhanced to also handle styled lines, and to do
  55. ; complex clipping more efficiently than the strips routines.
  56. ;
  57. ; 3) It is possible to derive the mathematical algorithm for drawing any
  58. ; GIQ line in NT's 32 bit space using the limited bit precision of the
  59. ; S3's hardware. What this means is that absolutely any GIQ line can
  60. ; be drawn precisely with at most two drawing commands to the line
  61. ; hardware (and so the strips line drawing routine could be eliminated
  62. ; entirely). Just be careful of your math...
  63. ;
  64. ; 4) Numerous other things I haven't thought of.
  65. ;
  66. ; Copyright (c) 1992-1994 Microsoft Corporation
  67. ;-----------------------------------------------------------------------;
  68. .386
  69. .model small,c
  70. assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
  71. assume fs:nothing,gs:nothing
  72. .xlist
  73. include stdcall.inc ;calling convention cmacros
  74. include i386\strucs.inc
  75. include i386\hw.inc
  76. include i386\lines.inc
  77. .list
  78. ; Line coordinates are given in 28.4 fixed point format:
  79. F equ 16
  80. FLOG2 equ 4
  81. ; The S3's hardware can have 13 bits of significance for the error and
  82. ; step terms:
  83. NUM_DDA_BITS equ 13
  84. ; GIQ lines have to dedicate 4 bits to the fractional term, so the largest
  85. ; delta we can handle for GIQ lines is calculated as follows (scaled by F
  86. ; so we can do the test in GIQ coordinates), and remembering that one bit
  87. ; has to be used as a sign bit:
  88. MAX_GIQ_DELTA equ (((1 shl (NUM_DDA_BITS - 5)) - 1) * F)
  89. ; The following values must match those in winddi.h!
  90. PD_BEGINSUBPATH equ 00000001h
  91. PD_ENDSUBPATH equ 00000002h
  92. PD_RESETSTYLE equ 00000004h
  93. PD_CLOSEFIGURE equ 00000008h
  94. PD_BEZIERS equ 00000010h
  95. PATHDATA struc
  96. pd_flags dd ?
  97. pd_count dd ?
  98. pd_pptfx dd ?
  99. PATHDATA ends
  100. ;-------------------------------------------------------------------------;
  101. ; I felt a compelling need to use 'ebp' as a 7th general register, and we
  102. ; have no nifty macros for dereferencing frame variables off 'esp'. So
  103. ; with this structure I am rolling my own stack frame:
  104. STATE_MEM_SIZE equ 4 ;4 dwords
  105. PROC_MEM_SIZE equ 9 ;9 dwords
  106. STACK_FRAME struc
  107. ; State variables (don't add/delete fields without modifying STATE_MEM_SIZE!)
  108. sf_ulOurEbp dd ?
  109. sf_ulOriginalEbx dd ?
  110. sf_ulOriginalEdi dd ?
  111. sf_ulOriginalEsi dd ?
  112. ; Frame variables (feel free to add/delete fields):
  113. sf_y0 dd ? ;GIQ variables
  114. sf_y1 dd ?
  115. sf_x1 dd ?
  116. sf_ptlOrg db (size POINTL) dup (?)
  117. ;our origin for normalizing the line
  118. sf_ptfxLast db (size POINTL) dup (?)
  119. ;the most recent point
  120. sf_ptfxStartFigure db (size POINTL) dup (?)
  121. ;the figure's 1st point
  122. sf_bMore dd ? ;more path records to get?
  123. sf_pptfxEnd dd ? ;points to last point in record
  124. sf_pptfx dd ? ;points to current point
  125. sf_pd db (size PATHDATA) dup (?)
  126. ;pathdata structure
  127. sf_ptfxStart db (size POINTL) dup (?)
  128. ;temporary spot for saving start point
  129. sf_ptfxEnd db (size POINTL) dup (?)
  130. ;temporary spot for saving end point
  131. sf_ulCmd dd ? ;S3 draw command
  132. sf_cPels dd ? ;length of line in pels
  133. sf_bSetCP dd ? ;1 if first line in figure, 0 otherwise
  134. sf_ulLastLength dd ? ;last value set as LINE_MAX
  135. sf_ulErrorTerm dd ? ;error term for line
  136. sf_xOffset dd ? ;stack copy of surface offset
  137. sf_yOffset dd ?
  138. sf_ioGp_stat_cmd dd ? ;local copy of gp_stat address
  139. sf_pjMmBase dd ? ;local copy of the memory-mapped I/O
  140. ; base address
  141. ; Procedure variables (don't add/delete fields without modifying
  142. ; PROC_MEM_SIZE!)
  143. sf_ulOriginalEbp dd ?
  144. sf_ulOriginalReturn dd ?
  145. sf_ppdev dd ?
  146. sf_ppo dd ?
  147. sf_prclClip dd ?
  148. sf_apfn dd ?
  149. sf_flags dd ?
  150. sf_color dd ?
  151. sf_ulHwMix dd ?
  152. STACK_FRAME ends
  153. .code
  154. EXTRNP PATHOBJ_bEnum,8
  155. EXTRNP bLines,36
  156. ROUND_X_DOWN equ 01h
  157. ROUND_Y_DOWN equ 02h
  158. ROUND_SLOPE_ONE equ 04h
  159. ROUND_X_AND_Y_DOWN equ (ROUND_X_DOWN + ROUND_Y_DOWN)
  160. ROUND_X_DOWN_SLOPE_ONE equ (ROUND_X_DOWN + ROUND_SLOPE_ONE)
  161. ROUND_Y_DOWN_SLOPE_ONE equ (ROUND_Y_DOWN + ROUND_SLOPE_ONE)
  162. ;--------------------------------Macro----------------------------------;
  163. ; GIQ flags
  164. ;
  165. ; This macros computes the start pixel, the number of pixels to
  166. ; be lit, and the initial error term given a GIQ line. The line must
  167. ; have already been normalized such that dM >= dN, dN >= 0.
  168. ;
  169. ; Input: ebx - M0
  170. ; ecx - N0
  171. ; esi - dM
  172. ; edi - dN
  173. ; Trashes:
  174. ; eax, edx
  175. ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
  176. ; Output:
  177. ; [esp].sf_x1 - x-coordinate of last pixel (exclusive)
  178. ; ebx - x-coordinate of first pixel
  179. ; ecx - error term
  180. ; esi - dM
  181. ; edi - dN
  182. ; ebp - y-coordinate of first pixel
  183. ;-----------------------------------------------------------------------;
  184. GIQ macro flags
  185. local compute_x1, compute_error_term
  186. ; We normalize our coordinate system so that if the start point is
  187. ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
  188. mov eax,ebx
  189. mov ebp,ecx
  190. sar eax,FLOG2
  191. sar ebp,FLOG2
  192. mov [esp].sf_ptlOrg.ptl_x,eax
  193. ;ptlOrg.x = floor(M0 / F)
  194. mov [esp].sf_ptlOrg.ptl_y,ebp
  195. ;ptlOrg.y = floor(N0 / F)
  196. ; Calculate the correct [esp].sf_x1:
  197. lea ebp,[ecx + edi] ;ebp = N1
  198. and ebp,F - 1
  199. if (flags AND ROUND_X_DOWN)
  200. if (flags AND ROUND_SLOPE_ONE)
  201. cmp ebp,8
  202. sbb ebp,-1
  203. endif
  204. cmp ebp,1
  205. sbb ebp,8 ;N1 -= 8
  206. else
  207. sub ebp,8 ;N1 -= 8
  208. endif
  209. sbb eax,eax
  210. xor ebp,eax
  211. sub ebp,eax ;N1 = ABS(N1)
  212. lea edx,[ebx + esi]
  213. mov eax,edx
  214. sar edx,FLOG2
  215. and eax,F - 1
  216. jz short @f ;special case for M1 == 0
  217. cmp eax,ebp ;cmp M1, N1
  218. sbb edx,-1 ;edx is now one pixel past the actual
  219. @@: ; end coordinate (note that it hasn't
  220. ; been affected by the origin shift)
  221. compute_error_term:
  222. ; ebx = M0
  223. ; ecx = N0
  224. ; edx = x1
  225. ; esi = dM
  226. ; edi = dN
  227. and ecx,F - 1
  228. mov [esp].sf_x1,edx ;save x1
  229. ; Calculate our error term for x = 0.
  230. ;
  231. ; NOTE: Since this routine is used only for lines that are unclipped, we
  232. ; are guaranteed by our screen size that the values will be far less
  233. ; than 32 bits in significance, and so we don't worry about overflow.
  234. ; If this is used for clipped lines, these multiplies will have to
  235. ; be converted to give 64 bit results, because we can have 36 bits of
  236. ; significance!
  237. lea ebp,[ecx + 8] ;ebp = N0 + 8
  238. mov eax,esi
  239. imul eax,ebp ;eax = dM * (N0 + 8)
  240. mov ebp,edi
  241. ; We have to special case when M0 is 0 -- we know x0 will be zero.
  242. ; So we jump ahead a bit to a place where 'ebx' is assumed to contain
  243. ; x0 -- and it just so happens 'ebx' is zero in this case:
  244. and ebx,F - 1
  245. jz short @f
  246. imul ebp,ebx ;ebp = dN * M0
  247. sub eax,ebp
  248. ; Calculate the x-coordinate of the first pixel:
  249. if (flags AND ROUND_X_DOWN)
  250. if (flags AND ROUND_SLOPE_ONE)
  251. cmp ecx,8
  252. sbb ecx,-1
  253. endif
  254. cmp ecx,1
  255. sbb ecx,8 ;N0 -= 8
  256. else
  257. sub ecx,8 ;N0 -= 8
  258. endif
  259. sbb ebp,ebp
  260. xor ecx,ebp
  261. sub ecx,ebp ;N0 = ABS(N0)
  262. cmp ebx,ecx
  263. sbb ebx,ebx
  264. not ebx ;ebx = -x0
  265. ; Now adjust the error term accordingly:
  266. @@:
  267. if (flags AND ROUND_Y_DOWN)
  268. dec eax
  269. endif
  270. sar eax,FLOG2 ;eax = floor((N0 + 8) dM - M0 dN] / 16)
  271. mov ecx,[esp].sf_ptlOrg.ptl_x
  272. mov ebp,[esp].sf_ptlOrg.ptl_y
  273. sub ecx,ebx ;ecx = ptlOrg.ptl_x + x0
  274. and ebx,edi
  275. add ebx,eax
  276. sub ebx,esi ;ebx = dN * x0 + initial error - dM
  277. jl short @f ;if the error term >= 0, we have to
  278. sub ebx,esi ; add 1 to the y position and subtract
  279. inc ebp ; dM off again
  280. @@:
  281. xchg ebx,ecx
  282. endm
  283. ;--------------------------------Macro----------------------------------;
  284. ; GIQR flags
  285. ;
  286. ; Same as above, except it handles flips about the line x = y.
  287. ;
  288. ; Input: ebx - M0
  289. ; ecx - N0
  290. ; esi - dM
  291. ; edi - dN
  292. ; Trashes:
  293. ; eax, edx
  294. ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
  295. ; Output:
  296. ; [esp].sf_y1 - y-coordinate of last pixel (exclusive)
  297. ; ebx - x-coordinate of first pixel
  298. ; ecx - error term
  299. ; esi - dM
  300. ; edi - dN
  301. ; ebp - y-coordinate of first pixel
  302. ;-----------------------------------------------------------------------;
  303. GIQR macro flags
  304. ; We normalize our coordinate system so that if the start point is
  305. ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
  306. mov eax,ebx
  307. mov ebp,ecx
  308. sar eax,FLOG2
  309. sar ebp,FLOG2
  310. mov [esp].sf_ptlOrg.ptl_x,eax
  311. ;ptlOrg.x = floor(M0 / F)
  312. mov [esp].sf_ptlOrg.ptl_y,ebp
  313. ;ptlOrg.y = floor(N0 / F)
  314. ; Calculate the correct [esp].sf_y1:
  315. lea ebp,[ebx + esi] ;ebp = M1
  316. and ebp,F - 1
  317. if (flags AND ROUND_Y_DOWN)
  318. cmp ebp,1
  319. sbb ebp,8 ;M1 -= 8
  320. else
  321. sub ebp,8 ;M1 -= 8
  322. endif
  323. sbb eax,eax
  324. xor ebp,eax
  325. sub ebp,eax ;M1 = ABS(M1)
  326. lea edx,[ecx + edi]
  327. mov eax,edx
  328. sar edx,FLOG2
  329. and eax,F - 1
  330. jz short @f ;special case for N1 == 0
  331. cmp eax,ebp ;cmp N1, M1
  332. sbb edx,-1 ;edx is now one pixel past the actual
  333. @@: ; end coordinate (note that it hasn't
  334. ; been affected by the origin shift)
  335. and ebx,F - 1
  336. mov [esp].sf_y1,edx
  337. ; Calculate our error term for y = 0.
  338. ;
  339. ; NOTE: Since this routine is used only for lines that are unclipped, we
  340. ; are guaranteed by our screen size that the values will be far less
  341. ; than 32 bits in significance, and so we don't worry about overflow.
  342. ; If this is used for clipped lines, these multiplies will have to
  343. ; be converted to give 64 bit results, because we can have 36 bits of
  344. ; significance!
  345. lea ebp,[ebx + 8] ;ebp = M0 + 8
  346. mov eax,edi
  347. imul eax,ebp ;eax = dN * (M0 + 8)
  348. mov ebp,esi
  349. ; We have to special case when N0 is 0 -- we know y0 will be zero.
  350. ; So we jump ahead a bit to a place where 'ecx' is assumed to contain
  351. ; y0 -- and it just so happens 'ecx' is zero in this case:
  352. and ecx,F - 1
  353. jz short @f
  354. imul ebp,ecx ;ebp = dM * N0
  355. sub eax,ebp
  356. ; Calculate the x-coordinate of the first pixel:
  357. if (flags AND ROUND_Y_DOWN)
  358. cmp ebx,1
  359. sbb ebx,8 ;M0 -= 8
  360. else
  361. sub ebx,8 ;M0 -= 8
  362. endif
  363. sbb ebp,ebp
  364. xor ebx,ebp
  365. sub ebx,ebp ;M0 = ABS(M0)
  366. cmp ecx,ebx
  367. sbb ecx,ecx
  368. not ecx ;ecx = -y0
  369. ; Now adjust the error term accordingly:
  370. @@:
  371. if (flags AND ROUND_X_DOWN)
  372. dec eax
  373. endif
  374. sar eax,FLOG2 ;eax = floor((M0 + 8) dN - N0 dM] / 16)
  375. mov ebx,[esp].sf_ptlOrg.ptl_x
  376. mov ebp,[esp].sf_ptlOrg.ptl_y
  377. sub ebp,ecx ;ebp = ptlOrg.ptl_y + y0
  378. and ecx,esi
  379. add ecx,eax
  380. sub ecx,edi ;ecx = dM * x0 + initial error - dN
  381. jl short @f ;if the error term >= 0, we have to
  382. sub ecx,edi ; add 1 to the x position and subtract
  383. inc ebx ; dN off again
  384. @@:
  385. endm
  386. ;---------------------------Public-Routine------------------------------;
  387. ; vIoFastLine(ppdev, ppo, prclClip, apfn, flags, color, ulHwMix)
  388. ;
  389. ; Draws fast lines. Or at least attempts to. Uses normal I/O.
  390. ;
  391. ; Input:
  392. ;
  393. ; ppdev - PDEV pointer
  394. ; ppo - path to be drawn
  395. ; prclClip - pointer to rectangle array for passing to 'bLines'
  396. ; apfn - pointer to strip routines for passing to 'bLines'
  397. ; flags - status flag for passing to 'bLines'
  398. ; color - color
  399. ; ulHwMix - mix
  400. ;
  401. ;-----------------------------------------------------------------------;
  402. ; NOTE: Don't go changing parameters without also changing STACK_FRAME!
  403. cProc vIoFastLine,28,< \
  404. uses esi edi ebx, \
  405. ebp_ppdev: ptr, \
  406. ebp_ppo: ptr, \
  407. ebp_prclClip: ptr, \
  408. ebp_apfn: ptr, \
  409. ebp_flags: dword,\
  410. ebp_color: dword,\
  411. ebp_ulHwMix: dword>
  412. ; Leave room for our stack frame.
  413. ;
  414. ; NOTE: Don't add local variables here -- you can't reference them with
  415. ; ebp anyway! Add them to the STACK_FRAME structure.
  416. local aj[(size STACK_FRAME) - 4 * (STATE_MEM_SIZE + PROC_MEM_SIZE)]: byte
  417. ; We save 'ebp' on the stack (note that STACK_FRAME accounts for this push):
  418. push ebp
  419. mov esi,[esp].sf_ppdev
  420. mov [esp].sf_ulLastLength,-1;make sure first line in path resets
  421. ; LINE_MAX value
  422. ; Warm up the hardware:
  423. mov edx,[esi].pdev_ioGp_stat_cmd
  424. @@: in ax,dx
  425. and eax,FIFO_3_EMPTY
  426. jnz short @b ;IO_FIFO_WAIT(ppdev, 3)
  427. mov edx,[esi].pdev_ioFrgd_color
  428. mov eax,[esp].sf_color
  429. out dx,ax ;IO_FRGD_COLOR(ppdev, color)
  430. mov edx,[esi].pdev_ioFrgd_mix
  431. mov eax,[esp].sf_ulHwMix
  432. or eax,FOREGROUND_COLOR
  433. out dx,ax ;IO_FRGD_MIX(ppdev, FOREGROUND_COLOR |
  434. ; ulHwMix)
  435. mov edx,[esi].pdev_ioMulti_function
  436. mov eax,DATA_EXTENSION
  437. out dx,ax ;IO_PIX_CNTL(ppdev, ALL_ONES)
  438. ; Now get some path stuff:
  439. io_next_record:
  440. mov eax,[esp].sf_ppo
  441. lea ebx,[esp].sf_pd
  442. cCall PATHOBJ_bEnum,<eax,ebx>
  443. mov [esp].sf_bMore,eax ;save away return code for later
  444. mov ebx,[esp].sf_pd.pd_count;if 0 points in record, get outta here
  445. or ebx,ebx
  446. jz io_check_for_closefigure
  447. lea ebp,[8 * ebx - 8]
  448. add ebp,[esp].sf_pd.pd_pptfx
  449. mov [esp].sf_pptfxEnd,ebp ;points to last point in record
  450. mov ecx,[esp].sf_pd.pd_flags
  451. test ecx,PD_BEGINSUBPATH
  452. jz short io_continue_subpath
  453. ; Handle a new sub-path:
  454. mov eax,[esp].sf_pd.pd_pptfx
  455. add eax,8
  456. mov [esp].sf_pptfx,eax
  457. mov esi,[ebp].ptl_x ;remember last point in case we have
  458. mov edi,[ebp].ptl_y ; to continue to another record
  459. mov [esp].sf_ptfxLast.ptl_x,esi
  460. mov [esp].sf_ptfxLast.ptl_y,edi
  461. mov ebx,[eax - 8].ptl_x ;load up current start and end point
  462. mov ecx,[eax - 8].ptl_y
  463. mov esi,[eax].ptl_x
  464. mov edi,[eax].ptl_y
  465. mov [esp].sf_ptfxStartFigure.ptl_x,ebx
  466. mov [esp].sf_ptfxStartFigure.ptl_y,ecx
  467. mov [esp].sf_bSetCP,1 ;this line is first in figure
  468. cmp eax,[esp].sf_pptfxEnd ;we have to be careful when the only
  469. ; point in the record is the start-
  470. ; figure point (pretty rare)
  471. jbe io_new_line
  472. jmp short io_next_record
  473. io_continue_subpath:
  474. ; This record continues the path:
  475. mov eax,[esp].sf_pd.pd_pptfx
  476. mov ebx,[esp].sf_ptfxLast.ptl_x ;load up current start point
  477. mov ecx,[esp].sf_ptfxLast.ptl_y
  478. mov esi,[ebp].ptl_x ;remember last point in case we have
  479. mov edi,[ebp].ptl_y ; to continue to another record
  480. mov [esp].sf_ptfxLast.ptl_x,esi
  481. mov [esp].sf_ptfxLast.ptl_y,edi
  482. mov esi,[eax].ptl_x ;load up current end point
  483. mov edi,[eax].ptl_y
  484. mov [esp].sf_pptfx,eax
  485. jmp io_new_line
  486. ;/////////////////////////////////////////////////////////////////////
  487. ;// Next Line Stuff
  488. ;/////////////////////////////////////////////////////////////////////
  489. io_handle_closefigure:
  490. mov [esp].sf_pd.pd_flags,0
  491. mov ebx,[esp].sf_ptfxLast.ptl_x
  492. mov ecx,[esp].sf_ptfxLast.ptl_y
  493. mov esi,[esp].sf_ptfxStartFigure.ptl_x
  494. mov edi,[esp].sf_ptfxStartFigure.ptl_y
  495. jmp io_new_line
  496. ; Before getting the next path record, see if we have to do a closefigure:
  497. io_check_for_closefigure:
  498. test [esp].sf_pd.pd_flags,PD_CLOSEFIGURE
  499. jnz io_handle_closefigure
  500. mov esi,[esp].sf_bMore
  501. or esi,esi
  502. jnz io_next_record
  503. io_all_done:
  504. pop ebp
  505. cRet vIoFastLine
  506. ;/////////////////////////////////////////////////////////////////////
  507. ;// Output Integer Line
  508. ;/////////////////////////////////////////////////////////////////////
  509. ; ebx = x
  510. ; ecx = y
  511. ; esi = dx
  512. ; edi = dy
  513. ; [esp].sf_ulErrorTerm = incomplete error term (actual value is 'dx - bias')
  514. ; [esp].sf_ulCmd = draw command
  515. ; NOTE: The port values retrieved from the PDEV are word values, and word
  516. ; moves have a one cycle penalty. For this reason, the values should
  517. ; actually be defined as dwords in the PDEV...
  518. public io_int_output_line
  519. io_int_output_line::
  520. mov ebp,[esp].sf_ppdev ;ebp = ppdev
  521. cmp [esp].sf_bSetCP,0
  522. je short io_int_current_position_already_set
  523. mov edx,[ebp].pdev_ioGp_stat_cmd
  524. @@: in ax,dx
  525. and eax,FIFO_7_EMPTY ;wait for 7 entries
  526. jnz short @b
  527. mov edx,[ebp].pdev_ioCur_x
  528. mov eax,ebx
  529. add eax,[ebp].pdev_xOffset
  530. out dx,ax
  531. mov edx,[ebp].pdev_ioCur_y
  532. mov eax,ecx
  533. add eax,[ebp].pdev_yOffset
  534. out dx,ax
  535. mov [esp].sf_bSetCP,0 ;the next integer line in this figure
  536. ; will have the CP set correctly
  537. jmp short io_int_output_common
  538. io_int_current_position_already_set:
  539. mov edx,[ebp].pdev_ioGp_stat_cmd
  540. @@: in ax,dx
  541. and eax,FIFO_5_EMPTY ;wait for 5 entries
  542. jnz short @b
  543. public io_int_output_common
  544. io_int_output_common::
  545. cmp esi,[esp].sf_ulLastLength
  546. je short @f
  547. mov edx,[ebp].pdev_ioMaj_axis_pcnt
  548. mov eax,esi
  549. out dx,ax
  550. mov [esp].sf_ulLastLength,eax
  551. @@:
  552. mov edx,[ebp].pdev_ioDesty_axstp
  553. mov eax,edi
  554. out dx,ax ;axial = dy
  555. mov edx,[ebp].pdev_ioDestx_diastp
  556. sub eax,esi
  557. out dx,ax ;diag = dy - dx
  558. mov edx,[esp].sf_ulErrorTerm
  559. sar edx,1
  560. add eax,edx
  561. mov edx,[ebp].pdev_ioErr_term
  562. out dx,ax ;err = dy - dx + floor((dx - bias) / 2)
  563. mov edx,[ebp].pdev_ioGp_stat_cmd
  564. mov eax,[esp].sf_ulCmd
  565. out dx,ax ;draw that puppy
  566. ;/////////////////////////////////////////////////////////////////////
  567. ;// Main Loop
  568. ;/////////////////////////////////////////////////////////////////////
  569. public io_next_line
  570. io_next_line::
  571. mov eax,[esp].sf_pptfx
  572. cmp eax,[esp].sf_pptfxEnd
  573. jae io_check_for_closefigure
  574. mov ebx,[eax].ptl_x
  575. mov ecx,[eax].ptl_y
  576. mov esi,[eax+8].ptl_x
  577. mov edi,[eax+8].ptl_y
  578. add eax,8
  579. mov [esp].sf_pptfx,eax
  580. public io_new_line
  581. io_new_line::
  582. ; Octants are numbered as follows:
  583. ;
  584. ; \ 5 | 6 /
  585. ; \ | /
  586. ; 4 \ | / 7
  587. ; \ /
  588. ; -----+-----
  589. ; /|\
  590. ; 3 / | \ 0
  591. ; / | \
  592. ; / 2 | 1 \
  593. ;
  594. ; ebx = M0
  595. ; ecx = N0
  596. ; esi = M1 (dM)
  597. ; edi = N1 (dN)
  598. mov [esp].sf_ptfxStart.ptl_x,ebx
  599. mov [esp].sf_ptfxStart.ptl_y,ecx
  600. mov [esp].sf_ptfxEnd.ptl_x,esi
  601. mov [esp].sf_ptfxEnd.ptl_y,edi
  602. ;save points in case we have to punt
  603. mov eax,ebx
  604. or eax,ecx
  605. or eax,esi
  606. or eax,edi
  607. and eax,F-1
  608. jnz io_non_integer
  609. ;/////////////////////////////////////////////////////////////////////
  610. ;// Integer Lines
  611. ;/////////////////////////////////////////////////////////////////////
  612. sar ebx,FLOG2 ;x0
  613. sar ecx,FLOG2 ;y0
  614. sar esi,FLOG2 ;x1 (soon to be dx)
  615. sar edi,FLOG2 ;y1 (soon to be dy)
  616. sub esi,ebx
  617. jl io_int_2_3_4_5
  618. jz io_int_radial_90_270
  619. sub edi,ecx
  620. jl io_int_6_7
  621. jz io_int_radial_0
  622. cmp esi,edi
  623. jl io_int_1
  624. je io_int_radial_315
  625. io_int_0:
  626. lea ebp,[esi - 1]
  627. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
  628. mov [esp].sf_ulErrorTerm,ebp
  629. jmp io_int_output_line
  630. public io_int_1
  631. io_int_1::
  632. xchg esi,edi
  633. lea ebp,[esi - 1]
  634. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
  635. mov [esp].sf_ulErrorTerm,ebp
  636. jmp io_int_output_line
  637. public io_int_2_3_4_5
  638. io_int_2_3_4_5::
  639. neg esi
  640. sub edi,ecx
  641. jl io_int_4_5
  642. jz io_int_radial_180
  643. cmp esi,edi
  644. jl io_int_2
  645. je io_int_radial_225
  646. public io_int_3
  647. io_int_3::
  648. lea ebp,[esi - 1]
  649. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
  650. mov [esp].sf_ulErrorTerm,ebp
  651. jmp io_int_output_line
  652. public io_int_2
  653. io_int_2::
  654. xchg esi,edi
  655. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
  656. mov [esp].sf_ulErrorTerm,esi
  657. jmp io_int_output_line
  658. public io_int_4_5
  659. io_int_4_5::
  660. neg edi
  661. cmp esi,edi
  662. jl io_int_5
  663. je io_int_radial_135
  664. io_int_4:
  665. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
  666. mov [esp].sf_ulErrorTerm,esi
  667. jmp io_int_output_line
  668. public io_int_5
  669. io_int_5::
  670. xchg esi,edi
  671. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
  672. mov [esp].sf_ulErrorTerm,esi
  673. jmp io_int_output_line
  674. public io_int_6_7
  675. io_int_6_7::
  676. neg edi
  677. cmp esi,edi
  678. jg io_int_7
  679. je io_int_radial_45
  680. io_int_6:
  681. xchg esi,edi
  682. lea ebp,[esi - 1]
  683. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
  684. mov [esp].sf_ulErrorTerm,ebp
  685. jmp io_int_output_line
  686. public io_int_7
  687. io_int_7::
  688. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
  689. mov [esp].sf_ulErrorTerm,esi
  690. jmp io_int_output_line
  691. ;/////////////////////////////////////////////////////////////////////
  692. ;// Lines In The 8 Cardinal Directions
  693. ;/////////////////////////////////////////////////////////////////////
  694. public io_int_radial_45
  695. io_int_radial_45::
  696. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  697. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_45
  698. jmp short io_int_output_radial
  699. public io_int_radial_135
  700. io_int_radial_135::
  701. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  702. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_135
  703. jmp short io_int_output_radial
  704. public io_int_radial_225
  705. io_int_radial_225::
  706. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  707. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_225
  708. jmp short io_int_output_radial
  709. public io_int_radial_315
  710. io_int_radial_315::
  711. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  712. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_315
  713. jmp short io_int_output_radial
  714. public io_int_radial_90_270
  715. io_int_radial_90_270::
  716. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  717. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_270
  718. mov esi,edi
  719. sub esi,ecx
  720. jg short io_int_output_radial ;if top-to-bottom vertical line
  721. jz io_next_line ;if zero length line
  722. neg esi
  723. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  724. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_90
  725. jmp short io_int_output_radial
  726. public io_int_radial_180
  727. io_int_radial_180::
  728. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  729. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_180
  730. jmp short io_int_output_radial
  731. io_int_radial_0:
  732. mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
  733. LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_0
  734. ; ebx = x
  735. ; ecx = y
  736. ; esi = dx (length of radial line since it's normalized)
  737. ; ebp = drawing command
  738. public io_int_output_radial
  739. io_int_output_radial::
  740. mov edi,[esp].sf_ppdev
  741. cmp [esp].sf_bSetCP,0
  742. je short io_int_radial_continue_figure
  743. mov edx,[edi].pdev_ioGp_stat_cmd
  744. @@: in ax,dx
  745. and eax,FIFO_4_EMPTY
  746. jnz short @b
  747. mov edx,[edi].pdev_ioCur_x
  748. mov eax,ebx
  749. add eax,[edi].pdev_xOffset
  750. out dx,ax
  751. mov edx,[edi].pdev_ioCur_y
  752. mov eax,ecx
  753. add eax,[edi].pdev_yOffset
  754. out dx,ax
  755. mov [esp].sf_bSetCP,0 ;the next integer line in this figure
  756. ; will have the CP set correctly
  757. cmp esi,[esp].sf_ulLastLength
  758. je short @f
  759. mov edx,[edi].pdev_ioMaj_axis_pcnt
  760. mov eax,esi
  761. out dx,ax
  762. mov [esp].sf_ulLastLength,eax
  763. @@:
  764. mov edx,[edi].pdev_ioGp_stat_cmd
  765. mov eax,ebp
  766. out dx,ax
  767. jmp io_next_line
  768. ; Jump to here if we don't have to update the current position first:
  769. public io_int_radial_continue_figure
  770. io_int_radial_continue_figure::
  771. cmp esi,[esp].sf_ulLastLength
  772. je short io_int_radial_skip_length
  773. mov edx,[edi].pdev_ioGp_stat_cmd
  774. @@: in ax,dx
  775. and eax,FIFO_2_EMPTY
  776. jnz short @b
  777. mov edx,[edi].pdev_ioMaj_axis_pcnt
  778. mov eax,esi
  779. out dx,ax
  780. mov [esp].sf_ulLastLength,eax
  781. mov edx,[edi].pdev_ioGp_stat_cmd
  782. mov eax,ebp
  783. out dx,ax
  784. jmp io_next_line
  785. ; Jump to here if we don't have to update the current position or the
  786. ; line length variable:
  787. public io_int_radial_skip_length
  788. io_int_radial_skip_length::
  789. mov edx,[edi].pdev_ioGp_stat_cmd
  790. @@: in ax,dx
  791. and eax,FIFO_1_EMPTY
  792. jnz short @b
  793. mov edx,[edi].pdev_ioGp_stat_cmd
  794. mov eax,ebp
  795. out dx,ax
  796. jmp io_next_line
  797. ;/////////////////////////////////////////////////////////////////////
  798. ;// Non-Integer Lines
  799. ;/////////////////////////////////////////////////////////////////////
  800. public io_non_integer
  801. io_non_integer::
  802. sub esi,ebx
  803. jl io_not_int_2_3_4_5
  804. sub edi,ecx
  805. jl io_not_int_6_7
  806. cmp esi,edi
  807. jl io_not_int_1
  808. je io_not_int_0_slope_one
  809. io_not_int_0:
  810. cmp esi,MAX_GIQ_DELTA
  811. jg io_punt_line
  812. GIQ ROUND_X_AND_Y_DOWN
  813. io_not_int_0_common:
  814. add ecx,edi ;err += dN
  815. mov [esp].sf_ulErrorTerm,ecx
  816. mov eax,[esp].sf_x1
  817. sub eax,ebx
  818. jle io_next_line
  819. mov [esp].sf_cPels,eax
  820. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
  821. public io_not_int_x_major
  822. io_not_int_x_major::
  823. mov ecx,[esp].sf_ppdev
  824. mov edx,[ecx].pdev_ioGp_stat_cmd
  825. @@: in ax,dx
  826. and eax,FIFO_7_EMPTY ;wait for 7 entries
  827. jnz short @b
  828. ; Fractional end-point lines aren't usually going to have the current
  829. ; position already set correctly, so we explicitly set the CP each time:
  830. mov edx,[ecx].pdev_ioCur_x
  831. mov eax,ebx
  832. add eax,[ecx].pdev_xOffset
  833. out dx,ax ;x0
  834. mov edx,[ecx].pdev_ioCur_y
  835. mov eax,ebp
  836. add eax,[ecx].pdev_yOffset
  837. out dx,ax ;y0
  838. mov [esp].sf_bSetCP,1 ;the next integer line in this figure
  839. ; will not necessarily have the CP set
  840. ; correctly
  841. mov eax,[esp].sf_cPels
  842. cmp eax,[esp].sf_ulLastLength
  843. je short @f
  844. mov edx,[ecx].pdev_ioMaj_axis_pcnt
  845. out dx,ax ;length = cPels
  846. mov [esp].sf_ulLastLength,eax
  847. @@:
  848. mov edx,[ecx].pdev_ioDesty_axstp
  849. mov eax,edi
  850. out dx,ax ;axial = dN
  851. mov edx,[ecx].pdev_ioDestx_diastp
  852. sub eax,esi
  853. out dx,ax ;diag = dN - dM
  854. mov edx,[ecx].pdev_ioErr_term
  855. mov eax,[esp].sf_ulErrorTerm
  856. out dx,ax ;error term
  857. mov edx,[ecx].pdev_ioGp_stat_cmd
  858. mov eax,[esp].sf_ulCmd
  859. out dx,ax ;output it
  860. jmp io_next_line
  861. ; Lines of slope one have a special rounding rule: when the line
  862. ; runs exactly half way between two pixels, the upper or right pel
  863. ; is illuminated. This translates into x=1/2 rounding up in value,
  864. ; and y=1/2 rounding down:
  865. public io_not_int_0_slope_one
  866. io_not_int_0_slope_one::
  867. or esi,esi
  868. jz io_next_line ;quick check for a zero length GIQ line
  869. cmp esi,MAX_GIQ_DELTA
  870. jg io_punt_line
  871. GIQ ROUND_Y_DOWN_SLOPE_ONE
  872. jmp io_not_int_0_common
  873. public io_not_int_1
  874. io_not_int_1::
  875. cmp edi,MAX_GIQ_DELTA
  876. jg io_punt_line
  877. GIQR ROUND_X_AND_Y_DOWN
  878. add ecx,esi ;err += dM
  879. mov [esp].sf_ulErrorTerm,ecx
  880. mov eax,[esp].sf_y1
  881. sub eax,ebp
  882. jle io_next_line
  883. mov [esp].sf_cPels,eax
  884. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
  885. public io_not_int_y_major
  886. io_not_int_y_major::
  887. mov ecx,[esp].sf_ppdev
  888. mov edx,[ecx].pdev_ioGp_stat_cmd
  889. @@: in ax,dx
  890. and eax,FIFO_7_EMPTY ;wait for 7 entries
  891. jnz short @b
  892. ; Fractional end-point lines aren't usually going to have the current
  893. ; position already set correctly, so we explicitly set the CP each time:
  894. mov edx,[ecx].pdev_ioCur_x
  895. mov eax,ebx
  896. add eax,[ecx].pdev_xOffset
  897. out dx,ax ;x0
  898. mov edx,[ecx].pdev_ioCur_y
  899. mov eax,ebp
  900. add eax,[ecx].pdev_yOffset
  901. out dx,ax ;y0
  902. mov [esp].sf_bSetCP,1 ;the next integer line in this figure
  903. ; will not necessarily have the CP set
  904. ; correctly
  905. mov eax,[esp].sf_cPels
  906. cmp eax,[esp].sf_ulLastLength
  907. je short @f
  908. mov edx,[ecx].pdev_ioMaj_axis_pcnt
  909. out dx,ax ;length = cPels
  910. mov [esp].sf_ulLastLength,eax
  911. @@:
  912. mov edx,[ecx].pdev_ioDesty_axstp
  913. mov eax,esi
  914. out dx,ax ;axial = dM
  915. mov edx,[ecx].pdev_ioDestx_diastp
  916. sub eax,edi
  917. out dx,ax ;diag = dM - dN
  918. mov edx,[ecx].pdev_ioErr_term
  919. mov eax,[esp].sf_ulErrorTerm
  920. out dx,ax ;error term
  921. mov edx,[ecx].pdev_ioGp_stat_cmd
  922. mov eax,[esp].sf_ulCmd
  923. out dx,ax ;output it
  924. jmp io_next_line
  925. public io_not_int_2_3_4_5
  926. io_not_int_2_3_4_5::
  927. neg esi ;dM = -dM (now positive)
  928. neg ebx ;M0 = -M0
  929. sub edi,ecx
  930. jl io_not_int_4_5
  931. cmp esi,edi
  932. jl io_not_int_2
  933. io_not_int_3:
  934. cmp esi,MAX_GIQ_DELTA
  935. jg io_punt_line
  936. GIQ ROUND_Y_DOWN
  937. add ecx,edi ;err += dN
  938. mov [esp].sf_ulErrorTerm,ecx
  939. neg ebx ;untransform x0
  940. mov eax,[esp].sf_x1
  941. add eax,ebx
  942. jle io_next_line
  943. mov [esp].sf_cPels,eax
  944. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
  945. jmp io_not_int_x_major
  946. public io_not_int_2
  947. io_not_int_2::
  948. cmp edi,MAX_GIQ_DELTA
  949. jg io_punt_line
  950. GIQR ROUND_Y_DOWN
  951. add ecx,esi ;err += dM
  952. mov [esp].sf_ulErrorTerm,ecx
  953. neg ebx ;untransform x0
  954. mov eax,[esp].sf_y1
  955. sub eax,ebp
  956. jle io_next_line
  957. mov [esp].sf_cPels,eax
  958. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
  959. jmp io_not_int_y_major
  960. public io_not_int_4_5
  961. io_not_int_4_5::
  962. neg edi ;dN = -dN (now positive)
  963. neg ecx ;N0 = -N0
  964. cmp esi,edi
  965. jl io_not_int_5
  966. je io_not_int_4_slope_one
  967. public io_not_int_4
  968. io_not_int_4::
  969. cmp esi,MAX_GIQ_DELTA
  970. jg io_punt_line
  971. GIQ 0
  972. io_not_int_4_common:
  973. add ecx,edi ;err += dN
  974. mov [esp].sf_ulErrorTerm,ecx
  975. neg ebx ;untransform x0
  976. neg ebp ;untransform y0
  977. mov eax,[esp].sf_x1
  978. add eax,ebx
  979. jle io_next_line
  980. mov [esp].sf_cPels,eax
  981. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
  982. jmp io_not_int_x_major
  983. ; Lines of slope one have a special rounding rule.
  984. public io_not_int_4_slope_one
  985. io_not_int_4_slope_one::
  986. cmp esi,MAX_GIQ_DELTA
  987. jg io_punt_line
  988. GIQ ROUND_X_DOWN_SLOPE_ONE
  989. jmp io_not_int_4_common
  990. public io_not_int_5
  991. io_not_int_5::
  992. cmp edi,MAX_GIQ_DELTA
  993. jg io_punt_line
  994. GIQR 0
  995. add ecx,esi ;err += dM
  996. mov [esp].sf_ulErrorTerm,ecx
  997. neg ebx ;untransform x0
  998. neg ebp ;untransform y0
  999. mov eax,[esp].sf_y1
  1000. add eax,ebp
  1001. jle io_next_line
  1002. mov [esp].sf_cPels,eax
  1003. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
  1004. jmp io_not_int_y_major
  1005. public io_not_int_6_7
  1006. io_not_int_6_7::
  1007. neg edi ;dN = -dN (now positive)
  1008. neg ecx ;M0 = -M0
  1009. cmp esi,edi
  1010. je io_not_int_7_slope_one
  1011. jg io_not_int_7
  1012. public io_not_int_6
  1013. io_not_int_6::
  1014. cmp edi,MAX_GIQ_DELTA
  1015. jg io_punt_line
  1016. GIQR ROUND_X_DOWN
  1017. add ecx,esi ;err += dM
  1018. mov [esp].sf_ulErrorTerm,ecx
  1019. neg ebp ;untransform y0
  1020. mov eax,[esp].sf_y1
  1021. add eax,ebp
  1022. jle io_next_line
  1023. mov [esp].sf_cPels,eax
  1024. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
  1025. jmp io_not_int_y_major
  1026. public io_not_int_7
  1027. io_not_int_7::
  1028. cmp esi,MAX_GIQ_DELTA
  1029. jg io_punt_line
  1030. GIQ ROUND_X_DOWN
  1031. io_not_int_7_common:
  1032. add ecx,edi ;err += dN
  1033. mov [esp].sf_ulErrorTerm,ecx
  1034. neg ebp ;untransform y0
  1035. mov eax,[esp].sf_x1
  1036. sub eax,ebx
  1037. jle io_next_line
  1038. mov [esp].sf_cPels,eax
  1039. mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
  1040. jmp io_not_int_x_major
  1041. public io_not_int_7_slope_one
  1042. io_not_int_7_slope_one::
  1043. cmp esi,MAX_GIQ_DELTA
  1044. jg io_punt_line
  1045. GIQ ROUND_X_DOWN_SLOPE_ONE
  1046. jmp io_not_int_7_common
  1047. ;/////////////////////////////////////////////////////////////////////
  1048. ;// Punt Line
  1049. ;/////////////////////////////////////////////////////////////////////
  1050. ; If the line is too long, we punt to our strip drawing routine.
  1051. public io_punt_line
  1052. io_punt_line::
  1053. mov esi,esp
  1054. lea eax,[esp].sf_ptfxStart
  1055. lea ebx,[esp].sf_ptfxEnd
  1056. cCall bLines,<[esi].sf_ppdev, eax, ebx, 0, 1, 0, \
  1057. [esi].sf_prclClip, [esi].sf_apfn, [esi].sf_flags>
  1058. mov [esp].sf_bSetCP,1 ;Always reset CP after punting
  1059. mov [esp].sf_ulLastLength,-1;Always reset line length after punting
  1060. jmp io_next_line
  1061. endProc vIoFastLine
  1062. end