;  __   ___   ___  _  _   ___   _  
; |  \ |   | |    | \/ | |   | | \ |
; |__/ |---| |    |    | |---| |  \|
; |    |   | |___ |    | |   | |   |
;
; written by blackbird
; copyright by namco

	processor f8

;===================;
; Assembly Switches ;
;===================;

switch_music	=	0					; turns music on and off

;=========;
; Equates ;
;=========;

;------------;
; BIOS Calls ;
;------------;
clrscrn         =       $00d0                                   ; uses r31
delay           =       $008f
pushk           =       $0107                                   ; used to allow more subroutine stack space
popk            =       $011e
drawchar        =       $0679

;-------------------;
; Color Definitions ;
;-------------------;
red             =       $40
blue            =       $80
green           =       $00
bkg             =       $C0
clear		=	$FF

;-----------------;
; RAM Definitions ;
;-----------------;
ram		= 	$2800					; use Schach RAM to hold pellet info

;--------------------;
; Register Reference ;
;--------------------;

; The registers in the f8 are used as both RAM and registers for
; this game. The reference is as follows:
;
; r0-r31 (o00-o37)	general-purpose registers
; r33 (o41)		level number
; r34 (o42)		(unused)
; r35 (o43)		pac-man x coordinate
; r36 (o44)		pac-man y coordinate
; r37 (o45)		pac-man direction
; r38 (o46)		animation index
;
; r60-r63 (o74-o77)	bcd score

;---------------;
; Object Colors ;
;---------------;

PACMAN_COLOR		=	green
MAZE_BGCOLOR		=	bkg
MAZE_FGCOLOR		=	blue
PELLET_COLOR		=	red
SIDEBAR_COLOR		=	blue

;------------------;
; Object Variables ;
;------------------;

PACMAN_START_X		=	38
PACMAN_START_Y		=	41

;===================;
; Main Program Code ;
;===================;

;---------------;
; Program Entry ;
;---------------;

	org	$800

cartridgeStart:
	.byte	$55, $2B					; cartridge header

cartridgeEntry:
	lis	0						; init the h/w
	outs	1
	outs	4
	outs	5
	outs	0
                
	lisu	4						; r32 = complement flag
	lisl	0
	lr	S, A
                
	li	$c6						; set to three color, grey background
	lr	3, A						; clear screen to grey
	pi	clrscrn						;

;-------------------;
; Main Program Loop ;
;-------------------;
				
mainLoop:
	; load in the level number
	lisu	4
	lisl	1
	li	0						; we're on level 0
	lr	S, A

	; make sure the score is cleared
	lisu	7
	lisl	4
	clr
	lr	I, A
	lr	I, A
	lr	I, A
	lr	S, A

startLevel:
	; draw the map
	pi	drawMap
	; the sidebar
	pi	drawSidebar
	; and the pellets
	pi	drawPellets
	; setup the pellet RAM
	pi	setupPellets

    IF switch_music
playLevelStartSong:
	dci	levelStartSong
	pi	playSong
    ENDIF

setupPacman:
	; load coordinates
	lisu	4
	lisl	3
	li	PACMAN_START_X
	lr	I, A						; load the pac-man x coordinate
	li	PACMAN_START_Y
	lr	I, A						; load the pac-man y coordinate

	; set the direction
	lis	%0001
	lr	I, A

	; load the animation index
	lis	0
	lr	S, A

playLoop:

	pi	drawScore

.clearPacman:
	; clear pac-man
	li	MAZE_BGCOLOR
	lr	1, A
	; load x coordinate
	lisu	4
	lisl	3
	lr	A, I
	lr	2, A
	; load y coordinate
	lr	A, S
	lr	3, A
	; set sprite index
	lis	0
	lr	4, A
	; set bitmask
	li	%11111000
	lr	5, A
	; draw the sprite
	pi	drawSprite

.checkController:
	; handle input and pacman's direction
	pi	pacmanControls

.checkDirection:
	; make sure current direction is still valid
	pi	checkIntersection
	ci	1
	bnz	.checkDirectionEnd				; not in an intersection

	; clear direction if necessary
	lisu	4
	lisl	5
	lr	A, S
	ns	0
	bnz	.checkDirectionEnd
	jmp	.playLoop.drawPacman				; can't go anywhere, so just skip updating pacman	
.checkDirectionEnd:

.playLoop.clearPacman:
	pi	clearPacman					; clear Pac-Man

.updatePacmanFrame:
	lisu	4
	lisl	6
	lr	A, S						; load the sprite index
	inc							; increase it
	ni	%00000011					; clear if on frame 4
	lr	S, A						; save the new index

.movePacman:
	; now we move pac-man based on the direction
	; which is set in r37

	lisl	5
	; up
	lis	%1000
	ns	S
	bnz	.movePacmanUp
	; down
	lis	%0100
	ns	S
	bnz	.movePacmanDown
	; left
	lis	%0010
	ns	S
	bnz	.movePacmanLeft
	; right
	lis	%0001
	ns	S
	bnz	.movePacmanRight
	; none
	br	.movePacmanEnd

.movePacmanUp:
	; subtract y by 1
	lisl	4
	ds	S						; subtract 1
	br	.movePacmanEnd
.movePacmanRight:
	; add x by 1
	lisl	3
	lr	A, S
	inc							; add 1
	lr	S, A
	br	.movePacmanEnd
.movePacmanDown:
	; add y by 1
	lisl	4
	lr	A, S
	inc							; add 1
	lr	S, A
	br	.movePacmanEnd
.movePacmanLeft:
	; subtract x by 1
	lisl	3
	ds	S						; subtract 1
	br	.movePacmanEnd
.movePacmanEnd:

;	ci	251						; see if it's fully off the left side
;	ci	80						; see if it's fully off the right side

.playLoop.drawPacman:
	pi	drawPacman					; draw Pac-Man

    IF switch_music
.playPelletEffect:
	lisu	4
	lisl	6						; sprite index
	lis	3
	xs	S						; compare with 4
	bnz	.playPelletEffectEnd

	; play dot-eating sound
	dci	pelletEffect
	pi	playSong
.playPelletEffectEnd:
    ENDIF

.checkPellets:
	; check to see if we've eaten any pellets
	pi	pelletCheck

.boardClearedCheck:
	; check to see if the board is cleared
	dci	ram

	; load our loop counter
	li	60
	lr	0, A
.boardClearedLoop:
	; decrease counter
	ds	0
	bm	.boardClearedEmpty				; there's no dots left, branch

	; check to see if it's empty
	lm
	ci	0
	bnz	.boardClearedEnd				; there's still some dots left, end the loop
	br	.boardClearedLoop				; continue the loop
.boardClearedEmpty:
	; the board is empty, end the level
	jmp	endGame
.boardClearedEnd:

.playLoopDelay:
	; delay for a little bit
	lis	1
	lr	5, A
	pi	delay

	jmp	playLoop
.playLoopEnd:
	jmp	mainLoop

endGame:
	; load loop counter
	li	4						; how many times to flash the screen
	lr	0, A						; stored in r0

.endGameLoop:
	li	$ff
	lr	1, A
	li	250
	lr	2, A
	li	58
	lr	3, A

.endGameLoop1:
	ds	3
	bm	.endGameLoop1End
	pi	plot
	br	.endGameLoop1
.endGameLoop1End:

	; delay for a little bit
	li	$5f
	lr	5, A
	pi	delay

	li	$0
	lr	1, A
	li	250
	lr	2, A
	li	58
	lr	3, A

.endGameLoop2:
	ds	3
	bm	.endGameLoop2End
	pi	plot
	br	.endGameLoop2
.endGameLoop2End:

	; delay for a little bit
	li	$5f
	lr	5, A
	pi	delay

.endGameLoopCheck:
	ds	0
	bnz	.endGameLoop

	; increase the level counter
	lisu	4
	lisl	1
	lr	A, S
	inc
	lr	S, A

	; delay for a little bit
	li	$8f
	lr	5, A
	pi	delay

.endGameEnd:
	; start the next level
	jmp	startLevel

;==================;
; Pellet Functions ;
;==================;

setupPellets:
	; load DC0 with the map address
	dci	maze_pellets
	xdc
	; and DC1 with the RAM address
	dci	ram
	xdc

	; and setup loop counter
	li	60
	lr	0, A

.setupPelletsLoop:
	; get and save a byte
	lm
	xdc
	st
	xdc

	; check to see if we've reached the end
	ds	0
	bnz	.setupPelletsLoop

.setupPelletsEnd:
	pop

;======================;
; Controller Functions ;
;======================;

;-----------------;
; Read Controller ;
;-----------------;

; spools for controller input,
; returns with controller input in A

readController:
	; see one of the hand controllers is moved
	clr
	outs	0
	outs	4
	ins	4
	com							; un-invert port data
	ni	$cf						; mask off twists, since we don't use them
	bnz	.readControllerEnd
	outs	1
	ins	1
	com
	ni	$cf
.readControllerEnd:
	pop

;-----------------;
; Pacman Controls ;
;-----------------;

; manages controller input and how to
; move pac-man

pacmanControls:
	; save return address
	lr	K, P
	pi	pushk

	; get controller input
	pi	readController
	; and save it
	lr	4, A
.pacmanControlsSetDirection:
	; check to see if we're on an intersection
	pi	checkIntersection

	; branch if false
	ci	1
	bnz	.pacmanControlsNoIntersection

.pacmanControlsInIntersection:
	; load the boundaries
	lr	A, 0

	; compare and set direction
	br	.pacmanControlsCheckEnd

.pacmanControlsNoIntersection:
	; if you're not in an intersection,
	; you can only go the opposite way
	; you're going right now

	; set ISAR for direction
	lisu	4
	lisl	5
	; up
	lis	%1000
	ns	S
	bnz	.pacmanControlsCheckUp
	; down
	lis	%0100
	ns	S
	bnz	.pacmanControlsCheckDown
	; left
	lis	%0010
	ns	S
	bnz	.pacmanControlsCheckLeft
	; right
	lis	%0001
	ns	S
	bnz	.pacmanControlsCheckRight
	; none
	clr
	br	.pacmanControlsCheckEnd

.pacmanControlsCheckUp:
	lis	%0100
	br	.pacmanControlsCheckEnd
.pacmanControlsCheckDown:
	lis	%1000
	br	.pacmanControlsCheckEnd
.pacmanControlsCheckLeft:
	lis	%0001
	br	.pacmanControlsCheckEnd
.pacmanControlsCheckRight:
	lis	%0010

.pacmanControlsCheckEnd:
	; see if the direction is allowed
	ns	4
	; invalid direction, forget about it
	bz	.pacmanControlsEnd

	; set ISAR for direction
	lisu	4
	lisl	5
	; load the direction in
	lr	S, A

.pacmanControlsEnd:
	pi	popk
	pk							; return from the subroutine

;--------------------;
; Check Intersection ;
;--------------------;	

; checks to see if pacman is in an intersection
; if so, it returns with A set to 1, else it
; returns with A set to 0. Also, if pacman is, it 
; returns with the intersection boundaries in r0
;
; modifies: ISAR, r0, r1, r2, r3, Q, K
;
; MISERABLY unoptimized, I'll need to fix that sometime...

checkIntersection:
	; save the return address
	lr	K, P
	pi	pushk

	; set the ISAR to the x position
	lisu	4
	lisl	3

	; check to see if we're on an intersection horizontally
	lr	A, I
.checkIntersectionHorizLoop:
	ai	%11111010					; keep subtracting 6
	bz	.checkIntersectionHorizLoopEnd			; if it's 0, we're on an intersection horizontally
	bnc	.checkIntersectionFalse				; if it's < 6, break
	br	.checkIntersectionHorizLoop			; continue the loop
.checkIntersectionHorizLoopEnd:

	; check to see if we're on an intersection vertically
	lr	A, S
	ai	7						; fix -1 offset of intersections
.checkIntersectionVerticLoop:
	ai	%11111010					; keep subtracting 6
	bz	.checkIntersectionTrue				; if it's 0, we're on an intersection vertically
	bnc	.checkIntersectionFalse				; if it's < 6, break
	br	.checkIntersectionVerticLoop			; continue the loop

.checkIntersectionFalse:
	; return from the subroutine
	pi	popk
	lis	0						; not on an intersection, return false
	pk

.checkIntersectionTrue:
	; load the map address
	dci	maze_map

	; add y offset to DC
	li	$ff						; 0, complemented
	lr	0, A						; r0 will be our offset	
	; get the y address
	lr	A, D						; r36
	inc							; fix -1 offset of intersections
.checkIntersectionYLoop:
	ai	%11111010					; keep subtracting 6
	bnc	.checkIntersectionYLoopEnd			; if it's < 0, then we're done
	ds	0						; "incrase" r0
	br	.checkIntersectionYLoop
.checkIntersectionYLoopEnd:
	; get the true offset
	lr	A, 0
	com							; invert r0
	lr	0, A
	lr	2, A						; save Y position
	li	12
.checkIntersectionYLoop2:
	ds	0
	bnc	.checkIntersectionYLoop2End
	adc							; add 24 bytes to the DC
	br	.checkIntersectionYLoop2			; repeat
.checkIntersectionYLoop2End:

	; add x offset to DC
	li	$ff						; 0, complemented
	lr	0, A						; r0 will be our offset	
	; get the x address
	lr	A, S						; r35
	ai	%11111010					; subtract the initial 6
.checkIntersectionXLoop:
	ai	%11111010					; keep subtracting 6
	bnc	.checkIntersectionXLoopEnd			; if it's < 0, then we're done
	ds	0						; "incrase" r0
	br	.checkIntersectionXLoop
.checkIntersectionXLoopEnd:
	; add the offset
	lr	A, 0
	com							; invert r0 to get the offset
	adc
	lr	3, A						; save X position

	; save the address
	lr	Q, DC

	; now we find this tile's boundaries	
	lm							; get the tile number
	lr	1, A						; save the tile number
	pi	checkIntersection.getBoundary			; find the boundaries
	lr	0, A						; and save those

	; find the right tile's left boundary,
	; mask it, and AND it to this one's
.checkRightBoundary:
	; check to see if we're on the edge
	lis	%1011
	xs	3
	bnz	.checkRightBoundaryTile				; not on the edge, get the boundary

	; mask off right boundary
	li	%00001110
	ns	0
	lr	0, A
	br	.checkRightBoundaryEnd

.checkRightBoundaryTile:
	; first, fix the address
	lr	DC, Q						; retrieve the address
	lm
	; retrieve the tile address
	lm							; get the tile number
	lr	1, A						; save the tile number
	pi	checkIntersection.getBoundary			; find the boundaries
	ni	%00000010					; mask it
	sr	1
	ai	%00001110					; set the other bits
	ns	0						; and AND it with this boundary
	lr	0, A						; finally, save it
.checkRightBoundaryEnd:	

	; find the bottom tile's upper boundary,
	; mask it, and AND it to this one's
.checkBottomBoundary:
	; check to see if we're on the edge
	lis	%1001
	xs	2
	bnz	.checkBottomBoundaryTile			; not on the edge, get the boundary

	; mask off bottom boundary
	li	%00001011
	ns	0
	lr	0, A
	br	.checkBottomBoundaryEnd

.checkBottomBoundaryTile:
	; first, fix the address
	lr	DC, Q						; retrieve the address
	lis	12
	adc
	; now, get the byte and work with it
	lm							; get the tile number
	lr	1, A						; save the tile number
	pi	checkIntersection.getBoundary			; find the boundaries
	ni	%00001000					; mask it
	sr	1
	ai	%00001011					; set the other bits
	ns	0						; and AND it with this boundary
	lr	0, A						; finally, save it	
.checkBottomBoundaryEnd:

.checkIntersectionEnd:
	; return from the subroutine
	pi	popk
	lis	1						; return true
	pk

; sub-subroutine, where we find the boundaries of a tile number

checkIntersection.getBoundary:
	; load the tile address into the DC
	dci	tiles
	lis	3						; three bytes for each tile
.checkIntersection.getBoundaryAddressLoop:
	ds	1
	bnc	.checkIntersection.getBoundaryAddressLoopEnd
	adc
	br	.checkIntersection.getBoundaryAddressLoop
.checkIntersection.getBoundaryAddressLoopEnd:
	lis	2
	adc
	lm

	pop

;--------------;
; Pellet Check ;
;--------------;

; this function checks to see if pac-man is overlapping a pellet
; and sets the bit in RAM for that respective pellet
;
; modifies: r0-r3, K, Q, H, DC, ISAR

pelletCheck:
	; save return address
	lr	K, P
	pi	pushk

	; set the DC0
	dci	ram

	; get the y value
	lisu	4
	lisl	4
	lr	A, D
	ai	4						; add -2 offset (and add +2 to calculate properly)
.pelletCheckYOffset:
	ai	%11111101					; subtract 3
	bnc	.pelletCheckYOffsetEnd				; if y < 0, break
	lr	0, A						; back up the current y value
	lis	3
	adc							; add three bytes, one row of pellets
	lr	A, 0						; get back the y value
	br	.pelletCheckYOffset				; branch back
.pelletCheckYOffsetEnd:
	; save pac-man's offset
	com
	lr	2, A

	; setup our bit counter
	li	$ff
	lr	0, A						; load r0 with #0, complemented
	; get the x value
	lr	A, S
	ai	%11111101					; subtract +5 offset (but add +2 to calculate properly)
.pelletCheckXOffset:
	ai	%11111101					; subtract 3
	bnc	.pelletCheckXOffsetEnd				; if x < 0, break
	ds	0						; "add" 1 to r0
	br	.pelletCheckXOffset				; branch back
.pelletCheckXOffsetEnd:
	; save pac-man's offset
	com
	lr	3, A

	; invert the bit offset
	lr	A, 0
	com							; get inverse
	lr	0, A

	; mask the bit
	pi	pelletCheck.setBit

.pelletCheckOverlap1:
	; adjust the address
	lr	A, 0
	inc
	lr	0, A
	; check to see if he's overlapping other pellets	
	lis	2
	xs	3						; see if r3 equals 2
	bz	.pelletCheckOverlap2				; if we're not overlapping horizontal, check vertical
	pi	pelletCheck.setBit				; set the pellet
.pelletCheckOverlap2:
	; adjust the address
	ds	0
	li	3
	adc							; go to next row of pellets
	; check to see if he's overlapping other pellets	
	lis	2
	xs	2						; see if r2 equals 2
	bz	.pelletCheckEnd					; if we're not overlapping vertical, break
	pi	pelletCheck.setBit				; set the pellet
.pelletCheckOverlap3:
	; adjust the address
	lr	A, 0
	inc
	lr	0, A
	; check to see if he's overlapping other pellets	
	lis	2
	xs	3						; see if r3 equals 2
	bz	.pelletCheckEnd					; if we're not overlapping horizontal, break
	pi	pelletCheck.setBit				; set the pellet

.pelletCheckEnd:
	pi	popk
	pk							; return from the subroutine

; sub-subroutine. takes the bit specified in r0,
; runs a loop using r1, and masks the bit in the address in DC0

pelletCheck.setBit:
	; save return address
	lr	K, P
	pi	pushk

	; backup the DC
	lr	H, DC
	; set the bytes
	lr	A, 0
	sr	1
	sr	1
	sr	1						; get the bytes
	adc							; add them to the DC
	; save this address
	lr	Q, DC

	; get the bitmask
	lis	%0111						; only the bits, or 0-7
	ns	0
	lr	1, A
	; load our bitmask
	li	%10000000
.pelletCheckClearLoop:
	ds	1
	bnc	.pelletCheckClearEnd
	sr	1
	br	.pelletCheckClearLoop
.pelletCheckClearEnd:
	; complement and save the bitmask
	com
	lr	1, A

	; set the bit
	lm							; load the byte (and inc DC0)
	lr	4, A						; save the byte
	lr	DC, Q						; restore the adjusted address
	ns	1						; mask the bit
	st							; store it back in
	lr	DC, H						; restore the original address

	; check to see if a pellet was changed
	lr	A, 4
	ns	1						; get the masked byte
	xs	4						; compare it with the original
	bz	.pelletCheck.setBit.end				; if they're equal, the result will be zero

	; add to the score
	li	%01100110
	lr	Ku, A
	lr	Kl, A
	lr	Qu, A
	li	%01110110
	lr	Ql, A						; 10 points
	pi	addScore

.pelletCheck.setBit.end:
	pi	popk
	pk							; return from the subroutine

;=================;
; Score Functions ;
;=================;

;-----------;
; Add score ;
;-----------;

; adds a 4-byte decimal adjusted number to the score
; stored in r12-r15 (K, Q)

addScore:
	; set the ISAR
	lisu	7
	lisl	7

	; non-carry loop
.addScore.loop1:
	; check to see if the loop is ended
	lr	A, IS
	ci	59
	bz	.addScoreEnd					; loop is ended, break

.addScore.loop1.addbyte:
	lisu	1
	lr	A, S
	lisu	7
	asd	S						; add two decimal-adjusted digits
	lr	D, A						; save digit and decrease ISAR

	bc	.addScore.loop2					; we've carried over, use loop 2
	br	.addScore.loop1					; loop

	; carry loop
.addScore.loop2:
	; check to see if the loop is ended
	lr	A, IS
	ns	%00000111
	ci	4
	bz	.addScoreEnd					; loop is ended, break

	; add carry
	lis	%0111						; 1 in BCD
	asd	S
	lr	S, A						; save digit in score
	bnc	.addScore.loop1.addbyte				; no carry, use loop 1

.addScore.loop2.addbyte:
	lisu	1
	lr	A, S
	lisu	7
	asd	S						; add two decimal-adjusted digits
	lr	D, A						; save digit in score and decrease ISAR

	br	.addScore.loop2					; loop

.addScoreEnd:
	; return from the subroutine
	pop

;===================;
; Drawing Functions ;
;===================;

	include "drawing.inc"
	; basic drawing functions

;-----------;
; Draw Tile ;
;-----------;

; draw an 8x8 tile from a data pointer
; r1 = color 1
; r2 = color 2
; r3 = x (to screen)
; r4 = y (to screen)
; r5 = tile number

drawTile:
	; registers reference:
	; r1 = reserved (plot color)
	; r2 = reserved (plot x)
	; r3 = reserved (plot y)
	; r4 = color 1
	; r5 = color 2
	; r6 = x
	; r7 = y
	; r8 = loop1 (row)
	; r9 = loop2 (column)
	; r10 = bitmask
	; r11 = graphics byte

	; save the return address
	lr	K, P
	pi	pushk

	; get the tile address
	dci	tiles
	; add the offset
	lr	A, 5
	inc							; make sure we hit 0
	lr	0, A
	lis	3						; three bytes for each tile
.drawTileAddressLoop:
	ds	0
	bz	.drawTileAddressLoopEnd
	adc
	br	.drawTileAddressLoop
.drawTileAddressLoopEnd:
	; got the tile data, now get the graphics address
	lm
	lr	Qu, A
	lm
	lr	Ql, A
	lr	DC, Q

	; load the width and height
	li	6
	lr	5, A
	lr	6, A

.drawTileBlit:
	; draw the tile itself
	pi	blit

.drawTileEnd:
	; return from the subroutine
	pi	popk
	pk

;----------;
; Draw Map ;
;----------;

; draw the pac-man maze in two colors

drawMap:
	; this function uses several reserve registers
	; because the functions called need the immediate ones
	; r17 (21o) = x counter
	; r18 (22o) = y counter

	; load the return address into memory
	lr	K, P
	pi	pushk

	; load the map address into DC1
	dci	maze_map
	xdc

	; clear address offset
	lisu	2
	lisl	5
	clr
	lr	S, A

	lisl	1						; 
	lis	10						; load r17 with 10 rows of tiles
	lr	S, A						;
.drawMapRow:

	lisl	2						;
	lis	12						; load r18 with 12 tiles per row
	lr	S, A						;
.drawMapColumn:

	; this is the start of drawing an individual tile

	; note of reference to drawTile:
	; r1 = color 1
	; r2 = color 2
	; r3 = x (to screen)
	; r4 = y (to screen)
	; r5 = tile number

	lisl	2						; load register r20
	lr	A, S
	lr	0, A						; set r0 as our row counter
	li	0						; clear A to the first pixel
.drawMapGetX:
	ds	0
	bz	.drawMapSaveX
	ai	6
	br	.drawMapGetX

.drawMapSaveX:
	com							; we want to display the tiles from
	inc							; top to bottom, so we inverse
	ai	71						; both coordinates
	lr	3, A						; store the x coordinate in r3

	lisl	1						; load register r19
	lr	A, S
	lr	0, A						; set r0 as our column counter
	li	0						; clear A to zero
.drawMapGetY:
	ds	0
	bz	.drawMapSaveY
	ai	6
	br	.drawMapGetY

.drawMapSaveY:
	com							; we want to display the tiles from
	inc							; top to bottom, so we inverse
	ai	52						; both coordinates
	lr	4, A						; store the y coordinate in r4

.drawMapSetupRegisters:

	; this block of code gets the next tile number
	; and increases the data offset

	; swap DC0 and DC1
	xdc
	; get and save the next tile byte
	lm
	lr	5, A
	; swap back
	xdc

	; set the colors
	; color 1
	li	MAZE_BGCOLOR
	lr	1, A						; r1
	; color 2
	li	MAZE_FGCOLOR
	lr	2, A						; r2

	; finally, call the tile function
	pi	drawTile

.drawMapCheckLoops:
	; check column loop
	lisu	2
	lisl	2
	ds	D						; r18
	bnz	.drawMapColumn

	; check row loop
	ds	S						; r17
	bnz	.drawMapRow

.drawMapReturn:
	; return to the program flow
	pi	popk
	pk							; return from the subroutine

;-------------;
; Draw Sprite ;
;-------------;

; draw a 8x5 sprite from a data pointer
; r1 = color
; r2 = x (to screen)
; r3 = y (to screen)
; r4 = sprite number
; r5 = bitmask

drawSprite:
	; registers reference:
	; r1 = reserved (plot color)
	; r2 = reserved (plot x)
	; r3 = reserved (plot y)
	; r4 = color
	; r6 = x
	; r7 = y
	; r8 = loop1 (row)
	; r9 = loop2 (column)
	; r10 = bitmask
	; r11 = graphics byte

	; save the return address
	lr	K, P
	pi	pushk

	; get the tile address
	dci	sprites
	; add the offset
	lr	A, 4
	inc							; make sure we hit 0
	lr	0, A
	lis	2						; two bytes for each sprite
.drawSpriteAddressLoop:
	ds	0
	bz	.drawSpriteAddressLoopEnd
	adc
	br	.drawSpriteAddressLoop
.drawSpriteAddressLoopEnd:
	; got the sprite data, now get the graphics address
	lm
	lr	Qu, A
	lm
	lr	Ql, A
	lr	DC, Q

	; rearrange the registers
	lr	A, 3
	lr	7, A
	lr	A, 2
	lr	6, A
	lr	A, 1
	lr	4, A

	; start row loop
	li	5
	lr	8, A
.drawSpriteRow:
	; setup the bit mask
	li	%10000000					; we only want the left-most pixel
	lr	10, A						; store it in r10
	; and get the graphics byte
	lm							; load the pixel and increase the DC
	ns	5						; apply the bitmask
	lr	11, A						; store it in r11

	; start column loop
	li	5
	lr	9, A
.drawSpriteColumn:

	; find out the x address
	lr	A, 9						; load the new offset
	com							; and subtract it from 5
	inc							; via 2's complement
	ai	5						; subtract A from 5, ignore carry
	as	6						; add the tile's real x offset
	lr	2, A						; save it for the subroutine call
	; and the y address
	lr	A, 8						; load the new offset
	com							; and subtract it from 5
	inc							; via 2's complement
	ai	5						; subtract A from 5, ignore carry
	as	7						; add the tile's real y offset
	lr	3, A						; save it for the subroutine call

	; see if the pixel is on or off
	lr	A, 11						; load the byte
	ns	10						; mask it with the bitmask
	bz	.drawSpriteRollBitmask				; if the pixel's off, don't draw anything
	; conditionally load color 1
	lr	A, 4
	lr	1, A

.drawSpritePixel:
	pi	plot

.drawSpriteRollBitmask:
	; roll the bitmask
	lr	A, 10
	sr	1
	lr	10, A

.drawSpriteCheckLoops:
	; check column loop
	ds	9
	bnz	.drawSpriteColumn

	; check row loop
	ds	8
	bnz	.drawSpriteRow

.drawSpriteEnd:
	pi	popk
	pk							; return from the subroutine

;--------------;
; Draw Sidebar ;
;--------------;

; draws the right sidebar rectangle

drawSidebar:
	; save return address
	lr	K, P
	pi	pushk

	; set sidebar color
	li	SIDEBAR_COLOR
	lr	1, A

	; start row loop
	li	57
	lr	3, A
.drawSidebarRow:
	li	97
	lr	2, A
.drawSidebarColumn:
	pi	plot

.drawSidebarCheck:
	; check column
	ds	2
	lr	A, 2
	ci	76						; see if we've reached the left edge
	bnz	.drawSidebarColumn

	; check row
	ds	3
	bc	.drawSidebarRow					; branch if > 0

.drawSidebarText:
	dci	scoreGraphic.parameters
	pi	blitGraphic
	dci	highGraphic.parameters
	pi	blitGraphic

.drawSidebarEnd:
	; return from subroutine
	pi	popk
	pk

;------------;
; Draw Score ;
;------------;

; draws the 8-digit score to the screen

drawScore:
	; save return address
	lr	K, P
	pi	pushk

	; load colors
	li	SIDEBAR_COLOR
	lr	1, A
	li	green
	lr	2, A

	; load position
	li	78
	lr	10, A
	lis	6
	lr	11, A

	; set number counter
	lis	8
	lr	0, A
	; and ISAR
	lisu	7
	lisl	4

.drawScoreLoop:
	; set DC
	dci	numberGraphics

	; load size
	lis	3
	lr	5, A
	lis	5
	lr	6, A

	; load position
	lr	A, 10
	lr	3, A
	lr	A, 11
	lr	4, A

	; see if the tile is even or odd
	lis	1
	ns	0
	bz	.drawScoreShiftByte				; if even, branch

	; get lower half-byte
	lis	%1111
	ns	I						; set ISAR to next byte
	br	.drawScoreDrawDigit
.drawScoreShiftByte:
	; get upper half-byte
	lr	A, S
	sr	4

.drawScoreDrawDigit:
	; add tile offset
	sl	1
	adc
	; and draw the number
	pi	blit

	; increase x position
	lis	4
	as	10
	lr	10, A						; each tile is three pixels + one pixel space

	; decrease the counter
	ds	0
	bz	.drawScoreEnd					; reached the end, break
	; check to see if we're on tile 4
	lis	4
	xs	0
	bnz	.drawScoreLoop

.drawScoreChangeRow:
	li	82
	lr	10, A
	lis	12
	lr	11, A
	br	.drawScoreLoop

.drawScoreEnd:
	; return from subroutine
	pi	popk
	pk

;==========================;
; Object Drawing Functions ;
;==========================;

;--------------;
; Draw Pellets ;
;--------------;

; this function draws out the pellets
; as specified by "maze_pellets" and
; "maze_powerPellets"

drawPellets:
	lr	K, P
	pi	pushk						; save return address

	; load our data pointer
	dci	maze_pellets

	; load our color
	li	PELLET_COLOR
	lr	1, A

	; set the initial coordinates
	lis	5						; 5
	lr	2, A						; x coordinate
	li	%11111110					; -2
	lr	3, A						; y coordinate

	; load our byte loop counter
	li	60
	lr	4, A
.drawPelletsByteLoop:

	; load next byte
	lm
	lr	6, A

	; load our bit mask
	li	%10000000
	lr	5, A
.drawPelletsBitLoop:
	; see if there's a pellet here
	lr	A, 6
	ns	5
	; if not, branch
	bz	.drawPelletsBitLoopEnd

	; plot the pixel
	pi	plot
	
.drawPelletsBitLoopEnd:
	; add three to the x coordinate
	lr	A, 2
	ai	3						; add three pixels
	lr	2, A						; store it back in
	; if we've reach the end, roll over
	ci	77
	bnz	.drawPelletsLoopCheck

	; reset x, add 3 to y
	li	5
	lr	2, A
	lr	A, 3
	ai	3						; add three pixels
	lr	3, A						; store it back in the y coordinate

.drawPelletsLoopCheck:
	; check bit loop
	lr	A, 5
	sr	1
	lr	5, A
	bnz	.drawPelletsBitLoop

	; check byte loop
	ds	4
	bnz	.drawPelletsByteLoop

.drawPelletsEnd:
	; return from the subroutine
	pi	popk
	pk

;--------------;
; Draw Pac-Man ;
;--------------;

; this function draws pac-man based on the data in all the registers
; also has a "clear pacman" prefix where pacman can be erased to the
; background color

drawPacman:
	; save return address
	lr	K, P
	pi	pushk

	; load Pac-Man's color
	li	PACMAN_COLOR
	lr	1, A

	; skip over the clearPacman routine
	br	.drawPacmanStart

clearPacman:
	; save return address
	lr	K, P
	pi	pushk

	; load Pac-Man's color
	li	MAZE_BGCOLOR
	lr	1, A

.drawPacmanStart:
	; load Pac-Man's direction
	lisu	4
	lisl	5
	lr	A, S						; r37

.drawPacmanIsDirectionDown:
	ci	%00000100					; is it down?
	bm	.drawPacmanDirectionUp				; negative result means up
	bnz	.drawPacmanIsDirectionRight			; if not zero check if down
	lis	5						; set index to #5
	br	.drawPacmanDirectionEnd

.drawPacmanIsDirectionRight:
	ci	%00000001					; is it right?
	bnz	.drawPacmanDirectionLeft			; if not direction is left
	lis	13						; set index to #13
	br	.drawPacmanDirectionEnd   

.drawPacmanDirectionUp:
	lis	1						; set index to #1
	br	.drawPacmanDirectionEnd

.drawPacmanDirectionLeft:
	lis	9						; set index to #9

.drawPacmanDirectionEnd:
	lr	4, A						; store index in r4

	; find the sprite's animation offset
	lisu	4
	lisl	6
	lr	A, S						; load the animation index
	lr	0, A						; r0 is our loop counter
	lr	A, 4						; get back the original index
.drawPacmanGetOffset:
	ds	0
	bm	.drawPacmanGetOffsetEnd
	inc							; use the next tile
	br	.drawPacmanGetOffset
.drawPacmanGetOffsetEnd:
	lr	4, A						; set the index again

.drawPacmanWarp:
	; set preliminary bitmask for when we're not looping
	li	%11111000
	lr	5, A

	lisu	4
	lisl	3
	lr	A, S
	ci	128						; check if it's off the left side
	bp	.drawPacmanWarpCheckRight			; if not, check the right

	; now we find the bitmask for pacman
	com							; invert the position and increase it
	inc							; to get the number of bits to be masked
	lr	0, A						; save it
	li	%11111000					; load our new bitmask
.drawPacmanWarpLeftLoop:
	ds	0
	bm	.drawPacmanWarpLeftLoopEnd			; if we've reached the end of the bits, end the loop
	sr	1
	br	.drawPacmanWarpLeftLoop
.drawPacmanWarpLeftLoopEnd:
	lr	5, A						; save the bitmask
	br	.drawPacmanWarpEnd				; end the warping check

.drawPacmanWarpCheckRight:
	lr	A, S
	ci	75						; check if it's off the right side
	bp	.drawPacmanWarpEnd				; if not, end the loop

	; now we find the bitmask for pacman
	ai	%10110101					; subtract 75 pixels to get the number of bits to be masked
	lr	0, A						; save it
	li	%11111000					; load our new bitmask
.drawPacmanWarpRightLoop:
	ds	0
	bm	.drawPacmanWarpRightLoopEnd			; if we've reached the end of the bits, end the loop
	sl	1
	br	.drawPacmanWarpRightLoop
.drawPacmanWarpRightLoopEnd:
	lr	5, A						; save the bitmask
.drawPacmanWarpEnd:

.drawPacmanSprite:						; draw the sprite
	; load x coordinate
	lisu	4
	lisl	3
	lr	A, I
	lr	2, A
	; load y coordinate
	lr	A, S
	lr	3, A
	; draw the sprite
	pi	drawSprite

.drawPacmanEnd:
	pi	popk
	pk							; return from the subroutine

;=================;
; Music Functions ;
;=================;

;-----------;
; Play Song ;
;-----------;

; plays a song from memory
; song address stored in DC0
; uses r3, r4, r5, r7

playSong:                       				; taken from Pro Football
	lis	3						; A = 3
	lr	4, A						; A -> r4, r4 = 3

.playSongDelay1:      
	inc							; increase A
	bnz	.playSongDelay1					; [Branch if not zero] back to .psdly1
	am							; Memory adressed by DC0 is added to A, flags set  - get duration data
	lr	7, A						; A -> r7
	bz	.playSongEnd					; Go to end if zero
	lm							; Load memory into A (adressed by DC0) - get frequency data
	lr	5, A						; A -> r5

	; check to see if we should delay
	inc
	bnc  .playSongLoop					; it didn't roll over, play a note instead

.playSongPause:
	li	$ff
	lr	6, A
.playSongPauseLoop:
	ds	6						; pause counter
	bnz	.playSongPauseLoop
	ds	7						; duration of the pause
	bnz	.playSongPause

	; play the next note
	br	playSong

.playSongLoop:
	li	$80						; A= $80 
	outs	5						; Send  A -> port 5
	lr	A, 5						; r5 -> A

.playSongDelay2:
	inc							; increase A
	bnz	.playSongDelay2						; [Branch if not zero] back to .psdly2
	outs	5						; A -> port 5
	lr	A, 5						; r5 -> A

.playSongDelay3:
	inc							; increase A
	bnz	.playSongDelay3					; [Branch if not zero] back to .psdly3
	ds	4						; decrease r4
	bnz	.playSongLoop					; [Branch if not zero] back to .psloop
	lis	3						; A = 3
	lr	4, A						; A -> r4
	ds	7						; decrease r7
	bnz	.playSongLoop						; [Branch if not zero] back to .psloop
	br	playSong					; start over - branch to beginning

.playSongEnd:
	pop							; return from the subroutine

;------------------;
; Dot-eating Sound ;
;------------------;

dotEatingSound:
	li	$80						; Load A with 40, 80 or C0
	outs	5						; Send A to port 5
	lis	0						; A = 0
	outs	5						; A -> port 5, (reset port)

	pop							; return from the subroutine

;==============;
; Program Data ;
;==============;

	include "graphics.inc"
	; all the graphics used in the game

	include "maze.inc"
	; data for the maze, the layout, and pellets

	include "music.inc"
	; includes the songs played in the game

;===========;
; Signature ;
;===========;

	org $ff0

signature:
	.byte	"ˇBlackbirdˇ 2004"