;************************************************************************
;* TITLE	: STARTUP.S						*
;* Function	: Startup Routines and Call to Main Event Handler	*
;*									*
;* NOTE		: Link this file <<<< FIRST >>>>			*
;* ----		  ------------------------------			*
;*									*
;* Project #	: JAGUAR CD+G						*
;* Programmer	: Pradip K Fatehpuria	06/28/94			*
;*									*
;* COPYRIGHT(c) 1992,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.							*
;************************************************************************

	.title	"STARTUP.S - System Startup Routines"

;*======================================================================*
;* HEADER FILES								*
;*======================================================================*

	.include 'jaguar.inc'
	.include 'video.inc'
	.include 'joypad.inc'
	.include 'objlist.inc'
	.include 'defs.inc'

;*======================================================================*
;* EXTERNAL ROUTINES							*
;*======================================================================*

	.extern	initapp

	.extern	HandleCD
	.extern	Hndlpad
	.extern	Subcode
	.extern	Subproc

;*======================================================================*
;* EXTERNAL DATA							*
;*======================================================================*

	.extern	Objects

;*======================================================================*
;* PUBLIC ROUTINES							*
;*======================================================================*

	.globl	readpad

;*======================================================================*
;* PUBLIC DATA								*
;*======================================================================*

	.globl	objData

	.globl	screnmem

	.globl	padnow			; joypad inputs (current state, ON or OFF)
	.globl	padshot			; joypad inputs (edge trigger)

	.globl	bldobflg		; re-build object list flag

;************************************************************************
;* SUBROUTINES AREA							*
;************************************************************************

	.text

;*======================================================================*
;* STARTUP - Processing Starts Here - Initialise and Call Main		*
;*======================================================================*

startup:
	move.l	#$00070007,G_END	; set the endian thing
	move.l	#$00070007,D_END	; set the endian thing

.stack:	move.l	#newstack,a7		; initialise the stack pointer

.clrmm:	move.l	#$10000,a0		; clear the bss area
	move.l	#$200000,d0
.clrlp:	move.l	#0,(a0)+
	cmp.l	a0,d0
	bne.s	.clrlp

.dorest:
	jsr	initjag			; initialise jaguar stuff
	jsr	main

_done:	illegal

;************************************************************************
;************************************************************************

;*======================================================================*
;* INITJAG	: Initialize Jaguar Video and Object List		*
;*======================================================================*

initjag:
	clr.l	d0			; clear d0

	move.l	d0,framecnt		; initialise frame count

	move.w	d0,bldobflg		; re-build object list

	move.l	d0,padnow		; clear joypad
	move.l	d0,padshot		;   paremeters

	jsr	vidinit			; initialize video
	jsr	vblinit			; initialize vblank routine

	jsr	InitObjList		; initialise object list in RAM
	jsr 	BuildObjList		; build object list
	jsr	CopyObjList		; objects for object processor
	jsr	InitObjProc		; initialise the object processor

	move.w	#$4C7,VMODE		; turn on display w/RGB (#$4C1 for CRY)

	jsr	initapp			; initialise this application
	rts

;*======================================================================*
;* VIDINIT	: Jaguar Video Initialization Routine			*
;*======================================================================*

vidinit:
	move.w	#n_hdb1,HDB1
	move.w	#n_hdb2,HDB2
	move.w	#n_hde,HDE

	move.w	#n_vdb,VDB
	move.w	#n_vde,VDE

	rts

;*======================================================================*
;* VBLANK	: Vertical Sync Interrupt Handler			*
;*									*
;* It restores object list for next frame				*
;*======================================================================*

vblank:
	movem.l	a0-a1/d0-d1,-(sp)

	move.w	INT1,d0			; get interrupt status register
	cmpi.w	#1,d0			; is this a video interrupt ?
	bne	.done			; no so quit

	jsr	CopyObjList		; refresh objlist list
	addq.l	#1,framecnt		; count a video frame
.done:	move.w	#$101,INT1		; enable video interrupt and clear all others pending
	move.w	#0,INT2

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

;*======================================================================*
;* VBLINIT	: Setup the Vblank Interrupt Handler			*
;*======================================================================*

vblinit:
	movem.l	d0,-(sp)		; save registers on stack

	move.l	#vblank,LEVEL0		; setup vblank interrupt handler

	move.w	#(n_vde|1),VI
	move.w	#$1,INT1		; enable interrupt

	move.w	sr,d0			; clear all pending interrupt
	and.w	#$f8ff,d0
	move.w	d0,sr

	movem.l	(sp)+,d0		; restore registers
	rts

;************************************************************************
;* OBJECT MANIPULATION ROUTINES						*
;************************************************************************

;*======================================================================*
;* InitObjList	: Initialize the Object List				*
;*======================================================================*

InitObjList:
	movem.l	d0-d2/a0-a1,-(sp)

	move.l	#olist+15,d0		; force bi-phrase alignement
	andi.w	#~15,d0			; at first bi-phrase boundary in olist
	move.l	d0,dlistptr		; save for use by vblank

	lea.l	Objects,a0		; pointer to ROM object list

	clr.l	d1
	lea.l	objData,a1		; pointer to RAM object list
	move.w	(a0)+,d1		; d1 = number objects in list
	move.l	d1,d2			; d2 = number objects in list
	sub.l	#1,d1			; d1 = last object index
	move.w	d1,lastObj		; save last object index
	mulu	#OBJSIZE,d2		; total bytes in object list
	subq.l	#1,d2
	lsr.l	#1,d2			; total words in object list
					; assuming even no of bytes
.cpyloop:
	move.w	(a0)+,(a1)+		; copy ROM data to RAM
	dbra	d2,.cpyloop

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

;*======================================================================*
;* BuildObjList	: Build the Object Display List				*
;*									*
;*	Register Useage:						*
;*		A1 = address of object					*
;*		A2 = the address of the object list being built		*
;*		D0 = holds the long being built				*
;*		D1 = used for temporary calculations			*
;*		D2 = the lines off screen, calculated in set_odata,	*
;*		     used by set_oheight				*
;*		D3 = #12 for shifting d0				*
;*		D4 = #10 for shifting d0				*
;*		D5 = #11 for shifting d0				*
;*		D6 = count of the object being processed		*
;*		D7 = the address of the boject used by the object	*
;*		     processor divided by 8				*
;*									*
;*======================================================================*

BuildObjList:
	movem.l	a0-a2/d0-d7,-(sp)

	lea.l	objData,a1
	clr.l	d6
	move.w	lastObj,d6
	add.w	#1,d6

	moveq	#12,d3			; for doing lsl.l on d0
	moveq	#10,d4			; for doing lsl.l on d0
	moveq	#11,d5			; for doing lsl.l on d0

	move.l	dlistptr,d7		; Here it is, bi-phrased
	lsr.l	#3,d7			; divide by 8 for object processor

	lea	olist_ram,a2		; address of copy of packed olist
	sub.w	#1,d6			; start dbra counter at total objects - 1

	;*==============================================================*
	;* PROCESS AN OBJECT						*
	;*==============================================================*

proc_obj:
	tst.b	O_TYPE(a1)		; check the delete flag for this object
	bmi	next_obj		; skip this object if negative bit is set

	;*==============================================================*
	;* PHRASE 1
	;*==============================================================*

set_odata:

	;*==============================================================*
	;* if the object is above top line of the screen, the data ptr	*
	;* is set to the first line of the object that is on screen	*
	;*==============================================================*

	move.l	O_DATA(a1),d0		; get normal data address
	move.w	#SCRNTOP-1,d2
	sub.w	O_YPOS(a1),d2		; top of screen - ypos = half lines off top of screen
	bpl	.above			; if object is above top of screen, use normal data address
	clr.w	d2			; 0 lines off screen, use normal data address
	bra	.setlink

.above:
	lsr.w	#1,d2			; convert half lines to lines
	move.w	O_DWIDTH(a1),d1		; get phrases per line of data in the object
	lsl.w	#3,d1			; multiply phrases * 8 bytes per phrase
	mulu.w	d2,d1			; lines off screen * bytes per line in the object
	add.l	d1,d0			; skip the total bytes that are off the top of the screen

.setlink:
	lsr.l	#3,d0			; divide by 8
	lsl.l	d5,d0			; shift left 11 bits to make room for link pointer

	;*==============================================================*
	;* set the link pointer to the next object			*
	;*==============================================================*

	cmpi.b	#BITOBJ,O_TYPE(a1)	; see if this is a bit map object
	bne	.chk_scal
	add.l	#BITSIZE,d7		; skip past bitmapped object
	bra	.adjlink

.chk_scal:
	cmpi.b	#SCBITOBJ,O_TYPE(a1)	; see if this is a scaled bit map object
	bne	.chk_gpu
	add.l	#SCBITSIZE,d7		; skip past scaled bit map object
	bra	.adjlink

.chk_gpu:
	cmpi.b	#GPUOBJ,O_TYPE(a1)	; see if this is a GPU object
	bne	.chk_branch
	add.l	#GPUSIZE,d7		; skip past GPU object
	bra	.adjlink

.chk_branch:
	cmpi.b	#BRANCHOBJ,O_TYPE(a1)	; see if this is a branch object
	bne	.chk_stop
	add.l	#BRANCHSIZE,d7		; skip past branch object
	bra	.adjlink

.chk_stop:
	add.l	#STOPSIZE,d7		; skip past branch object

.adjlink:
	move.l	d7,d1			; get address in olist of next object

	lsr.l	#8,d1			; the rightmost 8 bits go in the next long, so remove them
	or.l	d1,d0			; or with data pointer
	move.l	d0,(a2)+		; save first long in phrase 1, advance the pointer to next long

	move.b	d7,d0			; get the low 8 bits of the link pointer that was saved earlier
	lsl.l	d4,d0			; shift left 10 bits to make room for height

set_oheight:

	;*==============================================================*
	;* d2.w is set to the number of lines that are above the top of	*
	;* the screen.   if part of the object is above the top line of	*
	;* the screen, the height is reduced by the number of lines off	*
	;* the top of the screen					*
	;*==============================================================*

	move.w	O_HEIGHT(a1),d1		; get the height of the object
	cmpi.b	#SCBITOBJ,O_TYPE(a1)	; see if this is a scaled bit map object
	bne.b	.notscaled		; branch if object is not scaled
	sub.w	#1,d1			; an extra line is displayed at the bottom
					; of large scaled objects if this is not here

.notscaled:
	sub.w	d2,d1			; subtract lines off screen from scaled height
	bmi.b	.gtheight		; if lines off screen > height
					; make height 0 instead of negative
	or.w	d1,d0			; or with link pointer in second half of phrase 1

.gtheight:
	lsl.l	d5,d0			; shift left 11 bits to make room for ypos

set_oypos:

	;*==============================================================*
	;* if ypos is negative, it gets set to 0			*
	;* set_odata and set_oheight will adjust the data pointer and	*
	;* height accordingly						*
	;*==============================================================*

	move.w	O_YPOS(a1),d1
	bmi.b	.negypos		; if ypos is negative
					; object would not be displayed, so set ypos to 0
	and.w	#$7ff,d1		; only write 11 bits for ypos
	or.w	d1,d0			; or with link pointer and height

.negypos:
	lsl.l	#3,d0			; shift left 3 bits to make room for type

set_otype:
 	or.b	O_TYPE(a1),d0		; object type
	move.l	d0,(a2)+		; write second half of phrase 1

	;*==============================================================*
	;* PHRASE 2							*
	;*==============================================================*

set_ofirstpix:
	move.b	O_FIRSTPIX(a1),d0
	lsl.l	#4,d0			; shift left 4 bits to make room for release, transparent, rmw, reflect bits

set_flags:
	or.b	O_FLAGS(a1),d0		; or with firstpix
	lsl.l	#7,d0			; shift left 7 bits to make room for index

set_oindex:
	or.b	O_INDEX(a1),d0		; or with firstpix and flags
	lsl.l	#6,d0			; shift left 6 bits to make room for part of iwidth

set_oiwidth:
	move.w	O_IWIDTH(a1),d1			
	lsr.w	#4,d1			; right 4 bits go in the next long, so remove them
	or.b	d1,d0
	move.l	d0,(a2)+		; write first long of phrase 2

	move.w	O_IWIDTH(a1),d0			
	and.w	#$F,d0			; get low 4 bits of iwidth
	lsl.l	d4,d0			; shift left 10 bits to make room for dwidth

set_odwidth:
	or.w	O_DWIDTH(a1),d0		; or with part of iwidth
	lsl.l	#3,d0			; shift left 3 bits to make room for pitch

set_opitch:
	or.b	O_PITCH(a1),d0		; or with dwidth and iwidth
	lsl.l	#3,d0			; shift left 3 bits to make room for depth

set_odepth:
	or.b	O_DEPTH(a1),d0		; or with dwidth, iwidth, pitch
	lsl.l	d3,d0			; shift left 12 bits to make room for xpos

set_oxpos:
	move.w	O_XPOS(a1),d1		; object xpos
	and.w	#$fff,d1		; only write 12 bits for xpos
	or.w	d1,d0			; or with the rest of the stuff
	move.l	d0,(a2)+		; write second half of phrase 2

	cmpi.b	#SCBITOBJ,O_TYPE(a1)	; see if this is a scaled bit map object
	bne	next_obj		; branch if object is not scaled

	;*==============================================================*
	;* PHRASE 3							*
	;*==============================================================*

	;*==============================================================*
	;* for scaled bit map object only				*
	;*==============================================================*

	move.l	O_SCALE(a1),4(a2)	; write remainder, vscale, hscale bytes in 2nd long of phrase 3
	adda.l	#16,a2			; add extra 2 phrases for scaled object

next_obj:				; jump to here if delete flag is on
	adda.l	#OBJSIZE,a1		; point to next object in pseudo object list
	dbra	d6,proc_obj		; repeat until all objects have been processed
	
	;*==============================================================*
	;* write a stop object at the end of the list			*
	;*==============================================================*

	move.l	#0,(a2)+		
	move.l	#STOPOBJ,(a2)		; write object type 4 (stop object)

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

;*======================================================================*
;* CopyObjList	: Build Object List					*
;*======================================================================*

CopyObjList:
	movem.l	a0-a1,-(sp)

	lea	olist_ram,a0		; beginning of olist_ram
	move.l	dlistptr,a1		; bi-phrased olist

.cpyloop:
	move.l	(a0),(a1)+		; copy olist_ram to olist
	cmpi.l	#STOPOBJ,(a0)+		; see if we are at the stop object
	bne.b	.cpyloop

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

;*======================================================================*
;* InitObjProc	: Initialise the object processor registers		*
;*======================================================================*

InitObjProc:
	move.l	d0,-(sp)

	move.l	dlistptr,d0		; initialise object list
	swap	d0			; pointer register for the
	move.l	d0,OLP			; object processor

	move.l	(sp)+,d0
	rts

;************************************************************************
;* JOYPAD HANDLER							*
;************************************************************************

;*======================================================================*
;* READPAD	: Read the Jaguar Joypad				*
;*									*
;* padnow = Current reading of joypad (Asserted High)			*
;* padshot  = Edge-triggered One Shot of joypad				*
;*======================================================================*

readpad:
	movem.l	d0-d2,-(sp)

	move.l	#$F0FFFFFC,d1		; d1 = Joypad data mask
	moveq.l	#-1,d2			; d2 = Cumulative joypad reading

	move.w	#$810E,JOYOUT
	move.l	JOYIN,d0		; Read Stick and Pause/A
	or.l	d1,d0			; Mask off unused bits
	ror.l	#4,d0
	and.l	d0,d2			; d2 = xxApxxxx RLDUxxxx xxxxxxxx xxxxxxxx

	move.w	#$810D,JOYOUT
	move.l	JOYIN,d0		; Read *741 and B
	or.l	d1,d0			; Mask off unused bits
	ror.l	#8,d0
	and.l	d0,d2			; d2 = xxApxxBx RLDU741* xxxxxxxx xxxxxxxx

	move.w	#$810B,JOYOUT
	move.l	JOYIN,d0		; Read 2580 and C
	or.l	d1,d0			; Mask off unused bits
	rol.l	#6,d0
	rol.l	#6,d0
	and.l	d0,d2			; d2 = xxApxxBx RLDU741* xxCxxxxx 2580xxxx

	move.w	#$8107,JOYOUT
	move.l	JOYIN,d0		; Read 369# and Option
	or.l	d1,d0			; Mask off unused bits
	rol.l	#8,d0
	and.l	d0,d2			; d2 = xxApxxBx RLDU741* xxCxxxox 2580369#

	moveq.l	#-1,d1
	eor.l	d2,d1			; d1 = Inputs active high

	move.l	padnow,d0
	move.l	d1,padnow		; LEVELS = Current joypad reading
	eor.l	d1,d0			;	0 & 0 = 0
	and.l	d1,d0			;	0 & 1 = 1
					;	1 & 0 = 0
					;	1 & 1 = 1

	move.l	d0,padshot		; EDGES = Oneshot joypad

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

;************************************************************************
;* MAIN EVENT HANDLER							*
;************************************************************************

;*======================================================================*
;* MAIN : Routine to Handle Events					*
;*======================================================================*

main:
	jsr	HandleCD
mainloop:
	jsr	readpad		; read controller
	jsr	Hndlpad		; handle controller inputs
	jsr	Subcode		; read and process subcodes from a frame
	jsr	Subproc		; process subcodes

	move.w	bldobflg,d0	; do we have to rebuild the object list
	beq.s	mainloop	; no, continue handling events

	move.l	framecnt,d7	; save framecnt for SYNC later
	move.w	#0,bldobflg	; clear the object rebuild flag
	jsr	BuildObjList	; update the object data list
.wsync:	cmp.l	framecnt,d7	; synchronise to next frame
	beq.s	.wsync		; not in sync yet

	bra	mainloop
	rts


;************************************************************************
;* VARIABLE DATA AREA							*
;************************************************************************

	.bss

;*======================================================================*
;*======================================================================*

		.phrase

		.ds.l	2000			; 8k for stack
newstack:	.ds.l	1

		.phrase

dlistptr:	.ds.l	1			; pointer to our display list, bi-phrase aligned

		.phrase

objData:	.ds.b	(MAXOBJS * OBJSIZE)	; need 26 bytes for each object
lastObj:	.ds.w	1			; index of last object in list

		.phrase

olist:		.ds.l	2			; needed for bi-phrased
		.ds.l	(8 * MAXOBJS)		; need 8 longs for each object
		.ds.l	2			; 2 longs for a stop object
olist_ram:	.ds.l	(8 * MAXOBJS)		; 8 longs for each object
		.ds.l	2			; 2 longs for a stop object

		.phrase

screnmem:	.ds.w	(384*200)		; screen memory
dummy:		.ds.w	(256)			; just some dummy memory area

		.phrase

framecnt:	.ds.l	1

bldobflg:	.ds.w	1	; re-build object list flag

		.phrase

padnow:		.ds.l	1	; Levels Joypad reading ( = last && current )
		.ds.l	1	; PLAYER 2: xxApxxBx RLDU741* xxCxxxox 2580369#

padshot:	.ds.l	1	; Edges Trigger Joypad reading
		.ds.l	1	; PLAYER 2: xxApxxBx RLDU741* xxCxxxox 2580369#

		.phrase

;************************************************************************
;* END OF FILE								*
;************************************************************************

	.end
