;*******************************************************************************
;* TITLE	: SLIDSHOW.S						       *
;*									       *
;* Function	: Startup, object list and event handling routines for CDROM   *
;*		  sample programa.  Loads animation sequences from the CDROM   *
;*		  and playes them on the screen.			       *
;*									       *
;* Project #	: ANIMATION SEQUENCES from Sample CD			       *
;*									       *
;* Programmer	: Pradip K Fatehpuria	05-25-94			       *
;*									       *
;* COPYRIGHT(c) 1992,1993,1994 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.					       *
;*									       *
;*******************************************************************************

;*******************************************************************************
;* 25 MAY 94								       *
;*									       *
;* This program provides a slideshow of different animation sequences (RGB)    *
;* from Crescent Galaxy.  These files are added a header and are stored on a   *
;* CDROM.  Right now, the test is being done using the CDROM emulator.	       *
;*									       *
;* A-FIRE button to switch to the next picture				       *
;* B-FIRE button to switch to the previous picture			       *
;* C-FIRE button to change background colour				       *
;*									       *
;* KEY-* to decrease the frame rate					       *
;* KEY-# to increase the frame rate					       *
;*									       *
;* The ARROW key pad to position the animation on the screen		       *
;*									       *
;* KEY-0 to quit to the DB						       *
;*******************************************************************************

	.title	"SLIDSHOW.S - The animation sequences from CDROM demo"

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

	.include "jaguar.inc"
	.include "joypad.inc"
	.include "blit.inc"
	.include "objlist.inc"
	.include "defs.inc"
	.include "cd.inc"

	.include "filelist.inc"

;*=============================================================================*
;* EXTERNAL DATA AND ROUTINES						       *
;*=============================================================================*

	.extern	end			; needed for GPU program load by DB
	.extern GPU_done
	.extern GPU_state
	.extern	SETUP

	.extern	GPUSTART
	.extern	GPUEND

	.extern	SETUP
	.extern	CDREADER
	
;*=============================================================================*
;* LOCAL EQUATES							       *
;*=============================================================================*

;****************************************
;* The size of the horizontal and	*
;* vertical active areas are based on a	*
;* given center position and then built	*
;* as offsets of half the size off of	*
;* these.				*
;*					*
;* In the horizontal direction this	*
;* needs to take into account the	*
;* variable resolution that is		*
;* possible.				*
;****************************************

;****************************************
;* THESE ARE THE PAL DEFINITIONS	*
;****************************************

pal_width	EQU	1381
pal_hmid	EQU	843

pal_height	EQU	287
pal_vmid	EQU	322

ap_hdb		EQU	(pal_hmid - (pal_width / 2) + 4)
ap_hde		EQU	(((pal_width / 2) - 1) | $400)

ap_vdb		EQU	(pal_vmid - pal_height)
ap_vde		EQU	(pal_vmid + pal_height)

;****************************************
;* THESE ARE THE NTSC DEFINITIONS	*
;****************************************

ntsc_width	EQU	1409
ntsc_hmid	EQU	823

ntsc_height	EQU	241
ntsc_vmid	EQU	266

an_hdb		EQU	(ntsc_hmid - (ntsc_width / 2) + 4)
an_hde		EQU	(((ntsc_width / 2) - 1) | $400)

an_vdb		EQU	(ntsc_vmid - ntsc_height)
an_vde		EQU	(ntsc_vmid + ntsc_height)

;****************************************
;* THE DEFINITIONS USED HERE ARE NTSC	*
;****************************************

a_hdb		EQU	an_hdb
a_hde		EQU	an_hde

a_vdb		EQU	an_vdb
a_vde		EQU	an_vde

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

SCRNTOP		EQU	a_vdb+8		; ypos of first line at top of screen

SCRNWIDE	EQU	384
SCRNHITE	EQU	232

BLITWIDE	EQU	WID384

;****************************************
;* SINCE THE USEFUL SCREEN AREA IN	*
;* OVERSCAN MODE IS ONLY BAOUT 200 out	*
;* of 240 vertically and 266 out of 320	*
;* horizontally, so we limit the	*
;* movement of animation sequences to	*
;* this area only.			*
;****************************************

MAXUP		EQU	0
MAXDOWN		EQU	SCRNHITE
MAXLEFT		EQU	0
MAXRIGHT	EQU	SCRNWIDE

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

	.text

;*=============================================================================*
;* STARTUP	: processing starts here - initialise system and handle	stuff  *
;*		  according to user input				       *
;*=============================================================================*

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

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

	move.w	#0,bildobjc		; initialise rebuilt objlist flag
	move.l	#0,framecnt		; initialise frame count
	move.l	#1,pix_fps		; frames to wait before next blit

	move.w	#0,color		; colour of the screen

	move.w	#-1,pixno		; start with picture number zero
	move.w	#MAXLEFT,pixnux1	; starting x and y position in the
	move.w	#MAXUP,pixnuy1		; screen object for blitting frames

	move.w	#-1,pix_lod		; no picture being loaded right now
	jsr	initptrs		; initialise picture buffer pointer list

	jsr	initmem			; initialise memory management rutines

	jsr	vdiinit			; initialise video
	jsr	vblinit			; initialize vblank routine
	jsr	clrscren		; clear the screen

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

	move.w	#$6C7,VMODE		; turn on display with 16 bit RGB

.main:	move.l	framecnt,d7		; save framecnt for SYNC later
	add.l	pix_fps,d7		; add number of frames to wait

	jsr	readpad			; read current joypad inputs
	jsr	hndljoy			; handle joypad inputs
	jsr	clrscren		; clear the screen
	jsr	dispfrm			; display next frame
	jsr	chckload		; check if picture loaded

.chkobj:
	move.w	bildobjc,d0		; check if object list to be rebuilt
	beq.b	.sync			; no, so check for frame syncronisation
	move.w	#0,bildobjc		; reset the rebuilt object list flag
	jsr	BuildObjList		; update the object data list

.sync:	cmp.l	framecnt,d7		; sync to the vertical blanking intr
	bgt.b	.sync			; not in sync yet
	bra.b	.main			; do forever

done:	illegal

;*******************************************************************************
;* VIDEO INITIALISATION ROUTINES					       *
;*******************************************************************************

;*=============================================================================*
;* VDIINIT	: initialise video					       *
;*=============================================================================*

vdiinit:
	move.w	#a_hdb,HDB1
	move.w	#a_hdb,HDB2
	move.w	#a_hde,HDE
	move.w	#a_vdb,VDB
	move.w	#$FFFF,VDE

	move.w	#$0000,BG		; set the background colour
	move.l	#$00000000,BORD1	; set the border colour
	rts

;*=============================================================================*
;* VBLANK	: Vertical sync interrupt handler			       *
;*		  it restores object list for next frame		       *
;*=============================================================================*

vblank:
	move.l	d0,-(sp)		; save registers on stack

	move.w	INT1,d0			; get the interrupt status register
	cmpi.w	#1,d0			; is this a video interrupt?
	bne.b	.done			; no, do house keeping and quit 

	jsr	CopyObjList		; refresh the object list
	addq.l	#1,framecnt		; count a video frame

.done:	move.w	#$101,INT1		; enable video interrupt
	move.w	#0,INT2			; and clear all others pending

	move.l	(sp)+,d0		; restore registers
	rte

;*=============================================================================*
;* VBLINIT	: setup the vblank interrupt handler			       *
;*=============================================================================*

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

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

	move.w	#(a_vde|1),VI		; Get the maximum VBLANK time
	move.w	#$1,INT1		; enable interrupt

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

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

;*******************************************************************************
;* OBJECT LIST HANDLING							       *
;*									       *
;* The object list handling routines used here are not the best.  We suggest   *
;* users to use their own object handling routines, which can be done more     *
;* efficiently.								       *
;*									       *
;* For example, we use only one screen object in this particular example, so   *
;* the object list can be rebuild, only by changing the parameters which are   *
;* effected in the object used.						       *
;*									       *
;*******************************************************************************

;*=============================================================================*
;* InitObjList	: initialize the object list				       *
;*=============================================================================*

InitObjList:
	movem.l	d0/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
	lea.l	objData,a1		; pointer to RAM object list

	moveq.l	#0,d0
	move.w	(a0)+,d0		; d0 = number objects in list
	move.w	d0,maxObjs		; save total number of objects

	mulu	#OBJSIZE,d0		; d0 = # bytes in object list
	lsr.l	#1,d0			; d0 = # words in object list
	subq.l	#1,d0			; d0 = # words - 1 for dbra loop
.loop:	move.w	(a0)+,(a1)+		; make a copy of the object list
	dbra	d0,.loop		; to be used here

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

;*=============================================================================*
;* BuildObjList	: build the object display list				       *
;*									       *
;* Register Usage:							       *
;*	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 setdat, used by setoht	       *
;*	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 list divided by 8		       *
;*=============================================================================*

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

	lea.l	objData,a1		; address of the object tree in RAM
	lea.l	olist_ram,a2		; address of copy of packed object list

	move.w	maxObjs,d6		; total numner of objects
	sub.w	#1,d6			; start dbra counter at total objects - 1

	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

;****************************************
;* TO KEEP THE HARDWARE HAPPY		*
;****************************************

	add.l	#16,d7			; keep provision for two branch objects
	add.l	#16,a2			; at the begining of the object list

	lsr.l	#3,d7			; divide by 8 for object processor

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

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

;****************************************
;* PHRASE 1				*
;*					*
;* 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				*
;****************************************

.setdat:
	move.l	O_DATA(a1),d0		; get normal data address
	move.w	#SCRNTOP-1,d2		; top of the screen
	sub.w	O_YPOS(a1),d2		; top of screen - ypos = half lines off top of screen
	bpl.b	.above			; if object is above top of screen, use normal data address
	clr.w	d2			; 0 lines off screen, use normal data address
	bra.b	.setlnk
.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
.setlnk:
	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				*
;****************************************

.chkbot:
	cmpi.b	#BITOBJ,O_TYPE(a1)	; see if this is a bit map object
	bne	.chkscl			; no, go check if scalable type
	add.l	#BITSIZE,d7		; yes, skip past bitmapped object
	bra	.adjlnk			; and adjust the pointer link
.chkscl:
	cmpi.b	#SCBITOBJ,O_TYPE(a1)	; see if this is a scaled bit map object
	bne	.chkgpu			; no, go check is gpu type
	add.l	#SCBITSIZE,d7		; yes, skip past scaled bit map object
	bra	.adjlnk			; and adjust the pointer link
.chkgpu:
	cmpi.b	#GPUOBJ,O_TYPE(a1)	; see if this is a GPU object
	bne	.chkbra			; no, go check if branch type
	add.l	#GPUSIZE,d7		; yes, skip past GPU object
	bra	.adjlnk			; and adjust the pointer link
.chkbra:
	cmpi.b	#BRANCHOBJ,O_TYPE(a1)	; see if this is a branch object
	bne	.chkstp			; no, so it is a stop type object
	add.l	#BRANCHSIZE,d7		; yes, skip past branch object
	bra	.adjlnk			; and adjust the pointer link
.chkstp:
	add.l	#STOPSIZE,d7		; skip past branch object
.adjlnk:
	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

;****************************************
;* 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		*
;****************************************

.setoht:
	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	.notscl			; 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
.notscl:
	sub.w	d2,d1			; subtract lines off screen from scaled height
	bmi.b	.getoht			; 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
.getoht:
	lsl.l	d5,d0			; shift left 11 bits to make room for ypos

;****************************************
;* if ypos is -ve, it gets set to 0	*
;* .setdat and .setoht will adjust the	*
;* data pointer and height accordingly	*
;****************************************

.setyps:
	move.w	O_YPOS(a1),d1
	bmi.b	.negyps			; 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
.negyps:
	lsl.l	#3,d0			; shift left 3 bits to make room for type

;****************************************
;* set type of object and save phrase 1	*
;****************************************

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

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

	move.l	#0,d0			; clear d0
.setpix:
	move.b	O_FIRSTPIX(a1),d0
	lsl.l	#4,d0			; shift left 4 bits to make room for release, transparent, rmw, reflect bits
.setflg:
	or.b	O_FLAGS(a1),d0		; or with firstpix
	lsl.l	#7,d0			; shift left 7 bits to make room for index
.setind:
	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
.setowd:
	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
.setdwd:
	or.w	O_DWIDTH(a1),d0		; or with part of iwidth
	lsl.l	#3,d0			; shift left 3 bits to make room for pitch
.setpch:
	or.b	O_PITCH(a1),d0		; or with dwidth and iwidth
	lsl.l	#3,d0			; shift left 3 bits to make room for depth
.setdpt:
	or.b	O_DEPTH(a1),d0		; or with dwidth, iwidth, pitch
	lsl.l	d3,d0			; shift left 12 bits to make room for xpos
.setxps:
	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	.nextobj		; branch if object is not scaled

;****************************************
;* PHRASE 3				*
;*					*
;* for scaled bit map object only	*
;****************************************

	move.l	#0,(a2)+		; write zero in first long of phrase 3
	move.l	O_SCALE(a1),(a2)+	; write remainder, vscale, hscale bytes in 2nd long of phrase 3

.nextobj:				; jump to here if delete flag is on
	adda.l	#OBJSIZE,a1		; point to next object in pseudo object list
	dbra	d6,.procobj		; repeat until all objects have been processed

;****************************************
;* write a stop object at the end	*
;****************************************

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

;****************************************
;* TO KEEP THE HARDWARE HAPPY		*
;****************************************

	move.l	d7,d0			; get the address of stop object in d0
					; - to be used in the branch objects
					; - already in phrase notation

;****************************************
;* At this point we must add two branch	*
;* objects at the very begining of the	*
;* object list to keep the hardware	*
;* happy!				*
;*					*
;* the first one branches if greater	*
;* than n_vde the next one branches if	*
;* less than n_vdb first phrase has its	*
;* link address (in phrases) shifted	*
;* down by eight bits			*
;****************************************

	lea	olist_ram,a2		; address of copy of packed olist

; set first branch object

	move.l	d0,d2			; save stopobj address for later use

	lsr.l	#8,d0			; shift down link address by 8 bits
	move.l	d0,(a2)+		; write high longword

	move.l	d2,d0			; get stopobj address (in phrases) 
	moveq	#24,d1			; bit position of LINK field
	lsl.l	d1,d0			; shift into position of field LINK 

	or.l	#BRANCHOBJ,d0		; or in type bits for branch object

	move.w	#a_vde,d3		; get y_pos for branch 
	ext.l	d3			;
	lsl.l	#3,d3			; move to the correct bit position			
	or.l	d3,d0			; or in ypos 

	or.l	#2<<14,d0		; bit 15,14 = 1,0: branch greater than
	move.l	d0,(a2)+		; wirte low longword

; set second branch object

	move.l	d2,d0			; again get stopobj address
	lsr.l	#8,d0			; shift down link address by 8  
	move.l	d0,(a2)+		; shift into position of field LINK

	move.l	d2,d0			; get stopobj address (in phrases) 
	moveq	#24,d1			; bit position of LINK field
	lsl.l	d1,d0			; or in type bits for branch object

	ori.l	#BRANCHOBJ,d0		; make it a branch object

	move.w	#a_vdb,d3		; get y_pos for branch
	ext.l	d3			;
	lsl.l	#3,d3			; move to correct bit position 
	or.l	d3,d0			; or in ypos

	or.l	#1<<14,d0		; bit 15,14 = 0,1: branch less than
	move.l	d0,(a2)+		; write low long

; branch objects done so quit

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

;*=============================================================================*
;* CopyObjList	: build object list					       *
;*=============================================================================*

CopyObjList:
	movem.l	a0-a1,-(sp)		; save registers on stack

	lea	olist_ram,a0		; beginning of olist_ram
	move.l	dlistptr,a1		; bi-phrased olist
.loop:	move.l	(a0)+,(a1)+		; copy olist_ram to olist
	move.l	(a0),(a1)+		; copy olist_ram to olist
	cmpi.l	#STOPOBJ,(a0)+		; see if we are at the stop object
	bne.b	.loop

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

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

InitObjProc:
	move.l	d0,-(sp)		; save registers

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

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

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

;*=============================================================================*
;* READPAD	: read the Jaguar joypad				       *
;*									       *
;* levels = Current reading of joypad (Asserted High)			       *
;* edges  = Edge-triggered One Shot of joypad				       *
;*=============================================================================*

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

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

	move.w	#$800E,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	#$800D,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	#$800B,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	#$8007,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	levels,d0
	move.l	d1,levels		; LEVELS = Current joypad reading
	eor.l	d1,d0			; 0 & 0 = 0, 0 & 1 = 1
	and.l	d1,d0			; 1 & 0 = 0, 1 & 1 = 1

	move.l	d0,edges		; EDGES = Oneshot joypad

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

;*******************************************************************************
;* BLITTER ROUTINES USED IN THIS EXAMPLE				       *
;*******************************************************************************

;*=============================================================================*
;* CLRSCREN	: clears jaguar video screen using the blitter		       *
;*=============================================================================*

clrscren:
	move.l	d0,-(sp)

	move.w	color,d0
	swap	d0
	move.w	color,d0
	move.l	d0,B_PATD		; select the fill colour
	move.l	d0,B_PATD+4

	move.l	#0,d0
	move.l	d0,A1_PIXEL		; pointer to (0,0)

	move.w	#1,d0			; step +1 in y direction
	swap	d0
	move.w	#(-SCRNWIDE),d0		; and -width in x direction
	move.l	d0,A1_STEP		; for display each line

	move.w	#SCRNHITE,d0		; vertical count height
	swap	d0
	move.w	#SCRNWIDE,d0		; horizontal count is width
	move.l	d0,B_COUNT		; do (height|width)

	move.l	#screnmem,A1_BASE	; point to the buffer

	move.l	#(PITCH1|PIXEL16|BLITWIDE|XADDPHR),A1_FLAGS

	move.l	#(PATDSEL|UPDA1),B_CMD

	jsr	blitidle
	move.l	(sp)+,d0
	rts

;*=============================================================================*
;* BLITIDLE	: waits for the blitter idle				       *
;*=============================================================================*

blitidle:
	movem.l	d0/a0,-(sp)

	move.l	#B_CMD,a0
.waitblt:
	move.l	(a0),d0
	lsr.w	#1,d0
	bcc.b	.waitblt

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

;*******************************************************************************
;* DISPLAY THE CURRENT ANIMATION FRAME					       *
;*******************************************************************************

;*=============================================================================*
;* DISPFRM	: display the current frame of the current picture	       *
;*=============================================================================*

dispfrm:
	movem.l	d0-d7/a0-a2,-(sp)	; save registers

;****************************************
;* check if valid picture		*
;****************************************

.chkpix:
	moveq	#0,d0
	move.w	pixno,d0
	bmi	.retdisp

.loadpix:
	move.l	#pixptrs,a0		; a0 = pointer to picture list
	mulu	#12,d0			; index into picture list
	add.l	d0,a0			; get the address of the pointer to picture
	move.l	8(a0),d0		; d0 = pointer to picture to be displayed
	beq	.retdisp		; return, if picture not yet loaded

;****************************************
;* get the current frame parameters	*
;****************************************

.blitfrm:
	move.l	d0,a0			; pointer to picture data in a0
	add.l	(a0)+,d0		; add length of header
	move.l	d0,a2			; pointer to picture data
	add.l	#4,a0			; skip length of animation data

	move.l	a0,a1			; pointer to current frame number in a1
	moveq	#0,d2
	move.w	(a0)+,d2		; current frame number in d2

	move.w	(a0)+,d3		; total number of frames in d3
	move.w	(a0)+,d5		; picture width
	move.w	(a0)+,d7		; picture width in blitter format in d6

	clr.w	d0			; frame counter
	clr.w	d4			; find ypos in d4
.getypos:
	cmp.w	d0,d2
	beq	.gethite
	add.w	(a0)+,d4		; d4 = frame y in picture data
	add.w	#1,d0
	bra	.getypos
.gethite:
	move.w	(a0),d6			; save frame height in picture data

.nextfrm:
	add.w	#1,d2
	cmp.w	d2,d3
	bgt	.updtfrm
	clr.w	d2
.updtfrm:
	move.w	d2,(a1)			; save the current frame number

.savefrm:
	move.l	d1,d3			; save the frame number to wait

;****************************************
;* d4 = frame y position in buffer	*
;* d5 = picture width in pixel		*
;* d6 = picture height in pixel		*
;* d7 = picture width in blitter terms	*
;* a2 = pointer to animation data	*
;****************************************
.blitpix:

; get the pointer to the picture data and find the previous phrase boundary
.dataptr:
	move.l	a2,d1
	and.l	#7,d1
	ror.l	#1,d1

; clear the pattern data register for transparent copy
.bpatd:
	clr.l	d0
	move.l	d0,B_PATD
	move.l	d0,B_PATD+4

; set the destination clip rectangle
.a1clip:
	move.w	#SCRNHITE,d0
	swap	d0
	move.w	#SCRNWIDE,d0
	move.l	d0,A1_CLIP		; do (height|width)

; initialise the destination pixel pointer
.a1pixel:
	move.w	pixnuy1,d0
	swap	d0
	move.w	pixnux1,d0
.a1pix00:
	move.l	d0,A1_PIXEL

; initialise the source pixel pointer
.a2pixel:
	move.w	d4,d0
	swap	d0
	move.w	d1,d0
.a2pix00:
	move.l	d0,A2_PIXEL

; setup the destination step increments
.a1step:
	move.w	#1,d0
	swap	d0
	move.w	d5,d0
.a1step0:
	neg.w	d0
	move.l	d0,A1_STEP		; step for A1 = +1 y|-pixwide x

; setup the source step increments
.a2step:
	move.w	#1,d0
	swap	d0
	move.w	d5,d0
	tst.l	d1
	beq	.a2step0
	move.l	#4,d2
	sub.l	d1,d2
	add.l	d2,d0
.a2step0:
	neg.w	d0
	move.l	d0,A2_STEP		; step for A2 = +1 y|-pixwide x

; set the inner/outer loop counters for blitter
.bcount:
	move.w	d6,d0
	swap	d0
	move.w	d5,d0
	move.l	d0,B_COUNT

; setup the base address for destination
.a1base:
	move.l	#screnmem,A1_BASE	; pointer to destination buffer

; setup the base address for source
.a2base:
	move.l	a2,d0
	and.l	#$FFFFFFF8,d0
	move.l	d0,A2_BASE		; pointer to character data

; setup the destination flags
.a1flags:
	move.l	#(PITCH1|PIXEL16|BLITWIDE|XADDPHR),A1_FLAGS

; setup the source flags
.a2flags:
	move.l	#(PITCH1|PIXEL16|XADDPHR),d0
	or.w	d7,d0
	move.l	d0,A2_FLAGS

; set up the blitter commands
;
; Clip the blit on A1, because you can scroll the stuff
; out of the screen area

.bcmd:
	move.l	#(SRCEN|DSTEN|CLIP_A1|UPDA1|UPDA2|LFU_AN|LFU_A|DCOMPEN),d0
	tst.l	d1
	beq	.bcmd00
	or.l	#SRCENX,d0		; in case the picture is not phrase aligned
.bcmd00:
	move.l	d0,B_CMD		; set the blitter command register

; wait for blitter idle

	jsr	blitidle

;****************************************
;* return from display frame routine	*
;****************************************

.retdisp:
	movem.l	(sp)+,d0-d7/a0-a2	; restore registers
	rts

;*******************************************************************************
;* HANDLE THE USER INPUTS						       *
;*******************************************************************************

;*=============================================================================*
;* HNDLJOY	: handle joypad and take necessary action		       *
;*=============================================================================*

hndljoy:
	movem.l	d0-d2,-(sp)		; save registers
	move.l	levels,d0		; joypad level status
	move.l	edges,d1		; joypad edge status

;****************************************
;* check for joypad UP arrow		*
;****************************************

.chkjoyU:
	btst.l	#JOY_UP,d0
	beq.b	.ovrjoyU

	move.w	pixnuy1,d2
	cmp.w	#MAXUP,d2
	beq.b	.ovrjoyU
	sub.w	#4,d2
	move.w	d2,pixnuy1
.ovrjoyU:

;****************************************
;* check for joypad DOWN arrow		*
;****************************************

.chkjoyD:
	btst.l	#JOY_DOWN,d0
	beq.b	.ovrjoyD

	move.w	pixnuy1,d2
	cmp.w	#MAXDOWN,d2
	beq.b	.ovrjoyD
	add.w	#4,d2
	move.w	d2,pixnuy1
.ovrjoyD:

;****************************************
;* check for joypad LEFT arrow		*
;****************************************

.chkjoyL:
	btst.l	#JOY_LEFT,d0
	beq.b	.ovrjoyL

	move.w	pixnux1,d2
	cmp.w	#MAXLEFT,d2
	beq.b	.ovrjoyL
	sub.w	#4,d2
	move.w	d2,pixnux1
.ovrjoyL:

;****************************************
;* check for joypad RIGHT arrow		*
;****************************************

.chkjoyR:
	btst.l	#JOY_RIGHT,d0
	beq.b	.ovrjoyR

	move.w	pixnux1,d2
	cmp.w	#MAXRIGHT,d2
	beq.b	.ovrjoyR
	add.w	#4,d2
	move.w	d2,pixnux1
.ovrjoyR:

;****************************************
;* check for fire key A and B		*
;****************************************

	move.w	#0,d2
.chkfirA:
	btst.l	#FIRE_A,d1
	beq.b	.ovrfirA
	add.w	#1,d2
.ovrfirA:
.chkfirB:
	btst.l	#FIRE_B,d1
	beq.b	.ovrfirB
	sub.w	#1,d2
.ovrfirB:
.updtpix:
	tst.w	d2
	beq.b	.nextkey
	add.w	pixno,d2
.chkover:
	cmp.w	#MAXFILES,d2
	bne.b	.chkundr
	move.w	#MAXFILES-1,d2
.chkundr:
	tst.w	d2
	bmi.b	.negpix
	bra.b	.nextpix
.negpix:
	move.w	#0,d2
.nextpix:
	move.w	d2,pixno
	jsr	stopload		; otherwise, stop the disc 
	move.w	d2,pix_lod		; the requested picture
	jsr	initload		; initiate loading
.nextkey:

;****************************************
;* check for fire key C			*
;****************************************

.chkfirC:
	btst.l	#FIRE_C,d0
	beq.b	.ovrfirC

	add.w	#1,color
.ovrfirC:

;****************************************
;* check for key STAR			*
;****************************************

.chkkeyS:
	btst.l	#KEY_STAR,d1
	beq.b	.ovrkeyS

	move.l	pix_fps,d2
	add.l	#1,d2
	cmp.l	#61,d2
	bne.b	.updkeyS
	move.l	#1,d2
.updkeyS:
	move.l	d2,pix_fps
.ovrkeyS:

;****************************************
;* check for key HASH			*
;****************************************

.chkkeyH:
	btst.l	#KEY_HASH,d1
	beq.b	.ovrkeyH

	move.l	pix_fps,d2
	sub.l	#1,d2
	cmp.l	#0,d2
	bne.b	.updkeyH
	move.l	#60,d2
.updkeyH:
	move.l	d2,pix_fps
.ovrkeyH:

;****************************************
;* check for key ZERO			*
;****************************************

.chkkey0:
	btst.l	#KEY_0,d1
	beq.b	.ovrkey0

	movem.l	(sp)+,d0-d2
	bra	done
.ovrkey0:

;****************************************
;* return for joypad handler		*
;****************************************

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

;*******************************************************************************
;* ROUTINES TO LOAD PICTURES FROM THE CDROM DRIVE			       *
;*******************************************************************************

;*=============================================================================*
;* INITLOAD	: initialise loading of a picture			       *
;*=============================================================================*

initload:
	movem.l	d0-d4/a0-a2,-(sp)	; save the registers

;****************************************
;* check if picture is already loaded	*
;****************************************
.loadpix:
	move.l	#pixptrs,a0		; a0 = pointer to picture list
	moveq	#0,d0			; clear d0
	move.w	pix_lod,d0		; picture number to be loaded
	mulu	#$C,d0			; index into picture list
	add.l	d0,a0			; get the address of the pointer to picture
	tst.l	8(a0)			; is there any picture in this buffer
	beq.b	.pixinfo		; no, go load the picture

	move.l	#-1,pix_lod		; picture is already loaded
	bra	.retinit		; quit from this routine

;****************************************
;* get the start point for the picture	*
;* and seek to that location		*
;****************************************
.pixinfo:
	lea.l	filtable,a1		; get the pointer to the picture list
	moveq	#0,d0
	move.w	pix_lod,d0
	mulu.w	#$8,d0
	add.l	d0,a1
	move.l	(a1)+,pix_msf		; starting position of file on CD
	move.l	(a1)+,pix_len		; length of picture on CD
	add.l	#$8000,pix_len		; add a 32K buffer

;****************************************
;* get a buffer for this picture	*
;****************************************
.getbuff:
	move.l	(a0),d0			; get the pointer to buffer for picture
	move.l	#0,0(a0)		; clear the entry in the picture list
	move.l	#0,4(a0)
	move.l	#0,8(a0)
	jsr	freemem			; free this memory if it exist

.allocmm:
	move.l	pix_len,d0		; length of picture
	add.l	#$100,d0		; add 256 byte buffer
	jsr	getmem			; allocate memory buffer for picture
	move.l	d0,pixstrt		; get the pointer to memory allocated
	bne.b	.initGPU		; if successful, then go load the picture

	jsr	freepix			; free the picture which is farthest from
					; the picture to be loaded
	bra.b	.allocmm		; go back and try memory for picture to be loaded

;****************************************
;* initialise GPU			*
;****************************************
.initGPU:
	move.l	#0,G_CTRL		; stop the GPU
	move.l	#0,G_FLAGS		; clear GPU flags register

;****************************************
;* initialise CDROM unit		*
;****************************************
.initCD:
	jsr	CD_setup
	move.w	#3,d0			; go to double speed, CDROM mode
	jsr	CD_mode
	move.w	#1,d0			; wait till stop complete
	jsr	CD_stop

;****************************************
;* load GPU code			*
;****************************************
.loadGPU:
	move.l	#GPUSTART,a0		; load, & initialize host GPU program
	move.l	#GPUEND,a1
	move.l	a0,d1
	move.l	a1,d0
	sub.l	d1,d0			; size in bytes
	asr.l	#2,d0
	move.l	#G_RAM,a1
.xfer:	move.l	(a0)+,(a1)+
	dbra    d0,.xfer

;****************************************
;* Set up BIOS to read data		*
;* This MUST be done after the GPU	*
;* program is loaded so that the	*
;* vectors installed by the BIOS are	*
;* not overwritten !!			*
;****************************************
.setread:
	move.l	#CDREADER,a0
	jsr	CD_init

;****************************************
;* set the GPU PC and start GPU		*
;****************************************
.runGPU:
	move.l	#SETUP,G_PC		; set GPU PC to start of code in SRAM
	move.l	#1,G_CTRL		; start GPU

;****************************************
;* Start up the CD			*
;****************************************
.playCD:
	move.l	pixstrt,d4
	move.l	d4,a0			; start recording at a0
	add.l	pix_len,d4
	move.l	d4,a1			; stop recording at a1
	move.l	d4,pixstop		; stops loading at this position in memory
	move.l	pix_msf,d0		; starts at this time stamp on disc
	jsr	CD_read			; start play at the current picture

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

;*=============================================================================*
;* CHECKLOAD	: check if loading complete				       *
;*=============================================================================*

chckload:
	movem.l	d0-d2/a0-a2,-(sp)	; save the registers

;****************************************
;* any picture being loaded		*
;****************************************

.chklod:
	cmp.w	#-1,pix_lod		; any picture being loaded right now?
	beq	.retchck		; no, quit

;****************************************
;* Now we check to see if the loaded	*
;* data has reached the top.  The exact	*
;* return from CD_ptr for the current	*
;* location cannot be counted on. The	*
;* data will go AT LEAST to the stop	*
;* position. For this reason leave some	*
;* padding at the end of the buffer.	*
;****************************************
.checkCD:
	move.l	pixstop,d2
	jsr	CD_ptr
	cmp.l	a0,d2
	bgt	.retchck

;****************************************
;* At this point a1 contains the error	*
;* flag,				*
;* if A1  = 0 => no error		*
;* If A1 != 0 it is the APPROXIMATE	*
;*            location of the error	*
;****************************************

;****************************************
;* find the header			*
;*					*
;* In the example here, we used the	*
;* same header for all different anima-	*
;* tion sequences.  The header used is	*
;* 64 '1's (ASCII 31).  In practice we	*
;* should use different headers for	*
;* different files on the CDROM		*
;****************************************

.findhdr:
	move.l	pixstrt,a0
	move.l	#0,d1
.cmpare:
	move.l	(a0)+,d0
	cmp.l	#$31313131,d0
	beq.b	.found
	move.l	#0,d1
	bra.b	.next
.found:	add.l	#1,d1
.next:	cmp.l	a0,d2
	beq.b	.setnul
	cmp.l	#16,d1
	beq.b	.setptr
	bra.b	.cmpare
.setnul:
	move.l	#0,a0
.setptr:
	move.l	a0,d1

	move.l	#pixptrs,a0
	moveq	#0,d0
	move.w	pix_lod,d0
	mulu	#$C,d0
	add.l	d0,a0
	move.l	pixstrt,(a0)+		; save start of picture buffer in list
	move.l	pix_len,(a0)+		; save length of buffer in list
	move.l	d1,(a0)+		; save start of picture in list

;****************************************
;* stop the CD				*
;****************************************

	or.w	#$8000,pix_lod		; set the last bit to show the picture was loaded
	jsr	stopload		; stop the loading of data from the CDROM
.retchck:
	movem.l	(sp)+,d0-d2/a0-a2	; restore registers
	rts

;*=============================================================================*
;* STOPLOAD	: stop data loading from the CDROM			       *
;*=============================================================================*

stopload:
	movem.l	d0-d1,-(sp)		; save the registers
.chkstop:
	move.w	pix_lod,d0		; picture number to be loaded
	cmp.w	#-1,d0			; any picture being loaded right now?
	beq.b	.retstop		; no, so quit

.chkload:
	and.w	#$8000,d0		; check for last bit
	bne	.stopCD			; picture was loaded fully so stop the CD and quit

.freepix:				; otherwise free the picture
	move.l	pixstrt,d0		; d0 = buffer to be freed
	move.l	#0,pixstrt		; pixstrt is NULL
	jsr	freemem			; free the memory allocated for this picture

.stopCD:
	jsr	CD_uread		; stop the last read command
	move.w	#1,d0			; wait for stop
	jsr	CD_stop			; stop the CD
	move.w	#-1,pix_lod		; no picture being loaded
.retstop:
	movem.l	(sp)+,d0-d1		; restore registers
	rts

;*******************************************************************************
;* MEMORY MANAGEMENT ROUTINES						       *
;*									       *
;* The memory management routines implemented here follow the simple memory    *
;* allocation scheme, where the first available block, which is	greater than   *
;* or equal to the requested size, is allocated.  This memory management       *
;* scheme creates a lot of fragmentation in the heap and it is not the best    *
;* scheme.  We suggest users to use more efficient schemes, because memory     *
;* management is a very critical issue in a CDROM game			       *
;*									       *
;*******************************************************************************

MEMTOP		equ	$200000		; top of DRAM

;*=============================================================================*
;* INITPTRS:	: initialise picture buffer pointer list		       *
;*=============================================================================*

initptrs:
	movem.l	d0/a0,-(sp)		; save registers

	move.l	#pixptrs,a0		; pointer to list of pix pointers
	move.l	#MAXFILES-1,d0		; # pictures -1 for dbra loop
.loop:	move.l	#0,(a0)+		; initialise picture buffer pointer
	move.l	#0,(a0)+		; initialise picture length
	move.l	#0,(a0)+		; initialise picture pointer in buffer
	dbra	d0,.loop		; continue for all pictures

.retini:
	movem.l	(sp)+,d0/a0		; restore registers
	rts

;*=============================================================================*
;* FREEPIX	: free the picture farthest from pix_lod		       *
;*=============================================================================*

freepix:
	movem.l	d0-d2/a0-a2,-(sp)

.findmin:
	move.l	#pixptrs,a1		; get the pointer to the picture list
	move.l	#0,d1			; the count of the first available picture
.nextmin:
	move.l	(a1),d0			; does this picture buffer exist
	bne.b	.findmax		; yes, go free the memory for this picture
	add.l	#$C,a1			; go to next entry in the table
	add.l	#1,d1			; increment the counter
	bra.b	.nextmin		; go look for next
.findmax:
	move.l	#pixptrs,a2		; get the pointer to the picture list
	move.l	#MAXFILES-1,d2		; count of the last available picture
	move.l	d2,d0			; go to the entry
	mulu	#$C,d0			; for last picture
	add.l	d0,a2			; in the list
.nextmax:				; free pictures on the higher side
	move.l	(a2),d0			; does this picture exist
	bne.b	.findpix		; yes, go free memory for this picture
	sub.l	#$C,a2			; no, go to the previous picture
	sub.l	#1,d2			; decrement the counter
	bra.b	.nextmax		; and look for available memory
.findpix:
	moveq.l	#0,d0			; clear d0
	move.w	pix_lod,d0
.1111:
	sub.l	d0,d1
	bpl.b	.2222
	neg.l	d1
.2222:
	sub.l	d0,d2
	bpl.b	.3333
	neg.l	d2
.3333:
	cmp.l	d1,d2
	bge.b	.freed2
.freed1:
	move.l	a1,a0
	bra.b	.free
.freed2:
	move.l	a2,a0
.free:
	move.l	(a0),d0			; get pointer to memory buffer
	move.l	#0,(a0)+		; clear the entry for this picture
	move.l	#0,(a0)+		; in the table
	move.l	#0,(a0)+
	jsr	freemem			; free memory for the picture

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

;*=============================================================================*
;* INITMEM	: initialise memory pool				       *
;*									       *
;* memory pool will be devided into blocks, as the memory requests come.       *
;* Each block in this memory pool will start with a 12 byte header, where each *
;* block is structured as follows :-					       *
;*									       *
;*	struct	_block {						       *
;*		void	*next; /* pointer to next block, if any */	       *
;*		void	*prev; /* pointer to prev block, if any */	       *
;*		void	*size; /* size of block in bytes */		       *
;*		};							       *
;*									       *
;*	The first bit of "next" is used for special purposes.  This bit	is set *
;*	if the block is a free block, otherwise it is 0.		       *
;*									       *
;*	Initially, there is one big block of memory available, which is equal  *
;*	to the total memory available.					       *
;*									       *
;*	A block of memory may be devided into two blocks if the size requested *
;*	is less than avail memory in that block.			       *
;*=============================================================================*

initmem:
	movem.l	d0/a0,-(sp)		; save registers

	move.l	#rawdata,d0		; start of available memory
	move.l	d0,memavail		; pointer to first block of memory
	move.l	d0,a0
	move.l	#1,(a0)+		; available memory block
	move.l	#0,(a0)+		; no previous block
	move.l	#MEMTOP,d0		; top of memory
	sub.l	#rawdata,d0		; size of available memory
	sub.l	#$C,d0			; take out size of block header
	move.l	d0,(a0)+		; size of memory available

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

;*=============================================================================*
;* GETMEM	: gets a memory block, given block size in d0.l		       *
;*=============================================================================*

getmem:
	movem.l	d1-d3/a0-a2,-(sp)

	move.l	memavail,a0		; pointer to first block of memory
.ifavail:
	move.l	(a0),d1			; pointer to next block of memory
	btst.l	#0,d1			; is this block of memory available
	beq	.nxtblok		; yes, check if size match

.chksize:
	move.l	8(a0),d2		; get the size of the block
	cmp.l	d0,d2			; check size of block
	blt	.nxtblok		; yes, get the block of memory

.getblok:
	sub.l	#$C,d2			; take out the size of header
	cmp.l	d0,d2			; is size available more than needed
	bgt	.cutblok		; yes, go cut the block into two
	bra	.retsucc		; return with success

.cutblok:
	move.l	a0,d3			; pointer to current block in d3
	add.l	#$C,d3			; add block header size to d3
	add.l	d0,d3			; add requested size to d3
	move.l	d3,a2			; a2 = new;

	sub.l	d0,d2			; d2 = size of the new free block
	move.l	d1,(a2)+		; new->next = curr->next;
	move.l	a0,(a2)+		; new->prev = curr;
	move.l	d2,(a2)+		; new->size = size of new block

	move.l	d3,(a0)			; curr->next = new;
	move.l	d0,8(a0)		; curr->size = requested size

	and.l	#~1,d1			; clear the first bit
	beq	.retsucc		; next pointer was null so return success

	move.l	d1,a1			; a1 = new->next;
	move.l	d3,4(a1)		; new->next->prev = new;
	bra	.retsucc		; return with success

.nxtblok:
	and.l	#~1,d1			; clear the first bit
	move.l	d1,a0			; no, get the next block of memory
	tst.l	d1			; is next pointer NULL
	bne	.ifavail		; no, check for block available

.retfail:
	move.l	#0,d0			; yes, return null
	bra	.retgetm		; getmem fails
.retsucc:
	and.l	#~1,(a0)		; block is no more available
	move.l	a0,d0			; memory block available
	add.l	#$C,d0			; pointer to memory available
.retgetm:
	movem.l	(sp)+,d1-d3/a0-a2
	rts

;*=============================================================================*
;* FREEMEM	: free the given memory pointer in d0			       *
;*=============================================================================*

freemem:
	movem.l	d1-d3/a0-a2,-(sp)	; save registers

	tst.l	d0			; is the pointer null
	beq	.retfree		; yes, so return

	sub.l	#$C,d0			; get the pointer to header
	move.l	d0,a0			; a0 = pointer to header
	or.l	#1,(a0)			; it is a free block
.nextblk:
	move.l	(a0),d3			; d3 = pointer to next block
	and.l	#~1,d3			; clear the free bit, is the next pointer NULL
	beq.b	.prevblk		; yes, go check for previous

	move.l	d3,a1			; a1 = pointer to next block
	move.l	(a1),d1			; check for free block
	btst.l	#0,d1			; is it a free block
	beq.b	.prevblk		; no, so go for previous block

	move.l	8(a1),d3		; size of next block
	add.l	#$C,d3			; add size of header
	add.l	d3,8(a0)		; add size of next buffer
	move.l	d1,(a0)			; del->next = del->next->next

	and.l	#~1,d1			; clear the first bit
	move.l	d1,a2			; a2 = pointer to next block
	move.l	d0,4(a2)		; next->prev = del

.prevblk:
	move.l	4(a0),d3		; a1 = pointer to previous buffer
	tst.l	d3			; is it a NULL pointer
	beq.b	.retfree		; yes, quit

	move.l	d3,a1			; a1 = pointer to previous block
	move.l	(a1),d1			; check for free block
	btst.l	#0,d1			; is it a free block
	beq.b	.retfree		; no, so quit

	move.l	(a0),d1			; pointer to next block

	move.l	8(a0),d3		; size of current block
	add.l	#$C,d3			; add header size
	add.l	d3,8(a1)		; add size of block
	move.l	d1,(a1)			; prev->next = next

	and.l	#~1,d1			; clear the first bit
	move.l	d1,a2			; a2 = pointer to next block
	move.l	a1,4(a2)		; next->prev = del

.retfree:
	movem.l	(sp)+,d1-d3/a0-a2	; restore registers
	rts

;*******************************************************************************
;* CONSTANT DATA AREA							       *
;*******************************************************************************

		.data

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

		.phrase

Objects:	.dc.w	1			; Number of objects in list
;
;*****	SCREEN OBJECT (screen = SCRNWIDE x SCRNHITE)
;
		.dc.w	0			; o_xpos	
		.dc.w	SCRNTOP			; o_ypos
		.dc.l	screnmem		; o_data
		.dc.w	SCRNHITE		; o_height
		.dc.w	SCRNWIDE/4		; o_dwidth (width/(64/bits per pixel))
		.dc.w	SCRNWIDE/4		; o_iwidth (width/(64/bits per pixel))
		.dc.b	0			; o_flags
		.dc.b	0			; o_firstpix
		.dc.b	BITOBJ			; o_type	unScaled Bit Mapped type
		.dc.b	4			; o_depth	16 bits per pixel
		.dc.b	1			; o_pitch
		.dc.b	0			; o_index
		.dc.w	0			; o_desc
		.dc.w	$0020			; unused byte, remainder = 1.0 (3.5 format)
		.dc.w	$2020			; vscale = 1.0 (3.5 format), hscale = 1.0 (3.5 format)
		.dc.l	0			; o_rom
		.dc.l	M_ACTIVE		; mode flags

		.phrase

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

		.bss

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

		.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
		.phrase
olist_ram:	.ds.l	(8 * MAXOBJS)		; 8 longs for each object
		.ds.l	2			; 2 longs for a stop object
		.phrase
dlistptr:	.ds.l	1			; pointer to our display list, bi-phrase aligned
		.phrase
objData:	.ds.b	(MAXOBJS * OBJSIZE)	; need 34 bytes for each object
maxObjs:	.ds.w	1			; index of last object in list
		.phrase
screnmem:	.ds.w	SCRNWIDE*SCRNHITE	; screen memory
dummy1:		.ds.w	256			; just some dummy memory area
		.phrase
		.ds.l	4000			; 8k for stack
newstack:	.ds.l	1
		.phrase
levels:		.ds.l	1			; levels joypad reading ( = last && current )
		.ds.l	1			; xxApxxBx RLDU741* xxCxxxox 2580369#
edges:		.ds.l	1			; edges trigger joypad reading
		.ds.l	1			; xxApxxBx RLDU741* xxCxxxox 2580369#
		.phrase
framecnt:	.ds.l	1			; frame count
bildobjc:	.ds.w	1			; rebuilt object list flag
color:		.ds.w	1			; colour of the screen
		.phrase

pix_fps:	.ds.l	1			; frames to wait before next blit
pixno:		.ds.w	1			; current picture number to be displayed
pixnux1:	.ds.w	1			; x position of animation frame on screen
pixnuy1:	.ds.w	1			; y position of animation frame on screen

pix_lod:	.ds.w	1			; picture number to be loaded
pix_msf:	.ds.l	1			; CDROM drive starts play at this position
pix_len:	.ds.l	1			; length of the buffer to be loaded
pixstrt:	.ds.l	1			; start of picture buffer 
pixstop:	.ds.l	1			; pointer where the loading current picture stops

pixptrs:	.ds.l	3*MAXFILES		; the pointer for picture buffer in rawdata

memavail:	.ds.l	1			; pointer to the first block of memory

		.phrase
rawdata:	.ds.l	1			; raw data from the CDROM
		.phrase

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

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

	.end
