;-----------------------------------------------------------------------------
; Tetris © Copyright 1990 Software Alchemy of Anselm Hook All Rights Reserved
; V1.0 18-jan-89 Code generated by Anselm Hook using CED, Aztek and Transform
;-----------------------------------------------------------------------------

	include	'tetris.i'

	public	_main			; workbench support!
	public	AmigaJunk

;***************************************************************************
;***************************************************************************
;***************************************************************************
;***************************************************************************
;*
;*	This Amiga Port of Tetris is a public domain program which I am
;*	releasing in source code format as a tutorial on assembly
;*	language programming.  It is moderately accurate to the original
;*	product, featuring multiple players, beveled tiles, different
;*	levels and audio.  Has useful code on a software PAL/NTSC switch,
;*	interleaved-blitter, coppers, midi based music & raw serial i/o,
;*	CIAA,CIAB timer control, keyboard handler + extensive greetings also.
;*	Does 0 Dimensional Tetris, I'm not sure how else to describe it!
;*							Andy Hook
;*
;***************************************************************************

; I use the vertical beam for timing in two places, this will not work if
; you have light pen latch set for god knows what reason - Andy.

; Note that the music score is a direct dump of the Yamaha Demo mode, I
; couldn't find any copyright on this data dump, and although the song itself
; is uncopyrighted, there might be some cause for concern - Andy.

; The musical instruments you hear were graciously donated by Kevin Stratton.
; Kevin Stratton is the author of the fantastic music and Sound-FX in Vortex.

; I must also thank Mark Vange for teaching me about MIDI.

; Software pal switch is poke #32,$dff1dc for those who can't wait.
; As shown to me by David Black III coder and integrator extraordinare.

; Note: I've decided that the technique I used here for multitasking the
; players was somewhat inefficient.  A better way to do it would be to have
; a true interupt driven multitasking exec.  Unfortunately that would be
; way too slow for this type of application.

SOUND_ROW    equ 14*16
SOUND_LEVEL  equ 10*16
SOUND_PIECE  equ 12*16
SOUND_RAISER equ 9*16

;
;	Revision History:
;		Dec 29  1989	-  Played Arcade Game today, made mock outline.
;		Jan  1  1990	-  Wrote MachineTakeover(), ColorFader()
;				   ContinueDraw(), KeyboardInterupt()
;				   SetupVideo(), CopperList:
;		Jan 2-7 1990    -  Fleshed out game logic
;		Jan 8   1990	-  Wrote SoundEngine();  PrintText();
;		Jan 12  1990	-  Revamped block format for PatchCutLine();
;		Jan 15	1990	-  Wrote Multitasking Collapsed Line Flasher.
;		Jan 16  1990	-  Wrote header text, put in AllocMem();
;				   Demonstrated Beta at AMUC General Meeting
;		Jan 18	1990	-  Raisers(); Inter-level-delay
;				   Done.
;				(but wait...even more goodies ;-)
;		Jan 29  1990    -  Added Simple MusicEngine() + SoundLoader();
;
;			(Bien Gott Mr. Fish is up to #308!!! mama mia)
;
;	Variables is used as an index to Tetris variables, this allows
;	a pc-relative destination, which the 68000 instruction set doesn't
;	support naturally.  The cost is one permanently allocated register.
;


Variables					; Variables *must* be first
Tetris
	bra	AmigaJunk
_main	bsr	SaveZero
	lea	BaseSeed(pc),a0			; (randomize the seed)
	move.l	$dff006,(a0)
	bsr	CheckCompilerFailure		; See if Aztek had problems
	bne.s	Exit
	bsr	AllocatePieces			; get some RAM for graphics
	beq.s	Exit
	bsr	JustPlayIt			; Start muzak.
	bra	TetrisSuccess

TetrisExit
	bsr	StopTheMusic
Exit2	bsr	FreePieces
Exit	bsr	RestoreZero
	moveq	#1,d0
	move.l	d0,d1
	rts

TetrisSuccess
	bsr	GetTopaz			; Find System Font
	bsr	MachineTakeover			; Borrow Amiga for a while
	bsr	TetrisSetup			; A5 = Variables, setup view
	bsr	MakeMulu110			; Make Blit Mulu Table
	bsr	UnpackPieces			; Colorize Tetris Images
	bsr	ResolveDetails			; Make Images look connectable
	bsr	DoIntro				; (instructions)
	bsr	SayName				; (Whoops Almost forgot!)
	bsr	SuperDisplay			; Show all player play fields

;
;	Tetris Main Loop, calls all players in turn...
;		A5 = Pointer to Base of Variables
;
MainLoop					; MAIN LOOP
	bsr	WaitFrame			; Syncronize to screen refresh
	bsr	WatchInput			; Always watch for input

	lea	SupervisorPlayerList(pc),a0
1$	move.l	(a0)+,d0			; D0 = Next Player or -1
	bmi.s	MainLoop
	move.l	a0,-(a7)			; (save)
	lea	(a5,d0.l),a4			; A4 = Player One Variables
	bsr	PlayPlayer			; Play game with A4 Variables
	move.l	(a7)+,a0
	bra.s	1$


;
;	Setup screen display for all supplied players
;
SuperDisplay
	lea	SupervisorPlayerList(pc),a0
SuperDisplay100
	move.l	(a0)+,d0			; D0 = Next Player or -1
	bmi.s	SuperDisplay900
	move.l	a0,-(a7)
	lea	(a5,d0.l),a4			; A4 = Player #
	lea	PlayFieldOutline(pc),a3		; A3 = Draws a big empty box
	move.w	PlayerArea(a4),d5		; D5 = Player's area
	bsr	RenderLines			; (Draws big empty rectangle)
	bsr	DumpScore
	move.l	(a7)+,a0
	bra.s	SuperDisplay100
SuperDisplay900
	rts

;
;	Find level of any other living players, return such for this player
;	There may be a problem if all other players are inactive, but
;	I don't think that should be possible.
;
SuperScan
	moveq	#0,d0
	lea	SupervisorPlayerList(pc),a0
1$	move.l	(a0)+,d1			; D0 = Next Player or -1
	bmi.s	2$
	lea	(a5,d1.l),a1			; A4 = Player #
	cmp.l	a1,a4				; don't scan myself
	beq.s	1$
	tst.w	PlayerStats(a1)
	beq.s	1$
	move.b	PlayerStats+2(a1),d0		; use public level
	subq.b	#1,d0
	bpl.s	2$
	moveq	#0,d0
2$	rts

;
;	SuperWait Routine is called by a player wishing to advance levels.
;	Scan for no other active players, when no active players
;	stop the music, have a delay loop, start the music, continue,
;	also change the seed at this time.
;
;	You can join in a game at any point, but you can't advance to the
;	next level till all other players are done.
;
SuperWait
	lea	SupervisorPlayerList(pc),a0
1$	move.l	(a0)+,d1			; D0 = Next Player or -1
	bmi.s	SuperWait200
	lea	(a5,d1.l),a1			; A4 = Player #
	cmp.l	a1,a4				; don't scan myself
	beq.s	1$
	cmp.w	#12,PlayerStats(a1)		; If Players Active then fail
	bcs.s	1$
2$	moveq	#-1,d0				; failure
	rts

SuperWait200
	bsr	MusicEnginePause

	move.w	#3*60,d0			; 2 Second Delay at 60 Hz
1$	cmp.b	#30,$6(a6)
	bne.s	1$
2$	cmp.b	#20,$6(a6)
	bne.s	2$
	dbf	d0,1$

	lea	speed(pc),a0			; Allow Speed 24-18
	move.b	(a0),d0
	subq.b	#1,d0
	cmp.b	#24,d0
	bcs.s	3$
	move.b	#24,d0
3$	cmp.b	#18,d0
	bcc.s	4$
	move.b	#17,d0
4$	move.b	d0,(a0)
	bsr	MusicEngineUnPause

	move.l	BaseSeed(pc),d0
	mulu	RndSeed(a4),d0
	move.l	d0,BaseSeed(a5)			; Randomize the shared seed!
	moveq	#1,d0				; Success
	rts

SuperSearch
	lea	SupervisorPlayerList(pc),a0
1$	move.l	(a0)+,d1			; D0 = Next Player or -1
	bmi.s	3$
	lea	(a5,d1.l),a1			; A4 = Player #
	cmp.l	a1,a4				; don't scan myself
	beq.s	1$
	tst.w	PlayerStats(a1)
	beq.s	1$
2$	moveq	#-1,d0				; failure
	rts
3$	moveq	#1,d0
	rts

;
;  If any other players alive then return, else start music for everybody.
;  Start up any variables too while I'm at it...
;
SuperMusic
	bsr	SuperSearch			; -1 = others alive
	bmi.s	1$
	bsr	SuperVariables
	move.b	speed(pc),d0
	bsr	MusicEngineUnPause
	moveq	#0,d0
1$	rts

;
;  If any other players alive return, else stop music for everybody.
;
SuperUnMusic
	bsr	SuperSearch			; -1 = others alive
	bmi.s	1$
	bsr	MusicEnginePause
	moveq	#0,d0
1$	rts


;
;	Setup System Public Variables, start of new game.
;	Scramble the seed even more!!!! (arg!)
;
SuperVariables
	lea	speed(pc),a0
	move.b	#24,(a0)
	move.l	BaseSeed(pc),d0
	mulu	#47,d0
	move.l	d0,BaseSeed(a5)
	rts

speed: dc.b 24,0

SupervisorPlayerList
		dc.l	PlayerOneStart,PlayerTwoStart,-1

;-----------------------------------------------------------------------------


;
;	Support any player whose variable pointer is passed in A4
;
PlayPlayer
	move.w	PlayerStats(a4),d0
	jmp	PlayArbitration(pc,d0.w)
	nop
PlayArbitration
	bra.l	PlayerDead			;  0 Just plain dead
	bra.l	PlayerWaiting			;  4 Wait for others to catch up
	bra.l	PlayerNewLevel			;  8 Setting up new level
	bra.l	PlayerPlaying			; 12 In process of playing
	bra.l	PlayerEndLevel			; 16 Doing end of level
	bra.l	PlayerEndGame			; 20 Doing High Score (if any)
	bra.l	PlayerCheckLine			; 24
	bra.l	PlayerLine			; 28 Special Collapse Line Mode



return	rts
PlayerDead
 	move.w	PlayerPort+2(a4),d0		; Which port?
	move.b	$bfe001,d1			; Amiga port Button Hardware
	and.b	d0,d1				; User Want to play?
	bne.s	return
	bsr	SuperMusic
	bra	PlayerStart			; User selected a game!
PlayerWaiting
	bsr	SuperWait			; Any other players "ingame"
	bmi.s	return				; (wait till they aren't)
	move.w	#8,PlayerStats(a4)		; Goto New Level Mode
	rts
PlayerNewLevel
	bsr	GenerateLevel			; Setup Next Level
PlayerPlaying
	bsr	Raisers
	bsr	BlockHandle			; Get Input, Update Block X,Y
	bra	MoveBlock			; Move Block, Handle Hits
PlayerEndLevel
	move.w	#16,PlayerStats(a4)		; (set myself)
	move.w	#4,PlayerStats(a4)		; Goto Wait for others mode
	rts
PlayerEndGame
	move.w	#20,PlayerStats(a4)		; (set myself)
	move.w	#0,PlayerStats(a4)		; Set "just plain dead" mode
	bra	SuperUnMusic

;
;	Collapse any finished lines, See if won or died, setup next piece.
;
NextPiece
	move.w	PlayerArea2(a4),d0
	add.w	#4,d0
	cmp.b	BlockPos+6(a4),d0		; Stopped to High? (Game Over)
	bcc.s	PlayerEndGame

	move.l	PlayerXY(a4),BlockPos(a4)	; Start New Block at Top
	bsr	rnd				; D0 = Random(0-55)
	and.w	#63-7,d0			; Get (0-6) (*4*2)

;
; See next piece please
;
	move.w	d0,-(a7)
	movem.w	PlayerArea2(a4),d0/d1		; d1=x,d0=y
	add.w	#2,d1
	subq.w	#4,d0
	bsr	Erase
	move.l	RndSeed(a4),-(a7)
	bsr	rnd
	move.l	(a7)+,RndSeed(a4)
	and.w	#63-7,d0			; Get (0-6) (*4*2)
	move.w	d0,BlockID(a4)
	moveq	#0,d0
	move.l	d0,d1
	movem.w	PlayerArea2(a4),d0/d1		; d1=x,d0=y
	add.w	#2,d1
	subq.w	#4,d0
	clr.l	BlitRepairAddress(a4)		; (No predecessor)
	bsr	DrawBlockAt
	move.w	(a7)+,d0

	clr.l	BlitRepairAddress(a4)		; (No predecessor)
	clr.b	RotateFlag(a4)			; No special rotate state
	move.w	d0,BlockID(a4)			; Block # of default orient
;
;	Generalized Checkline routine, checks every single
;	screen line for possible completion.
;
PlayerCheckLine
	lea	TetrisByteMap(pc),a3		; a3 = Player Space
	add.w	PlayerArea(a4),a3		; Where Am I?
	add.w	#1,a3				; Get active region
	move.l	a3,a2				; a2 = Current Line #
	moveq	#0,d5
	moveq	#RECT_Y-1,d7			; Check tall
3$	moveq	#RECT_X-1,d6			; Check across
	move.l	a2,a1				; a1 = Current Line
2$	tst.b	(a1)+				; Any Holes?
	beq.s	4$				; Empty spot here, skip...
	dbf	d6,2$				; check rest of line
	bra	FoundLine
4$	add.w	#44,a2				; Next Line
	dbf	d7,3$

	move.w	#12,PlayerStats(a4)		; Return to normal mode

	tst.b	PlayerStats+3(a4)		; Any rows left to solve?
	bpl.s	5$
	move.w	#SOUND_LEVEL,d0			; End Level triumphant noise
	bsr	SoundPlay
	bra	PlayerEndLevel			; Oh my God!!! I finished it..
5$	rts




;
;	Start a brand new player game
;
PlayerStart
	move.l	#'0000',d0			; Wipe Score on New Game
	move.l	d0,ScoreText+4(a4)
	move.l	d0,ScoreText+8(a4)
	bsr	SuperScan			; D1.b = Level to start on
	move.b	d0,PlayerStats+2(a4)		; use public level
	move.w	#8,PlayerStats(a4)		; Set Generate Mode (no wait)
	rts


GenerateLevel
	add.b	#1,PlayerStats+2(a4)		; Level += 1;

	move.b	PlayerStats+2(a4),d0		; Algorithm for # of Rows req.
	lsl.b	#1,d0
	cmp.b	#20,d0
	bcs.s	0$
	move.b	#20,d0
0$	cmp.b	#7,d0
	bcc.s	1$
	move.b	#7,d0
1$	move.b	d0,PlayerStats+3(a4)

	move.l	#$00400020,BlockSpeed(a4)
	move.b	PlayerStats+2(a4),d0		; Algorithm for Speed Per Lev
	cmp.b	#15,d0
	bcs.s	2$
	move.b	#15,d0
2$	lsl.b	#3,d0
	add.b	#$10,d0
	move.b	d0,BlockSpeed+3(a4)		; Speed up per level

	move.l	#$00000080,Counter(a4)
	lea	PlayFieldClear(pc),a3		; A3 = Draws a big empty box
	move.w	PlayerArea(a4),d5		; D5 = Player's area
	bsr	RenderLines			; (Draws big empty rectangle)
	bsr	DumpScore
	bsr	Difference			; Make Unique Backdrop
	move.w	#-1,BlockPos+6(a4)		; Force Very Low First Entry!
	move.l	BaseSeed(pc),d0			; Get shared seed for game
	move.w	PlayerStats+2(a4),d1		; Modify according to level
	mulu	d1,d0				;
	move.l	d0,RndSeed(a4)			; My private seed per piece
	bra	NextPiece			; Setup Next Piece



;
;	Generate each levels unique backdrop, enemies ect
;

Difference
	moveq	#0,d0
	move.b	PlayerStats+2(a4),d0
	and.b	#15,d0				; 16 different levels...
	lea	TetrisLevels(pc),a3
	mulu	#5*2,d0
	add.w	d0,a3
	move.b	1(a3),CollapseJunk+2(a4)	; (stash mode here)

	moveq	#0,d4
	move.w	PlayerArea(a4),d4		; D4 = Player's area
	add.l	#(RECT_Y-5)*44+1,d4		; get to bottom

	moveq	#5,d2
	moveq	#5-1,d7
0$	move.w	(a3)+,d3
	moveq	#RECT_X-1,d6
1$	add.w	d3,d3
	bcc.s	2$
	add.w	#8*4,d2
	and.w	#8*4*4-1,d2
	bsr	PokeScreen
2$	add.w	#1,d4
	dbf	d6,1$
	add.w	#44-RECT_X,d4
	dbf	d7,0$
	rts

;
;	"Physically" Move the Block, and handle any collisions with wall ect.
;
MoveBlock
	bsr	GetBlockPos			; D4 = Block Pos
	move.w	BlockID(a4),d2			; D2 = Pointer to Block Image
	bsr	TestCollision			; Time to stop?
	bne.s	BlockHit
	move.l	BlockPos(a4),BlockPos+4(a4)	; Remember Pos for Un-Move
	bra	DrawBlock			; Visibly Move Block

BlockHit
	move.b	BlockPos+4(a4),d0		; Detect Sideways Moves
	cmp.b	BlockPos(a4),d0			; Compare Old X to new X
	bne.s	MoveItWall			; Moved Sideways? XO != XN
	move.w	#SOUND_PIECE,d0			; Make "Click" Noise
	bsr	SoundPlay
	bsr	GetBlockOldPos			; Where is block
	move.w	BlockID(a4),d2			; D2 = Block to byte screen
	bsr	DrawBlockMem			; Write to byte screen
	bra	NextPiece

MoveItWall
	move.w	BlockPos+4(a4),BlockPos(a4)	; Discard XNew sideways move
	rts




;
;	Handle users input, move block as requested, and also move it down.
;
BlockHandle

	move.w	BlockSpeed+2(a4),d0		; Get Suggested Speed Y
	add.w	d0,BlockPos+2(a4)		; Add Speed to Position Y
	bsr	ReadJoy				; D1 = Joystick %LU------RD
	move.w	BlockSpeed+2(a4),d0		; Get Y Speed
	lsr.w	#1,d1				; D1 = %LU-----R
	bcc.s	BlockHandle100
	add.w	d0,d0
	add.w	d0,BlockPos+2(a4)
BlockHandle100
	move.b	BlockPos+6(a4),d0		; Disallow Angle Movements
	cmp.b	BlockPos+2(a4),d0
	bne.s	BlockHandle500
	move.w	BlockSpeed(a4),d0		; Get X Speed
	lsr.w	#1,d1				; D1 = %LU------
	bcs.s	BlockHandle400
	add.b	d1,d1				; D1 = %U-------
	bcc.s	BlockHandle500
	neg.w	d0
BlockHandle400
	add.w	d0,BlockPos(a4)
BlockHandle500

;Check for block rotate:
;	Done after move because when block stops the image is not redrawn
;	and if block rotated just before stop it would appear in previous
;	orientation.  Wheras rotate never occurs before impending collisions.

	move.b	$bfe001,d0			; Test Left Port Button
	move.w	PlayerPort+2(a4),d1		; Get Amiga Hardware Bit #
	and.b	d1,d0
	cmp.b	LastButton(a4),d0		; Any change in button state?
	beq.s	BlockHandle900
	move.b	d0,LastButton(a4)		; Accept only Button Down
	bne.s	BlockHandle900

	move.w	BlockID(a4),d5			; Attempt Rotation
	move.w	d5,d1
	add.w	#2,d1				; Get Next Rotation of Color
	and.w	#6,d1				; Allow rotate only
	and.w	#$f8,d5				; Clean
	or.w	d1,d5				; D5 = New Block Rotation
	bsr	GetBlockPos			; D4 = Block ByteMap pos
	move.w	d5,d2				; D2 = Rotation to check out
	bsr	TestCollision
	bne.s	BlockHandle900			; 0 = Successful rotation
	move.w	d5,BlockID(a4)			; Rotation was possible
BlockHandle900
	rts


	dc.b	"On dreamless night"
	dc.b	"Ere the moon does rise"
	dc.b	"Laughlight Shadows"
	dc.b	"Across dreamless eyes,"


;
;	Found Line to Kill!!!
;
FoundLine
	move.w	#28,PlayerStats(a4)		; Set Collapse Line Mode!!!
	move.w	#$0805,CollapseJunk(a4)		; Some stuff for collapse
	move.l	a3,CollapseJunk+4(a4)		; A3 = Top of Field
	move.l	a2,CollapseJunk+8(a4)		; A2 = Line Addr to Cut
	sub.w	#RECT_Y-1,d7
	neg.w	d7
	move.w	d7,CollapseJunk+12(a4)		; D7 = Line # to cut
	move.w	d7,d0
	subq.w	#1,d0
	bsr	DrawLine			; (Draw Patch)
	move.w	d7,d0
	addq.w	#1,d0
	bsr	DrawLine			; (Draw Patch if any)
	move.w	#SOUND_ROW,d0			; Make "Line Eat" Noise
	bra	SoundPlay



;
;	Separated to be multitasking with other players
;
PlayerLine
	move.l	CollapseJunk+4(a4),a3		; A3 = Top of Field
	move.l	CollapseJunk+8(a4),a2		; A2 = Line to Cut
	move.w	CollapseJunk+12(a4),d7		; D7 = Line # to cut

	sub.b	#1,CollapseJunk(a4)
	move.b	CollapseJunk(a4),d0
	beq.s	PlayerLine100
	bsr	GargleBlast
	move.w	d7,d0				; D0 = Line to draw
	bra	DrawLine
	rts

PlayerLine100
	move.w	#24,PlayerStats(a4)		; Be in PlayerLineDone Mode
	bsr	CollapseLine			; Kill Line in Byte Map
	sub.b	#1,PlayerStats+3(a4)		; Decrement Required Count
	bsr	LineScore
	bra	RedrawPlayField
	rts

GargleBlast
	move.l	a2,a0				; A2 = Line to flash
	move.b	CollapseJunk+3(a4),d0		; Random # to flash of
	or.b	#5,d0				; (block)
	add.b	#8*4,d0
	move.b	d0,CollapseJunk+3(a4)		; Random # to flash of
	moveq	#RECT_X-1,d6			; Flash Line Prettily
1$	add.b	#8*4,d0
	and.b	#8*4*4-1,d0
	move.b	d0,(a0)+
	dbf	d6,1$
	rts


;How to write Collapse Line:
;	-Collapse line will just set a new mode, that mode will flash the
;	 line and collapse it itself, it will also add the score and count
;	required, finally it will go back to itself to check for more...
;	score count, single double triple tetris is computed at this time.
;


;
;	Collapse line on byte map
;
CollapseLine
	bsr	PatchCutLine			; Breakup tiles, Pass A2

						; a3 = Top of Field
						; a2 = Current Line to Kill
	move.l	a2,a1				; A1 = Line to Collapse
CollapseLine100
	cmp.l	a3,a1				; Is at top?
	beq.s	CollapseLine900
	move.w	#RECT_X-1,d2			; Do one Line
1$	move.b	-44(a1),(a1)+			; Copy Down one line
	dbf	d2,1$
	sub.l	#RECT_X+44,a1			; Goto Previous Line
	bra.s	CollapseLine100
CollapseLine900
	rts


PatchCutLine
						; A2 = Current Line
	lea	-44(a2),a1			; A1 = Do Line above
	move.w	#$ff-1,d3			; D3 = Cut Bottom off
	bsr	PatchCut100
	lea	44(a2),a1			; A1 = Do Line below
PatchThisLine
	move.w	#$ff-4,d3			; D3 = Cut top off
PatchCut100
	move.w	#RECT_X-1,d2			; Do one Line
1$	move.b	(a1),d0				; READ
	beq.s	2$
	move.b	d0,d1				; (save)
	and.w	#31,d0
	move.b	UnResolveType(pc,d0.w),d0	; Get Originator type
	and.b	d3,d0				; Cut one side
	move.b	ResolveType2(pc,d0.w),d0	; Get proper back
	and.b	#$e0,d1				; Mask off old walls info
	or.b	d1,d0				; Insert new walls info
2$	move.b	d0,(a1)+			; WRITE
	dbf	d2,1$
	rts

;
;	Convert back to quirky resolve type format
;
UnResolveType	dc.b	15,9, 5,4,13,0,0,0
		dc.b	0,3,10,8,11,0,0,0
		dc.b	0,6, 0,1, 7,0,0,0
		dc.b	0,12,0,2,14,0,0,0
;
;	A handy copy of the ResolveType Table...
;
ResolveType2	dc.b	5,2*8+3,3*8+3,8+1,3,2,2*8+1,2*8+4
		dc.b	8+3,1,8+2,8+4,3*8+1,4,3*8+4,0



;
;	Every x pieces raise up screen one.
;
Raisers
	move.b	CollapseJunk+2(a4),d0
	btst	#1,d0
	beq.s	RaisersStop
	add.w	#1,Counter(a4)			; Increment player counter
	move.w	Counter(a4),d0
	cmp.w	Counter+2(a4),d0
	beq.s	RaisersGo
RaisersStop
	rts
RaisersGo
	add.w	#$200,d0			; Till next time
	move.w	d0,Counter+2(a4)

	move.w	PlayerArea2(a4),d0		; Is block below here?
	addq.w	#4,d0
	cmp.b	BlockPos+6(a4),d0
	bcc.s	RaisersStop

	move.l	d0,-(a7)			; Raiser Noise
	move.l	#SOUND_RAISER,d0
	bsr	SoundPlay
	move.l	(a7)+,d0

	sub.b	#1,BlockPos+6(a4)		; Move Real Block Up!
	move.b	BlockPos+6(a4),BlockPos+2(a4)

	bsr	RaisersDraw

	lea	TetrisByteMap(pc),a3
	add.w	PlayerArea(a4),a3
	add.w	#1,a3				; A3 = top of player region
	move.l	a3,a2
						; Copy ByteMap up one line
	move.w	#RECT_Y-2,d3			; (don't do last line)
0$	move.w	#RECT_X-1,d2
1$	move.b	44(a2),(a2)+
	dbf	d2,1$
	add.w	#44-RECT_X,a2
	dbf	d3,0$

	bsr	GargleBlast			; Write random pattern once

	move.b	BlockPos(a4),d0			; Force hole in pattern
	sub.b	#RECT_X,d0
	neg.b	d0
	cmp.b	#RECT_X,d0
	bcs.s	3$
	and.w	#7,d0
3$	and.w	#15,d0
	;eor.b	#%111,d0			; randomize
	move.b	#0,(a2,d0.w)

	move.l	a3,a1				; Patch top line
	bsr	PatchThisLine

	moveq	#0,d0
	bsr	DrawLine			; Draw Top (was patched)
	moveq	#RECT_Y-2,d0
	bsr	DrawLine			; hack, fix any teat glitch.
	moveq	#RECT_Y-1,d0
	bra	DrawLine			; Draw Bot (was gargleblasted)


RedrawPlayField
	moveq	#0,d7
1$	move.l	d7,d0				; D0 = Line # to Draw
	bsr	DrawLine
	add.w	#1,d7
	cmp.w	#RECT_Y+1,d7			; (over-do to get teats)
	bne.s	1$
	rts


GetBlockPos
	moveq	#0,d4
	move.b	BlockPos+2(a4),d4		; Y byte # pos of block
	mulu	#44,d4				; get raw
	moveq	#0,d1
	move.b	BlockPos(a4),d1			; X byte # pos of block
	add.w	d1,d4
	rts


GetBlockOldPos
	moveq	#0,d4
	move.b	BlockPos+6(a4),d4		; Y byte # pos of block
	mulu	#44,d4				; get raw
	moveq	#0,d1
	move.b	BlockPos+4(a4),d1		; X byte # pos of block
	add.w	d1,d4
	rts


TestCollision
						; D4 = Screen Pos of block
	lea	TetrisShapes(pc),a0
	move.w	(a0,d2.w),d2			; D2 = Block Data
	lea	TetrisByteMap(pc),a0		; A0 = Byte Mapped Screen
	add.w	d4,a0
	moveq	#4-1,d0				; Examine Each Block Byte
TestIt200
	moveq	#4-1,d1
TestIt300
	add.w	d2,d2				; (<<BITSHIFT onto Carry Flag)
	bcc.s	TestIt500			; Anything in block here?
	tst.b	(a0)				; Coincident with Real World?
	beq.s	TestIt500
	moveq	#-1,d0				; Whoops, hit something...
	rts
TestIt500
	lea	1(a0),a0			; Next HLine
	dbf	d1,TestIt300
	lea	40(a0),a0			; Next VLine
	dbf	d0,TestIt200
	moveq	#0,d0
	rts



DrawBlockMem
						; D4 = Where to draw
						; D2 = Block #
	lsl.w	#3,d2				; Get to multiples of 16
	lea	TetrisDetails(pc),a1
	lea	(a1,d2.w),a1			; Pointer to Real Block Data
	lea	TetrisByteMap(pc),a0		; A0 = Byte Mapped Screen base
	moveq	#4-1,d7				; Examine Each Block Byte
1$	moveq	#4-1,d6
2$	move.b	(a1)+,d2			; D2 = Data
	beq.s	3$
	move.b	d2,(a0,d4.w)			; To Byte Mapped
3$	add.w	#1,d4				; Next HLine of render
	dbf	d6,2$
	add.w	#40,d4				; Next VLine of render
	dbf	d7,1$
	rts


;
;
;	Clear out work area + draw walls ect...
;	each play zone is 24 deep and 16 wide, centered
;						; A3 = Command List
;
RenderLines
	move.w	(a3)+,d4			; D4 = Line Start,End
	bmi	RenderLines900			; Done?
	add.w	d5,d4				; + Offset
	move.w	(a3)+,d3			; D3 = Iterations to perform
	move.w	(a3)+,d2			; D2 = Color (byte graphic #)
	move.b	-2(a3),d1			; D1 = Horiz/Vert/Area?
	bmi.s	RenderLinesArea
	beq.s	RenderLinesHoriz
	cmp.b	#2,d1
	beq.s	RenderLinesAngled
RenderLinesVert
	bsr	PokeScreen			; Put D2 at D4 + Show it
	add.w	#44,d4
	dbf	d3,RenderLinesVert
	bra	RenderLines
RenderLinesAngled
	bsr	PokeScreen			; Put D2 at D4 + Show it
	add.w	#45,d4
	dbf	d3,RenderLinesAngled
	bra	RenderLines
RenderLinesHoriz
	bsr	PokeScreen			; Put D2 at A0 + Show it
	addq.w	#1,d4
	dbf	d3,RenderLinesHoriz
	bra	RenderLines

RenderLinesArea
						; D3 = Horizontal Size to do
	and.b	#$7f,d1				; D1 = Vertical Area to do
	ext.w	d1
	move.w	d1,d6				; D6 = Vertical Lines to do
	move.w	d3,d7				; Save Width
RenderLinesArea100
	bsr	PokeScreen
	addq.w	#1,d4				; Next H
	dbf	d3,RenderLinesArea100		; Next H Pos
	move.w	d7,d3				; Reset Horiz Count
	add.w	#43,d4				; Get to next vertical start
	sub.w	d3,d4
	dbf	d6,RenderLinesArea100
	bra	RenderLines
RenderLines900
	rts

	dc.b	"A Knight there was"
	dc.b	"And that a Noble man"
	dc.b	"That fro the time he first bigan"
	dc.b	"To Riden out, he loved"
	dc.b	"Truthe and Honour"
	dc.b	"Justyice and Curteysie."

PokeScreen
	move.w	d4,d0				; Screen Address
	ext.l	d0
	divu	#44,d0				; Get vertical lines #
	move.l	d0,d1				; D0 = Vert Byte Pos
	swap	d1				; D1 = Horiz Byte Pos
	bra	DrawOne				; Draw one element at D0/D1

EraseByteMap
	lea	TetrisByteMap(pc),a0
	move.w	#11*29-1,d0			; Wipe Byte Map
1$	clr.l	(a0)+
	dbf	d0,1$
	rts


LineScore
	moveq	#5,d2				; add up new score
	lea	ScoreText+4(a4),a0
	moveq	#6,d0
0$	move.b	(a0,d0.w),d1
	add.b	d2,d1
	move.b	d1,(a0,d0.w)
	cmp.b	#$3a,d1
	bcs.s	1$
	sub.b	#$39,d1
	and.b	#15,d1
	or.b	#$30,d1
	move.b	d1,(a0,d0.w)
	moveq	#1,d2
	dbf	d0,0$
1$

DumpScore
	lea	ScoreText(a4),a0		; show new score
	move.l	GameVideo(pc),a1		; Dest
	lea	44*4(a1),a1
	bsr	PrintText			; Print it!!!

	move.b	PlayerStats+3(a4),d0
	bpl.s	4$
	moveq	#0,d0
4$	divu	#10,d0				; Get Decimal...
	or.b	#$30,d0
	move.b	d0,LayerText+4(a4)		; 10's
	swap	d0
	or.b	#$30,d0
	move.b	d0,LayerText+5(a4)		; 1's
	lea	LayerText(a4),a0
	move.l	GameVideo(pc),a1
	lea	44(a1),a1
	bsr	PrintText

	move.b	PlayerStats+2(a4),d0
	bpl.s	5$
	moveq	#0,d0
5$	divu	#10,d0				; Get Decimal...
	or.b	#$30,d0
	move.b	d0,LevelText+4(a4)		; 10's
	swap	d0
	or.b	#$30,d0
	move.b	d0,LevelText+5(a4)		; 1's
	lea	LevelText(a4),a0
	move.l	GameVideo(pc),a1
	lea	44*4(a1),a1
	bra	PrintText


;---------------------------------------------------------------------

;
;	Watch Keyboard, start a player game if requested
;
WatchInput
	moveq	#0,d0
	lea	KeyEvent(pc),a0			; Get Key
	move.b	(a0),d0
	clr.b	(a0)				; Wipe Key from buffer
	cmp.b	#'P',d0				; P to Pause
	beq.s	Pause
	cmp.b	#$7b,d0				; ESC == Return to System
	beq	MachineReturn
	cmp.b	#$11,d0				; is ESC/F1 or greater?
	bcs.s	TetrisHandle900
	cmp.b	#$1b,d0				; is F10 or lesser?
	bcc.s	TetrisHandle900
	sub.b	#$11,d0
	lsl.b	#2,d0				; Index subroutines
	jmp	TetrisInputOptions(pc,d0.w)	; Goto Subroutine
TetrisHandle900
	rts

Pause	lea	KeyEvent(pc),a0
	btst	#6,$bfe001
	beq.s	PauseEnd
	btst	#7,$bfe001
	beq.s	PauseEnd
	move.b	(a0),d0
	cmp.b	#$7b,d0				; ESC == Return to System
	beq	MachineReturn
	tst.b	d0
	beq.s	Pause
PauseEnd
	clr.b	(a0)
	rts

TetrisInputOptions
	bra.l	TetrisF1			; (this wacko thing is
	bra.l	TetrisF2			;  due to p c relative code)
	bra.l	TetrisF3
	bra.l	TetrisF4
	bra.l	TetrisF5
	bra.l	TetrisF6
	bra.l	TetrisF7
	bra.l	TetrisF8
	bra.l	TetrisF9
	bra.l	TetrisFA
	nop					; (stop compiler optimizing)
TetrisF1
TetrisF2
TetrisF3
TetrisF4
TetrisF5
TetrisF6
TetrisF7
TetrisF8
TetrisF9
TetrisFA
	rts

;
;	Generate detailed tile face-pieces for Tetris Objects
;	The first pass merely sets down used positions, the second
;	pass actually sets up proper tile faces
;

ResolveDetails
	bsr	ResolveDetails2
ResolveDetails2
	lea	TetrisShapes(pc),a0		; A0 = Simple Tile Shape Data
	lea	TetrisDetails(pc),a1		; A1 = Detailed Tiles (to be)
	moveq	#0,d7				; Do 7 Sets of 4 Tiles
0$	bsr	ResolveSet
	add.w	#32,d7				; Every 4th tile has new color
	cmp.w	#32*7,d7			; End of color spectrum?
	bne.s	0$
	rts


ResolveSet
	moveq	#4-1,d6				; Do 4 tiles (1 set of rots)
1$	move.w	(a0)+,d5			; D5 = One Tile
	bsr	ResolveOne
	dbf	d6,1$
	rts


ResolveOne
	moveq	#16-1,d4			; Bitscan 16 bits of one tile
2$	add.w	d5,d5				; Active bit here?
	bcc.s	3$
	bsr	DecideType			; D0 = Decide what connectable
	move.b	d0,(a1)				; Store Connection to Detailed
3$	lea	1(a1),a1			; Next Dest
	dbf	d4,2$
	rts

DecideType
	move.w	d4,d1				; D1 = Position in array
	sub.w	#15,d1
	neg.w	d1
	clr.w	d0				; D0 = Faces Active Bits

	cmp.b	#12,d1				; On bottom row?
	bpl.s	1$
	tst.b	4(a1)				; Anything below face?
	beq.s	1$
	or.b	#1,d0

1$	cmp.b	#4,d1				; On top row?
	bmi.s	2$
	tst.b	-4(a1)				; Above face?
	beq.s	2$
	or.b	#4,d0

2$	bclr	#2,d1				; At Left Edge?
	tst.b	d1
	beq.s	3$
	tst.b	-1(a1)				; Left of face?
	beq.s	3$
	or.b	#2,d0
						; (no pieces are like x00x)
3$	tst.b	1(a1)				; Right of face?
	beq.s	4$
	or.b	#8,d0
4$

;
;	Convert from my random drawn format to logical format also.
;
	and.w	#$000f,d0
	move.b	ResolveType(pc,d0.w),d0
	or.b	d7,d0				; What color is face on?
5$	rts
	; format: something below / to left / to top / to right

ResolveType	dc.b	5,2*8+3,3*8+3,8+1,3,2,2*8+1,2*8+4
		dc.b	8+3,1,8+2,8+4,3*8+1,4,3*8+4,0


;*****************************************************************************
;*****************************************************************************
;
;	Returns D0 within range of 0-x, This rnd function is from MANX

rnd
	move.l	RndSeed(a4),d0
	add.l	d0,d0
	bhi.L	3$
	eori.l	#$1d872b41,d0
3$	move.l	d0,RndSeed(a4)
	andi.l	#$0000ffff,d0
	divu	#7*4*2,d0			; Range = 0 to 7 ((*4*2)-1)
	swap	d0
	rts



; soon aztek, soon....

CheckCompilerFailure
	lea	Variables(pc),a5		; A5 = Tetris Variables
	lea	TetrisByteMap(a5),a0
	lea	TetrisByteMap(pc),a1
	cmp.l	a0,a1
	bne.s	AztekFailedAsUsual
	lea	PlayerTwoStart(a5),a4
	move.l	PlayerPort(a4),d0
	cmp.l	#$000a0040,d0
	bne.s	AztekFailedAsUsual
	moveq	#0,d0			; Compiler managed to compile
	rts
AztekFailedAsUsual
	moveq	#-1,d0
	rts




;
; This is for debugging purposes only.
;
SaveZero
	lea	$60,a0				; save zero page against use
	lea	zeropage(pc),a1
Cheat	moveq	#12-1,d0
1$	move.l	(a0)+,(a1)+
	dbf	d0,1$
	rts
RestoreZero
	lea	$60,a1				; restore zero page
	lea	zeropage(pc),a0
	bra.s	Cheat
zeropage:dcb.l	12,0


;*****************************************************************************
;*****************************************************************************
;
;	Initialize and Enter Tetris
;
TetrisSetup
	lea	$dff000,a6			; A6 = Amiga Hardware
	lea	Variables(pc),a5		; A5 = Tetris Variables
	bsr	SetupVideo			; Initialize Video
	bra	EraseByteMap			; Erase bytemap


;*****************************************************************************
;*****************************************************************************
; Amiga Hardware System Specific Support & Resources

SetupVideo
	bsr	VideoBitPlanes			; Setup Copper Bitplanes
	bsr	VideoClear

	lea	CopperList(pc),a0		; (copy list to chip alloced)
	move.l	GameCopper(pc),a1
	move.l	a1,d1
	move.w	#130-1,d0			; (guestimate)
1$	move.l	(a0)+,(a1)+
	dbf	d0,1$

	move.l	d1,$80(a6)			; Turn on View
	move.w	d1,$88(a6)
	move.w	#$87c0,$96(a6)			; All DMA On + Blitter Nasty
	move.w	#2,$2e(a6)			; Allow Copper Blits
	rts

VideoClear
	move.l	GameVideo(pc),a0
	move.w	#11*240-1,d0
1$	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	dbf	d0,1$
	rts

;
;  **** note this works only once, afterwards I use a copy of this list 
;

VideoBitPlanes
	move.l	ScreenMem(pc),d0		; D0 = Address for Screen
	move.l	d0,d1
	lsr.l	#1,d1
	move.l	d1,GameVideo2(a5)		; 1/2 Screen for fast blits
	lea	CopperPlanes(pc),a0		; A0 = Copper Plane Op codes
	move.l	d0,GameVideo(a5)
	moveq	#5-1,d1				; Setup plane pointers
1$	move.w	d0,6(a0)
	swap	d0
	move.w	d0,2(a0)
	swap	d0
	add.w	#8,a0				; Next Copper Poke
	add.l	#44,d0				; Next Interleave Start
	dbf	d1,1$
	rts

WaitFrame
	cmp.b	#$e0,$dff006			; Wait till past animations
	bne.s	WaitFrame
1$	cmp.b	#$e2,$dff006
	bne.s	1$
	rts


;*****************************************************************************
;*****************************************************************************
;
;	Perform an Ultra smooth and efficient Color Fade in or out
;	Supply	A0 = Destination CopperList (pass it -4)
;		A1 = Source Color Table
;

ColorFader
	moveq	#0,d4				; Start Pass on RED
	move.w	#48-1,d5			; Takes 48 Passes to finish
3$	bsr	WaitFrame			; Syncronize to frame


;	lea	CopperColors-4(pc),a0		; A0 = Copper Color Opcodes-4

; (lets find the list in chip...yawn)

 lea CopperColors(pc),a0
 lea CopperList(pc),a1
 sub.l a1,a0
 add.l GameCopper(pc),a0
 subq.w #4,a0			; (its wants to be -4 of it)


; **** note: the reason I always sub.w #4,address-register is because
;            the MC68xxx chips always sign extend to longword internally
;	     and its a moderately faster operation, as well as taking less
;	     memory than the sub.l counterpart (sub.b is not-legal by the way).



	lea	ColorTable(pc),a1		; A1 = Color Table to Seek


	bsr	Col200				; Perform pass of seek
	dbf	d5,3$				; till done
	rts
Col200	addq.w	#4,a0
	move.w	(a0),d0
	cmp.w	#$ffff,d0			; End of Copper List?
	beq.s	Col500
	cmp.w	#$0180,d0			; Valid Color?
	bmi.s	Col200
	cmp.w	#$01c0,d0
	bpl.s	Col200
	move.w	2(a0),d1			; Source Color
	moveq	#$f,d0				; R G or B
	lsl.w	d4,d0
	move.w	(a1)+,d3			; D3 = Dest Color
	move.w	d1,d2
	and.w	d0,d3
	and.w	d0,d2				; Mask out Color
	and.w	#$111,d0
	cmp.w	d3,d2
	beq.s	Col400
	bmi.s	Col300
	neg.w	d0
Col300	add.w	d0,d1
Col400	move.w	d1,2(a0)			; DEST
	bra.s	Col200
Col500	addq.w	#4,d4				; R G or B?
	cmp.w	#12,d4
	bne.s	Col600
	moveq	#0,d4
Col600	rts


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

AllocMem	equ	-30-168
FreeMem		equ	-30-180

piecessize	equ 7*(8*4)*(5*2*8)		; #*pieces*rots * dep*wid*het
memorysize	equ 240*44*5+9000+piecessize	; (scn het*wid*dep+snd+pieces)
memorysize2	equ memorysize + 80*4		; (copper list too)


AllocatePieces
	move.l	#memorysize2,d0
	move.l	#2,d1
	move.l	4,a6				; Exec base
	jsr	AllocMem(a6)
	tst.l	d0				; D0 = 0 = Failed
	beq.s	AllocateMemory900

						; Divvy up the memory
	lea	GameCopper(pc),a0
	move.l	d0,(a0)
	add.l	#130*4,d0

	lea	ScreenMem(pc),a0
	move.l	d0,(a0)
	add.l	#240*44*5,d0

	;lea	AudioMem(pc),a0		(used proper loader now jan 29 90)
	;move.l	d0,(a0)
	;add.l	#9000,d0

	lea	PiecesMem(pc),a0
	move.l	d0,(a0)
						; D0 != 0 = Success
AllocateMemory900
	rts

FreePieces
	move.l	4,a6
	move.l	GameCopper(pc),a1
	move.l	#memorysize2,d0
	jmp	FreeMem(a6)

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

ReadJoy	move.w	PlayerPort(a4),d0		; Read Amiga Port Hardware
	move.w	(a6,d0.w),d0			; Amiga Hardware
	and.w	#$0303,d0			; (see hardware ref manual)
	move.w	d0,d1
	lsr.w	#1,d1
	eor.w	d0,d1				; D0 = %LU------RD
	rts

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

; When playing with custom registers be careful about $dff02a, because
; you can physically damage the Amiga by de-syncronizing the vertical
; framing, the same applies for $dff1dc (PAL/NTSC switch) on FAT AGNES ONLY.

MachineTakeover
	move.b	#32,$dff1dc			; Goto PAL Mode

	move.l	4,a6				; Exec
	lea	library(pc),a1			; Find Amiga DosLibrary
	jsr	-408(a6)			; D0 = Open Graphics Library

	lea	MachineVariables(pc),a3		; a3 = Variable Workspace
	move.l	d0,a6				; GraphicsBase
	move.l	38(a6),-(a3)			; Save System Copper Ptr

	move.w #3*60,d1				; Allow Amiga to Cool off
9$	cmp.b  #$20,$dff006
	bne.s  9$
8$	cmp.b  #$22,$dff006
	bne.s  8$
	move.w d1,$dff180
	dbf   d1,9$
				;(can't do this: overrides my music)
	;move.l	4,a6				; ExecBase
	;jsr	-120(a6)			; Call Disable();

	lea	$dff000,a6			; A6 = Amiga Hardware
	move.w	$2(a6),-(a3)			; Save DMA Enabled
	move.w	$1c(a6),-(a3)			; Save Interupts Enabled
	move.l	$68,-(a3)			; Save System Keyboard Handler
	move.l	$c,-(a3)			; Trap Guru Meditations
	move.l	$10,-(a3)			;
	lea	MachineCrash(pc),a0
	move.l	a0,$c
	move.l	a0,$10
				;(i decided to be nicer to the system here)
	;move.w	#$7fff,$9a(a6)			; Freeze Interupts
	;move.w	#$7fff,$96(a6)			; Freeze DMA

	lea	MachineReturn(pc),a0		; Trap Gurus to my handler
	move.l	a0,$10				;
	move.l	a0,$12				;
	lea	KeyboardInterupt(pc),a0		; A0 = Interupt Handler
	move.l	a0,$68				; Setup My Keyboard Handler
	move.w	#$c008,$9a(a6)			; Enable Keyboard Interupts
	lea	oldtrap(pc),a0
	move.l	$80,(a0)
	lea	Supervisor(pc),a0		; Enter Supervisor Mode
	move.l	a0,$80				; Setup Trap
	move.l	(a7)+,a0			; A0 = Caller
	move.l	a7,-(a3)			; Remember User Stack
	trap	#0				; Goto Hardware Trap
library	dc.b	"graphics.library",0,0		; Dos Library Name
Supervisor
	move.l	a7,-(a3)			; Remember Supervisor Stack
	jmp	(a0)				; Return to Caller
;-----------------------------------------------------------------------------

MachineCrash
	lea	Variables(pc),a0
	move.l	a0,$7ff00			; Store Crash to ABS place
	move.l	2(a7),$7ff04
	move.l	6(a7),$7ff08
	move.l	10(a7),$7ff0c

MachineReturn
	btst	#14,$02(a6)
1$	btst	#14,$02(a6)			; Blitter done?
	bne.s	1$

	move.b	#2,$1dc(a6)			; Exit PAL, goto NTSC Mode
	;move.w	#$7fff,$96(a6)			; Stop all DMA
	;move.w	#$7fff,$9a(a6)			; Stop all Interupts

	lea	Supervisor2(pc),a0		; Goto supervisor (again)
	move.l	a0,$80				; Setup Trap
	trap	#0				; Goto Hardware Trap
Supervisor2
	move.l	oldtrap(pc),$80

	lea	MachineVariables2(pc),a3	; a3 = Machine Variables
	move.l	(a3)+,a7			; Restore Supervisor stack
	move.w	#0,sr				; Exit Supervisor Mode
	move.l	(a3)+,a7			; Restore User stack

	move.l	(a3)+,$10			; Restore Guru ""handlers""
	move.l	(a3)+,$c			;
	move.l	(a3)+,$68			; Replace Sys Keyboard Handler
	move.l	(a3)+,d1			; System DMA bits

	lea	$dff000,a6			; A6 = Hardware
	move.w	#0,$2e(a6)			; Turn Off Copper Blits
	move.l	(a3),$80(a6)			; Restore System Copper
	move.w	d0,$88(a6)			; Forceload Copper

; Note the super-fat agnes is using some undefined DMA channels, I
; have found that I can no longer safely turn off all the DMA, so I just
; try to manage what I can glean.

	or.w	#$8200,d1			; Set Enable DMA Bit
	move.w	#$1ff,$96(a6)
	move.w	d1,$96(a6)			; Reenable System DMA

	swap	d1				; System Interupt Bits
	not.w	d1				;
	bclr.l	#15,d1				;
	move.w	d1,$9a(a6)			; (verify off non-sys Ints)
	not.w	d1				; (i do this as example only)
	move.w	#$7fff,$9c(a6)			; Clear all Interupts Pending
	move.w	d1,$9a(a6)			; Reenable System Interupts

	;move.l	4,a6				; ExecBase
	;jsr	-126(a6)			; Enable();
	bra	TetrisExit			; Free system type resources

MachineVariables2				; (list tail)
		dcb.l	7,0
MachineVariables
oldtrap		dc.l	0

;-----------------------------------------------------------------------------
KeyboardInterupt
	movem.l	d0/a0,-(a7)			; Save Registers
	move.b	$bfed01,d0			; Clear Interupt Requests
	;($bfed01 must be done pronto or next int will be cleared instead)
	btst.l	#3,d0				; Not My Interupt?
	beq.s	KeyboardInterupt900
	move.b	$bfec01,d0			; D0 = Read Keyboard
	or.b	#64,$bfee01			; PULSE KDAT LOW FOR 75 usec
	eor.b	#254,d0				; Mask of Hardware Shift
	lsr.b	#1,d0				; Get to Real
	bcc.s	KeyboardInterupt500		; Get Key Down Events Only
	ext.w	d0				; Clean for 68030 indirect
	move.b	KeyMap(pc,d0.w),d0		; Get Real Key
	lea	KeyEvent(pc),a0
	move.b	d0,(a0)				; Remember Key
KeyboardInterupt500
	move.w	#999,d0				; >= 75 usec(should be timed)
1$	dbf	d0,1$
	and.b	#$bf,$bfee01			; UNPULSE KEYBOARD
KeyboardInterupt900
	movem.l	(a7)+,d0/a0			; Return Registers
	move.w	#8,$dff09c			; Clear Interupt Bit
	rte					; Exit

; (I used this for reference on doing SERIAL input for the MIDI stuff)

KeyMap		dc.l	$60313233,$34353637,$3839302d,$3d5c0000
		dc.l	$51574552,$54595549,$4f505b5d,$00313233
		dc.l	$41534446,$47484a4b,$4c3b270d,$00343536
		dc.l	$005a5843,$56424e4d,$2c2e2f00,$00373839
		dc.l	$200a000d,$0d7b0a00,$00002d00,$1c1d1e1f
		dc.l	$11121314,$15161718,$191a0000,$00000000
		dc.l	$00000000,$00000000,$00000000,$00000000
		dc.l	$00000000,$00000000,$00000000,$00000000


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

SayName
	lea	Name(pc),a0	; (whoops, almost forgot)
	move.l	GameVideo(pc),a1
	lea	44(a1),a1	; (change color)
	bra	PrintText
Name:	dc.w	14*8,29*8
	dc.b	'An Anselm Hook Game',0
	even

DoIntro
	lea	ScrollMessage(pc),a0
	lea	ScrollPos(pc),a1
	move.l	a0,(a1)

	lea	IntroText(pc),a0		; Print Instructions
	move.l	GameVideo(pc),a1
	bsr	PrintText
	bsr	ColorFader			; Bring on view nicely

1$	lea	KeyEvent(pc),a0
	cmp.b	#$7b,(a0)
	beq	MachineReturn
	bsr	DoScrollText

	btst	#7,$bfe001			; Wait for User Done
	beq.s	2$				; Go right into game no pause
	btst	#6,$bfe001
	bne.s	1$
2$	bra	VideoClear

DoScrollText
	lea	ScrollPos(pc),a0
	tst.l	4(a0)
	beq.s	2$
	subq.l	#1,4(a0)
	bra	WaitFrame
2$	move.l	(a0),a1
	move.b	(a1)+,d0			; D0 = Character #
	bne.s	1$
	lea	ScrollMessage(pc),a1
	move.b	(a1)+,d0			; (prefetch!)
1$	move.l	a1,(a0)
	moveq	#7,d1				; D1 = Horiz Bit Pos #
0$	movem.w	d0/d1,-(a7)
	bsr	PrintColumn
	movem.w	(a7)+,d0/d1
	dbf	d1,0$
	rts

ScrollPos:	dc.l	0,60*5

IntroText:
	dc.w	20,40
		;0123456789012345678901234567890123456789
	dc.b	$84,"             Amiga Tetris",13
	dc.b	13
	dc.b	"   Press Escape to Exit to Workbench",13
	dc.b	"       Press ",$87,"Fire",$80,$82," Button to play",13
	dc.b	13
	dc.b	"   Move falling pieces with Joystick",13
	dc.b	"    Rotate pieces with fire button ",13
	dc.b	"  Interlock pieces to make an unbroken",13
	dc.b	" line before pieces overflow playfield ",13
	dc.b	"   Completed lines are automagically",13
	dc.b	"  zapped away.  Lines required to win",13
	dc.b	"   level are shown at left of field ",13
	dc.b	13
	dc.b	"     Amiga Tetris is public domain",13
	dc.b	" and is intended primarily as a tutorial",13
	dc.b	"     on Machine Language programming",13
	dc.b	13
	dc.b	" Due to Tetris.s running in PAL Mode you",13
	dc.b	" may have to:",13
	dc.b	$84,"                   ADJUST VERTICAL HOLD!",13
	dc.b	13
	dc.b	0

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

;
;	I reserve some address registers for specific use
;
;		A6  = Amiga Hardware
;		A5  = Variables, allowing me to do  move.w d0,variable(a5)
;		      (which is more flexible than  move.w d0,variable(pc))
;		A4  = Current player vars, allowing infinite # of players.
;

; To Do:
; * - pause at level end, with music restart.
; * - music 1/2 volume
; * - different end of line sound
; * - raisers later, and slower
; * - (slow music, music speeds up every level)
; * - tiles speed up every level
; * - instructions
; * - turn off sprites
; * - make easier, raisers later
; * - supervisor multi-player merge/scan
; * - sound for end of line + bonuses + randoms + raisers
; * - pause
; * - less rows at start
; * - inc rows, music speed algorithm of level
;   - single, double, three, tetris bonuses
;   - statistics on side bar
;   - support rotate agains right wall
;   - remove mulus

	end

; end of tetris.s
