	.ORIG x3000

	JUMP MAIN

CHECK_POINT_TR
	STR R7, R6, #0
	INC R6, #-1
	TRAP x43
	INC R6, #1
	LDR R7, R6, #0
	JUMPR R7

; ------------------------------------------------------

; Main Function

; Register Assignments:
;	R5 - Return Value
;	R6 - Stack Pointer
;	R7 - Return Address
MAIN

	; Initialize stack
	LD R6, TOS

	; Initialize Timer Interval
	; LD R0, INIT_TIMER_INTERVAL
	; STI R0, TIMER_INTERVAL

	; Initialize globals
	LD R0, BALL_START_ROW
	ST R0, BALL_ROW_POS
	LD R0, BALL_START_COL
	ST R0, BALL_COL_POS
	LD R0, BALL_START_VEL_ROW
	ST R0, BALL_ROW_VEL
	LD R0, BALL_START_VEL_COL
	ST R0, BALL_COL_VEL
	LD R0, PADDLE_START_COL
	ST R0, PADDLE_COL_POS

	; Initialize video memory
	JSR INIT_VIDEO_MEMORY

MAINLOOP
	TRAP x44	; GET_EVENT
	BRn R5, GAME_OVER
	BRp R5, EVENT_MOVE

	; Timer Event
	JSR MOVE_BALL
	BRn R5, GAME_OVER
	JUMP MAINLOOP

EVENT_MOVE
	CONST R0, #-1
	ADD R0, R0, R5
	JSR MOVE_PADDLE
	JUMP MAINLOOP

GAME_OVER
	HALT

; ------------------------------------------------------

; Data Section for Top Level

; Top of Stack
TOS			.FILL xBFFF

; Timer Interval
TIMER_INTERVAL		.FILL xFE0A
INIT_TIMER_INTERVAL	.FILL x0040

; Paddle Stats

PADDLE_ROW_POS		.FILL #116
PADDLE_START_COL	.FILL #58
PADDLE_HEIGHT		.FILL #2
PADDLE_WIDTH		.FILL #12
PADDLE_VEL		.FILL #2

; Ball Stats

BALL_START_ROW		.FILL #114
BALL_START_COL		.FILL #63
BALL_SIZE		.FILL #2

BALL_START_VEL_ROW	.FILL #-1
BALL_START_VEL_COL	.FILL #1

; Field Stats

FIELD_ROW		.FILL #3
FIELD_COL		.FILL #8
FIELD_HEIGHT		.FILL #116
FIELD_WIDTH		.FILL #112


; ------------------------------------------------------

; Init Video Memory Subroutine - initializes video memory
INIT_VIDEO_MEMORY
; Arguments: None
; Return: void

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R4, R6, #-4
	STR R5, R6, #-5
	STR R7, R6, #-6
	INC R6, #-7

	; Clear previous dead paddles
	LD R0, PADDLE_ROW_POS
	LD R1, FIELD_COL
	CONST R2, #4			; height = 4 (two for paddle, two for ball)
	LD R3, FIELD_WIDTH
	CONST R4, #0			; color = black
	TRAP x42 	; DRAW_RECTANGLE 

	; Draw walls
	LD R4, COLOR_WALL

	; Left wall
	LD R0, FIELD_ROW
	LD R1, FIELD_COL
	LD R2, FIELD_HEIGHT
	CONST R3, #1			; width = 1
	TRAP x42 	; DRAW_RECTANGLE 

	; Right wall
	LD R5, FIELD_WIDTH
	ADD R1, R1, R5			; col = field_col + field_width
	TRAP x42 	; DRAW_RECTANGLE 


	; Top wall
	LD R1, FIELD_COL
	AND R2, R3, R3			; height = 1	
	LD R3, FIELD_WIDTH
	TRAP x42 	; DRAW_RECTANGLE 
	
	; Floor
	LD R4, COLOR_FLOOR

	LD R5, FIELD_HEIGHT
	ADD R0, R0, R5			; row = field_row + field_height
	TRAP x42 	; DRAW_RECTANGLE 

	; Draw paddle
	LD R4, COLOR_PADDLE

	LD R0, PADDLE_ROW_POS
	LD R1, PADDLE_START_COL
	LD R2, PADDLE_HEIGHT
	LD R3, PADDLE_WIDTH
	TRAP x42 	; DRAW_RECTANGLE 

	; Draw Ball
	LD R0, BALL_START_ROW
	LD R1, BALL_START_COL
	LD R2, COLOR_BALL
	TRAP x41 	; DRAW_BALL 


        ; Register Restoring
        INC R6, #7
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R4, R6, #-4
	LDR R5, R6, #-5
        LDR R7, R6, #-6
        
	JUMPR R7

; ------------------------------------------------------

; Move Paddle subroutine - moves the paddle one frame
; local variables
MP_POS_DELTA	.FILL x0000
; Subroutine
MOVE_PADDLE
; Arguments: R0 - left (0), right (1)
; Return: void

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R4, R6, #-4
	STR R5, R6, #-5
	STR R7, R6, #-6
	INC R6, #-7

	CONST R1, #0		; col checkpoint = 0

	CONST R3, #2
	MUL R0, R0, R3
	INC R0, #-1		; sets R0 to -1 for left, 1 for right
	BRn R0, MP_SKIP_ADD
	LD R1, PADDLE_WIDTH	; col checkpoint = paddle width - 1
	INC R1, #-1
MP_SKIP_ADD
	LD R2, PADDLE_COL_POS
	ADD R1, R1, R2		; col checkpoint += paddle col pos

	LD R4, PADDLE_VEL
	BRp R0, MP_SKIP_MUL
	MUL R0, R0, R4
	ADD R1, R1, R0		; col checkpoint += direction * paddle vel
	JUMP MP_END_SKIP_MUL
MP_SKIP_MUL
	ADD R1, R1, R0		; col checkpoint += direction * paddle vel
	MUL R0, R0, R4
MP_END_SKIP_MUL
	ST R0, MP_POS_DELTA

	LD R0, PADDLE_ROW_POS
	TRAP x43		; CHECK_POINT
	BRnp R5, MP_END

	INC R1, #1
	TRAP x43		; CHECK_POINT
	BRnp R5, MP_END

	; clear for movement, first erase the paddle
	LD R0, PADDLE_ROW_POS
	LD R1, PADDLE_COL_POS
	LD R2, PADDLE_HEIGHT
	LD R3, PADDLE_WIDTH
	CONST R4, #0
	TRAP x42		; DRAW_RECTANGLE

	; now move it and redraw it
	LD R4, MP_POS_DELTA
	ADD R1, R1, R4
	ST R1, PADDLE_COL_POS
	LD R4, COLOR_PADDLE
	TRAP x42		; DRAW_RECTANGLE

MP_END
        ; Register Restoring
        INC R6, #7
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R4, R6, #-4
	LDR R5, R6, #-5
        LDR R7, R6, #-6

	JUMPR R7

; ------------------------------------------------------

; Move Ball subroutine - moves the ball one frame based on velocity
MOVE_BALL
; Arguments: None
; Return: R5 - game over (-1), no game over (0)

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R4, R6, #-4
	STR R7, R6, #-5
	INC R6, #-6

	; check for and handle collision
	JSR HANDLE_COLLISION
	BRn R5, MB_END

	; move ball

	; erase old ball
	LD R0, BALL_ROW_POS
	LD R1, BALL_COL_POS
	CONST R2, #0		; color = black
	TRAP x41		; DRAW_BALL

	; update row position
	LD R4, BALL_ROW_VEL
	ADD R0, R0, R4
	ST R0, BALL_ROW_POS

	; update col position
	LD R4, BALL_COL_VEL
	ADD R1, R1, R4
	ST R1, BALL_COL_POS

	; draw new ball
	LD R2, COLOR_BALL
	TRAP x41		; DRAW_BALL

MB_END
        ; Register Restoring
        INC R6, #6
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R4, R6, #-4
        LDR R7, R6, #-5

	JUMPR R7

; ------------------------------------------------------

; global variables
BALL_ROW_POS		.FILL x0000
BALL_COL_POS		.FILL x0000
BALL_ROW_VEL		.FILL x0000
BALL_COL_VEL		.FILL x0000
PADDLE_COL_POS		.FILL x0000

; Colors

COLOR_BALL		.FILL xFFFF	; White
COLOR_BRICK		.FILL x43FF	; Light Cyan
COLOR_PADDLE		.FILL xFFFE	; Almost White
COLOR_WALL		.FILL x4210	; Gray
COLOR_FLOOR		.FILL x0001	; Almost Black

; ------------------------------------------------------

; Handle Collision subroutine - checks for and handles a ball collision
; local variables
HC_CACHE_ROW	.FILL x0000
HC_CACHE_COL	.FILL x0000
; Subroutine
HANDLE_COLLISION
; Arguments: None
; Return: R5 - game over (-1), no game over (0)

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R4, R6, #-4
	STR R7, R6, #-5
	INC R6, #-6

	CONST R3, #0		; row checkpoint = 0
	LD R5, BALL_ROW_VEL
	BRnz R5, HC_SKIP_ADD_ROW
	LD R3, BALL_SIZE	; if (ball_row_vel > 0) row checkpoint = ball_size - 1
	INC R3, #-1
HC_SKIP_ADD_ROW
	LD R0, BALL_ROW_POS
	ADD R3, R3, R0		; row checkpoint += ball_row_pos
	ST R3, HC_CACHE_ROW	; cache the checkpoint before it's changed by velocity
	ADD R3, R3, R5		; row checkpoint += ball_row_vel

	CONST R4, #0		; col checkpoint = 0
	LD R5, BALL_COL_VEL
	BRnz R5, HC_SKIP_ADD_COL
	LD R4, BALL_SIZE	; if (ball_col_vel > 0) col checkpoint = ball_size - 1
	INC R4, #-1
HC_SKIP_ADD_COL
	LD R1, BALL_COL_POS
	ADD R4, R4, R1		; col checkpoint += ball_col_pos
	ST R4, HC_CACHE_COL	; cache the checkpoint before it's changed by velocity
	ADD R4, R4, R5		; col checkpoint += ball_col_vel

	CONST R2, #0		; flag = 0

	; first approximation: check the x and y movements independently

	AND R0, R3, R3
	LD R1, HC_CACHE_COL
	JSR GET_PIXEL_TYPE	; Check_Collision(row_checkpoint, col)

	; check for game over
	BRzp R5, HC_END_TRAMPOLINE1
	JUMP HC_END
HC_END_TRAMPOLINE1
		
	BRz R5, HC_SKIP_REV_ROW

	; check for brick clearing
	CONST R0, #-3
	ADD R0, R5, R0
	BRnp R0, HC_SKIP_CLEAR_ROW
	AND R0, R3, R3
	JSR CLEAR_BRICK

HC_SKIP_CLEAR_ROW

	; row collision detected, reverse row velocity
	LD R0, BALL_ROW_VEL
	NOT R0, R0
	INC R0, #1
	ST R0, BALL_ROW_VEL

	INC R5, #-2
	BRnp R5, HC_SKIP_REV_PADDLE
	LD R0, HC_CACHE_COL
	JSR HANDLE_PADDLE_COLL

HC_SKIP_REV_PADDLE
	INC R2, #1		; flag = positive (collision detected)

HC_SKIP_REV_ROW

	LD R0, HC_CACHE_ROW
	AND R1, R4, R4
	JSR GET_PIXEL_TYPE ; Check_Collision(row, col_checkpoint)

	; check for game over
	BRn R5, HC_END

	BRz R5, HC_SKIP_REV_COL

	; check for brick clearing
	CONST R0, #-3
	ADD R0, R5, R0	
	BRnp R0, HC_SKIP_CLEAR_COL
	LD R0, HC_CACHE_ROW
	JSR CLEAR_BRICK

HC_SKIP_CLEAR_COL

	; col collision detected, reverse col velocity
	LD R0, BALL_COL_VEL
	NOT R0, R0
	INC R0, #1
	ST R0, BALL_COL_VEL

	INC R2, #1		; flag = positive (collision detected)

HC_SKIP_REV_COL	

	BRp R2, HC_NO_GAME_OVER

	; second approximation: check both checkpoints at once (diagonal)
	; This is only done if first approximation doesn't detect anything.

	AND R0, R3, R3
	JSR GET_PIXEL_TYPE	; Check_Collision(row_checkpt, col_checkpt)
	
	; check for game over
	BRn R5, HC_END
	BRz R5, HC_NO_GAME_OVER

	; check for brick clearing
	CONST R0, #-3	
	ADD R0, R5, R0
	BRnp R0, HC_SKIP_CLEAR
	AND R0, R3, R3
	JSR CLEAR_BRICK

HC_SKIP_CLEAR

	; reverse both velocities
	LD R0, BALL_ROW_VEL
	NOT R0, R0
	INC R0, #1
	ST R0, BALL_ROW_VEL

	LD R0, BALL_COL_VEL
	NOT R0, R0
	INC R0, #1
	ST R0, BALL_COL_VEL

HC_NO_GAME_OVER
	CONST R5, #0
HC_END

        ; Register Restoring
        INC R6, #6
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R4, R6, #-4
        LDR R7, R6, #-5

	JUMPR R7

; ------------------------------------------------------

; Get Pixel Type subroutine - returns the object type at a given pixel
GET_PIXEL_TYPE
; Arguments: R0 - row, R1 - col
; Return: R5 - game over (-1), no collision (0), wall collision (1), paddle collision (2), brick collision (3)

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R7, R6, #-4
	INC R6, #-5

	JSR CHECK_POINT_TR
	AND R3, R5, R5		; R3 = color
	CONST R5, #0		; R5 = return value

	
	BRz R3, CCP_END		; if (color == black) return 0

	LD R2, COLOR_FLOOR
	SUB R2, R3, R2
	BRz R2, CCP_GAME_OVER	; else if (color == floor_color) return -1

	LD R2, COLOR_WALL
	SUB R2, R3, R2
	BRz R2, CCP_COLL	; else if (color == wall_color) return 1

	LD R2, COLOR_PADDLE
	SUB R2, R3, R2		 
	BRz R2, CCP_PADDLE	; else if (color == paddle_color) return 2

	; Must be brick now	
	JUMP CCP_BRICK		; else return 3

CCP_GAME_OVER
	INC R5, #-1
	JUMP CCP_END
CCP_COLL
	INC R5, #1
	JUMP CCP_END
CCP_PADDLE
	INC R5, #2
	JUMP CCP_END
CCP_BRICK
	INC R5, #3
	JUMP CCP_END

CCP_END
        ; Register Restoring
        INC R6, #5
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R7, R6, #-4

	JUMPR R7

; ------------------------------------------------------

; Handle Paddle Collision subroutine - changes ball's col velocity based on where it hits the paddle
HANDLE_PADDLE_COLL
; Arguments: R0 - col
; Return: None

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R7, R6, #-3
	INC R6, #-4

	LD R1, PADDLE_COL_POS
	SUB R1, R0, R1		; R1 = collision col pos - paddle col pos

	INC R1, #-6
	BRn R1, HPC_LEFT
	JUMP HPC_RIGHT

HPC_LEFT
	; ball col vel = -abs(ball col vel)

	LD R2, BALL_COL_VEL
	BRnz R2, HPC_END

	NOT R2, R2
	INC R2, #1
	ST R2, BALL_COL_VEL
	JUMP HPC_END

HPC_RIGHT
	; ball col vel = abs(ball col vel)

	LD R2, BALL_COL_VEL
	BRzp R2, HPC_END

	NOT R2, R2
	INC R2, #1
	ST R2, BALL_COL_VEL

HPC_END
        ; Register Restoring
        INC R6, #4
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R7, R6, #-3

	JUMPR R7

; ------------------------------------------------------

; Clear Brick At Point subroutine - clears a brick at a given point
CLEAR_BRICK
; Arguments: R0 - row, R1 - col
; Return: None

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R5, R6, #-3
	STR R7, R6, #-4
	INC R6, #-5

	JSR GET_PIXEL_TYPE
	CONST R2, #-3
	ADD R2, R5, R2
	BRnp R2, CBAP_END		; sanity check, make sure we're clearing a brick!
	JSR CHECK_POINT_TR
	AND R2, R5, R5
	JSR CLEAR_BRICK_RECURSE

CBAP_END

        ; Register Restoring
        INC R6, #5
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
	LDR R5, R6, #-3
        LDR R7, R6, #-4

	JUMPR R7

; ------------------------------------------------------

; Clear Brick subroutine - recursively clears a brick
CLEAR_BRICK_RECURSE
; Arguments: R0 - row, R1 - col, R2 - color
; Return: None

	; Register Saving
	STR R0, R6, #0
	STR R1, R6, #-1
	STR R2, R6, #-2
	STR R3, R6, #-3
	STR R4, R6, #-4
	STR R5, R6, #-5
	STR R7, R6, #-6
	INC R6, #-7

	JSR CHECK_POINT_TR
	SUB R5, R2, R5
	BRnp R5, CB_END

	; Cache Color
	AND R3, R2, R2

	; Erase Point
	CONST R2, #0
	TRAP x40		; DRAW_POINT

	; Restore Color
	AND R2, R3, R3

	; Test up
	INC R0, #-1
	JSR CLEAR_BRICK_RECURSE

	; Test down
	INC R0, #2
	JSR CLEAR_BRICK_RECURSE

	; Test left
	INC R0, #-1
	INC R1, #-1
	JSR CLEAR_BRICK_RECURSE

	; Test Right
	INC R1, #2
	JSR CLEAR_BRICK_RECURSE

CB_END
        ; Register Restoring
        INC R6, #7
	LDR R0, R6, #0
        LDR R1, R6, #-1
        LDR R2, R6, #-2
        LDR R3, R6, #-3
        LDR R4, R6, #-4
	LDR R5, R6, #-5
        LDR R7, R6, #-6

	JUMPR R7

