	SECTION	Main,CODE_C
*18/03-89*********************************************************
*	ManyBobsProgram						 *
******************************************************************

******** Macros and nececarry definitions ************************

_custom		equ	$dff000

******** Screen info *********************************************

PicYOff		equ	10			; offset for picture
PicHeight	equ	260			; height of picture
PicWidth	equ	20			; width of picture
PicDepth	equ	2			; depth of picture

******** Bob info ************************************************

BobH		equ	16			; height of bob
BobW		equ	1			; width of bob
BobD		equ	2			; depth
BobNum		equ	100			; number of bobs

******** Copperlist macros

** Move word commando

Mov	Macro				; Syntax: Mov Constant,Destination
	dc.w	(\2)&%111111110
	dc.w	(\1)
	EndM

** Move longword commando

MovL	Macro				; Syntax: Mov Constant,Destination
	dc.w	(\2)&%111111110		; Hi-word
	dc.w	(\1)>>16		; Shift 16 right left
	dc.w	(\2)&%111111110+2	; Lo-word
	dc.w	(\1)&$ffff
	EndM

WaitB	Macro
	dc.w	1			; wait for the blitter only
	dc.w	%0
	dc.w	1			; two waits because the blitter
	dc.w	%0			; is slower than the coppercomandos
	EndM

** Wait	commando

Wait	Macro
	dc.w	(((\1)&$ff)*%10)!(((\2)&$ff)*%100000000)!%1
	dc.w	%1111111111111110
	EndM

EndCop	Macro				; End of copperlist
	Wait	255,255
	EndM

***********************
******** Includes
***********************

	incdir	"include:"
	include	"graphics/graphics_lib.i"
	include	"graphics/gfxbase.i"
	include	"hardware/custom.i"
	include	"exec/exec_lib.i"

******************************************************************
******** Setup and main loop *************************************
******************************************************************

Main
	move.l	#GfxName,a1
	clr.l	d0
	move.l	_SysBase,a6
	jsr	_LVOOpenLibrary(a6)		; need this library to
;						; restore * to cop1lc
	move.l	d0,_GfxBase
	cmp.l	#0,d0				; NOTE! Z flag is NOT
;						; set above
	beq	Abort				; No such library found

** Clear chip memory

	move.l	#StopChip-StartChip,d0
	move.l	#StartChip,a0
.NB	clr.b	(a0)+
	subq.l	#1,d0
	bne	.NB

** Clear public memory

	move.l	#StopPublic-StartPublic,d0
	move.l	#StartPublic,a0
.NB1	clr.b	(a0)+
	subq.l	#1,d0
	bne	.NB1

** Setup bobs

	jsr	SetupCopperBob	; setup bobcopperlist
	jsr	BobPos
	jsr	BobPos
	jsr	BobPos	
	jsr	BobPos		; X4 to fill all the copperbob
;				; double buffering *rs

** Main loop

	jsr	StartCopper
	jsr	StartInt

	jsr	BounceBobs

	jsr	StopInt
	jsr	StopCopper
	
** Abort routine

Abort	
	clr.l	d0
	rts

_GfxBase	ds.l	1		; * to gfx. base
GfxName		GRAFNAME
		even

*******************************************************
******** Setup the bobcopperlist **********************
*******************************************************


SetupCopperBob

** Set up multiply table

	move	#PicHeight-1,d0
	move.l	#Multiply,a0
	clr	d1

.Loop	move	d1,d2
	mulu	#PicWidth*PicDepth*2,d2	; this is a table of line
;					; offsets used to save cycles
	move.l	d2,(a0)+
	addq	#1,d1
	dbra	d0,.Loop

** Set bob and cookie *

	move	#BobNum-1,d0
	move.l	#CoppBobs,a2
.Loop2
	move.l	#BobCookie,d1
	move	d1,24+6(a2)	; store low word
	swap	d1
	move	d1,24+2(a2)	; store high word

	move.l	#BobData,d1
	move	d1,24+6+8(a2)	; store low word
	swap	d1
	move	d1,24+2+8(a2)	; store high word

	add.l	#BobSize,a2	; next bob
	dbra	d0,.Loop2

** Make cookie cut.

	move	#BobH-1,d0
	move.l	#BobData,a0
	move.l	#BobCookie,a1

.Loop4	move.l	(a0)+,d1
	or.l	(a0)+,d1
	move.l	d1,(a1)+
	move.l	d1,(a1)+
	dbra	d0,.Loop4

** Copy Copperlist

	move.l	#CopperList,a0
	move.l	#SecCopperList,a1
	move.l	#EndCopperList-CopperList-1,d0

.Loop5	move.b	(a0)+,(a1)+
	dbra	d0,.Loop5

** End of session

	rts


CBob	MACRO			; clear the other BP
	MovL	0,bltdpt		; dest
	MovL	0,bltcpt		; source
	Mov	0,bltcon0		; lsr #
	Mov	0,bltcon1		; lsr #
	MovL	0,bltapt		; cookie cut
	MovL	0,bltbpt		; data
	Mov	(BobH*BobD)<<6!BobW+1,bltsize
	WaitB				; just wait for the blitter.
	ENDM

ClearBob MACRO
	MovL	0,bltdpt		; clear the bobs
	Mov	(BobH*BobD)<<6!BobW+1,bltsize ; start blitter
	WaitB
	ENDM

SBob
	CBob			; to messure the size of one bob
EBob

CSBob
	ClearBob			; to messure size of clear bob
CEBob

BobSize		equ	EBob-SBob	; size of a blitterbob
ClearSize	equ	CEBob-CSBob	; size of a blitterbobclear

******************************************************************
************** Copper lists for Play *****************************
******************************************************************

	SECTION	CopperList,DATA_C
CopperList

** Pic window **

	Mov	0,spr+sd_dataa
	Mov	0,spr+sd_datab
	Mov	0,spr+sd_ctl
	Mov	0,spr+sd_pos

	Mov	$000,bplcon0  

** Set colors

	Mov	$000,color
	Mov	$f00,color+2
	Mov	$f80,color+4
	Mov	$ff0,color+6

** Set window data fetch Pict and stop

	Mov	$38,ddfstrt			; 20words)
	Mov	$d0,ddfstop			; Maximum 

** Set Window size

	Mov	$2c50,diwstrt
	Mov	$30d0,diwstop

** Set modulo

	Mov	PicWidth*(PicDepth-1)*2,bpl1mod
	Mov	PicWidth*(PicDepth-1)*2,bpl2mod

	Mov	0,bplcon0
CoppPicP
	MovL	0,bplpt
	MovL	0,bplpt+4
	Wait	0,PicYOff
	Mov	%0010000000000000,bplcon0	; 4 bitplanes hires
	Mov	%0000000000000000,bplcon1	; Scroll codes (defined in program)
	Mov	%0000000000000000,bplcon2	; Priority set to default

*****************
** Clean bobs  **
*****************

** Setup bobs

	WaitB				; just wait for the blitter.
	Mov	(PicWidth-BobW-1)*2,bltdmod ; setup blitter to clear
;					; bobs
	Mov	0,bltcon1
	Mov	(%0001<<8),bltcon0

CoppClear
	REPT	BobNum		; clear all the bobs
	ClearBob
	ENDR

*****************
** Bob storing **
*****************

** Setup bobs

	WaitB				; just wait for the blitter.

	Mov	0,bltamod		; Modulu
	Mov	0,bltbmod
	Mov	(PicWidth-BobW-1)*2,bltcmod
	Mov	(PicWidth-BobW-1)*2,bltdmod
	Mov	$ffff,bltalwm		; last and
	Mov	$ffff,bltafwm		; last and

** Bob copp. blit.

CoppBobs
	REPT	BobNum
	CBob
	ENDR
	Mov	$8000!(1<<4),intreq	; re update blitter info

** End of session

	EndCop
EndCopperList

SecCopperList	ds.b	EndCopperList-CopperList ; double buffering
;						; on coppperlist

******************************************************************
****** Start Copper **********************************************
******************************************************************

	Section	Copper,CODE_C

StartCopper
	move.l	#_custom,a5
	move	#$ffff,copcon(a5)		; enable blitter
;						; from copperlist
	move.l	#CopperList,cop1lc(a5)
	move	#$8000!%1110000000,dmacon(a5)	; turn off sprites
	move	#%100000,dmacon(a5)		; turn off sprites
	clr.l	d0
	rts

******************************************************************
******** End Copper **********************************************
******************************************************************

StopCopper	
	move.l	#_custom,a5
	move	#$0000,copcon(a5)		; no blitter from copperlist
	move	#$8000!%100000,dmacon(a5)	; turn on sprites
	move.l	_GfxBase,a0
	move.l	gb_copinit(a0),cop1lc(a5)	; reset copperlist
	rts


*********************** Constants ********************************

INTP		equ	$60
IntNum		equ	9

** Setup and Start Interrupt

*NOTE: this routine must be called before any action is taken of the bobs concerned

StartInt
	move.l	#_custom,a5
	move.w	intenar(a5),IntOnSave			; Save current ON/OFF state
	move.w	#%0111100001111111,intena(a5)		; Turns off all interrupts
	move.w	#%1000000000110000,intena(a5)		; Set Blitter&VerticalBlank interrupts
	move.w	#%0000000000000000,intreq(a5)

SeI03	btst	#14,dmaconr(a5)		; wait until Blitter is finished
	bne	SeI03

	move.l	#INTP,a0
	move.l	#SaveIntP,a1
	move.l	#IntLevelP,a2
	move.l	#IntNum-1,d0

SeI01	move.l	(a0),(a1)+
	move.l	(a2)+,d1
	bne	SeI02
	move.l	(a0),d1				; keep old int
SeI02	move.l	d1,(a0)+
	dbra	d0,SeI01

	move.w	#%1000000000000000,intreq(a5)		; Force blitter finished interrupt
	move.w	#%1100000000001000,intena(a5)		; Set interrupt on

** End of session

	rts

** Pointer to all the levels

IntLevelP
	dc.l	Interrupt					; spurious
	dc.l	Interrupt				; 1 serial b.e,soft,dskblk
	dc.l	Interrupt					; 2 IO timers
	dc.l	IntVertBlit				; 3 copp. blit. vert.
	dc.l	Interrupt					; 4 audio
	dc.l	Interrupt					; 5 ser rec b. full,dsk
	dc.l	Interrupt					; 6 extern
	dc.l	Interrupt
	dc.l	Interrupt

** Close down interrupt and stop interrupt

StopInt
	move.l	#_custom,a5
	move.w	#%0100010000100000,intena(a5)		; Turns off all interrupts

	move.l	#INTP,a0
	move.l	#SaveIntP,a1
	move.l	#IntNum-1,d0

RSeI01	move.l	(a1)+,(a0)+
	dbra	d0,RSeI01

	move	#$8000!%1000000,dmacon(a5)
	move.w	IntOnSave,d0
	or.w	#%1100000000000000,d0
	move.w	d0,intena(a5)				; reload old On/Off state
	rts			

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
** Interrupt Blitter, and vertb 
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

IntVertBlit
	movem.l	a0-a6/d0-d7,-(sp)			; Save all register on stack

	move.l	#_custom,a5

	move.w	intreqr(a5),d0				; Read which interrupt that occured
	and.w	#$3fff,d0
	move.w	d0,intreq(a5)
	and.w	intenar(a5),d0

	btst	#4,d0
	beq	No02				; no copper interrupt
	move	d0,-(sp)
	bsr	CopperVTB	
	move	(sp)+,d0
No02

	btst	#5,d0
	beq	No01				; no copper interrupt
	addq	#1,CountVTB	; start calcuclating pos.
No01

** End of Interrupt Main

EndOfVer
	movem.l	(sp)+,a0-a6/d0-d7			; Reload all register from stack
	rte

** Interrupt Main Routine

Interrupt
	movem.l	a0-a6/d0-d7,-(sp)			; Save all register on stack

	move.l	#_custom,a5

	move.w	intreqr(a5),d0				; Read which interrupt that occured
	and.w	#$3fff,d0
	and.w	intenar(a5),d0
	move.w	d0,intreq(a5)

	btst	#3,d0
	beq	No2					; Key in interrupt					
	move	d0,-(sp)
	bsr	KeyUp					; Key in interrupt					
	move	(sp)+,d0
No2

** End of Interrupt Main

EndOfInt
	move.l	#_custom,a5
	move	#$c000,intena(a5)
	movem.l	(sp)+,a0-a6/d0-d7			; Reload all register from stack
	rte

** This routine catches up the keys, if you don't do it, the program
** will crash when you press a key.

KeyUp	
	clr.l	d0
	move.b	$bfed01,d0
	move.b	$bfec01,d0
	eor.b	#$ff,d0
	move	d0,KeyDown
	move.b	#$40,$bfee01
	move.b	#$00,$bfee01
	rts

*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
************ Copper VTB ******************************************
*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

CopperVTB
	jsr	BobPos		; update bob pos
	rts

** Variables

IntOnSave	ds.w	1			; Old interrupt ON/OFF state
SaveIntP	ds.l	IntNum			; Pointer to Old interrupt routine
CountVTB	ds	1			; Counter that are increased by one each Vertical Blank(must be decreased be the main program).
KeyDown		ds	1			; current key code


******************************************************************
***************** Move bobs **************************************
******************************************************************

BounceBobs
	move	#BobNum-1,d1
	move.l	#BobDXY,a1
	move.l	#BobXY,a2
SCB01
	move	#(PicWidth-BobW-1)*16,d0
	jsr	Random
	move	d0,(a2)+

	move	#PicHeight-BobH-1,d0
	jsr	Random
	move	d0,(a2)+

	move	#3,d0			; MaxX Speed
	jsr	Random	
	addq	#1,d0
	move	d0,(a1)+
	move	#3,d0			; MaxY Speed
	jsr	Random	
	addq	#1,d0
	move	d0,(a1)+
	
	dbra	d1,SCB01

MB02	tst	CountVTB
	beq	MB02
	clr	CountVTB

	move	#BobNum-1,d2
	move.l	#BobXY,a0
	move.l	#BobDXY,a2
MB09
	move	(a2),d0
	move	2(a2),d1
	add	d0,(a0)
	add	d1,2(a0)

	tst	(a0)
	bpl	MB03
MB05	neg	(a2)
	move	(a2),d0
	add	d0,(a0)
	bra	MB04
MB03	cmp	#(PicWidth-BobW-1)*16,(a0)
	bgt	MB05
MB04

	tst	2(a0)
	bpl	MB08
MB06	neg	2(a2)
	move	2(a2),d0
	add	d0,2(a0)
	bra	MB07
MB08	cmp	#PicHeight-BobH-1,2(a0)
	bgt	MB06
MB07
	addq.l	#4,a0
	addq.l	#4,a2
	dbra	d2,MB09

	btst	#6,$bfe001
	bne	MB02			; continue if no button pressed

MB02a

** End of session

	rts

******************************************************************
***************** Store bob pos **********************************
******************************************************************

BobPos
	eor	#$ffff,FlipFlop		; double buffering change
;					; screens.

** Decide which of the double buffers to use.

	move.l	#CopperList,NextCopperList
	move.l	#SecCopperList,CurCopperList
	move.l	#RasterPic2,Screen
	move.l	#RasterPic,Screen2
	tst	FlipFlop
	bne	BP04
	move.l	#RasterPic2,Screen2
	move.l	#RasterPic,Screen
	move.l	#CopperList,CurCopperList
	move.l	#SecCopperList,NextCopperList
BP04

** Update copperlists

	move.l	Screen2,d0		; to be showed screen
	move	#PicDepth-1,d1
	move.l	#CoppPicP-CopperList,a0
	add.l	NextCopperList,a0
BP03	move	d0,6(a0)
	swap	d0
	move	d0,2(a0)
	swap	d0
	addq.l	#8,a0
	add.l	#PicWidth*2,d0
	dbra	d1,BP03

** Store XY loop

	move	#BobNum-1,d0
	move.l	#CoppBobs-CopperList,a0
	add.l	NextCopperList,a0
	move.l	#BobXY,a1		; XY pos of bobs		
	move.l	#CoppClear-CopperList,a2
	add.l	NextCopperList,a2	; * to clear bob area
	move.l	#Multiply,a3
	clr.l	d2

BP01	move	(a1)+,d2		; X Pos	
	move	(a1)+,d1		; Y
	asl	#2,d1			; X4
	move.l	(a3,d1),d1		; same as mulu	#PicDepth*PicWidth*2,d1
	move	d2,d3			; copy X
	lsr	#3,d2
	and	#~$1,d2
	add.l	d2,d1
	add.l	Screen,d1		; to be showed screen

	move	6(a0),6(a2)		; clear bltdptl
	move	2(a0),2(a2)		; bltdpth

	move	d1,6(a0)		; bltdptl
	move	d1,6+8(a0)		; bltcptl
	swap	d1
	move	d1,2(a0)		; bltdpth
	move	d1,2+8(a0)		; bltcpth

	and	#$f,d3			; right shift
	ror	#4,d3
	move	d3,22(a0)		; store BPLCON0

	or	#(%1111<<8)!$ca,d3	; ABCD in use, AB + notAC
	move	d3,18(a0)		; store BPLCON1

	add.l	#BobSize,a0
	add.l	#ClearSize,a2		; next bob
	dbra	d0,BP01			; store next bob	

;					; next copperlist
	move.l	CurCopperList,cop1lc+_custom

** End of session

	rts

FlipFlop	ds	1	; flipflop double buffering flag
Screen		ds.l	1	; which screen that is on
Screen2		ds.l	1	; Screen to clear
CurCopperList	ds.l	1	; * to current copperlist
NextCopperList	ds.l	1	; the copper list to be used next

******************************************************************
********* Random seed routine ************************************
******************************************************************
** Stolen from a Fish disk.
** Input
** d0		equ		rnd seed


RandomSeed	ADD.L	D0,D1		user seed in d0 (d1 too)
		MOVEM.L	D0/D1,RND

; drops through to the main random function (not user callable)

LongRnd		MOVEM.L	D2-D3,-(SP)	
		MOVEM.L	RND,D0/D1	D0=LSB's, D1=MSB's of random number
		ANDI.B	#$0E,D0		ensure upper 59 bits are an...
		ORI.B	#$20,D0		...odd binary number
		MOVE.L	D0,D2
		MOVE.L	D1,D3
		ADD.L	D2,D2		accounts for 1 of 17 left shifts
		ADDX.L	D3,D3		[D2/D3] = RND*2
		ADD.L	D2,D0
		ADDX.L	D3,D1		[D0/D1] = RND*3
		SWAP	D3		shift [D2/D3] additional 16 times
		SWAP	D2
		MOVE.W	D2,D3
		CLR.W	D2
		ADD.L	D2,D0		add to [D0/D1]
		ADDX.L	D3,D1
		MOVEM.L	D0/D1,RND	save for next time through
		MOVE.L	D1,D0		most random part to D0
		MOVEM.L	(SP)+,D2-D3
		RTS

********* Random routine *********************************************
** Input  upper limit of RND (MAX $FFFF)
** output rnd in range
** IN  : d0
** OUT : d0

Random		
		movem.l	d1-d7,-(sp)
		MOVE.W	D2,-(SP)
		MOVE.W	D0,D2		save upper limit
		BEQ.S	Ra01		range of 0 returns 0 always
		BSR.S	LongRnd		get a longword random number
		CLR.W	D0		use upper word (it's most random)
		SWAP	D0
		DIVU.W	D2,D0		divide by range...
		CLR.W	D0		...and use remainder for the value
		SWAP	D0		result in D0.W
Ra01		MOVE.W	(SP)+,D2
		movem.l	(sp)+,d1-d7
		RTS

RND		DS.L	2		random number

******** Bob data ************************************************

	Section	BobData,DATA_C
BobData
	dc.l	$03e00000,$00000000
	dc.l	$0c180000,$03e00000
	dc.l	$11040000,$0ff80000
	dc.l	$27020000,$1ffc0000
	dc.l	$2e020000,$1ffc0000

	dc.l	$5c010000,$3ffe0000
	dc.l	$58010000,$3ffe0000
	dc.l	$50010000,$3ffe0000
	dc.l	$40010000,$3ffe0000

	dc.l	$40010000,$3ffe0000
	dc.l	$20020000,$1ffc0000
	dc.l	$20020000,$1ffc0000
	dc.l	$10040000,$0ff80000

	dc.l	$0c180000,$03e00000
	dc.l	$03e00000,$00000000
	dc.l	$00000000,$00000000

BobCookie ds	BobH*(BobW+1)*BobD		; Cookie data

******** Allocations *********************************************

	Section	PublicMemory,BSS_C
StartPublic
BobXY	ds	BobNum*2		; Pos of bobs+c
BobDXY	ds	BobNum*2		; Pos of bobs+c
Multiply ds.l	PicHeight		; a finished calculated 
;					; table of line offsets
StopPublic


	SECTION	ChipMemory,BSS_C
StartChip
RasterPic	ds	PicWidth*PicDepth*PicHeight	
RasterPic2	ds	PicWidth*PicDepth*PicHeight	
StopChip
	END

