;----------------------------------------------------------------------- ; MODULE XBEZIER ; ; ; Compile with TASM. ; C near-callable. ; ; This module was written by Matthew MacKenzie ; matm@eng.umd.edu ;----------------------------------------------------------------------- include xlib.inc include xbezier.inc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; _x_bezier ; ; Plot a Bezier curve, which is described by a box of two endpoints ; and two control points: ; C1--------C2 ; / \ ; / \ ; E1..............E2 ; ; All coordinates must be in the range -1024 to 3071. ; No clipping is performed. ; ; C near-callable as: ; x_bezier (int E1x, int E1y, int C1x, int C1y, int C2x, int C2y, ; int E2x, int E2y, int levels, char color, ; unsigned int PageOffset); ; ; All four main registers are totaled. ; This function may use as many as 162 bytes of stack space. ; Bezier curves need 32-bit precision, so we'll define macros and ; constants for operations on 32-bit virtual registers V0, V1, and V2. ; V0 is made up of DI and AX, V1 of SI and BX, and V2 of CX and DX. LowWord equ 0 HighWord equ 2 ; to load data -- LoadV0 macro loc, field mov ax, word ptr [bp + loc + field + LowWord] mov di, word ptr [bp + loc + field + HighWord] endm LoadV1 macro loc, field mov bx, word ptr [bp + loc + field + LowWord] mov si, word ptr [bp + loc + field + HighWord] endm LoadV2 macro loc, field mov dx, word ptr [bp + loc + field + LowWord] mov cx, word ptr [bp + loc + field + HighWord] endm ; to store data -- StoreV0 macro loc, field mov word ptr [bp + loc + field + LowWord], ax mov word ptr [bp + loc + field + HighWord], di endm StoreV1 macro loc, field mov word ptr [bp + loc + field + LowWord], bx mov word ptr [bp + loc + field + HighWord], si endm ; to take the average of two registers (result is in first register) -- AverageV0nV1 macro add ax, bx adc di, si shr di, 1 rcr ax, 1 endm AverageV0nV2 macro add ax, dx adc di, cx shr di, 1 rcr ax, 1 endm AverageV1nV2 macro add bx, dx adc si, cx shr si, 1 rcr bx, 1 endm ; to take the average of a register and data -- AverageV1nData macro loc, field add bx, word ptr [bp + loc + field + LowWord] adc si, word ptr [bp + loc + field + HighWord] shr si, 1 rcr bx, 1 endm AverageV2nData macro loc, field add dx, word ptr [bp + loc + field + LowWord] adc cx, word ptr [bp + loc + field + HighWord] shr cx, 1 rcr dx, 1 endm ; to turn a 32-bit fixed point data into a regular integer -- Extract macro reg, source, field mov reg, word ptr [bp + source + field + HighWord] shr reg, 3 adc reg, 0 ; round endm ; to turn an integer argument into a 32-bit fixed point number ; and store it as local data -- Convert macro source, dest, field mov ax, source add ax, 1024 shl ax, 3 push ax push 0 endm ; useful numbers for dealing with Bezier boxes (sets of four points) -- XCoord equ 4 YCoord equ 0 ; stack offsets for splitting boxes E1Src equ 48 C1Src equ 40 C2Src equ 32 E2Src equ 24 E1Dest equ 48 P1Dest equ 40 P4Dest equ 32 P6Dest equ 24 P5Dest equ 16 P2Dest equ 8 E2Dest equ 0 ; stack offsets for drawing boxes E1Draw equ 24 C1Draw equ 16 C2Draw equ 8 E2Draw equ 0 .data align 2 ; depth of recursion at which to plot WhenToDraw label byte db 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 db 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 ; since we'll be moving bp and sp in our recursion, ; we need to put local storage in the data segment level dw (?) gencount dw (?) AdjustedOffs dw (?) p1xt dw (?) p1yt dw (?) p2xt dw (?) p2yt dw (?) p4xt dw (?) p4yt dw (?) p5xt dw (?) p5yt dw (?) c1xt dw (?) c1yt dw (?) c2xt dw (?) c2yt dw (?) xdiff dw (?) ydiff dw (?) moveline dw (?) stepper dw (?) bptemp dw (?) ; by the same token we need a copy of this argument Colort dw (?) ColumnMasks label byte db 011h, 022h, 044h, 088h .code align 2 _x_bezier proc ARG E1x, E1y, C1x, C1y, C2x, C2y, E2x, E2y, Levels, Color, PageOffs:word push bp mov bp, sp ; caller's stack frame push si push di push es ; set local variables mov ax, -1024 ; 1024 rows imul [_ScrnLogicalByteWidth] add ax, PageOffs sub ax, 256 ; 1024 columns mov AdjustedOffs, ax ; subtract 1024 rows and 1024 columns ; copy color into data segment so we can change bp & sp mov ax, Color mov Colort, ax mov cx, Levels dec cx ; gencount (number of boxes we will actually plot) = mov ax,1 ; 2^(Levels - 1) shl ax,cl dec ax mov gencount, ax mov [level], 0 ; variable to tell us where we are in the stack mov bptemp, bp ; when the dust settles at the end ; translate coordinates for adjusted offset, convert 'em to fixed-point ; with 13 bits for the integer part and 19 for the fractional part, ; and push them onto the stack in the right order to for a Bezier box Convert E2x Convert E2y Convert C2x Convert C2y Convert C1x Convert C1y Convert E1x Convert E1y mov bp, sp ; we are using them as basically one pointer mov ax, 0a000h ; point extra segment to VGA memory mov es, ax mov dx, SC_INDEX mov al, MAP_MASK out dx, al MainLoop: mov si, gencount mov ax, 0 mov al, WhenToDraw[si] cmp ax, level ; are we at a terminal curve? jne Recurse jmp PlotCurve Recurse: ; not at a terminal -- so expand this curve into two more and recurse ; start with: ; C1___C2 ; | | ; E1...E2 ; ; stack looks like: E1 C1 C2 E2 ; expand like this: ; C1.....P3.....C2 ; . . . . ; . _P4_P6__P5_ . ; P1- .. .. P2 ; | .. .. | ; |.. ..| ; E1............E2 ; ; stack looks like: E1 P1 P4 P6 P5 P2 E2 ; Since P6 is part of both new boxes, we use it twice. sub sp, 24 sub bp, 24 ; new points for X -- LoadV0 E1Src, XCoord LoadV1 E2Src, XCoord StoreV1 E2Dest, XCoord LoadV2 C1Src, XCoord AverageV0nV2 StoreV0 P1Dest, XCoord AverageV1nData C2Src, XCoord StoreV1 P2Dest, XCoord AverageV2nData C2Src, XCoord AverageV0nV2 StoreV0 P4Dest, XCoord AverageV1nV2 StoreV1 P5Dest, XCoord AverageV0nV1 StoreV0 P6Dest, XCoord ; same thing for Y -- LoadV0 E1Src, YCoord LoadV1 E2Src, YCoord StoreV1 E2Dest, YCoord LoadV2 C1Src, YCoord AverageV0nV2 StoreV0 P1Dest, YCoord AverageV1nData C2Src, YCoord StoreV1 P2Dest, YCoord AverageV2nData C2Src, YCoord AverageV0nV2 StoreV0 P4Dest, YCoord AverageV1nV2 StoreV1 P5Dest, YCoord AverageV0nV1 StoreV0 P6Dest, YCoord inc level ; one level further into stack jmp MainLoop PlotCurve: ; pull 16-bit coordinates back out of 32-bit fixed-point coordinates; ; integer part is highest 13 bits Extract cx, C1Draw, XCoord Extract di, E1Draw, XCoord mov c1xt, cx add cx, di shr cx, 1 mov p1xt, cx Extract ax, C1Draw, YCoord Extract si, E1Draw, YCoord mov c1yt, ax add ax, si shr ax, 1 mov p1yt, ax call ShortLine ; line from P1 to E1 Extract cx, E2Draw, XCoord Extract di, C2Draw, XCoord mov c2xt, di add di, cx shr di, 1 mov p2xt, di Extract ax, E2Draw, YCoord Extract si, C2Draw, YCoord mov c2yt, si add si, ax shr si, 1 mov p2yt, si call ShortLine ; line from E2 to P2 ; P3 is not in any line we draw, so we'll use it now to find both P5 ; for the line after this on, and P4 for this line, then discard it -- mov bx, c1xt add bx, c2xt shr bx, 1 mov dx, c1yt add dx, c2yt shr dx, 1 ; find P5 x and save for later lines mov cx, p2xt add cx, bx shr cx, 1 mov p5xt, cx ; find P4 x for this line mov cx, p1xt add cx, bx shr cx, 1 mov p4xt, cx mov di, p1xt ; find P5 y and save for later lines mov ax, p2yt add ax, dx shr ax, 1 mov p5yt, ax ; find P4 y for this line mov ax, p1yt add ax, dx shr ax, 1 mov p4yt, ax mov si, p1yt call ShortLine ; line from P4 to P1 -- finally! ; we've already done all the work for these last two -- mov cx, p2xt mov di, p5xt mov ax, p2yt mov si, p5yt call ShortLine ; line from P2 to P5 mov cx, p5xt mov di, p4xt mov ax, p5yt mov si, p4yt call ShortLine ; line from P5 to P4 ; we've drawn our five lines; this bezier box ; can be dropped off the stack add bp, 24 add sp, 24 dec gencount mov cx, gencount cmp cx, -1 je WrapUp ; if we've generated all the terminal nodes we ; are supposed to, we pack our bags and go. dec level jmp MainLoop WrapUp: ; plot the final point, which is simply the original E1 mov bp, bptemp ; back where we started mov ax, E1y add ax, 1024 mul [_ScrnLogicalByteWidth] mov di, E1x add di, 1024 mov si, di shr di, 2 add di, ax add di, AdjustedOffs and si, 3 mov al, ColumnMasks[si] mov ah, byte ptr Color mov dx, SC_INDEX + 1 out dx, al mov es:[di], ah pop es pop di pop si mov sp, bp pop bp ret ; git ; ShortLine subfunction -- ; ; This is designed for short line segments. For longer lines, ; especially horizontal ones, the x_line routine in the XLINE module ; would be faster. But calling that from here it would be a lot of ; extra complication. This is part of the x_bezier routine because ; it has no particular outside use, and we can call it much faster ; through registers than through the stack. ; ; Since our line segments overlap, the second endpoint is not drawn. ; These routines are all out of order for the sake of conditional jumps. LRHorz: dec di LRHorzLoop: rol al, 1 adc bx, 0 out dx, al mov es:[bx], ah sub di, 1 jg LRHorzLoop retn ; You are in a maze of little subroutines, all alike... LR45Deg: dec si LR45DegLoop: add bx, cx rol al, 1 adc bx, 0 out dx, al mov es:[bx], ah sub si, 1 jg LR45DegLoop retn LRXMajor: mov cx, di dec cx shr di, 1 LRXMajorLoop: sub di, si jge LRXMajorIterate add di, xdiff add bx, moveline LRXMajorIterate: rol al, 1 adc bx, 0 out dx, al mov es:[bx], ah sub cx, 1 jg LRXMajorLoop retn LeftToRight: mov xdiff, di ; store distance across line mov cx, [_ScrnLogicalByteWidth] cmp si, 0 ; check if height is positive, negative, or zero je LRHorz jg LRTopToBottom neg si neg cx LRTopToBottom: mov ydiff, si mov moveline, cx cmp di, si jg LRXMajor je LR45Deg LRYMajor: mov cx, si dec cx shr si, 1 LRYMajorLoop: add bx, moveline sub si, di jge LRYMajorIterate add si, ydiff rol al, 1 adc bx, 0 LRYMajorIterate: out dx, al mov es:[bx], ah sub cx, 1 jg LRYMajorLoop retn ; This is the actual starting point. ; On entry, registers look like this: ; X1: cx ; Y1: ax ; X2: di ; Y2: si ShortLine: sub si, ax ; height goes out in si sub di, cx ; width goes out in di mul [_ScrnLogicalByteWidth] mov dx, cx shr dx, 2 add ax, dx add ax, AdjustedOffs mov bx, ax ; starting byte of X1, Y1 goes out in bx and cx, 3 mov ax, 011h shl al, cl ; column mask goes out in al mov dx, SC_INDEX + 1 mov ah, byte ptr Colort; color goes out in ah out dx, al mov es:[bx], ah ; plot first point cmp di, 0 jg LeftToRight je VerticalLine RightToLeft: neg di ; much more useful this way mov xdiff, di mov cx, [_ScrnLogicalByteWidth] cmp si, 0 ; check if height is positive, negative, or zero je RLHorz jg RLTopToBottom neg si neg cx RLTopToBottom: mov ydiff, si mov moveline, cx cmp di, si jg RLXMajor je RL45Deg RLYMajor: mov cx, si dec cx shr si, 1 RLYMajorLoop: add bx, moveline sub si, di jge RLYMajorIterate add si, ydiff ror al, 1 sbb bx, 0 RLYMajorIterate: out dx, al mov es:[bx], ah sub cx, 1 jg RLYMajorLoop retn VerticalLine: mov cx, [_ScrnLogicalByteWidth] cmp si, 0 ; check if height is positive jg VTopToBottom neg si neg cx VTopToBottom: dec si VLoop: add bx, cx mov es:[bx], ah sub si, 1 jg VLoop retn RLHorz: dec di RLHorzLoop: ror al, 1 sbb bx, 0 out dx, al mov es:[bx], ah sub di, 1 jg RLHorzLoop retn RL45Deg: dec si RL45DegLoop: add bx, cx ror al, 1 sbb bx, 0 out dx, al mov es:[bx], ah sub si, 1 jg RL45DegLoop retn RLXMajor: mov cx, di dec cx shr di, 1 RLXMajorLoop: sub di, si jge RLXMajorIterate add di, xdiff add bx, moveline RLXMajorIterate: ror al, 1 sbb bx, 0 out dx, al mov es:[bx], ah sub cx,1 jg RLXMajorLoop retn _x_bezier endp end