;...................................
;: (c) 1993 Rebellion Software Ltd :
;: Hand coded by R.C.Dibley - 1993 :
;:.................................:
; uses for registers - hopefully consistent
; d0 - main counter of objects
; d1 - last address for links
; d2 - general workings - eg object type to address mask conversion
; d3 - previous object pitch for deciding on double buffered ones
; 
; a0 - input pointer
; a1 - output pointer
; a2 - animation list pointer
; a3 - double buffered object list pointer
; a4 - address of eventual work place for list


.text

ObjSetup::				; a0 is input address

	movem.l	d0-d5/a0-a3,-(sp)

	moveq	#0,d5
	tst.b	ntsc_type.w
	bne.b	.ntsc
	move.l	#PALyoff,d5

.ntsc:	tas.b	newolist.w		; prevents any changes being taken up whilst under way
	movea.w	#new_list,a1		; address for object list (physical output)
	movea.w	#anim_new_list,a2	; in case of any animated objects
	movea.w	#obj_new_adjust,a3	; list of adjustments to make
	movea.w	#use_list,a4		; address for object list (final active address)
	move.l	a1,-(sp)		; stack for much later use

	move.w	(a0)+,d0		; number of objects
	beq	.stop			; for the rare occasion of a zero length list (just a stop object)
	subq.w	#1,d0			; cut by one
	move.l	a1,-(sp)		; stack the address

	bra.b	.skipit			; avoid the link address calcs when doing first object

.linkit:				; address now is the start of the next object and can be stuffed into the last one
					; but first test whether object is "on"

	btst.b	#7,8(a0)		; put a high bit in the type to indicate "OFF"
	beq.b	.obon			; if object is "ON"
	lea	objlen(a0),a0		; move address to next object
	bra	.odone			; and jump to the loop counter

.obon:	move.l	a1,d1			; copy current output address
	moveq.l	#0,d2			; clear it
	move.b	8(a0),d2		; get the NEXT items type in case its 0 or 1
	cmp.b	#2,d2			; check
	bge.b	.linkgo			; no need to adjust
	addq	#1,d2			; make 0/1 into 1/2
	asl.b	#4,d2			; make 1/2 into 16/32 
	subq	#1,d2			; cut to 15/31
	add.l	d2,d1			; add to address
	not.l	d2			; turn offset into mask (eg FFFFFFF0)
	and.l	d2,d1			; apply mask to sort out address
	add.l	d1,a4			; add to 'final' address
	sub.l	a1,a4			; and remove 'current' address to apply any extra offset needed

.linkgo:				; having sorted out the address, put it into previous and retain
	move.l	(sp)+,a1		; get previous object address - this is the build address, not the use address
	cmpi.b	#2,d3
	bne.b	.no_ak
	lea	use_list-new_list(a1),a1
	move.l	a1,(a3)+		; write out correction point address
	lea	new_list-use_list(a1),a1
.no_ak:	move.l	d1,-(sp)		; stack the address
	move.l	a4,d2			; copy of use address
	asl.l	#5,d2			; push up
	swap	d2			; thereby putting the required parts in place
	addq.w	#2,a1			; move along
	or.w	d2,(a1)+		; or in part of link address
	swap	d2
	or.w	d2,(a1)+		; and the rest of link address

	btst.b	#6,8(a0)		; additional flag bit for animation status
	beq.b	.nanim
	move.w	a4,(a2)+		; store use address of object in OL
	move.l	a0,-(sp)		; push pointer to allow return
	movea.l	(a0),a0
	move.w	(a0)+,(a2)+		; first entry is always a word, remainder add up to longs
.lp1:	move.l	(a0)+,(a2)+		; data transfer
	bne.b	.lp1			; until the zero end marker
	subq.l	#4,a2			; to recover the zero which is not required
	movea.l	(sp)+,a0		; recover address and continue as if nothing had happened

.nanim:	movea.l	d1,a1			; put back into address register

.skipit:
	move.l	(a0)+,d1		; get data address
	and.b	#$F8,d1			; make sure its phrase aligned
	asl.l	#8,d1			; shift up to make room for link address

	move.l	d1,(a1)+		; note the link address isn't in yet - leave to the end

	moveq.l	#0,d1
	move.w	(a0)+,d1		; get height
	swap	d1
	asr.l	#5,d1			; overall = shift up by 11

	or.w	(a0)+,d1		; or in the Ypos
	add.l	d5,d1			; d5 contains pal offset
	asl.l	#3,d1			; push up
	move.b	(a0)+,d2		; type number
	and.b	#$3F,d2			; clear flags
	or.b	d2,d1			; done this way because need type number for checking #MIDNIGHT

	move.l	d1,(a1)+		; write out rest of phrase
	addq.l	#8,a4			; apply one phrase

	cmp.b	#2,d2			; check if its a short one
	bge.b	.odone

; must be 0 or 1 type so write another phrase, but keep type in d2 !

	moveq.l	#0,d1
	move.b	(a0)+,d1		; get first pixel
	asl.l	#4,d1
	or.b	(a0)+,d1		; get release etc flags
	asl.l	#7,d1
	or.b	(a0)+,d1		; get index
	asl.l	#6,d1
	move.l	d1,(a1)+

	moveq.l	#0,d1
	move.w	(a0)+,d1		; get IWIDTH - not incremented
	ror.l	#4,d1			; require just upper bits for now
	or.w	d1,-2(a1)
	rol.l	#4,d1			; get IWIDTH back again
	andi.w	#$F,d1			; dump junk
	swap	d1	
	lsr.l	#6,d1			; ready for DWIDTH
	or.w	(a0)+,d1		; get DWIDTH
	asl.l	#3,d1
	move.b	(a0)+,d3		; get pitch
	or.b	d3,d1
	asl.l	#3,d1
	or.b	(a0)+,d1		; get depth
	swap	d1
	ror.l	#4,d1
	move.w	(a0)+,d4
	andi.w	#$fff,d4
	or.w	d4,d1			; get Xpos
	move.l	d1,(a1)+
	addq.l	#8,a4			; another phrase done

	tst.b	d2
	beq.b	.odone			; if its not scaled.


	moveq.l	#0,d1
	move.l	d1,(a1)+		; blank part
	move.b	(a0)+,d1		; get remainder
	swap	d1
	move.b	(a0)+,d1		; get v-scale
	asl.w	#8,d1
	move.b	(a0)+,d1		; get h-scale
	addq.w	#1,a0			; for the sake of even-ness
	move.l	d1,(a1)+		; output final piece
	addq.l	#8,a4			; last phrase done

.odone:	dbra	d0,.linkit

; need to link last object to the stop object at the end . . .

	move.l	a4,d1			; get current use address
	move.l	(sp)+,a0		; get previous object address
	cmpi.b	#2,d3
	bne.b	.no_aj
	lea	use_list-new_list(a0),a0
	move.l	a0,(a3)+		; write out correction point address
	lea	new_list-use_list(a0),a0
.no_aj:	asl.l	#5,d1			; push up
	swap	d1			; thereby putting the required parts in place
	addq.w	#2,a0			; move along
	or.w	d1,(a0)+		; or in
	swap	d1
	or.w	d1,(a0)+		; and the rest

.stop:	moveq.l	#0,d1
	move.l	d1,(a1)+		; empty part of stop object
	move.l	d1,(a2)+		; halt at end of anim list
	move.l	d1,(a3)+		; halt at end of double buffering list
	addq.l	#4,d1
	move.l	d1,(a1)+		; stop object done

; now put the updated quantity to move - moving the new data into place is left to the interrupt routine

	move.l	a1,d1			; to calculate longs to move
	move.l	(sp)+,a1		; regain (initial) start address
	sub.l	a1,d1			; work out length
	asr.w	#2,d1			; divide by 4 - no concern about leftover bits, because all objects are phrase multiples
	move.w	d1,new_obj_count.w	; so that total number of longs is available to reset routine

	clr.b	newolist.w		; clearing this means that new version will be installed

	movem.l	(sp)+,d0-d5/a0-a3

	rts


; this routine sets up a stop object in the new object list, and then sets the
; reset routine to do just this, and triggers an auto swap


VidOff::
	movea.w	#new_list,a0
	moveq.l	#0,d0
	move.l	d0,anim_new_list.w	; stop animations
	move.l	d0,obj_new_adjust.w	; stop double buffering
	move.l	d0,(a0)+		; start setting up a stop object
	moveq.l	#4,d0
	move.l	d0,(a0)+
	move.w	d0,new_obj_count.w	; set reset count to 4 - really only needs two, but won't hurt
	clr.b	newolist.w		; triggers interrupt driven swap
	rts

; next a routine to reset the object list in its entirety.
; in order to take account of the few double buffered objects,
; an extra list is used, and similarly the animated objects.

object_reset::

	movem.l	d0-d5/a0-a3,-(sp)

	tas	newolist.w		; test, and if clear then need to work from new list
	bne	std

;=============================================================================
; this section is only used when a new list has been prepared, and copies the new list into place, updating everything as it goes

	movea.w	#new_list,a0
	movea.w	#reset_list,a1
	movea.w	#use_list,a2

	move.w	new_obj_count.w,d5
	move.w	d5,object_count.w
	addq.w	#3,d5
	lsr.w	#2,d5
	moveq	#16,d4
	bra.b	.lp

.next_set:
	movem.l	(a0)+,d0-d3		; read four longs = 1 standard object	; timing = 12+4*8
	movem.l	d0-d3,(a1)		; note not allowed to use (a1)+		; timing = 8+4*8
	adda.w	d4,a1			; move address				; timing = 8
	movem.l	d0-d3,(a2)		; note not allowed to use (a1)+		; timing = 8+4*8
	adda.w	d4,a2			; move address				; timing = 8
.lp:	dbra	d5,.next_set		; run around again			; total 92 per round vs 80 per round for move.l

	movea.w	#anim_new_list,a0	; copy new animation list into place
	movea.w	#anim_list,a1
.lpa:	move.l	(a0)+,(a1)+
	bne	.lpa

	movea.w	#obj_new_adjust,a0	; and copy new double buffered list into place
	movea.w	#obj_adjust,a1
.lpd:	move.l	(a0)+,(a1)+
	bne	.lpd

	bra.b	anim

;=============================================================================
; this is the standard section which copies the reset version into the in use version

std:	movea.w	#reset_list,a0		; reset table address
	movea.w	#use_list,a1		; actual table address

	move.w	object_count.w,d5
	addq.w	#3,d5
	lsr.w	#2,d5
	bra.b	.lp

.next_set:
	movem.l	(a0)+,d0-d3		; read four longs = 1 standard object	; timing = 12+4*8
	movem.l	d0-d3,(a1)		; note not allowed to use (a1)+		; timing = 8+4*8
	lea	16(a1),a1		; move address				; timing = 8

;	move.l	(a0)+,(a1)+		; transfer first long			; timing = 20
;	move.l	(a0)+,(a1)+		; and second long			; timing = 20
;	addq.l	#8,a0			; and point at next object		; timing = 8
;	addq.l	#8,a1			; in both sets				; timing = 8
.lp:	dbra	d5,.next_set		; run around again			; total 92 per round vs 80 per round for move.l*4, and 56 for new type

;==============================================================================

anim:	movea.w	#anim_list,a0
	lea	anim_subs-*-4(PC),a2	; read address of table of subroutines with extra 2 off to allow for no zero entry
alp:	move.w	(a0)+,d0		; read output address (0 if end of list)
	beq.b	dbuf
	movea.w	d0,a1			; object address gets extended to long
	move.w	(a0)+,d0		; read type code
	asl.w	#1,d0			;
	move.w	(a2,d0),d0		; read subroutine address in to a2
basic:	jsr	-2(PC,d0.w)
	bra.b	alp

dbuf:	move.l	screen.w,d4		; get screen number
	eori.w	#8,d4			; turn 0/8 into 8/0
	asl.l	#8,d4			; up by 8 - for placement in addresses of double buffered objects
.dan:	movea.w	#obj_adjust,a0
	bra.b	.lpe

.adj:	movea.l	d0,a1
	add.l	d4,(a1)
.lpe:	move.l	(a0)+,d0
	bne	.adj

	move.l	#MOBJ,d0		; not sure if this is needed - but has been in since day 1
	swap	d0
	move.l	d0,OLP

clut::	movea.w	CLUT_out.w,a0		; get current buffer position
	movea.w	CLUT_in.w,a1		; get buffer inward position

.clp:	cmpa.l	a0,a1			; if equal then no more changes
	beq.b	.exit

	movea.w	#CLUT_table,a2		; lower address limit
	movea.l	#CLUT,a3		; address of actual clut

	move.w	-(a0),d0		; read target address (offset)
	move.w	-(a0),(a3,d0)		; read new CRY colour to install and install it

	cmpa.l	a0,a2			; test against limit address - if hit, then need to add length of table
	bne	.clp			; if not hit then can go round checking again

	lea	$100(a0),a0		; move to top of buffer
	bra	.clp

.exit:	move.w	a0,CLUT_out.w		; re-write address

	movem.l	(sp)+,d0-d5/a0-a3

	rts

CLUT_write::				; writes colour d0 to offset d1 in the CLUT using the interrupt driven system
	movem.l	a0-a1,-(sp)
	movea.w	CLUT_in.w,a0
	movea.w	#CLUT_table,a1

	move.w	d1,-(a0)
	move.w	d0,-(a0)

	cmpa.l	a0,a1
	bne.b	.exit

	lea	$100(a0),a0

.exit:	move.w	a0,CLUT_in.w
	movem.l	(sp)+,a0-a1
	rts

simple:	move.w	(a0)+,d1		; get limiter
	move.w	(a0),d0			; get counter
	addq.w	#1,d0
	cmp.w	d1,d0
	bmi.b	nol
	moveq.l	#0,d0
nol:	move.w	d0,(a0)+		; write back new counter
	bpl.b	.ok
	neg.w	d0
.ok:	asl.w	#2,d0			; scale up for address use
	move.l	(a0,d0.w),d0		; get new address
	asl.w	#2,d1			; scale up counter
	add.w	d1,a0			; and adjust input address

wrad:	asl.l	#8,d0			; scale up 
	moveq.l	#0,d2
	move.w	2(a1),d2		; get current object data - low part
	and.w	#$7ff,d2		; strip spare
	or.l	d0,d2			; mix it	
	move.l	d2,(a1)			; and write out
	rts

reverse:move.w	(a0)+,d1		; get limit
	move.w	(a0),d0			; get counter
	addq.w	#1,d0			; increment
	cmp.w	d1,d0			; test
	bmi	nol
	moveq	#1,d0
	sub.w	d1,d0			; new
	bra	nol

step_simple:
	movem.l	(a0)+,d1-d3		; read all three at once (start/limit/increment)
	move.l	(a0),d0			; read current
	add.l	d3,d0			; add increment
	cmp.l	d2,d0			; test against limit
	bmi.b	nor
	sub.l	d2,d0			; remove limit
	add.l	d1,d0			; and move back to start
nor:	move.l	d0,(a0)+		; write out new current
	bpl	wrad
	neg.l	d0
	bra	wrad

step_reverse:
	movem.l	(a0)+,d1-d3
	move.l	(a0),d0
	add.l	d3,d0
	cmp.l	d2,d0
	bmi.b	nor
	neg.l	d0
	add.l	d3,d0
	bra	nor

fader:
	move.b	(a0)+,d1		; increment to apply to CLUT entry 0
	beq.b	.nofa
	move.l	CLUT,d0			; read current entry
	swap	d0
	add.b	d1,d0
	move.b	(a0)+,d1
	cmp.b	d1,d0
	bne.b	.wri

	clr.b	-2(a0)			; cancels increment
	clr.b	fade_over.w		; notes completion
	
.wri:	swap	d0
	move.l	d0,CLUT
.wra:	moveq	#0,d0			; essential to prevent cockups
	move.w	(a0)+,d0
	bra	wrad

.nofa:	addq.l	#1,a0
	bra	.wra

anim_subs:
	entry	simple
	entry	reverse
	entry	step_simple
	entry	step_reverse
	entry	fader

set_object_iwidth::			; set object number d0 (where d0 is the number from objdefs) to width d1
	movem.l	d0/a0,-(sp)
	movea.l	#reset_list,a0		; get address of object data
	lea.l	10(a0,d0),a0		; address of the long which includes iwidth
		; trrxxxxxxxiiiiiiiiiiddddddddddpp [trans,rmw,reflect,index,iwidth,dwidth,pitch]
	move.l	(a0),d0
	and.l	#%11111111110000000000111111111111,d0
	or.l	d1,d0
	move.l	d0,(a0)
	movem.l	(sp)+,d0/a0
	rts

set_object_xpos::
	movem.l	d0-d1/a0,-(sp)
	movea.l	#reset_list,a0		; get address of object data
	lea.l	14(a0,d0),a0		; address of the long which includes xpos
	move.w	(a0),d0
	and.w	#$f000,d0
	and.w	#$0fff,d1		; make signed xpos fit in 12 bits
	or.w	d1,d0
	move.w	d0,(a0)
	movem.l	(sp)+,d0-d1/a0
	rts

set_object_address::
	movem.l	d0-d1/a0,-(sp)
	movea.l	#reset_list,a0		; get address of object data
	adda.l	d0,a0
	move.l	(a0),d0
	and.l	#$7ff,d0		; remove old address
	asl.l	#8,d1
	or.l	d1,d0
	move.l	d0,(a0)
	movem.l	(sp)+,d0-d1/a0
	rts

set_ypos::
	movem.l	d0-d1/a0,-(sp)
	movea.w	#reset_list,a0
	lea.l	6(a0,d0),a0
	asl.w	#1,d1			; turn into number of half lines
	add.w	#ScnSt,d1		; gives proper ypos value
	tst.b	ntsc_type.w
	bne.b	.ntsc
	add.l	#PALyoff,d1
.ntsc:	asl.w	#3,d1			; step over type
	move.w	(a0),d0			; read current value
	and.w	#%1100000000000111,d0
	or.w	d1,d0
	move.w	d0,(a0)			; write out
	movem.l	(sp)+,d0-d1/a0
	rts

set_object_height::
	movem.l	d0-d1/a0,-(sp)
	movea.l	#reset_list,a0
	lea	4(a0,d0),a0
	move.l	(a0),d0
	; format: llllllllhhhhhhhhhhyyyyyyyyyyyttt
	and.l	#%11111111000000000011111111111111,d0
	swap	d1
	lsr.l	#2,d1
	or.l	d1,d0
	move.l	d0,(a0)
	movem.l	(sp)+,d0-d1/a0
	rts



;============================================================

.end
; animations - alternative approach :
; allow multiple types :
; 1. simple cycle - when read last entry, start again at entry zero
; 2. reversing cycle - when read last entry, reverse direction - by using negative numbers
; 3. stepped cycle - increment address by fixed amount until passes limit, then subtract fixed amount
; 4. stepped reversing cycle - same, but when passes limit, negate the increment and check against other limit
; 5. palette fade : applies a word increment to the CLUT 0 colour to allow interrupt driven fades (hence ultra smooth)

; implementation :
; 1 word address before anything else - you always need to know which object to change
; 1 word identifier : 1-4 as above but *4 for offsets into a table
; table contains addresses of four (or more) routines and these are run as subroutines
; type 1 : limit.w, counter.w	followed by [limit]*address.l
;	read counter and limit
;	increment counter
;	check against limit
;	if over-run, reset to zero
;	write out new counter and get address	
;	move pointer to end of list

; type 2 : limit.w, counter.w	followed by [limit]*address.l
;	read counter and limit
;	increment counter
;	check against limit
;	if over-run, reset to 1-limit [eg with limit 3, goes 0,1,2,3,-2,-1,0,1,... ]
;	write out new counter and get address [which uses |c| to give 0,1,2,3,2,1,0,1,...]
;	move pointer to end of list

; type 3 : start.l, limit.l, increment.l, current.l
;	read start, limit and increment
;	add current to increment
;	check against limit
; 	if over then subtract limit and add start - this allows rolling anims which are not exact multiples
;	write out new current
;	pointer automatically moves

; type 4 : stepped reverse

; type 5 : intensity increment.b, target.b
; the problem here is that we want it to STOP when it hits the end !
; so to attain this, we need careful thought...
; current two requirements are - up in steps of 1 or 2 to 0, and down to $80, to give the full
; range of darkening.  This is not all though - we need a flag for finishing, so that it can be used in
; various places, and we need to be sure it can't over-run.  Problems would occur if the two step version
; started at an odd number, but can we detect them easily ?

;imagine 1,ff,fd,...,83,81, then what

;81-2 = 7f, check when using -ve increment for unsigned lt - works, then trigger inputting limit and note completion, and zero inc.
;going back the other way, would have to use signed test so that it only triggers at the right time
;for now we'll use a equality test and only use single steps - just hope I remember !

; others are useful - 1 for current flashing blob, 2 for updating it, 3 for current and new rain anim
; note however that 4 could be used if implemented to restructure and simplify flashing blob 


backwards loops can be accomplished as follows :
-limit,-start,increment,-start

then current goes forwards through negative addresses, which get negated before use,
and then when lower limit is hit, it starts again from -limit.


animation data structure sizes ...

1 n.w,x.w,n*a.l	= 6+[2(a0).w<<2]
2 ditto
3 s.l,l.l,i.l,c.l = 18
4 ditto
5 i.b,t.b,a.w = 6

