|
|
1.1 root 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-1993 Microsoft Corporation
67: ;-----------------------------------------------------------------------;
68:
69: .386
70:
71: .model small,c
72:
73: assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
74: assume fs:nothing,gs:nothing
75:
76: .xlist
77: include stdcall.inc ;calling convention cmacros
78: include i386\strucs.inc
79: include i386\lines.inc
80: .list
81:
82: ; Line coordinates are given in 28.4 fixed point format:
83:
84: F equ 16
85: FLOG2 equ 4
86:
87: ; The S3's hardware can have 13 bits of significance for the error and
88: ; step terms:
89:
90: NUM_DDA_BITS equ 13
91:
92: ; GIQ lines have to dedicate 4 bits to the fractional term, so the largest
93: ; delta we can handle for GIQ lines is calculated as follows (scaled by F
94: ; so we can do the test in GIQ coordinates), and remembering that one bit
95: ; has to be used as a sign bit:
96:
97: MAX_GIQ_DELTA equ (((1 shl (NUM_DDA_BITS - 5)) - 1) * F)
98:
99: ; The following values must match those in winddi.h!
100:
101: PD_BEGINSUBPATH equ 00000001h
102: PD_ENDSUBPATH equ 00000002h
103: PD_RESETSTYLE equ 00000004h
104: PD_CLOSEFIGURE equ 00000008h
105: PD_BEZIERS equ 00000010h
106:
107: PATHDATA struc
108:
109: pd_flags dd ?
110: pd_count dd ?
111: pd_pptfx dd ?
112:
113: PATHDATA ends
114:
115: ;-------------------------------------------------------------------------;
116: ; I felt a compelling need to use 'ebp' as a 7th general register, and we
117: ; have no nifty macros for dereferencing frame variables off 'esp'. So
118: ; with this structure I am rolling my own stack frame:
119:
120: STATE_MEM_SIZE equ 4 ;4 dwords
121: PROC_MEM_SIZE equ 7 ;7 dwords
122:
123: STACK_FRAME struc
124:
125: ; State variables (don't add/delete fields without modifying STATE_MEM_SIZE!)
126:
127: sf_ulOurEbp dd ? ;useful for debugging
128: sf_ulOriginalEbx dd ?
129: sf_ulOriginalEdi dd ?
130: sf_ulOriginalEsi dd ?
131:
132: ; Frame variables (feel free to add/delete fields):
133:
134: sf_y0 dd ? ;GIQ variables
135: sf_y1 dd ?
136: sf_x1 dd ?
137: sf_ptlOrg db (size POINTL) dup (?)
138: ;our origin for normalizing the line
139: sf_ptfxLast db (size POINTL) dup (?)
140: ;the most recent point
141: sf_ptfxStartFigure db (size POINTL) dup (?)
142: ;the figure's 1st point
143: sf_bMore dd ? ;more path records to get?
144: sf_pptfxEnd dd ? ;points to last point in record
145: sf_pptfx dd ? ;points to current point
146: sf_pd db (size PATHDATA) dup (?)
147: ;pathdata structure
148: sf_ptfxStart db (size POINTL) dup (?)
149: ;temporary spot for saving start point
150: sf_ptfxEnd db (size POINTL) dup (?)
151: ;temporary spot for saving end point
152: sf_ulCmd dd ? ;S3 draw command
153: sf_cPels dd ? ;length of line in pels
154: sf_bSetCP dd ? ;1 if first line in figure, 0 otherwise
155: sf_ulLastLength dd ? ;last value set as LINE_MAX
156: sf_ulErrorTerm dd ? ;error term for line
157:
158: ; Procedure variables (don't add/delete fields without modifying
159: ; PROC_MEM_SIZE!)
160:
161: sf_ulOriginalEbp dd ?
162: sf_ulOriginalReturn dd ?
163: sf_ppdev dd ?
164: sf_ppo dd ?
165: sf_prclClip dd ?
166: sf_apfn dd ?
167: sf_flags dd ?
168:
169: STACK_FRAME ends
170:
171: .data
172:
173: EXTRNP PATHOBJ_bEnum,8
174: EXTRNP bLines,36
175:
176: .code
177:
178: ROUND_X_DOWN equ 01h
179: ROUND_Y_DOWN equ 02h
180: ROUND_SLOPE_ONE equ 04h
181: ROUND_X_AND_Y_DOWN equ (ROUND_X_DOWN + ROUND_Y_DOWN)
182: ROUND_X_DOWN_SLOPE_ONE equ (ROUND_X_DOWN + ROUND_SLOPE_ONE)
183: ROUND_Y_DOWN_SLOPE_ONE equ (ROUND_Y_DOWN + ROUND_SLOPE_ONE)
184:
185: ;--------------------------------Macro----------------------------------;
186: ; GIQ flags
187: ;
188: ; This macros computes the start pixel, the number of pixels to
189: ; be lit, and the initial error term given a GIQ line. The line must
190: ; have already been normalized such that dM >= dN, dN >= 0.
191: ;
192: ; Input: ebx - M0
193: ; ecx - N0
194: ; esi - dM
195: ; edi - dN
196: ; Trashes:
197: ; eax, edx
198: ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
199: ; Output:
200: ; [esp].sf_x1 - x-coordinate of last pixel (exclusive)
201: ; ebx - x-coordinate of first pixel
202: ; ecx - error term
203: ; esi - dM
204: ; edi - dN
205: ; ebp - y-coordinate of first pixel
206: ;-----------------------------------------------------------------------;
207:
208: GIQ macro flags
209: local compute_x1, compute_error_term
210:
211: ; We normalize our coordinate system so that if the start point is
212: ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
213:
214: mov eax,ebx
215: mov ebp,ecx
216: sar eax,FLOG2
217: sar ebp,FLOG2
218: mov [esp].sf_ptlOrg.ptl_x,eax
219: ;ptlOrg.x = floor(M0 / F)
220: mov [esp].sf_ptlOrg.ptl_y,ebp
221: ;ptlOrg.y = floor(N0 / F)
222:
223: ; Calculate the correct [esp].sf_x1:
224:
225: lea ebp,[ecx + edi] ;ebp = N1
226: and ebp,F - 1
227:
228: if (flags AND ROUND_X_DOWN)
229:
230: if (flags AND ROUND_SLOPE_ONE)
231: lea edx,[ebx + esi]
232: and edx,F - 1 ;edx = M1
233: sub edx,8
234: cmp edx,ebp ;cmp M1 - 8, N1
235: jne short compute_x1
236:
237: lea edx,[ebx + esi]
238: sar edx,FLOG2
239: jmp short compute_error_term
240: endif
241:
242: compute_x1:
243:
244: cmp ebp,1
245: sbb ebp,8 ;N1 -= 8
246: else
247: sub ebp,8 ;N1 -= 8
248: endif
249: sbb eax,eax
250: xor ebp,eax
251: sub ebp,eax ;N1 = ABS(N1)
252:
253: lea edx,[ebx + esi]
254: mov eax,edx
255: sar edx,FLOG2
256: and eax,F - 1
257: jz short @f ;special case for M1 == 0
258: cmp eax,ebp ;cmp M1, N1
259: sbb edx,-1 ;edx is now one pixel past the actual
260: @@: ; end coordinate (note that it hasn't
261: ; been affected by the origin shift)
262:
263: compute_error_term:
264:
265: ; ebx = M0
266: ; ecx = N0
267: ; edx = x1
268: ; esi = dM
269: ; edi = dN
270:
271: and ecx,F - 1
272: mov [esp].sf_x1,edx ;save x1
273:
274: ; Calculate our error term for x = 0.
275: ;
276: ; NOTE: Since this routine is used only for lines that are unclipped, we
277: ; are guaranteed by our screen size that the values will be far less
278: ; than 32 bits in significance, and so we don't worry about overflow.
279: ; If this is used for clipped lines, these multiplies will have to
280: ; be converted to give 64 bit results, because we can have 36 bits of
281: ; significance!
282:
283:
284: lea ebp,[ecx + 8] ;ebp = N0 + 8
285: mov eax,esi
286: imul eax,ebp ;eax = dM * (N0 + 8)
287: mov ebp,edi
288:
289: ; We have to special case when M0 is 0 -- we know x0 will be zero.
290: ; So we jump ahead a bit to a place where 'ebx' is assumed to contain
291: ; x0 -- and it just so happens 'ebx' is zero in this case:
292:
293: and ebx,F - 1
294: jz short @f
295: imul ebp,ebx ;ebp = dN * M0
296: sub eax,ebp
297:
298: ; Calculate the x-coordinate of the first pixel:
299:
300: if (flags AND ROUND_X_DOWN)
301:
302: if (flags AND ROUND_SLOPE_ONE)
303: cmp ecx,8
304: sbb ecx,-1
305: endif
306:
307: cmp ecx,1
308: sbb ecx,8 ;N0 -= 8
309: else
310: sub ecx,8 ;N0 -= 8
311: endif
312: sbb ebp,ebp
313: xor ecx,ebp
314: sub ecx,ebp ;N0 = ABS(N0)
315: cmp ebx,ecx
316: sbb ebx,ebx
317: not ebx ;ebx = -x0
318:
319: ; Now adjust the error term accordingly:
320:
321: @@:
322: if (flags AND ROUND_Y_DOWN)
323: dec eax
324: endif
325: sar eax,FLOG2 ;eax = floor((N0 + 8) dM - M0 dN] / 16)
326:
327: mov ecx,[esp].sf_ptlOrg.ptl_x
328: mov ebp,[esp].sf_ptlOrg.ptl_y
329:
330: sub ecx,ebx ;ecx = ptlOrg.ptl_x + x0
331:
332: and ebx,edi
333: add ebx,eax
334: sub ebx,esi ;ebx = dN * x0 + initial error - dM
335: jl short @f ;if the error term >= 0, we have to
336: sub ebx,esi ; add 1 to the y position and subtract
337: inc ebp ; dM off again
338: @@:
339: xchg ebx,ecx
340:
341: endm
342:
343: ;--------------------------------Macro----------------------------------;
344: ; GIQR flags
345: ;
346: ; Same as above, except it handles flips about the line x = y.
347: ;
348: ; Input: ebx - M0
349: ; ecx - N0
350: ; esi - dM
351: ; edi - dN
352: ; Trashes:
353: ; eax, edx
354: ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
355: ; Output:
356: ; [esp].sf_y1 - y-coordinate of last pixel (exclusive)
357: ; ebx - x-coordinate of first pixel
358: ; ecx - error term
359: ; esi - dM
360: ; edi - dN
361: ; ebp - y-coordinate of first pixel
362: ;-----------------------------------------------------------------------;
363:
364: GIQR macro flags
365:
366: ; We normalize our coordinate system so that if the start point is
367: ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
368:
369: mov eax,ebx
370: mov ebp,ecx
371: sar eax,FLOG2
372: sar ebp,FLOG2
373: mov [esp].sf_ptlOrg.ptl_x,eax
374: ;ptlOrg.x = floor(M0 / F)
375: mov [esp].sf_ptlOrg.ptl_y,ebp
376: ;ptlOrg.y = floor(N0 / F)
377:
378: ; Calculate the correct [esp].sf_y1:
379:
380: lea ebp,[ebx + esi] ;ebp = M1
381: and ebp,F - 1
382:
383: if (flags AND ROUND_Y_DOWN)
384: cmp ebp,1
385: sbb ebp,8 ;M1 -= 8
386: else
387: sub ebp,8 ;M1 -= 8
388: endif
389: sbb eax,eax
390: xor ebp,eax
391: sub ebp,eax ;M1 = ABS(M1)
392:
393: lea edx,[ecx + edi]
394: mov eax,edx
395: sar edx,FLOG2
396: and eax,F - 1
397: jz short @f ;special case for N1 == 0
398: cmp eax,ebp ;cmp N1, M1
399: sbb edx,-1 ;edx is now one pixel past the actual
400: @@: ; end coordinate (note that it hasn't
401: ; been affected by the origin shift)
402: and ebx,F - 1
403: mov [esp].sf_y1,edx
404:
405: ; Calculate our error term for y = 0.
406: ;
407: ; NOTE: Since this routine is used only for lines that are unclipped, we
408: ; are guaranteed by our screen size that the values will be far less
409: ; than 32 bits in significance, and so we don't worry about overflow.
410: ; If this is used for clipped lines, these multiplies will have to
411: ; be converted to give 64 bit results, because we can have 36 bits of
412: ; significance!
413:
414: lea ebp,[ebx + 8] ;ebp = M0 + 8
415: mov eax,edi
416: imul eax,ebp ;eax = dN * (M0 + 8)
417: mov ebp,esi
418:
419: ; We have to special case when N0 is 0 -- we know y0 will be zero.
420: ; So we jump ahead a bit to a place where 'ecx' is assumed to contain
421: ; y0 -- and it just so happens 'ecx' is zero in this case:
422:
423: and ecx,F - 1
424: jz short @f
425: imul ebp,ecx ;ebp = dM * N0
426: sub eax,ebp
427:
428: ; Calculate the x-coordinate of the first pixel:
429:
430: if (flags AND ROUND_Y_DOWN)
431: cmp ebx,1
432: sbb ebx,8 ;M0 -= 8
433: else
434: sub ebx,8 ;M0 -= 8
435: endif
436: sbb ebp,ebp
437: xor ebx,ebp
438: sub ebx,ebp ;M0 = ABS(M0)
439: cmp ecx,ebx
440: sbb ecx,ecx
441: not ecx ;ecx = -y0
442:
443: ; Now adjust the error term accordingly:
444:
445: @@:
446: if (flags AND ROUND_X_DOWN)
447: dec eax
448: endif
449: sar eax,FLOG2 ;eax = floor((M0 + 8) dN - N0 dM] / 16)
450:
451: mov ebx,[esp].sf_ptlOrg.ptl_x
452: mov ebp,[esp].sf_ptlOrg.ptl_y
453:
454: sub ebp,ecx ;ebp = ptlOrg.ptl_y + y0
455:
456: and ecx,esi
457: add ecx,eax
458: sub ecx,edi ;ecx = dM * x0 + initial error - dN
459: jl short @f ;if the error term >= 0, we have to
460: sub ecx,edi ; add 1 to the x position and subtract
461: inc ebx ; dN off again
462: @@:
463:
464: endm
465:
466: ;---------------------------Public-Routine------------------------------;
467: ; vFastLine(ppdev, ppo, prclClip, apfn, flags)
468: ;
469: ; Draws fast lines. Or at least attempts to.
470: ;
471: ; Input:
472: ;
473: ; ppdev - PDEV pointer
474: ; ppo - path
475: ; prclClip - pointer to rectangle array for passing to 'bLines'
476: ; apfn - pointer to strip routines for passing to 'bLines'
477: ; flags - status flag for passing to 'bLines'
478: ;
479: ;-----------------------------------------------------------------------;
480:
481: ; NOTE: Don't go changing parameters without also changing STACK_FRAME!
482:
483: cProc vFastLine,20,< \
484: uses esi edi ebx, \
485: ebp_ppdev: ptr, \
486: ebp_ppo: ptr, \
487: ebp_prclClip: ptr, \
488: ebp_apfn: ptr, \
489: ebp_flags: dword >
490:
491: ; Leave room for our stack frame.
492: ;
493: ; NOTE: Don't add local variables here -- you can't reference them with
494: ; ebp anyway! Add them to the STACK_FRAME structure.
495:
496: local aj[(size STACK_FRAME) - 4 * (STATE_MEM_SIZE + PROC_MEM_SIZE)]: byte
497:
498: ; We save 'ebp' on the stack (note that STACK_FRAME accounts for this push):
499:
500: push ebp
501:
502: mov [esp].sf_ulLastLength,-1;make sure first line in path resets
503: ; LINE_MAX value
504:
505: ; Now get some path stuff:
506:
507: next_record:
508:
509: mov eax,[esp].sf_ppo
510: lea ebx,[esp].sf_pd
511: cCall PATHOBJ_bEnum,<eax,ebx>
512: mov [esp].sf_bMore,eax ;save away return code for later
513:
514: mov ebx,[esp].sf_pd.pd_count;if 0 points in record, get outta here
515: or ebx,ebx
516: jz check_for_closefigure
517:
518: lea ebp,[8 * ebx - 8]
519: add ebp,[esp].sf_pd.pd_pptfx
520: mov [esp].sf_pptfxEnd,ebp ;points to last point in record
521:
522: mov ecx,[esp].sf_pd.pd_flags
523: test ecx,PD_BEGINSUBPATH
524: jz short continue_subpath
525:
526: ; Handle a new sub-path:
527:
528: mov eax,[esp].sf_pd.pd_pptfx
529: add eax,8
530: mov [esp].sf_pptfx,eax
531:
532: mov esi,[ebp].ptl_x ;remember last point in case we have
533: mov edi,[ebp].ptl_y ; to continue to another record
534: mov [esp].sf_ptfxLast.ptl_x,esi
535: mov [esp].sf_ptfxLast.ptl_y,edi
536:
537: mov ebx,[eax - 8].ptl_x ;load up current start and end point
538: mov ecx,[eax - 8].ptl_y
539: mov esi,[eax].ptl_x
540: mov edi,[eax].ptl_y
541: mov [esp].sf_ptfxStartFigure.ptl_x,ebx
542: mov [esp].sf_ptfxStartFigure.ptl_y,ecx
543:
544: mov [esp].sf_bSetCP,1 ;this line is first in figure
545:
546: cmp eax,[esp].sf_pptfxEnd ;we have to be careful when the only
547: ; point in the record is the start-
548: ; figure point (pretty rare)
549: jbe new_line
550: jmp short next_record
551:
552: continue_subpath:
553:
554: ; This record continues the path:
555:
556: mov eax,[esp].sf_pd.pd_pptfx
557: mov ebx,[esp].sf_ptfxLast.ptl_x ;load up current start point
558: mov ecx,[esp].sf_ptfxLast.ptl_y
559:
560: mov esi,[ebp].ptl_x ;remember last point in case we have
561: mov edi,[ebp].ptl_y ; to continue to another record
562: mov [esp].sf_ptfxLast.ptl_x,esi
563: mov [esp].sf_ptfxLast.ptl_y,edi
564:
565: mov esi,[eax].ptl_x ;load up current end point
566: mov edi,[eax].ptl_y
567: mov [esp].sf_pptfx,eax
568:
569: jmp new_line
570:
571: ;/////////////////////////////////////////////////////////////////////
572: ;// Next Line Stuff
573: ;/////////////////////////////////////////////////////////////////////
574:
575: handle_closefigure:
576: mov [esp].sf_pd.pd_flags,0
577: mov ebx,[esp].sf_ptfxLast.ptl_x
578: mov ecx,[esp].sf_ptfxLast.ptl_y
579: mov esi,[esp].sf_ptfxStartFigure.ptl_x
580: mov edi,[esp].sf_ptfxStartFigure.ptl_y
581:
582: jmp new_line
583:
584: ; Before getting the next path record, see if we have to do a closefigure:
585:
586: check_for_closefigure:
587: test [esp].sf_pd.pd_flags,PD_CLOSEFIGURE
588: jnz handle_closefigure
589: mov esi,[esp].sf_bMore
590: or esi,esi
591: jnz next_record
592:
593: all_done:
594:
595: pop ebp
596: cRet vFastLine
597:
598: ;/////////////////////////////////////////////////////////////////////
599: ;// Output Integer Line
600: ;/////////////////////////////////////////////////////////////////////
601:
602: ; ebx = x
603: ; ecx = y
604: ; esi = dx
605: ; edi = dy
606: ; [esp].sf_ulErrorTerm = incomplete error term (actual value is 'dx - bias')
607: ; [esp].sf_ulCmd = draw command
608:
609: ; NOTE: The port values retrieved from the PDEV are word values, and word
610: ; moves have a one cycle penalty. For this reason, the values should
611: ; actually be defined as dwords in the PDEV...
612:
613: public int_output_line
614: int_output_line:
615: mov ebp,[esp].sf_ppdev ;ebp = ppdev
616: cmp [esp].sf_bSetCP,0
617: je short int_current_position_already_set
618:
619: mov dx,[ebp].pdev_gp_stat
620: @@: in ax,dx
621: and eax,FIFO_7_EMPTY ;wait for 7 entries
622: jnz short @b
623:
624: mov dx,[ebp].pdev_cur_x
625: mov eax,ebx
626: out dx,ax
627:
628: mov dx,[ebp].pdev_cur_y
629: mov eax,ecx
630: out dx,ax
631:
632: mov [esp].sf_bSetCP,0 ;the next integer line in this figure
633: ; will have the CP set correctly
634:
635: jmp short int_output_common
636:
637: int_current_position_already_set:
638: mov dx,[ebp].pdev_gp_stat
639: @@: in ax,dx
640: and eax,FIFO_5_EMPTY ;wait for 5 entries
641: jnz short @b
642:
643: public int_output_common
644: int_output_common:
645: cmp esi,[esp].sf_ulLastLength
646: je short @f
647:
648: mov dx,[ebp].pdev_line_max
649: mov eax,esi
650: out dx,ax
651: mov [esp].sf_ulLastLength,eax
652: @@:
653: mov dx,[ebp].pdev_axstp
654: mov eax,edi
655: out dx,ax ;axial = dy
656:
657: mov dx,[ebp].pdev_diastp
658: sub eax,esi
659: out dx,ax ;diag = dy - dx
660:
661: mov edx,[esp].sf_ulErrorTerm
662: sar edx,1
663: add eax,edx
664: mov dx,[ebp].pdev_err_term
665: out dx,ax ;err = dy - dx + floor((dx - bias) / 2)
666:
667: mov dx,[ebp].pdev_cmd
668: mov eax,[esp].sf_ulCmd
669: out dx,ax ;draw that puppy
670:
671: ;/////////////////////////////////////////////////////////////////////
672: ;// Main Loop
673: ;/////////////////////////////////////////////////////////////////////
674:
675: public next_line
676: next_line:
677: mov eax,[esp].sf_pptfx
678: cmp eax,[esp].sf_pptfxEnd
679: jae check_for_closefigure
680:
681: mov ebx,[eax].ptl_x
682: mov ecx,[eax].ptl_y
683: mov esi,[eax+8].ptl_x
684: mov edi,[eax+8].ptl_y
685: add eax,8
686: mov [esp].sf_pptfx,eax
687:
688: public new_line
689: new_line:
690:
691: ; Octants are numbered as follows:
692: ;
693: ; \ 5 | 6 /
694: ; \ | /
695: ; 4 \ | / 7
696: ; \ /
697: ; -----+-----
698: ; /|\
699: ; 3 / | \ 0
700: ; / | \
701: ; / 2 | 1 \
702: ;
703:
704: ; ebx = M0
705: ; ecx = N0
706: ; esi = M1 (dM)
707: ; edi = N1 (dN)
708:
709: mov [esp].sf_ptfxStart.ptl_x,ebx
710: mov [esp].sf_ptfxStart.ptl_y,ecx
711: mov [esp].sf_ptfxEnd.ptl_x,esi
712: mov [esp].sf_ptfxEnd.ptl_y,edi
713: ;save points in case we have to punt
714: mov eax,ebx
715: or eax,ecx
716: or eax,esi
717: or eax,edi
718: and eax,F-1
719: jnz non_integer
720:
721: ;/////////////////////////////////////////////////////////////////////
722: ;// Integer Lines
723: ;/////////////////////////////////////////////////////////////////////
724:
725: sar ebx,FLOG2 ;x0
726: sar ecx,FLOG2 ;y0
727: sar esi,FLOG2 ;x1 (soon to be dx)
728: sar edi,FLOG2 ;y1 (soon to be dy)
729:
730: sub esi,ebx
731: jl int_2_3_4_5
732: jz int_radial_90_270
733: sub edi,ecx
734: jl int_6_7
735: jz int_radial_0
736: cmp esi,edi
737: jl int_1
738: je int_radial_315
739:
740: int_0:
741: lea ebp,[esi - 1]
742: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
743: mov [esp].sf_ulErrorTerm,ebp
744:
745: jmp int_output_line
746:
747: public int_1
748: int_1:
749: xchg esi,edi
750: lea ebp,[esi - 1]
751: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
752: mov [esp].sf_ulErrorTerm,ebp
753:
754: jmp int_output_line
755:
756: public int_2_3_4_5
757: int_2_3_4_5:
758: neg esi
759: sub edi,ecx
760: jl int_4_5
761: jz int_radial_180
762: cmp esi,edi
763: jl int_2
764: je int_radial_225
765:
766: public int_3
767: int_3:
768: lea ebp,[esi - 1]
769: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
770: mov [esp].sf_ulErrorTerm,ebp
771:
772: jmp int_output_line
773:
774: public int_2
775: int_2:
776: xchg esi,edi
777: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
778: mov [esp].sf_ulErrorTerm,esi
779:
780: jmp int_output_line
781:
782: public int_4_5
783: int_4_5:
784: neg edi
785: cmp esi,edi
786: jl int_5
787: je int_radial_135
788:
789: int_4:
790: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
791: mov [esp].sf_ulErrorTerm,esi
792:
793: jmp int_output_line
794:
795: public int_5
796: int_5:
797: xchg esi,edi
798: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
799: mov [esp].sf_ulErrorTerm,esi
800:
801: jmp int_output_line
802:
803: public int_6_7
804: int_6_7:
805: neg edi
806: cmp esi,edi
807: jg int_7
808: je int_radial_45
809:
810: int_6:
811: xchg esi,edi
812: lea ebp,[esi - 1]
813: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
814: mov [esp].sf_ulErrorTerm,ebp
815:
816: jmp int_output_line
817:
818: public int_7
819: int_7:
820: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
821: mov [esp].sf_ulErrorTerm,esi
822:
823: jmp int_output_line
824:
825: ;/////////////////////////////////////////////////////////////////////
826: ;// Lines In The 8 Cardinal Directions
827: ;/////////////////////////////////////////////////////////////////////
828:
829: public int_radial_45
830: int_radial_45:
831: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
832: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_45
833: jmp short int_output_radial
834:
835: public int_radial_135
836: int_radial_135:
837: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
838: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_135
839: jmp short int_output_radial
840:
841: public int_radial_225
842: int_radial_225:
843: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
844: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_225
845: jmp short int_output_radial
846:
847: public int_radial_315
848: int_radial_315:
849: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
850: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_315
851: jmp short int_output_radial
852:
853: public int_radial_90_270
854: int_radial_90_270:
855: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
856: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_270
857: mov esi,edi
858: sub esi,ecx
859: jg short int_output_radial ;if top-to-bottom vertical line
860: jz next_line ;if zero length line
861:
862: neg esi
863: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
864: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_90
865: jmp short int_output_radial
866:
867: public int_radial_180
868: int_radial_180:
869: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
870: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_180
871: jmp short int_output_radial
872:
873: int_radial_0:
874: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
875: LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_0
876:
877: ; ebx = x
878: ; ecx = y
879: ; esi = dx (length of radial line since it's normalized)
880: ; ebp = drawing command
881:
882: public int_output_radial
883: int_output_radial:
884: mov edi,[esp].sf_ppdev
885: cmp [esp].sf_bSetCP,0
886: je short int_radial_continue_figure
887:
888: mov dx,[edi].pdev_gp_stat
889: @@: in ax,dx
890: and eax,FIFO_4_EMPTY
891: jnz short @b
892:
893: mov dx,[edi].pdev_cur_x
894: mov eax,ebx
895: out dx,ax
896:
897: mov dx,[edi].pdev_cur_y
898: mov eax,ecx
899: out dx,ax
900:
901: mov [esp].sf_bSetCP,0 ;the next integer line in this figure
902: ; will have the CP set correctly
903:
904: cmp esi,[esp].sf_ulLastLength
905: je short @f
906:
907: mov dx,[edi].pdev_line_max
908: mov eax,esi
909: out dx,ax
910: mov [esp].sf_ulLastLength,eax
911: @@:
912: mov dx,[edi].pdev_cmd
913: mov eax,ebp
914: out dx,ax
915: jmp next_line
916:
917: ; Jump to here if we don't have to update the current position first:
918:
919: public int_radial_continue_figure
920: int_radial_continue_figure:
921: cmp esi,[esp].sf_ulLastLength
922: je short int_radial_skip_length
923:
924: mov dx,[edi].pdev_gp_stat
925: @@: in ax,dx
926: and eax,FIFO_2_EMPTY
927: jnz short @b
928:
929: mov dx,[edi].pdev_line_max
930: mov eax,esi
931: out dx,ax
932: mov [esp].sf_ulLastLength,eax
933:
934: mov dx,[edi].pdev_cmd
935: mov eax,ebp
936: out dx,ax
937: jmp next_line
938:
939: ; Jump to here if we don't have to update the current position or the
940: ; line length variable:
941:
942: public int_radial_skip_length
943: int_radial_skip_length:
944: mov dx,[edi].pdev_gp_stat
945: @@: in ax,dx
946: and eax,FIFO_1_EMPTY
947: jnz short @b
948:
949: mov dx,[edi].pdev_cmd
950: mov eax,ebp
951: out dx,ax
952: jmp next_line
953:
954: ;/////////////////////////////////////////////////////////////////////
955: ;// Non-Integer Lines
956: ;/////////////////////////////////////////////////////////////////////
957:
958: public non_integer
959: non_integer:
960: sub esi,ebx
961: jl non_int_2_3_4_5
962: sub edi,ecx
963: jl non_int_6_7
964: cmp esi,edi
965: jl non_int_1
966: je non_int_0_slope_one
967:
968: non_int_0:
969: cmp esi,MAX_GIQ_DELTA
970: jg punt_line
971:
972: GIQ ROUND_X_AND_Y_DOWN
973:
974: non_int_0_common:
975: add ecx,edi ;err += dN
976: mov [esp].sf_ulErrorTerm,ecx
977:
978: mov eax,[esp].sf_x1
979: sub eax,ebx
980: jle next_line
981: mov [esp].sf_cPels,eax
982: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
983:
984: public non_int_x_major
985: non_int_x_major:
986: mov ecx,[esp].sf_ppdev
987: mov dx,[ecx].pdev_gp_stat
988: @@: in ax,dx
989: and eax,FIFO_7_EMPTY ;wait for 7 entries
990: jnz short @b
991:
992: ; Fractional end-point lines aren't usually going to have the current
993: ; position already set correctly, so we explicitly set the CP each time:
994:
995: mov dx,[ecx].pdev_cur_x
996: mov eax,ebx
997: out dx,ax ;x0
998:
999: mov dx,[ecx].pdev_cur_y
1000: mov eax,ebp
1001: out dx,ax ;y0
1002:
1003: mov [esp].sf_bSetCP,1 ;the next integer line in this figure
1004: ; will not necessarily have the CP set
1005: ; correctly
1006:
1007: mov eax,[esp].sf_cPels
1008: cmp eax,[esp].sf_ulLastLength
1009: je short @f
1010:
1011: mov dx,[ecx].pdev_line_max
1012: out dx,ax ;length = cPels
1013: mov [esp].sf_ulLastLength,eax
1014: @@:
1015: mov dx,[ecx].pdev_axstp
1016: mov eax,edi
1017: out dx,ax ;axial = dN
1018:
1019: mov dx,[ecx].pdev_diastp
1020: sub eax,esi
1021: out dx,ax ;diag = dN - dM
1022:
1023: mov dx,[ecx].pdev_err_term
1024: mov eax,[esp].sf_ulErrorTerm
1025: out dx,ax ;error term
1026:
1027: mov dx,[ecx].pdev_cmd
1028: mov eax,[esp].sf_ulCmd
1029: out dx,ax ;output it
1030:
1031: jmp next_line
1032:
1033: ; Lines of slope one have a special rounding rule: when the line
1034: ; runs exactly half way between two pixels, the upper or right pel
1035: ; is illuminated. This translates into x=1/2 rounding up in value,
1036: ; and y=1/2 rounding down:
1037:
1038: public non_int_0_slope_one
1039: non_int_0_slope_one:
1040: or esi,esi
1041: jz next_line ;quick check for a zero length GIQ line
1042:
1043: cmp esi,MAX_GIQ_DELTA
1044: jg punt_line
1045:
1046: GIQ ROUND_Y_DOWN_SLOPE_ONE
1047: jmp non_int_0_common
1048:
1049: public non_int_1
1050: non_int_1:
1051: cmp edi,MAX_GIQ_DELTA
1052: jg punt_line
1053:
1054: GIQR ROUND_X_AND_Y_DOWN
1055: add ecx,esi ;err += dM
1056: mov [esp].sf_ulErrorTerm,ecx
1057:
1058: mov eax,[esp].sf_y1
1059: sub eax,ebp
1060: jle next_line
1061: mov [esp].sf_cPels,eax
1062: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
1063:
1064: public non_int_y_major
1065: non_int_y_major:
1066: mov ecx,[esp].sf_ppdev
1067: mov dx,[ecx].pdev_gp_stat
1068: @@: in ax,dx
1069: and eax,FIFO_7_EMPTY ;wait for 7 entries
1070: jnz short @b
1071:
1072: ; Fractional end-point lines aren't usually going to have the current
1073: ; position already set correctly, so we explicitly set the CP each time:
1074:
1075: mov dx,[ecx].pdev_cur_x
1076: mov eax,ebx
1077: out dx,ax ;x0
1078:
1079: mov dx,[ecx].pdev_cur_y
1080: mov eax,ebp
1081: out dx,ax ;y0
1082:
1083: mov [esp].sf_bSetCP,1 ;the next integer line in this figure
1084: ; will not necessarily have the CP set
1085: ; correctly
1086:
1087: mov eax,[esp].sf_cPels
1088: cmp eax,[esp].sf_ulLastLength
1089: je short @f
1090:
1091: mov dx,[ecx].pdev_line_max
1092: out dx,ax ;length = cPels
1093: mov [esp].sf_ulLastLength,eax
1094: @@:
1095: mov dx,[ecx].pdev_axstp
1096: mov eax,esi
1097: out dx,ax ;axial = dM
1098:
1099: mov dx,[ecx].pdev_diastp
1100: sub eax,edi
1101: out dx,ax ;diag = dM - dN
1102:
1103: mov dx,[ecx].pdev_err_term
1104: mov eax,[esp].sf_ulErrorTerm
1105: out dx,ax ;error term
1106:
1107: mov dx,[ecx].pdev_cmd
1108: mov eax,[esp].sf_ulCmd
1109: out dx,ax ;output it
1110:
1111: jmp next_line
1112:
1113: public non_int_2_3_4_5
1114: non_int_2_3_4_5:
1115: neg esi ;dM = -dM (now positive)
1116: neg ebx ;M0 = -M0
1117: sub edi,ecx
1118: jl non_int_4_5
1119: cmp esi,edi
1120: jl non_int_2
1121:
1122: non_int_3:
1123: cmp esi,MAX_GIQ_DELTA
1124: jg punt_line
1125:
1126: GIQ ROUND_Y_DOWN
1127: add ecx,edi ;err += dN
1128: mov [esp].sf_ulErrorTerm,ecx
1129:
1130: neg ebx ;untransform x0
1131:
1132: mov eax,[esp].sf_x1
1133: add eax,ebx
1134: jle next_line
1135: mov [esp].sf_cPels,eax
1136: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
1137:
1138: jmp non_int_x_major
1139:
1140: public non_int_2
1141: non_int_2:
1142: cmp edi,MAX_GIQ_DELTA
1143: jg punt_line
1144:
1145: GIQR ROUND_Y_DOWN
1146: add ecx,esi ;err += dM
1147: mov [esp].sf_ulErrorTerm,ecx
1148:
1149: neg ebx ;untransform x0
1150:
1151: mov eax,[esp].sf_y1
1152: sub eax,ebp
1153: jle next_line
1154: mov [esp].sf_cPels,eax
1155: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
1156:
1157: jmp non_int_y_major
1158:
1159: public non_int_4_5
1160: non_int_4_5:
1161: neg edi ;dN = -dN (now positive)
1162: neg ecx ;N0 = -N0
1163: cmp esi,edi
1164: jl non_int_5
1165: je non_int_4_slope_one
1166:
1167: public non_int_4
1168: non_int_4:
1169: cmp esi,MAX_GIQ_DELTA
1170: jg punt_line
1171:
1172: GIQ 0
1173:
1174: non_int_4_common:
1175: add ecx,edi ;err += dN
1176: mov [esp].sf_ulErrorTerm,ecx
1177:
1178: neg ebx ;untransform x0
1179: neg ebp ;untransform y0
1180:
1181: mov eax,[esp].sf_x1
1182: add eax,ebx
1183: jle next_line
1184: mov [esp].sf_cPels,eax
1185: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
1186:
1187: jmp non_int_x_major
1188:
1189: ; Lines of slope one have a special rounding rule.
1190:
1191: public non_int_4_slope_one
1192: non_int_4_slope_one:
1193: cmp esi,MAX_GIQ_DELTA
1194: jg punt_line
1195:
1196: GIQ ROUND_X_DOWN_SLOPE_ONE
1197: jmp non_int_4_common
1198:
1199: public non_int_5
1200: non_int_5:
1201: cmp edi,MAX_GIQ_DELTA
1202: jg punt_line
1203:
1204: GIQR 0
1205: add ecx,esi ;err += dM
1206: mov [esp].sf_ulErrorTerm,ecx
1207:
1208: neg ebx ;untransform x0
1209: neg ebp ;untransform y0
1210:
1211: mov eax,[esp].sf_y1
1212: add eax,ebp
1213: jle next_line
1214: mov [esp].sf_cPels,eax
1215: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
1216:
1217: jmp non_int_y_major
1218:
1219: public non_int_6_7
1220: non_int_6_7:
1221: neg edi ;dN = -dN (now positive)
1222: neg ecx ;M0 = -M0
1223: cmp esi,edi
1224: je non_int_7_slope_one
1225: jg non_int_7
1226:
1227: public non_int_6
1228: non_int_6:
1229: cmp edi,MAX_GIQ_DELTA
1230: jg punt_line
1231:
1232: GIQR ROUND_X_DOWN
1233: add ecx,esi ;err += dM
1234: mov [esp].sf_ulErrorTerm,ecx
1235:
1236: neg ebp ;untransform y0
1237:
1238: mov eax,[esp].sf_y1
1239: add eax,ebp
1240: jle next_line
1241: mov [esp].sf_cPels,eax
1242: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
1243:
1244: jmp non_int_y_major
1245:
1246: public non_int_7
1247: non_int_7:
1248: cmp esi,MAX_GIQ_DELTA
1249: jg punt_line
1250:
1251: GIQ ROUND_X_DOWN
1252:
1253: non_int_7_common:
1254: add ecx,edi ;err += dN
1255: mov [esp].sf_ulErrorTerm,ecx
1256:
1257: neg ebp ;untransform y0
1258:
1259: mov eax,[esp].sf_x1
1260: sub eax,ebx
1261: jle next_line
1262: mov [esp].sf_cPels,eax
1263: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
1264:
1265: jmp non_int_x_major
1266:
1267: public non_int_7_slope_one
1268: non_int_7_slope_one:
1269: cmp esi,MAX_GIQ_DELTA
1270: jg punt_line
1271:
1272: GIQ ROUND_X_DOWN_SLOPE_ONE
1273: jmp non_int_7_common
1274:
1275: ;/////////////////////////////////////////////////////////////////////
1276: ;// Punt Line
1277: ;/////////////////////////////////////////////////////////////////////
1278:
1279: ; If the line is too long, we punt to our strip drawing routine.
1280:
1281: public punt_line
1282: punt_line:
1283: mov esi,esp
1284: lea eax,[esp].sf_ptfxStart
1285: lea ebx,[esp].sf_ptfxEnd
1286:
1287: cCall bLines,<[esi].sf_ppdev, eax, ebx, 0, 1, 0, \
1288: [esi].sf_prclClip, [esi].sf_apfn, [esi].sf_flags>
1289:
1290: mov [esp].sf_bSetCP,1 ;Always reset CP after punting
1291: mov [esp].sf_ulLastLength,-1;Always reset line length after punting
1292:
1293: jmp next_line
1294:
1295: endProc vFastLine
1296:
1297: end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.