;---------------------------Module-Header------------------------------; ; Module Name: fastline.asm ; ; This module draws solid lines using the line hardware of the S3. ; It handles entirely unclipped lines, or lines clipped to a single ; rectangle by using the S3's hardware clipping. ; ; All lines that have integer end-points are drawn directly using the ; line hardware, and any lines having fractional end-points and are ; less than 256 pels long are also drawn directly by the line hardware. ; (Fractional end-point lines require 4 more bits of precision from the ; DDA hardware than do integer end-point lines, and the S3 has but 13 ; bits to use. Lines longer than 255 pels are punted to the general ; purpose strip routines, which won't overflow the hardware). ; ; There are a number of ways unclipped lines can be sped up on the S3: ; ; 1) Optimize NT's GDI. This isn't an option for everybody. ; ; 2) Use memory-mapped I/O. ; ; 3) If you can't use memory-mapped I/O, you can at least minimize ; outs and ins because they are so painfully expensive. One ; way to do this is to amortize the cost of the in used for checking ; the FIFO: do one in to make sure a large number of entries on ; the FIFO are free (perhaps 6), then do the next 6 outs without ; having to check the FIFO. ; ; This will be a win on figures not requiring many outs (such as when ; radial lines are drawn as with a rectangle), assuming the hardware ; isn't always being overdriven. ; ; 4) In conjunction with 3) above, since short integer lines occur so ; frequently with CAD programs, it would be beneficial to use short- ; stroke vectors to do those lines, as they could reduce the number ; of outs required. Perhaps any line 10 pels or shorter could be ; output by using an array lookup to determine the short stroke vectors ; needed to represent that line. ; ; 5) Numerous other things I haven't thought of. ; ; Other improvements that should be done to make lines faster in general: ; ; 1) Simple clipping should be improved. Currently, we simply set the ; clip rectangle and let the hardware do everything. We should at least ; perform a trivial clip test to see if there's no chance the line ; intersects the clip rectangle. ; ; Also, for single lines it's expensive to do the 4 outs required to ; set the S3's clip rectangle, draw the line, then do 4 outs to reset ; the clip rectangle again. In those cases, it would be faster to do the ; clipping entirely in software. ; ; 2) This code can be enhanced to also handle styled lines, and to do ; complex clipping more efficiently than the strips routines. ; ; 3) It is possible to derive the mathematical algorithm for drawing any ; GIQ line in NT's 32 bit space using the limited bit precision of the ; S3's hardware. What this means is that absolutely any GIQ line can ; be drawn precisely with at most two drawing commands to the line ; hardware (and so the strips line drawing routine could be eliminated ; entirely). Just be careful of your math... ; ; 4) Numerous other things I haven't thought of. ; ; Copyright (c) 1992-1994 Microsoft Corporation ;-----------------------------------------------------------------------; .386 .model small,c assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT assume fs:nothing,gs:nothing .xlist include stdcall.inc ;calling convention cmacros include i386\strucs.inc include i386\hw.inc include i386\lines.inc .list ; Line coordinates are given in 28.4 fixed point format: F equ 16 FLOG2 equ 4 ; The S3's hardware can have 13 bits of significance for the error and ; step terms: NUM_DDA_BITS equ 13 ; GIQ lines have to dedicate 4 bits to the fractional term, so the largest ; delta we can handle for GIQ lines is calculated as follows (scaled by F ; so we can do the test in GIQ coordinates), and remembering that one bit ; has to be used as a sign bit: MAX_GIQ_DELTA equ (((1 shl (NUM_DDA_BITS - 5)) - 1) * F) ; The following values must match those in winddi.h! PD_BEGINSUBPATH equ 00000001h PD_ENDSUBPATH equ 00000002h PD_RESETSTYLE equ 00000004h PD_CLOSEFIGURE equ 00000008h PD_BEZIERS equ 00000010h PATHDATA struc pd_flags dd ? pd_count dd ? pd_pptfx dd ? PATHDATA ends ;-------------------------------------------------------------------------; ; I felt a compelling need to use 'ebp' as a 7th general register, and we ; have no nifty macros for dereferencing frame variables off 'esp'. So ; with this structure I am rolling my own stack frame: STATE_MEM_SIZE equ 4 ;4 dwords PROC_MEM_SIZE equ 9 ;9 dwords STACK_FRAME struc ; State variables (don't add/delete fields without modifying STATE_MEM_SIZE!) sf_ulOurEbp dd ? sf_ulOriginalEbx dd ? sf_ulOriginalEdi dd ? sf_ulOriginalEsi dd ? ; Frame variables (feel free to add/delete fields): sf_y0 dd ? ;GIQ variables sf_y1 dd ? sf_x1 dd ? sf_ptlOrg db (size POINTL) dup (?) ;our origin for normalizing the line sf_ptfxLast db (size POINTL) dup (?) ;the most recent point sf_ptfxStartFigure db (size POINTL) dup (?) ;the figure's 1st point sf_bMore dd ? ;more path records to get? sf_pptfxEnd dd ? ;points to last point in record sf_pptfx dd ? ;points to current point sf_pd db (size PATHDATA) dup (?) ;pathdata structure sf_ptfxStart db (size POINTL) dup (?) ;temporary spot for saving start point sf_ptfxEnd db (size POINTL) dup (?) ;temporary spot for saving end point sf_ulCmd dd ? ;S3 draw command sf_cPels dd ? ;length of line in pels sf_bSetCP dd ? ;1 if first line in figure, 0 otherwise sf_ulLastLength dd ? ;last value set as LINE_MAX sf_ulErrorTerm dd ? ;error term for line sf_xOffset dd ? ;stack copy of surface offset sf_yOffset dd ? sf_ioGp_stat_cmd dd ? ;local copy of gp_stat address sf_pjMmBase dd ? ;local copy of the memory-mapped I/O ; base address ; Procedure variables (don't add/delete fields without modifying ; PROC_MEM_SIZE!) sf_ulOriginalEbp dd ? sf_ulOriginalReturn dd ? sf_ppdev dd ? sf_ppo dd ? sf_prclClip dd ? sf_apfn dd ? sf_flags dd ? sf_color dd ? sf_ulHwMix dd ? STACK_FRAME ends .code EXTRNP PATHOBJ_bEnum,8 EXTRNP bLines,36 ROUND_X_DOWN equ 01h ROUND_Y_DOWN equ 02h ROUND_SLOPE_ONE equ 04h ROUND_X_AND_Y_DOWN equ (ROUND_X_DOWN + ROUND_Y_DOWN) ROUND_X_DOWN_SLOPE_ONE equ (ROUND_X_DOWN + ROUND_SLOPE_ONE) ROUND_Y_DOWN_SLOPE_ONE equ (ROUND_Y_DOWN + ROUND_SLOPE_ONE) ;--------------------------------Macro----------------------------------; ; GIQ flags ; ; This macros computes the start pixel, the number of pixels to ; be lit, and the initial error term given a GIQ line. The line must ; have already been normalized such that dM >= dN, dN >= 0. ; ; Input: ebx - M0 ; ecx - N0 ; esi - dM ; edi - dN ; Trashes: ; eax, edx ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y ; Output: ; [esp].sf_x1 - x-coordinate of last pixel (exclusive) ; ebx - x-coordinate of first pixel ; ecx - error term ; esi - dM ; edi - dN ; ebp - y-coordinate of first pixel ;-----------------------------------------------------------------------; GIQ macro flags local compute_x1, compute_error_term ; We normalize our coordinate system so that if the start point is ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)): mov eax,ebx mov ebp,ecx sar eax,FLOG2 sar ebp,FLOG2 mov [esp].sf_ptlOrg.ptl_x,eax ;ptlOrg.x = floor(M0 / F) mov [esp].sf_ptlOrg.ptl_y,ebp ;ptlOrg.y = floor(N0 / F) ; Calculate the correct [esp].sf_x1: lea ebp,[ecx + edi] ;ebp = N1 and ebp,F - 1 if (flags AND ROUND_X_DOWN) if (flags AND ROUND_SLOPE_ONE) cmp ebp,8 sbb ebp,-1 endif cmp ebp,1 sbb ebp,8 ;N1 -= 8 else sub ebp,8 ;N1 -= 8 endif sbb eax,eax xor ebp,eax sub ebp,eax ;N1 = ABS(N1) lea edx,[ebx + esi] mov eax,edx sar edx,FLOG2 and eax,F - 1 jz short @f ;special case for M1 == 0 cmp eax,ebp ;cmp M1, N1 sbb edx,-1 ;edx is now one pixel past the actual @@: ; end coordinate (note that it hasn't ; been affected by the origin shift) compute_error_term: ; ebx = M0 ; ecx = N0 ; edx = x1 ; esi = dM ; edi = dN and ecx,F - 1 mov [esp].sf_x1,edx ;save x1 ; Calculate our error term for x = 0. ; ; NOTE: Since this routine is used only for lines that are unclipped, we ; are guaranteed by our screen size that the values will be far less ; than 32 bits in significance, and so we don't worry about overflow. ; If this is used for clipped lines, these multiplies will have to ; be converted to give 64 bit results, because we can have 36 bits of ; significance! lea ebp,[ecx + 8] ;ebp = N0 + 8 mov eax,esi imul eax,ebp ;eax = dM * (N0 + 8) mov ebp,edi ; We have to special case when M0 is 0 -- we know x0 will be zero. ; So we jump ahead a bit to a place where 'ebx' is assumed to contain ; x0 -- and it just so happens 'ebx' is zero in this case: and ebx,F - 1 jz short @f imul ebp,ebx ;ebp = dN * M0 sub eax,ebp ; Calculate the x-coordinate of the first pixel: if (flags AND ROUND_X_DOWN) if (flags AND ROUND_SLOPE_ONE) cmp ecx,8 sbb ecx,-1 endif cmp ecx,1 sbb ecx,8 ;N0 -= 8 else sub ecx,8 ;N0 -= 8 endif sbb ebp,ebp xor ecx,ebp sub ecx,ebp ;N0 = ABS(N0) cmp ebx,ecx sbb ebx,ebx not ebx ;ebx = -x0 ; Now adjust the error term accordingly: @@: if (flags AND ROUND_Y_DOWN) dec eax endif sar eax,FLOG2 ;eax = floor((N0 + 8) dM - M0 dN] / 16) mov ecx,[esp].sf_ptlOrg.ptl_x mov ebp,[esp].sf_ptlOrg.ptl_y sub ecx,ebx ;ecx = ptlOrg.ptl_x + x0 and ebx,edi add ebx,eax sub ebx,esi ;ebx = dN * x0 + initial error - dM jl short @f ;if the error term >= 0, we have to sub ebx,esi ; add 1 to the y position and subtract inc ebp ; dM off again @@: xchg ebx,ecx endm ;--------------------------------Macro----------------------------------; ; GIQR flags ; ; Same as above, except it handles flips about the line x = y. ; ; Input: ebx - M0 ; ecx - N0 ; esi - dM ; edi - dN ; Trashes: ; eax, edx ; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y ; Output: ; [esp].sf_y1 - y-coordinate of last pixel (exclusive) ; ebx - x-coordinate of first pixel ; ecx - error term ; esi - dM ; edi - dN ; ebp - y-coordinate of first pixel ;-----------------------------------------------------------------------; GIQR macro flags ; We normalize our coordinate system so that if the start point is ; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)): mov eax,ebx mov ebp,ecx sar eax,FLOG2 sar ebp,FLOG2 mov [esp].sf_ptlOrg.ptl_x,eax ;ptlOrg.x = floor(M0 / F) mov [esp].sf_ptlOrg.ptl_y,ebp ;ptlOrg.y = floor(N0 / F) ; Calculate the correct [esp].sf_y1: lea ebp,[ebx + esi] ;ebp = M1 and ebp,F - 1 if (flags AND ROUND_Y_DOWN) cmp ebp,1 sbb ebp,8 ;M1 -= 8 else sub ebp,8 ;M1 -= 8 endif sbb eax,eax xor ebp,eax sub ebp,eax ;M1 = ABS(M1) lea edx,[ecx + edi] mov eax,edx sar edx,FLOG2 and eax,F - 1 jz short @f ;special case for N1 == 0 cmp eax,ebp ;cmp N1, M1 sbb edx,-1 ;edx is now one pixel past the actual @@: ; end coordinate (note that it hasn't ; been affected by the origin shift) and ebx,F - 1 mov [esp].sf_y1,edx ; Calculate our error term for y = 0. ; ; NOTE: Since this routine is used only for lines that are unclipped, we ; are guaranteed by our screen size that the values will be far less ; than 32 bits in significance, and so we don't worry about overflow. ; If this is used for clipped lines, these multiplies will have to ; be converted to give 64 bit results, because we can have 36 bits of ; significance! lea ebp,[ebx + 8] ;ebp = M0 + 8 mov eax,edi imul eax,ebp ;eax = dN * (M0 + 8) mov ebp,esi ; We have to special case when N0 is 0 -- we know y0 will be zero. ; So we jump ahead a bit to a place where 'ecx' is assumed to contain ; y0 -- and it just so happens 'ecx' is zero in this case: and ecx,F - 1 jz short @f imul ebp,ecx ;ebp = dM * N0 sub eax,ebp ; Calculate the x-coordinate of the first pixel: if (flags AND ROUND_Y_DOWN) cmp ebx,1 sbb ebx,8 ;M0 -= 8 else sub ebx,8 ;M0 -= 8 endif sbb ebp,ebp xor ebx,ebp sub ebx,ebp ;M0 = ABS(M0) cmp ecx,ebx sbb ecx,ecx not ecx ;ecx = -y0 ; Now adjust the error term accordingly: @@: if (flags AND ROUND_X_DOWN) dec eax endif sar eax,FLOG2 ;eax = floor((M0 + 8) dN - N0 dM] / 16) mov ebx,[esp].sf_ptlOrg.ptl_x mov ebp,[esp].sf_ptlOrg.ptl_y sub ebp,ecx ;ebp = ptlOrg.ptl_y + y0 and ecx,esi add ecx,eax sub ecx,edi ;ecx = dM * x0 + initial error - dN jl short @f ;if the error term >= 0, we have to sub ecx,edi ; add 1 to the x position and subtract inc ebx ; dN off again @@: endm ;---------------------------Public-Routine------------------------------; ; vIoFastLine(ppdev, ppo, prclClip, apfn, flags, color, ulHwMix) ; ; Draws fast lines. Or at least attempts to. Uses normal I/O. ; ; Input: ; ; ppdev - PDEV pointer ; ppo - path to be drawn ; prclClip - pointer to rectangle array for passing to 'bLines' ; apfn - pointer to strip routines for passing to 'bLines' ; flags - status flag for passing to 'bLines' ; color - color ; ulHwMix - mix ; ;-----------------------------------------------------------------------; ; NOTE: Don't go changing parameters without also changing STACK_FRAME! cProc vIoFastLine,28,< \ uses esi edi ebx, \ ebp_ppdev: ptr, \ ebp_ppo: ptr, \ ebp_prclClip: ptr, \ ebp_apfn: ptr, \ ebp_flags: dword,\ ebp_color: dword,\ ebp_ulHwMix: dword> ; Leave room for our stack frame. ; ; NOTE: Don't add local variables here -- you can't reference them with ; ebp anyway! Add them to the STACK_FRAME structure. local aj[(size STACK_FRAME) - 4 * (STATE_MEM_SIZE + PROC_MEM_SIZE)]: byte ; We save 'ebp' on the stack (note that STACK_FRAME accounts for this push): push ebp mov esi,[esp].sf_ppdev mov [esp].sf_ulLastLength,-1;make sure first line in path resets ; LINE_MAX value ; Warm up the hardware: mov edx,[esi].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_3_EMPTY jnz short @b ;IO_FIFO_WAIT(ppdev, 3) mov edx,[esi].pdev_ioFrgd_color mov eax,[esp].sf_color out dx,ax ;IO_FRGD_COLOR(ppdev, color) mov edx,[esi].pdev_ioFrgd_mix mov eax,[esp].sf_ulHwMix or eax,FOREGROUND_COLOR out dx,ax ;IO_FRGD_MIX(ppdev, FOREGROUND_COLOR | ; ulHwMix) mov edx,[esi].pdev_ioMulti_function mov eax,DATA_EXTENSION out dx,ax ;IO_PIX_CNTL(ppdev, ALL_ONES) ; Now get some path stuff: io_next_record: mov eax,[esp].sf_ppo lea ebx,[esp].sf_pd cCall PATHOBJ_bEnum, mov [esp].sf_bMore,eax ;save away return code for later mov ebx,[esp].sf_pd.pd_count;if 0 points in record, get outta here or ebx,ebx jz io_check_for_closefigure lea ebp,[8 * ebx - 8] add ebp,[esp].sf_pd.pd_pptfx mov [esp].sf_pptfxEnd,ebp ;points to last point in record mov ecx,[esp].sf_pd.pd_flags test ecx,PD_BEGINSUBPATH jz short io_continue_subpath ; Handle a new sub-path: mov eax,[esp].sf_pd.pd_pptfx add eax,8 mov [esp].sf_pptfx,eax mov esi,[ebp].ptl_x ;remember last point in case we have mov edi,[ebp].ptl_y ; to continue to another record mov [esp].sf_ptfxLast.ptl_x,esi mov [esp].sf_ptfxLast.ptl_y,edi mov ebx,[eax - 8].ptl_x ;load up current start and end point mov ecx,[eax - 8].ptl_y mov esi,[eax].ptl_x mov edi,[eax].ptl_y mov [esp].sf_ptfxStartFigure.ptl_x,ebx mov [esp].sf_ptfxStartFigure.ptl_y,ecx mov [esp].sf_bSetCP,1 ;this line is first in figure cmp eax,[esp].sf_pptfxEnd ;we have to be careful when the only ; point in the record is the start- ; figure point (pretty rare) jbe io_new_line jmp short io_next_record io_continue_subpath: ; This record continues the path: mov eax,[esp].sf_pd.pd_pptfx mov ebx,[esp].sf_ptfxLast.ptl_x ;load up current start point mov ecx,[esp].sf_ptfxLast.ptl_y mov esi,[ebp].ptl_x ;remember last point in case we have mov edi,[ebp].ptl_y ; to continue to another record mov [esp].sf_ptfxLast.ptl_x,esi mov [esp].sf_ptfxLast.ptl_y,edi mov esi,[eax].ptl_x ;load up current end point mov edi,[eax].ptl_y mov [esp].sf_pptfx,eax jmp io_new_line ;///////////////////////////////////////////////////////////////////// ;// Next Line Stuff ;///////////////////////////////////////////////////////////////////// io_handle_closefigure: mov [esp].sf_pd.pd_flags,0 mov ebx,[esp].sf_ptfxLast.ptl_x mov ecx,[esp].sf_ptfxLast.ptl_y mov esi,[esp].sf_ptfxStartFigure.ptl_x mov edi,[esp].sf_ptfxStartFigure.ptl_y jmp io_new_line ; Before getting the next path record, see if we have to do a closefigure: io_check_for_closefigure: test [esp].sf_pd.pd_flags,PD_CLOSEFIGURE jnz io_handle_closefigure mov esi,[esp].sf_bMore or esi,esi jnz io_next_record io_all_done: pop ebp cRet vIoFastLine ;///////////////////////////////////////////////////////////////////// ;// Output Integer Line ;///////////////////////////////////////////////////////////////////// ; ebx = x ; ecx = y ; esi = dx ; edi = dy ; [esp].sf_ulErrorTerm = incomplete error term (actual value is 'dx - bias') ; [esp].sf_ulCmd = draw command ; NOTE: The port values retrieved from the PDEV are word values, and word ; moves have a one cycle penalty. For this reason, the values should ; actually be defined as dwords in the PDEV... public io_int_output_line io_int_output_line:: mov ebp,[esp].sf_ppdev ;ebp = ppdev cmp [esp].sf_bSetCP,0 je short io_int_current_position_already_set mov edx,[ebp].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_7_EMPTY ;wait for 7 entries jnz short @b mov edx,[ebp].pdev_ioCur_x mov eax,ebx add eax,[ebp].pdev_xOffset out dx,ax mov edx,[ebp].pdev_ioCur_y mov eax,ecx add eax,[ebp].pdev_yOffset out dx,ax mov [esp].sf_bSetCP,0 ;the next integer line in this figure ; will have the CP set correctly jmp short io_int_output_common io_int_current_position_already_set: mov edx,[ebp].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_5_EMPTY ;wait for 5 entries jnz short @b public io_int_output_common io_int_output_common:: cmp esi,[esp].sf_ulLastLength je short @f mov edx,[ebp].pdev_ioMaj_axis_pcnt mov eax,esi out dx,ax mov [esp].sf_ulLastLength,eax @@: mov edx,[ebp].pdev_ioDesty_axstp mov eax,edi out dx,ax ;axial = dy mov edx,[ebp].pdev_ioDestx_diastp sub eax,esi out dx,ax ;diag = dy - dx mov edx,[esp].sf_ulErrorTerm sar edx,1 add eax,edx mov edx,[ebp].pdev_ioErr_term out dx,ax ;err = dy - dx + floor((dx - bias) / 2) mov edx,[ebp].pdev_ioGp_stat_cmd mov eax,[esp].sf_ulCmd out dx,ax ;draw that puppy ;///////////////////////////////////////////////////////////////////// ;// Main Loop ;///////////////////////////////////////////////////////////////////// public io_next_line io_next_line:: mov eax,[esp].sf_pptfx cmp eax,[esp].sf_pptfxEnd jae io_check_for_closefigure mov ebx,[eax].ptl_x mov ecx,[eax].ptl_y mov esi,[eax+8].ptl_x mov edi,[eax+8].ptl_y add eax,8 mov [esp].sf_pptfx,eax public io_new_line io_new_line:: ; Octants are numbered as follows: ; ; \ 5 | 6 / ; \ | / ; 4 \ | / 7 ; \ / ; -----+----- ; /|\ ; 3 / | \ 0 ; / | \ ; / 2 | 1 \ ; ; ebx = M0 ; ecx = N0 ; esi = M1 (dM) ; edi = N1 (dN) mov [esp].sf_ptfxStart.ptl_x,ebx mov [esp].sf_ptfxStart.ptl_y,ecx mov [esp].sf_ptfxEnd.ptl_x,esi mov [esp].sf_ptfxEnd.ptl_y,edi ;save points in case we have to punt mov eax,ebx or eax,ecx or eax,esi or eax,edi and eax,F-1 jnz io_non_integer ;///////////////////////////////////////////////////////////////////// ;// Integer Lines ;///////////////////////////////////////////////////////////////////// sar ebx,FLOG2 ;x0 sar ecx,FLOG2 ;y0 sar esi,FLOG2 ;x1 (soon to be dx) sar edi,FLOG2 ;y1 (soon to be dy) sub esi,ebx jl io_int_2_3_4_5 jz io_int_radial_90_270 sub edi,ecx jl io_int_6_7 jz io_int_radial_0 cmp esi,edi jl io_int_1 je io_int_radial_315 io_int_0: lea ebp,[esi - 1] mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X mov [esp].sf_ulErrorTerm,ebp jmp io_int_output_line public io_int_1 io_int_1:: xchg esi,edi lea ebp,[esi - 1] mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y mov [esp].sf_ulErrorTerm,ebp jmp io_int_output_line public io_int_2_3_4_5 io_int_2_3_4_5:: neg esi sub edi,ecx jl io_int_4_5 jz io_int_radial_180 cmp esi,edi jl io_int_2 je io_int_radial_225 public io_int_3 io_int_3:: lea ebp,[esi - 1] mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y mov [esp].sf_ulErrorTerm,ebp jmp io_int_output_line public io_int_2 io_int_2:: xchg esi,edi mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y mov [esp].sf_ulErrorTerm,esi jmp io_int_output_line public io_int_4_5 io_int_4_5:: neg edi cmp esi,edi jl io_int_5 je io_int_radial_135 io_int_4: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD mov [esp].sf_ulErrorTerm,esi jmp io_int_output_line public io_int_5 io_int_5:: xchg esi,edi mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y mov [esp].sf_ulErrorTerm,esi jmp io_int_output_line public io_int_6_7 io_int_6_7:: neg edi cmp esi,edi jg io_int_7 je io_int_radial_45 io_int_6: xchg esi,edi lea ebp,[esi - 1] mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X mov [esp].sf_ulErrorTerm,ebp jmp io_int_output_line public io_int_7 io_int_7:: mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X mov [esp].sf_ulErrorTerm,esi jmp io_int_output_line ;///////////////////////////////////////////////////////////////////// ;// Lines In The 8 Cardinal Directions ;///////////////////////////////////////////////////////////////////// public io_int_radial_45 io_int_radial_45:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_45 jmp short io_int_output_radial public io_int_radial_135 io_int_radial_135:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_135 jmp short io_int_output_radial public io_int_radial_225 io_int_radial_225:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_225 jmp short io_int_output_radial public io_int_radial_315 io_int_radial_315:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_315 jmp short io_int_output_radial public io_int_radial_90_270 io_int_radial_90_270:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_270 mov esi,edi sub esi,ecx jg short io_int_output_radial ;if top-to-bottom vertical line jz io_next_line ;if zero length line neg esi mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_90 jmp short io_int_output_radial public io_int_radial_180 io_int_radial_180:: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_180 jmp short io_int_output_radial io_int_radial_0: mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \ LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_0 ; ebx = x ; ecx = y ; esi = dx (length of radial line since it's normalized) ; ebp = drawing command public io_int_output_radial io_int_output_radial:: mov edi,[esp].sf_ppdev cmp [esp].sf_bSetCP,0 je short io_int_radial_continue_figure mov edx,[edi].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_4_EMPTY jnz short @b mov edx,[edi].pdev_ioCur_x mov eax,ebx add eax,[edi].pdev_xOffset out dx,ax mov edx,[edi].pdev_ioCur_y mov eax,ecx add eax,[edi].pdev_yOffset out dx,ax mov [esp].sf_bSetCP,0 ;the next integer line in this figure ; will have the CP set correctly cmp esi,[esp].sf_ulLastLength je short @f mov edx,[edi].pdev_ioMaj_axis_pcnt mov eax,esi out dx,ax mov [esp].sf_ulLastLength,eax @@: mov edx,[edi].pdev_ioGp_stat_cmd mov eax,ebp out dx,ax jmp io_next_line ; Jump to here if we don't have to update the current position first: public io_int_radial_continue_figure io_int_radial_continue_figure:: cmp esi,[esp].sf_ulLastLength je short io_int_radial_skip_length mov edx,[edi].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_2_EMPTY jnz short @b mov edx,[edi].pdev_ioMaj_axis_pcnt mov eax,esi out dx,ax mov [esp].sf_ulLastLength,eax mov edx,[edi].pdev_ioGp_stat_cmd mov eax,ebp out dx,ax jmp io_next_line ; Jump to here if we don't have to update the current position or the ; line length variable: public io_int_radial_skip_length io_int_radial_skip_length:: mov edx,[edi].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_1_EMPTY jnz short @b mov edx,[edi].pdev_ioGp_stat_cmd mov eax,ebp out dx,ax jmp io_next_line ;///////////////////////////////////////////////////////////////////// ;// Non-Integer Lines ;///////////////////////////////////////////////////////////////////// public io_non_integer io_non_integer:: sub esi,ebx jl io_not_int_2_3_4_5 sub edi,ecx jl io_not_int_6_7 cmp esi,edi jl io_not_int_1 je io_not_int_0_slope_one io_not_int_0: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_X_AND_Y_DOWN io_not_int_0_common: add ecx,edi ;err += dN mov [esp].sf_ulErrorTerm,ecx mov eax,[esp].sf_x1 sub eax,ebx jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X public io_not_int_x_major io_not_int_x_major:: mov ecx,[esp].sf_ppdev mov edx,[ecx].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_7_EMPTY ;wait for 7 entries jnz short @b ; Fractional end-point lines aren't usually going to have the current ; position already set correctly, so we explicitly set the CP each time: mov edx,[ecx].pdev_ioCur_x mov eax,ebx add eax,[ecx].pdev_xOffset out dx,ax ;x0 mov edx,[ecx].pdev_ioCur_y mov eax,ebp add eax,[ecx].pdev_yOffset out dx,ax ;y0 mov [esp].sf_bSetCP,1 ;the next integer line in this figure ; will not necessarily have the CP set ; correctly mov eax,[esp].sf_cPels cmp eax,[esp].sf_ulLastLength je short @f mov edx,[ecx].pdev_ioMaj_axis_pcnt out dx,ax ;length = cPels mov [esp].sf_ulLastLength,eax @@: mov edx,[ecx].pdev_ioDesty_axstp mov eax,edi out dx,ax ;axial = dN mov edx,[ecx].pdev_ioDestx_diastp sub eax,esi out dx,ax ;diag = dN - dM mov edx,[ecx].pdev_ioErr_term mov eax,[esp].sf_ulErrorTerm out dx,ax ;error term mov edx,[ecx].pdev_ioGp_stat_cmd mov eax,[esp].sf_ulCmd out dx,ax ;output it jmp io_next_line ; Lines of slope one have a special rounding rule: when the line ; runs exactly half way between two pixels, the upper or right pel ; is illuminated. This translates into x=1/2 rounding up in value, ; and y=1/2 rounding down: public io_not_int_0_slope_one io_not_int_0_slope_one:: or esi,esi jz io_next_line ;quick check for a zero length GIQ line cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_Y_DOWN_SLOPE_ONE jmp io_not_int_0_common public io_not_int_1 io_not_int_1:: cmp edi,MAX_GIQ_DELTA jg io_punt_line GIQR ROUND_X_AND_Y_DOWN add ecx,esi ;err += dM mov [esp].sf_ulErrorTerm,ecx mov eax,[esp].sf_y1 sub eax,ebp jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y public io_not_int_y_major io_not_int_y_major:: mov ecx,[esp].sf_ppdev mov edx,[ecx].pdev_ioGp_stat_cmd @@: in ax,dx and eax,FIFO_7_EMPTY ;wait for 7 entries jnz short @b ; Fractional end-point lines aren't usually going to have the current ; position already set correctly, so we explicitly set the CP each time: mov edx,[ecx].pdev_ioCur_x mov eax,ebx add eax,[ecx].pdev_xOffset out dx,ax ;x0 mov edx,[ecx].pdev_ioCur_y mov eax,ebp add eax,[ecx].pdev_yOffset out dx,ax ;y0 mov [esp].sf_bSetCP,1 ;the next integer line in this figure ; will not necessarily have the CP set ; correctly mov eax,[esp].sf_cPels cmp eax,[esp].sf_ulLastLength je short @f mov edx,[ecx].pdev_ioMaj_axis_pcnt out dx,ax ;length = cPels mov [esp].sf_ulLastLength,eax @@: mov edx,[ecx].pdev_ioDesty_axstp mov eax,esi out dx,ax ;axial = dM mov edx,[ecx].pdev_ioDestx_diastp sub eax,edi out dx,ax ;diag = dM - dN mov edx,[ecx].pdev_ioErr_term mov eax,[esp].sf_ulErrorTerm out dx,ax ;error term mov edx,[ecx].pdev_ioGp_stat_cmd mov eax,[esp].sf_ulCmd out dx,ax ;output it jmp io_next_line public io_not_int_2_3_4_5 io_not_int_2_3_4_5:: neg esi ;dM = -dM (now positive) neg ebx ;M0 = -M0 sub edi,ecx jl io_not_int_4_5 cmp esi,edi jl io_not_int_2 io_not_int_3: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_Y_DOWN add ecx,edi ;err += dN mov [esp].sf_ulErrorTerm,ecx neg ebx ;untransform x0 mov eax,[esp].sf_x1 add eax,ebx jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y jmp io_not_int_x_major public io_not_int_2 io_not_int_2:: cmp edi,MAX_GIQ_DELTA jg io_punt_line GIQR ROUND_Y_DOWN add ecx,esi ;err += dM mov [esp].sf_ulErrorTerm,ecx neg ebx ;untransform x0 mov eax,[esp].sf_y1 sub eax,ebp jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y jmp io_not_int_y_major public io_not_int_4_5 io_not_int_4_5:: neg edi ;dN = -dN (now positive) neg ecx ;N0 = -N0 cmp esi,edi jl io_not_int_5 je io_not_int_4_slope_one public io_not_int_4 io_not_int_4:: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ 0 io_not_int_4_common: add ecx,edi ;err += dN mov [esp].sf_ulErrorTerm,ecx neg ebx ;untransform x0 neg ebp ;untransform y0 mov eax,[esp].sf_x1 add eax,ebx jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD jmp io_not_int_x_major ; Lines of slope one have a special rounding rule. public io_not_int_4_slope_one io_not_int_4_slope_one:: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_X_DOWN_SLOPE_ONE jmp io_not_int_4_common public io_not_int_5 io_not_int_5:: cmp edi,MAX_GIQ_DELTA jg io_punt_line GIQR 0 add ecx,esi ;err += dM mov [esp].sf_ulErrorTerm,ecx neg ebx ;untransform x0 neg ebp ;untransform y0 mov eax,[esp].sf_y1 add eax,ebp jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y jmp io_not_int_y_major public io_not_int_6_7 io_not_int_6_7:: neg edi ;dN = -dN (now positive) neg ecx ;M0 = -M0 cmp esi,edi je io_not_int_7_slope_one jg io_not_int_7 public io_not_int_6 io_not_int_6:: cmp edi,MAX_GIQ_DELTA jg io_punt_line GIQR ROUND_X_DOWN add ecx,esi ;err += dM mov [esp].sf_ulErrorTerm,ecx neg ebp ;untransform y0 mov eax,[esp].sf_y1 add eax,ebp jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X jmp io_not_int_y_major public io_not_int_7 io_not_int_7:: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_X_DOWN io_not_int_7_common: add ecx,edi ;err += dN mov [esp].sf_ulErrorTerm,ecx neg ebp ;untransform y0 mov eax,[esp].sf_x1 sub eax,ebx jle io_next_line mov [esp].sf_cPels,eax mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X jmp io_not_int_x_major public io_not_int_7_slope_one io_not_int_7_slope_one:: cmp esi,MAX_GIQ_DELTA jg io_punt_line GIQ ROUND_X_DOWN_SLOPE_ONE jmp io_not_int_7_common ;///////////////////////////////////////////////////////////////////// ;// Punt Line ;///////////////////////////////////////////////////////////////////// ; If the line is too long, we punt to our strip drawing routine. public io_punt_line io_punt_line:: mov esi,esp lea eax,[esp].sf_ptfxStart lea ebx,[esp].sf_ptfxEnd cCall bLines,<[esi].sf_ppdev, eax, ebx, 0, 1, 0, \ [esi].sf_prclClip, [esi].sf_apfn, [esi].sf_flags> mov [esp].sf_bSetCP,1 ;Always reset CP after punting mov [esp].sf_ulLastLength,-1;Always reset line length after punting jmp io_next_line endProc vIoFastLine end