;*======================================================================*
;*                TITLE:                  TURTLE.S                      *
;*                Function:               Complete 3D Turtle Demo       *
;*                                                                      *
;*                Project #:              JAGUAR                        *
;*                Programmer:             Rob Zdybel                    *
;*                                                                      *
;*              COPYRIGHT 1993 Atari Computer Corporation               *
;*          UNATHORIZED REPRODUCTION, ADAPTATION, DISTRIBUTION,         *
;*          PERFORMANCE OR DISPLAY OF THIS COMPUTER PROGRAM OR          *
;*        THE ASSOCIATED AUDIOVISUAL WORK IS STRICTLY PROHIBITED.       *
;*                            ALL RIGHTS RESERVED.                      *
;*                                                                      *
;*======================================================================*

BASE		EQU	$00D00000	;* 68000 GPU register base address

	include "jaguar.inc"
	include "blit.inc"
	include "joypad.inc"

;
;	EXTERNAL SYMBOLS
;
	.extern	cube		; Hand-Made Cube

	.extern	gpuload
	.extern	gpurun
	.extern	gpuwait
	.extern	make_olist
	.extern	pad_now
	.extern	pad_shot
	.extern	readpad
	.extern	sort
	.extern	turtlpak
	.extern	vblank
	.extern	vidinit
	.extern	fastdraw

	.extern	framecnt

;
;	PUBLIC SYMBOLS
;
	.globl	disp_ptr
	.globl	draw_ptr
	.globl	DISPBUF1


;
;	USEFUL DEFINES
;
BUFFERSIZE	EQU	(320*200*2)	; screen 320 X 200 in 16.16 color mode
DISPBUF1	EQU	ENDRAM-BUFFERSIZE
DISPBUF0	EQU	DISPBUF1-BUFFERSIZE

OFF68K		EQU	$900000
RAMSIZE		EQU	$1000		; 4K bytes
GPUPROG		EQU	G_RAM-OFF68K	; from the GPU's eye
INPUT6		EQU	G_ENDRAM-$4	; pointer to parameter 6 for GPU
INPUT5		EQU	INPUT6-$4	; pointer to parameter 5 for GPU
INPUT4		EQU	INPUT5-$4	; pointer to parameter 4 for GPU
INPUT3		EQU	INPUT4-$4	; pointer to parameter 3 for GPU
INPUT2		EQU	INPUT3-$4	; pointer to parameter 2 for GPU
INPUT1		EQU	INPUT2-$4	; pointer to parameter 1 for GPU

;
;	LOCAL MACROS
;
.macro	LONGALIGN	addr
	addq.l	#3,\addr		; make sure the address will remain
	andi.l	#$FFFFFFFC,\addr	; within the buffer area
.endm

;*======================================================================*
;
;	PROGRAM SEGMENT
;
;*======================================================================*
	.text

main:
	move.l	#$b000,sp	; **KLUDGE** Init the stack

	jsr	initsys		; initialize the system
	jsr	initurtl	; initialize turtles

	moveq.l	#0,d0
	moveq.l	#-1,d1
	move	d0,gameover	; Game not over yet
	move.l	d1,pad_now	; Clear the joypad
	move	d0,ncubes
	move	d0,rotate_flg
	move	d1,amb_flg
	move	d1,sun_flg
	move	d1,bulb_flg
	move	d0,scrublist	; ScrubList = Nil
	move	#1,drawlist	; One Object

	;*==============================================================*
	;* main processing loop 
	;*==============================================================*
framelp:
	jsr	do_sort		; Start the GPU Sorting Turtles

	jsr	clrscreen	; Clear the draw buffer

	jsr	gpuwait
	lea	fastdraw,a0
	jsr	gpuload		; Load Polyhedron Draw Routine into GPU RAM

	move	#0,newlist	; Newlist = Nil

	;*==============================================================*
	;* setup and draw all visible turtles
	;*==============================================================*
drawlp:	move	drawlist,d0	; WHILE (DrawList <> Nil) DO Draw Object
	beq	.drawxit
	move	d0,d1
	mulu	#turtlsiz,d0
	add.l	#turtle,d0	; D0 = Ptr to Turtle
	move.l	d0,a0
	move	turtlnk(a0),drawlist
	move	newlist,turtlnk(a0)
	move	d1,newlist	; Newlist = Drawlist inverted (This speeds sorting)
	lea	turtle+turtlsiz,a1
	move.l	(a1)+,(a0)+	; Cheat rotation by copying from one cube
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+
	move.w	(a1)+,(a0)+
	jsr	gpuwait		; Wait for GPU Idle
	jsr	copymodel	; BLTTER copy Model to Instance
	jsr	do_fastdraw	; Start GPU/BLTTER Polyhedron Draw
	bra	drawlp

	;*==============================================================*
	;* draw complete, read inputs and swap display buffers
	;*==============================================================*
.drawxit:
	move	newlist,drawlist	; DrawList is now inverted

	jsr	readpad		; Read the Joypad

	jsr	gpuwait		; Wait for GPU Idle

	move.l	disp_ptr,d0
	move.l	draw_ptr,disp_ptr
	move.l	framecnt,d7	; D7 = Current Frame for sync later
	move.l	d0,draw_ptr	; Swap buffers

	jsr	input		; Interpret the Joypad input

	;*==============================================================*
	;* wait for video to finish processing
	;*==============================================================*
.wsync:	cmp.l	framecnt,d7
	beq	.wsync		; Wait for video sync

	move	gameover,d0
	beq	framelp		; UNTIL (Game Over)

	;*==============================================================*
	;* End of Program
	;*==============================================================*
stop:
	illegal			; **KLUDGE** FORCE EXIT TO ADB
	nop


;
;	SUBROUTINE AREA
;

;
;	INPUT  Interpret Joypad results and act accordingly
;
;	Given:
;		PAD_NOW = Current Joypad reading
;		PAD_SHOT = One-Shot Joypad
;
;	Returns:
;		Input handled
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		GPULOAD, DO_TURTLE
;
input:
	movem.l	d0-d7/a0-a1,-(sp)

	lea	turtlpak,a0
	jsr	gpuload		; Load Turtle Package into GPU
;
;	Handle ViewPoint Rotation and Translation
;
	move.l	pad_now,d0
	moveq	#0,d1
	btst.l	#JOY_UP,d0
	beq	.noup
	addq	#1,d1		; PITCH Down
.noup:	btst.l	#JOY_DOWN,d0
	beq	.nodwn
	subq	#1,d1		; PITCH Up
.nodwn:	moveq	#0,d2
	btst.l	#JOY_LEFT,d0
	beq	.nolft
	addq	#1,d2		; YAW Left
.nolft:	btst.l	#JOY_RIGHT,d0
	beq	.norit
	subq	#1,d2		; YAW Right
.norit:	moveq	#0,d3
	btst.l	#KEY_1,d0
	beq	.nocw
	subq	#1,d3		; ROLL Clockwise
.nocw:	btst.l	#KEY_3,d0
	beq	.noccw
	addq	#1,d3		; ROLL CounterClockwise
.noccw:	moveq	#0,d4
	btst.l	#KEY_STAR,d0
	beq	.norev
	subq	#1,d4		; REVERSE
.norev:	btst.l	#KEY_HASH,d0
	beq	.nofor
	addq	#1,d4		; FORWARD
.nofor:	btst.l	#FIRE_A,d0
	beq	.nomor
	add	#10,d4		; FAST FORWARD
.nomor:	moveq	#0,d0
	jsr	do_turtle	; ViewPoint Rotation and Translation
;
;	Handle all other input
;
	move.l	pad_shot,d0
	btst.l	#KEY_0,d0
	beq	.noquit
	move	#-1,gameover	; 0 = Exit
.noquit:
;
;	Light Source Toggle and Intensity
;
	btst.l	#FIRE_B,d0
	beq	.nob
	eor	#-1,sun_flg	; Toggle Sun On/Off
.nob:	btst.l	#FIRE_C,d0
	beq	.noc
	eor	#-1,bulb_flg	; Toggle InScene On/Off
.noc:	btst.l	#KEY_5,d0
	beq	.noa
	eor	#-1,amb_flg	; Toggle Ambient On/Off
.noa:	move.l	pad_now,d1
	moveq	#0,d2
	btst.l	#KEY_6,d1
	beq	.noinc
	addq	#1,d2		; Increase Intensity
.noinc:	btst.l	#KEY_4,d1
	beq	.nodec
	subq	#1,d2		; Decrease Intensity
.nodec:	move	amb_flg,d1
	beq	.litem		; IF (Ambient On) THEN Alter Ambient Intensity
	add.b	d2,ambient+1
.litem:
	lea	litemodel,a0
	move	ambient,(a0)+
	move	sun_flg,d1	; Build a lighting model
	beq	.nosun
	add.b	d2,sun+1	; IF (Sun On) THEN Update Intensity and add Sun
	lea	sun,a1
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+
.nosun:	move	bulb_flg,d1
	beq	.nobulb
	add.b	d2,bulb+1	; IF (Bulb On) THEN Update Intensity and add Bulb
	lea	bulb,a1
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+
.nobulb:
	move.w	#0,(a0)+
;
;	CUBE Rotation and Propogation
;
	btst.l	#KEY_7,d0
	bne	.less
	btst.l	#KEY_9,d0
	beq	.same
	move	ncubes,d2	; More Cubes
	addq	#1,d2
	cmp	#8,d2
	bge	.same		; BUT No more than EIGHT
	bra	.cubes	
.less:	move	ncubes,d2	; Fewer Cubes
	subq	#1,d2
	bmi	.same		; BUT No fewer than ONE
.cubes:	lea	turtle+turtlsiz,a0
	move	d2,ncubes	; Build the CUBES
	move	#0,drawlist
	move	#0,scrublist
	move	d2,d4
	move	d2,d5
	move	d2,d6
	moveq	#0,d1
	moveq	#0,d2
	moveq	#0,d3
	moveq	#1,d7
.xcube:	move	d1,xposn(a0)	; Initialize a Cube
	move	d2,yposn(a0)
	move	d3,zposn(a0)
	move	scrublist,turtlnk(a0)
	move	d7,scrublist	; Link to Scrublist
	addq	#1,d7
	adda.l	#turtlsiz,a0
	add	#$200,d1
	dbra	d4,.xcube	; All X
	move	ncubes,d4
	moveq	#0,d1
	add	#$200,d2
	dbra	d5,.xcube	; All Y
	move	ncubes,d5
	moveq	#0,d2
	add	#$200,d3
	dbra	d6,.xcube	; All Z
.same:
  	btst.l	#KEY_8,d0
	beq	.norot
	eor	#-1,rotate_flg
.norot:	move	rotate_flg,d1
	beq	.done
	moveq	#1,d0
	moveq	#1,d1
	moveq	#1,d2
	moveq	#1,d3
	moveq	#0,d4
	jsr	do_turtle	; Cube Rotation
.done:
	movem.l	(sp)+,d0-d7/a0-a1
	rts


;
;	INITSYS   Initialize the system
;
;	Given:
;		Control
;
;	Returns:
;		320x200 Double-Buffered Video Enabled
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		VIDINIT, MAKE_OLIST
;
initsys:
	move.l	d0,-(sp)

	jsr	vidinit		; Initialize 320x200 NTSC Video

	move.w	MEMCON1,d0
	or	#$60,d0
	move.w	d0,MEMCON1	; Enable fast DRAM

	move.l	#$00020002,G_BIGEND	; Run GPU in Intel I/O mode, Motorola Pixel Mode

	jsr	make_olist	; Initialize Object List to single 320x200 Object

	move.l	#DISPBUF1,disp_ptr
	move.l	#DISPBUF0,draw_ptr	; Initialize Double-Buffer Ptrs

	move.l	#vblank,LEVEL2	; Capture VBLANK Interrupt
	move.w	#463,VI		; (must be ODD) NOTE: Actually very end of screen!
	move.w	#1,INT1
	move.w	sr,d0
	and.w	#$f8ff,d0
	move.w	d0,sr		; Enable Interrupt

	move.w	#$C1,VMODE	; Turn on the display

	move.l	(sp)+,d0
	rts


;
;	INITURTL   Initialize the TURTLE database
;
;	Given:
;		Control
;
;	Returns:
;		All Turtles initialized to Origin
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		
;
initurtl:
	movem.l	d0-d2/a0,-(sp)

	lea	turtle,a0
	clr	d0
	move	#$4000,d1
	move	#maxturtl-1,d2
.clrlp:	move	d1,xrite(a0)	; FOR (All Turtles) DO
	move	d0,yrite(a0)	; Init to Origin - Ordirnally aligned
	move	d0,zrite(a0)
	move	d0,xdown(a0)
	move	d1,ydown(a0)
	move	d0,zdown(a0)
	move	d0,xhead(a0)
	move	d0,yhead(a0)
	move	d1,zhead(a0)
	move	d0,xposn(a0)
	move	d0,yposn(a0)
	move	d0,zposn(a0)
	move	d0,turtlnk(a0)
	add.l	#turtlsiz,a0
	dbra	d2,.clrlp

	lea	turtle,a0
	move	#$fe80,zposn(a0)	; *KLUDGE* Position Viewpt in front of object

	movem.l	(sp)+,d0-d2/a0
	rts


;
;	CLRSCREEN   BLTTER Zero fill a 320x200 screen buffer
;
;	Given:
;		DRAW_PTR = Ptr to Draw buffer
;
;	Returns:
;		Draw buffer zeroed
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		None
;
clrscreen:
	movem.l	d0/a0,-(sp)

 	move.l	#(PITCH1|PIXEL16|WID320|XADDPHR),d0
	swap	d0
	move.l	d0,A1_FLAGS

	move.l	draw_ptr,d0
	swap	d0
	move.l	d0,A1_BASE	; Point to Draw buffer

	move.w	#-320,d0
	swap	d0
	move.w	#1,d0
	move.l	d0,A1_STEP	; Step -320 x, +1 y each line

	move.w	#320,d0
	swap	d0
	move.w	#200,d0
	move.l	d0,B_COUNT	; Do 320x200

	clr.l	d0
	move.l	d0,A1_PIXEL
	move.l	d0,B_PATD
	move.l	d0,B_PATD+4	; Zero fill

	move.l	#(PATDSEL|UPDA1),d0
	swap	d0
	move.l	d0,B_CMD	; Start the BLTTER

	move.l	#B_CMD,a0
.waitblit:			; Wait for BLTTER Idle
	move.l	(a0),d0
	swap	d0
	btst	#11,d0
	beq	.waitblit

	movem.l	(sp)+,a0/d0
	rts


;
;	COPYMODEL   Copy a Model to the Instance
;
;	Given:
;		INSTANCE = Buffer large enuff for model
;
;	Returns:
;		INSTANCE initialized
;		MODEL_PTR = Ptr to aligned instance data
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		None
;
copymodel:
	movem.l	a0-a1/d0-d2,-(sp)

	move.l	#cube,a0	; A0 = Ptr to Raw data

	move.l	#instance,model_ptr
	LONGALIGN model_ptr
	move.l	model_ptr,d0	; Model_ptr = Ptr to Aligned data

	move.l	d0,d1
	and.l	#$fffffff8,d0
	sub.l	d0,d1
	swap	d0
	move.l	d0,A1_BASE	; Point to Phrase-aligned destination
	asr.l	#1,d1		; Convert to pixels
	swap d1
	move.l	d1,A1_PIXEL	; D1 = "pixel" offset from base

	move.l	(a0)+,d0	; D0 = Model length
	asr.l	#1,d0		; Convert to "pixels"
	swap 	d0
	or.l	#1,d0		; No outer loop
	move.l	d0,B_COUNT

	move.l	a0,d0
	move.l	d0,d2
	and.l	#$fffffff8,d0
	sub.l	d0,d2
	swap	d0
	move.l	d0,A2_BASE	; Point to Phrase-aligned source
	asr.l	#1,d2		; Convert to pixels
	swap d2
	move.l	d2,A2_PIXEL	; D2 = "pixel" offset from base

	move.l	#PITCH1|PIXEL16|WID256|XADDPHR,d0
	swap	d0
	move.l	d0,A1_FLAGS
	move.l	d0,A2_FLAGS	; Flat Phrase Blt

	move.l	#SRCEN|LFU_AN|LFU_A,d0
	or.l	d1,d2
	beq.s	.aligned
	or.l	#SRCENX,d0	; Only do extra source read as needed (else fatal)
.aligned:
	swap	d0
	move.l	d0,B_CMD	; Turn on the BLTTER
;
;	NOTE: No Wait for BLTTER Idle Here .. CAUTION!
;
	addq.l	#8,a0		; A0 = Ptr to Seglist Ptr
	move.l	a0,a1
	move.l	(a1)+,d0	; A1 = Ptr to Vertlist Ptr
	move.l	(a1),d1
	sub.l	a0,d0		; D0 = relative offset to Seglist
	sub.l	a1,d1		; D1 = Relative offset to Vertlist
	move.l	model_ptr,a0
	addq.l	#8,a0
	add.l	a0,d0		; D0 = Ptr to aligned Seglist
	move.l	d0,(a0)+	; Fixup Seglist Ptr
	add.l	a0,d1		; D1 = Ptr to aligned Vertlist
	move.l	d1,(a0)		; Fixup Vertlist Ptr

	movem.l	(sp)+,a0-a1/d0-d2
	rts


;
;	DO_TURTLE   Execute the turtle package
;
;	Given:
;		d0 = Index of Turtle to Operate on
;		d1 = PITCH
;		d2 = YAW
;		d3 = ROLL
;		d4 = VELOCITY
;
;	Returns:
;		Turtle updated
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		GPURUN, GPUWAIT
;
do_turtle:
	movem.l	a0/d0-d4,-(sp)

	mulu	#turtlsiz,d0
	add.l	#turtle,d0	; D0 = Address of Turtle Record
	swap	d0
	move.l	d0,INPUT1

	swap	d1
	move.l	d1,INPUT2
	swap	d2
	move.l	d2,INPUT3
	swap	d3
	move.l	d3,INPUT4
	swap	d4
	move.l	d4,INPUT5	; Remaining inputs

	lea	turtlpak,a0
	jsr	gpurun		; run the GPU program
	jsr	gpuwait

	movem.l	(sp)+,a0/d0-d4
	rts


;
;	DO_SORT   Execute GPU Bubble Sort
;
;	Given:
;		DRAWLIST = Last Frame's Sorted See-ables (Inverted, Please)
;		SCRUBLIST = Last Frame's UNSee-ables
;		NCUBES = Number of Active Turtles
;		TURTLE = Turtle Record Array
;
;	Returns:
;		DRAWLIST = Painter's Sorted See-ables list root index
;		SCRUBLIST =  UNSee-ables list root index
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		GPURUN, GPULOAD
;
do_sort:
	movem.l	a0/d0-d1,-(sp)

	lea	sort,a0
	jsr	gpuload		; Load Polyhedron Draw Routine into GPU RAM

	move.l	#turtle,d0
	swap	d0
	move.l	d0,INPUT1	; Address of Viewpt Record
	move.l	#drawlist,d0
	swap	d0
	move.l	d0,INPUT2	; Address of Drawlist Root
	move.l	#scrublist,d0
	swap	d0
	move.l	d0,INPUT3	; Address of Scrublist Root
	move.l	#turtle+turtlsiz,d0
	swap	d0
	move.l	d0,INPUT4	; Address of Turtle[1]
	move	ncubes,d0
	addq	#1,d0
	move	d0,d1
	mulu	d1,d0
	mulu	d1,d0
	swap	d0
	move.l	d0,INPUT5	; Number of Active Turtles
	move.l	#turtlsiz,d0
	swap	d0
	move.l	d0,INPUT6	; Length of a Turtle Record
   
	lea	sort,a0
	jsr	gpurun		; run the GPU program

	movem.l	(sp)+,a0/d0-d1
	rts


;
;	DO_FASTDRAW   Execute the Polyhedron Draw package
;
;	Given:
;		D0 = Ptr to Turtle to Draw
;
;	Returns:
;		Object Drawn
;
;	Register Usage:
;		All Registers Restored
;
;	Externals:
;		GPURUN
;
do_fastdraw:
	movem.l	a0/d0,-(sp)

	swap	d0
	move.l	d0,INPUT3	; Pass Ptr to Viewed Object

	move.l	model_ptr,d0
	swap	d0		; GPU and 68000 read addresses swapped
	move.l	d0,INPUT1	; tell GPU where to find parameters

	move.l	#turtle,d0
	swap	d0
	move.l	d0,INPUT2	; Turtle[0] is Viewer

	move.l	#litemodel,d0
	swap	d0
	move.l	d0,INPUT4

	move.l	draw_ptr,d0
	swap	d0		; GPU and 68000 read addresses swapped
	move.l	d0,INPUT5	; tell GPU where to find parameters

	lea	fastdraw,a0	; get base address of GPU code
	jsr	gpurun		; run the GPU program

	movem.l	(sp)+,a0/d0
	rts


;
;	VARIABLE RAM
;
	.bss
	.even

disp_ptr:		; Display buffer ptr
	.ds.l	1
draw_ptr:		; Draw buffer ptr
	.ds.l	1

drawlist:		; Root (Index really) to List of See-able turtles
	.ds.w	1
scrublist:		; Root (Index really) to List of UNSee-able turtles
	.ds.w	1
newlist:
	.ds.w	1

model_ptr:		; Ptr to Instance
	.ds.l	1
instance:		; Storage for INSTANCE
	.ds.l	2048	; 8K Bytes
litemodel:		; Lighting Model
	.ds.w	12	; Enough for Ambient and 2 sources

gameover:		; Game Over Flag (0 ==> Game Active)
	.ds.w	1
rotate_flg:		; Cube rotation Flag (0 ==> Sit Still)
	.ds.w	1
sun_flg:		; Sun Lighting Flag (0 ==> No Sun)
	.ds.w	1
bulb_flg:		; InScene Lighting Flag (0 ==> No Bulb)
	.ds.w	1
amb_flg:		; Ambient Lighting Flag (0 ==> No Intensity Change)
	.ds.w	1
ncubes:			; Number of Cubes in space (Less One)
	.ds.w	1

;
;	TURTLE RECORDS
;
xrite	=	0		; IMPORTANT NOTE:
yrite	=	xrite+2		; These first record offsets are hard-wired
zrite	=	yrite+2		; into associated GPU routines.
xdown	=	zrite+2		; DO NOT Insert, Delete or Swap any of these
ydown	=	xdown+2		;	^
zdown	=	ydown+2		;	|
xhead	=	zdown+2		;	|
yhead	=	xhead+2		;	|
zhead	=	yhead+2		;	|
xposn	=	zhead+2		;	|
yposn	=	xposn+2		;	|
zposn	=	yposn+2		;	v
turtlnk =	zposn+2		; Dont change this or anything above here
turtlsiz =	turtlnk+2
maxturtl =	1000

turtle:			; All Motion Objects
	.ds.b	maxturtl*turtlsiz


;
;	CONSTANT DATA
;
	.data
	.even
ambient:			; Ambient Intensity
	.dc.w	$40
bulb:				; InScene Light
	.dc.w	$80FF		; Intensity
	.dc.w	$0000		; X
	.dc.w	$f000		; Y
	.dc.w	$0000		; Z
sun:				; Sun
	.dc.w	$FF		; Intensity
	.dc.w	$4000		; X
	.dc.w	$0000		; Y
	.dc.w	$0000		; Z

;*======================================================================*
;*                                 EOF                                  *
;*======================================================================*
