IDEAL
%TITLE	"De Casteljau Algorithm"
LOCALS	@@
P386

MASM
	INCLUDE	fixedop.asm
IDEAL

	Degree = 2

;
; Data Segment
;

SEGMENT	DATA	USE16 WORD PUBLIC

Buffer	DQ	(Degree + 1) DUP (?)
Prep	DQ	(Degree + 1) DUP (?)

EXTRN   SplineDeltaU:	DWORD, \
	SplineDense:	WORD

ENDS

;
; Macro Definitions
;

MACRO	loadq	Source
	mov	eax, [DWORD Source]
	mov	edx, [DWORD (Source+4)]
ENDM

MACRO	storq	Dest
	mov     [DWORD Dest], eax
	mov	[DWORD (Dest+4)], edx
ENDM

MACRO	movq	Dest, Source
	loadq	Source
	storq	Dest
ENDM

MACRO	addq	Value
	add	eax, [DWORD Value]
	add	edx, [DWORD (Value+4)]
ENDM

MACRO	sarq
	sar	eax, 1
	sar	edx, 1
ENDM

;
; Code Segment
;

SEGMENT	CODE	USE16 BYTE PUBLIC

ASSUME	CS:	CODE, \
	DS:	DATA

EXTRN	LineTo:	NEAR

;
; Casteljau: Apply the de Casteljau algorithm
;
; expects Buffer filled
;
; in	ebx	u, fixed
; 	ebp	1-u, fixed
;	si	Buffer offset
;
; returns calculated point in QWORD Buffer[0]
;

PROC 	Casteljau NEAR

	mov	cx, Degree
@@1:
	mov	di, si
	push	cx
@@2:
	mov	eax, [DWORD di]
	mulf	ebp
	mov	[DWORD di], eax
	mov	eax, [DWORD di+QWORD]
	mulf	ebx
	add	[DWORD di], eax
	add	di, QWORD
	loop	@@2

	pop	cx
	loop	@@1

	ret

ENDP

;
; uLoop: Loop u in the range [0, 1]
;
; expects Prep filled
;

PROC 	uLoop	NEAR
	LOCAL	ru: DWORD, left: WORD, temp: DWORD = LocalSize
	enter	LocalSize, 0
	mov	ax, [SplineDense]
	or	ax, ax
	jz	SHORT @@2

	mov	[Left], ax
	mov	[ru], 0
@@1:

; Copy Prep to Buffer

	mov	ax, ds
	mov	es, ax
	mov	cx, (Degree + 1) * 2
	cld
	mov	si, OFFSET Prep
	mov	di, OFFSET Buffer
	rep	movsd

; Forward ru

ifdef	FastSpline
					; Fast variant
	mov	eax, [SplineDeltaU]
	add	[ru], eax
	mov	ebx, [ru]

else
					; More exact variant
	mov	eax, 10000H
	add	eax, [ru]
	mov	[ru], eax
	xor	edx, edx
	movzx	ebx, [SplineDense]
	idiv	ebx
	mov	ebx, eax

endif

; Calc one point

	push	bp
	mov	ebp, 10000H
	sub	ebp, ebx
	mov	si, OFFSET Buffer
	call	Casteljau
	add	si, DWORD		; -> y
	call	Casteljau
	sub	si, DWORD		; -> x
	pop	bp

	mov	eax, [DWORD si]		; EAX x2.fixed
	mov	ecx, [DWORD si + DWORD]	; ECX y2.fixed
	call	LineTo

	dec	[Left]
	jnz	@@1

@@2:
	leave
	ret
ENDP

;
; Fill the calculation buffer
;
; in	es:si	Current coordinates

PROC	Fill2	NEAR		; es:si	current coord

	movq	Prep+1*QWORD, es:si
	addq	es:si+1*QWORD
	sarq
	storq	Prep+2*QWORD
	push	edx eax
	push	es si
	call	uLoop
	pop	si es
	pop	[DWORD Prep]
	pop	[DWORD Prep + DWORD]
	ret

ENDP

PROC	Fill2L	NEAR

	movq	Prep+1*QWORD, es:si
	movq	Prep+2*QWORD, es:si+1*QWORD
	push	es si
	call	uLoop
	pop	si es
	ret

ENDP

;
; Public Spline Procedure
;

PUBLIC	Spline

PROC	Spline	NEAR
	ARG	Count: WORD = ArgSize RETURNS CoordPtr: DWORD, Last: QWORD

	push	bp
	mov	bp, sp

	les	si, [CoordPtr]
	movq	Prep, Last
	sub	[Count], 2
	jz	SHORT @@2
@@1:
	call	Fill2
	add	si, QWORD
	dec	[Count]
	jnz	@@1
@@2:
	call	Fill2L
	movq	Last, Prep+degree*QWORD
	add	si, 2*QWORD
	mov	[WORD CoordPtr], si
	pop	bp
	ret	ArgSize

ENDP

ENDS

END
