SECTION "rst 00", ROM0
	di
	jp Entry

SECTION "rst 10", ROM0
	jp DelayFrame

SECTION "rst 18", ROM0
	jp JumpToFuncInTable

SECTION "rst 20", ROM0
	jp _ReadHalfword

SECTION "VBlankInt", ROM0
	jp VBlank

SECTION "STATInt", ROM0
	jp LCDCStatus

SECTION "TimerInt", ROM0
	jp Timer

SECTION "SerialInt", ROM0
	jp Serial

SECTION "JoypadInt", ROM0
	jp Joypad

SECTION "Entry", ROM0

Entry: ; 0x100
	nop
	jp Start

SECTION "Header", ROM0

	; The header is generated by rgbfix.
	; The space here is allocated to prevent code from being overwritten.
	rept $150 - $104
		db 0 ;using ds fills the area with the fill value (which may not be $00); if this is changed from $00 the ROM won't build
	endr

SECTION "Main", ROM0

Start: ; 0x150
	ld [hGameBoyColorFlag], a
	ld sp, hGameBoyColorFlag
	di
	xor a
	ld [rIF], a
	ld a, [rLCDC]    ; LCD Control
	bit 7, a         ; Check if LCD Display is enabled
	jr nz, .LCDDisplayEnabled
	set 7, a
	ld [rLCDC], a
.LCDDisplayEnabled
	ld bc, $0002
	call SGBWait1750
.waitForVBlank
	ld a, [rLY]   ; LY register (LCDC Y-Coordinate)
	cp 145          ; > 144 means V-Blank
	jr c, .waitForVBlank
	ld a, $81
	ld [rLCDC], a   ; Enable LCD Display
	xor a
	ld [rBGP], a   ; Clear Palette Data
	ld [rOBP0], a
	ld [rOBP1], a
	ld bc, $0002
	call SGBWait1750
.waitForVBlank2
	ld a, [rLY]   ; LY register (LCDC Y-Coordinate)
	cp 145          ; > 144 means V-Blank
	jr c, .waitForVBlank2
	xor a
	ld [rLCDC], a   ; Disable LCD Display
	ld hl, wc000
	ld bc, $2000
	call ClearData  ; Clear WRAM Bank 0
	ld hl, vTilesOB
	ld bc, $1000
	call ClearData  ; Clear First half of VRAM
	ld a, SRAM_ENABLE
	ld [MBC5SRamEnable], a   ; Enable RAM
	ld a, $1
	ld [MBC5RomBank], a   ; Load ROM Bank $1
	ld a, $0
	ld [MBC5RomBankOn], a   ; Enable ROM Banking Mode
	ld a, $0
	ld [MBC5SRamBank], a   ; Set bits 5 and 6 of ROM Bank Number
	ld a, $1
	ld [hLoadedROMBank], a
	ld a, $1
	ld [MBC5RomBankOn], a   ; Enable RAM Banking Mode
	ld a, $0
	ld [MBC5SRamBank], a   ; Load RAM Bank $0
	ld sp, wStack    ; Initialize stack pointer to the end of WRAM Bank $1
	ld hl, hPushOAM
	ld bc, $007e
	call ClearData  ; Clear High RAM (HRAM)
	call WriteDMACodeToHRAM
	call ClearOAMBuffer
	xor a
	ld [wd7fb], a
	ld [wd7fc], a
	ld [wd7fd], a
	ld [hStatIntrRoutine], a
	ld [$ffb1], a
	ld [wd8e1], a
	ld [wd7fe], a
	ld [hSGBInit], a
	ld hl, hLCDC
	xor a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld a, $8f
	ld [hli], a
	ld a, $a6
	ld [hli], a
	ld a, $0
	ld [wUpdateAudioEngineUsingTimerInterrupt], a
	ld [wToggleAudioEngineUpdateMethod], a
	ld a, Bank(PlaySong_BankF)
	call SetSongBank
	call Func_23b
	ld a, [hGameBoyColorFlag]
	and a
	jr nz, .asm_222
	call InitSGB
	rl a
	and $1
	ld [hSGBFlag], a
	call SendSGBBorder
	ld a, [hSGBFlag]
	and a
	jr z, .asm_222
	ld a, $1
	ld [wd917], a
.asm_222
	ld a, $1
	ld [rIE], a  ; Only enable LCD Status interrupt
	ei
	ld a, $ff
	ld [wRNGModulus], a
	call ResetRNG
	xor a
	ld [wBootCheck], a
	ld a, BANK(Main)
	ld hl, Main
	call BankSwitchSimple
Func_23b: ; 0x23b
	ld a, [hGameBoyColorFlag]
	cp $11
	jr nz, .asm_248
	ld a, $1
	ld [hGameBoyColorFlag], a
	ld [hGameBoyColorFlagBackup], a
	ret

.asm_248
	xor a
	ld [hGameBoyColorFlag], a
	ld [hGameBoyColorFlagBackup], a
	ret

SoftReset:
	di
	ld sp, hGameBoyColorFlag
	xor a
	ld [rIF], a
	ld bc, $2
	call SGBWait1750
	ld hl, wc000
	ld bc, $2000
	call ClearData
	ld hl, $8000
	ld bc, $1000
	call ClearData
	ld a, SRAM_ENABLE
	ld [MBC5SRamEnable], a
	ld a, $1
	ld [MBC5RomBank], a
	ld a, $0
	ld [MBC5RomBankOn], a
	ld a, $0
	ld [MBC5SRamBank], a
	ld a, $1
	ld [hLoadedROMBank], a
	ld a, $1
	ld [MBC5RomBankOn], a
	ld a, $0
	ld [MBC5SRamBank], a
	ld sp, wStack
	call WriteDMACodeToHRAM
	call ClearOAMBuffer
	xor a
	ld [wd7fb], a
	ld [wd7fc], a
	ld [wd7fd], a
	ld [hStatIntrRoutine], a
	ld [$ffb1], a
	ld [wd8e1], a
	ld [wd7fe], a
	ld hl, hLCDC
	xor a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld [hli], a
	ld a, $8f
	ld [hli], a
	ld a, $a6
	ld [hli], a
	ld a, $0
	ld [wUpdateAudioEngineUsingTimerInterrupt], a
	ld [wToggleAudioEngineUpdateMethod], a
	ld a, BANK(Func_3c000)
	call SetSongBank
	ld a, [hSGBFlag]
	and a
	jr z, .asm_02d5
	ld a, $1
	ld [wd917], a
.asm_02d5
	ld a, $1
	ld [rIE], a
	ei
	ld a, $ff
	ld [wRNGModulus], a
	call ResetRNG
	ld a, [hGameBoyColorFlag]
	ld [hGameBoyColorFlagBackup], a
	xor a
	ld [wBootCheck], a
	ld a, Bank(Main)
	ld hl, Main
	call BankSwitchSimple
	; fallthrough

VBlank: ; 0x2f2
	push af
	push bc
	push de
	push hl
	call hPushOAM ; OAM DMA transfer
	ld a, [hLCDC]
	ld [rLCDC], a
	call Func_113a
	ei
	ld a, [rLY]
	cp $90
	jr c, .asm_328
	ld hl, hSTAT
	ld c, rSTAT - $ff00
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	ld a, [hli]
	ld [$ff00+c], a
	inc c
	ld a, [hli] ;hWY
	ld [$ff00+c], a ;into FF4A
	inc c
	ld a, [hli]
	ld [$ff00+c], a
.asm_328
	ld a, [hLYC]
	ld [hLastLYC], a
	ld a, [hNextLYCSub]
	ld [hLYCSub], a
	ld a, [hNextFrameHBlankSCX]
	ld [hHBlankSCX], a
	ld a, [hNextFrameHBlankSCY]
	ld [hHBlankSCY], a
	call ReadJoypad
	ld a, [wBootCheck]
	and a
	jr nz, .skipBootCheck
	ld a, [hJoypadState]
	cp $f
	jr nz, .skipBootCheck
	ld a, [hNewlyPressedButtons]
	and $f
	jr z, .skipBootCheck
	ld hl, sp + 8
	ld [hl], FadeAndSoftReset & $ff
	inc hl
	ld [hl], FadeAndSoftReset >> 8
	ld a, $1
	ld [wBootCheck], a
.skipBootCheck
	ld hl, hNumFramesSinceLastVBlank
	ld a, [hl]
	inc [hl]
	and a
	jr nz, .asm_365
	ld hl, hFrameCounter
	inc [hl]
.asm_365
	ld hl, hVBlankCount
	inc [hl]
	ld a, [wd8e1]
	and a
	call nz, Func_167b
	ld a, [wUpdateAudioEngineUsingTimerInterrupt]
	and a
	jr nz, .skipAudioEngineUpdate
	ld a, [wAudioEngineEnabled]
	and a
	call nz, UpdateSFX
.skipAudioEngineUpdate
	ld a, [wToggleAudioEngineUpdateMethod]
	and a
	jr z, .skipTimerToggle
	; Enable timer interrupts for audio engine updating.
	xor a
	ld [wToggleAudioEngineUpdateMethod], a
	ld a, $1
	ld [wUpdateAudioEngineUsingTimerInterrupt], a
	ld a, $78
	ld [rTMA], a
	ld a, $0
	ld [rTAC], a
	ld hl, rIE
	set 2, [hl]
	ld a, $4
	ld [rTAC], a ; Timer interrupt will fire ~60 times per second
.skipTimerToggle
	ld hl, MBC5SRamBank
	ld a, [wd917]
	and a
	jr nz, .asm_3b5
	ld a, [wRumblePattern]
	rrca
	ld [wRumblePattern], a
	and $1
	jr z, .asm_3b5
	set 3, [hl]
	jr .asm_3b7

.asm_3b5
	res 3, [hl]
.asm_3b7
	ld a, [wDrawBottomMessageBox]
	and a
	call nz, DrawBottomMessageBox
	pop hl
	pop de
	pop bc
	pop af
	reti

FadeAndSoftReset:
	ld a, [rLCDC]
	bit 7, a
	jr z, .LCD_disabled
	call FadeOut
	; Fades palettes in from white screen.
	call DisableLCD
.LCD_disabled
	ld hl, hSTAT
	res 6, [hl] ; disable LYC=LY interrupt
	ld hl, rIE
	res 1, [hl] ; disable STAT interrupt
	xor a
	ld [MBC5SRamEnable], a
	ld [rSB], a
	ld [rSC], a
	ld [rIE], a
	ld [rNR52], a
	ld a, [hGameBoyColorFlagBackup]
	ld [hGameBoyColorFlag], a
	jp SoftReset

LCDCStatus: ; 0x3ec
	push af
	push bc
	push de
	push hl
	ld a, [hStatIntrRoutine]
	sla a
	ld c, a
	ld b, 0
	ld hl, StatIntrRoutines
	add hl, bc
	ld a, [hli]
	ld h, [hl]
	ld l, a
	jp hl

StatIntrDone: ; 0x3ff
	ld a, $1
	ld [hStatIntrFired], a
	pop hl
	pop de
	pop bc
	pop af
	reti

StatIntrRoutines: ; 0x408s
	dw StatIntrNothing
	dw StatIntrTogglePinballWindow
	dw StatIntrTogglePokedexWindow
	dw StatIntrToggleHighScoresWindow
	dw StatIntrNothing2
	dw StatIntrNothing3
	dw StatIntrNothing4
	dw StatIntrNothing5

Timer: ; 0x418
	ei
	push af
	push bc
	push de
	push hl
	ld a, [wUpdateAudioEngineUsingTimerInterrupt]
	and a
	jr z, .asm_42a
	ld a, [wAudioEngineEnabled]
	and a
	call nz, UpdateSFX
.asm_42a
	ld a, [wToggleAudioEngineUpdateMethod]
	and a
	jr z, .skipTimer
	xor a
	ld [wToggleAudioEngineUpdateMethod], a
	ld [wUpdateAudioEngineUsingTimerInterrupt], a
	; disable timer
	ld a, $0
	ld [rTAC], a
	ld hl, rIE
	res 2, [hl]
.skipTimer
	pop hl
	pop de
	pop bc
	pop af
	reti

Serial: ; 0x445
	push af
	push bc
	push de
	push hl
	ld hl, Data_45d
	push hl
	ld a, [$ffb1]
	sla a
	ld c, a
	ld b, $0
	ld hl, Data_462
	add hl, bc
	ld c, [hl]
	inc hl
	ld b, [hl]
	push bc
	ret

Data_45d:
	db $e1, $d1, $c1, $f1, $d9

Data_462:
	db $64, $16, $66, $04, $c9

Joypad: ; 0x467
	reti

DelayFrame: ; 0x468
	ld a, [rLCDC]
	bit 7, a
	ret z
	ld hl, hNumFramesSinceLastVBlank
	xor a
	ld [hl], a
.asm_472
	ld a, [hl]
	and a
	jr z, .asm_472
	ret

JumpToFuncInTable: ; 0x477
; Jumps to a function in the pointer table immediately following
; a "rst JumpTable" call.  Function must be in the same Bank as the pointer table.
; input:  a = index of function in table
	sla a
	pop hl
	push de
	ld e, a
	ld d, $0
	add hl, de
	ld e, [hl]
	inc hl
	ld d, [hl]
	ld l, e
	ld h, d
	pop de
	jp hl

_ReadHalfword: ; 0x486
	rlca
	add l
	ld l, a
	jr nc, .noCarry
	inc h
.noCarry
	ld a, [hli]
	ld h, [hl]
	ld l, a
	ret

INCLUDE "home/audio.asm"

CallInFollowingTable: ; 0x532
; Calls a function in a table located immediately after a call to this function.
; Inputs:  a = entry in the table
	ld e, a
	ld d, $0
	sla e
	rl d
	sla e
	rl d  ; multiplied a by 4 because entries in the table are 4 bytes each
	pop hl
	add hl, de
	ld e, [hl]
	inc hl
	ld d, [hl]
	inc hl
	ld a, [hl]
	ld h, d
	ld l, e
	jp BankSwitch

BankSwitchSimple: ; 0x549
; Switches to Bank in register a and jumps to hl.
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a  ; Load Bank
	jp hl

BankSwitch: ; 0x54f
	ld e, a
	ld a, [hLoadedROMBank]  ; currently-loaded Bank
	cp e
	jr z, .doJump
	push af
	ld a, e
	call .loadNewBank
	call .doJump
	pop de
	ld a, d
.loadNewBank
	push hl
	push de
	ld hl, rIE
	ld d, [hl]
	ld [hl], $0
	ld [MBC5RomBank], a
	ld [hLoadedROMBank], a
	ld [hl], d
	pop de
	pop hl
	ret

.doJump
	ld a, [hFarCallTempE]
	ld e, a
	ld a, [hFarCallTempA]
	jp hl

DisableLCD: ; 0x576
	ld a, [rLCDC]
	bit 7, a
	ret z
	ld a, [hLCDC]
	res 7, a
	ld [hLCDC], a
.asm_581
	ld a, [rLCDC]
	bit 7, a
	jr nz, .asm_581
	ret

EnableLCD: ; 0x588
	ld a, [hFFC4]
	and a
	call nz, .UpdatePals
	ld a, [hLCDC]
	set 7, a
	ld [rLCDC], a
	ld [hLCDC], a
	ret

.UpdatePals
	ld de, rBGPI
	ld a, $80
	ld [de], a
	inc de
	ld b, $8
.asm_5a0
	ld a, [wBGP]
	call .UpdateDMGPals
	dec b
	jr nz, .asm_5a0
	ld de, rOBPI
	ld a, $80
	ld [de], a
	inc de
	ld b, $4
.asm_5b2
	ld a, [wOBP0]
	call .UpdateDMGPals
	ld a, [wOBP1]
	call .UpdateDMGPals
	dec b
	jr nz, .asm_5b2
	ret

.UpdateDMGPals
	push bc
	ld b, $4
.asm_5c5
	push af
	push bc
	and $3
	sla a
	ld c, a
	ld b, $0
	ld hl, GreyscalePalette
	add hl, bc
	ld a, [hli]
	ld [de], a
	ld a, [hli]
	ld [de], a
	pop bc
	pop af
	srl a
	srl a
	dec b
	jr nz, .asm_5c5
	pop bc
	ret

GreyscalePalette:
	RGB 31, 31, 31
	RGB 21, 21, 21
	RGB 11, 11, 11
	RGB 0, 0, 0

VBlankIntDisable:
	ld a, [rIE]
	res 0, a
	ld [rIE], a
	ret

VBlankIntEnable:
	ld a, [rIE]
	set 0, a
	ld [rIE], a
	ret

WriteDMACodeToHRAM: ; 0x5f7
; Initializes registers hPushOAM - hFarCallTempA
	ld c, $80
	ld b, $a  ; number of bytes to load
	ld hl, DMARoutine
.loop
	ld a, [hli]
	ld [$ff00+c], a  ; add register c to $ff00, and store register a into the resulting address
	inc c
	dec b
	jr nz, .loop
	ret

DMARoutine:
; This routine is initially loaded into hPushOAM - hFarCallTempA by WriteDMACodeToHRAM.
	ld a, (wOAMBuffer >> 8)
	ld [rDMA], a   ; start DMA
	ld a, $28 ;reducing this to account for doublespeed is a free optimisation
.waitLoop               ; wait for DMA to finish
	dec a
	jr nz, .waitLoop
	ret

WaitForLCD: ; 0x60f
; Wait for LCD controller to stop reading from both OAM and VRAM because
; CPU can't access OAM, VRAM, or palette data ($ff69, $ff6b) during this time.
	ld a, [rSTAT]    ; LCDC Status register
	and $3
	jr nz, WaitForLCD
	ld a, $a
.delay40Cycles
	dec a
	jr nz, .delay40Cycles
	ret

INCLUDE "home/copy.asm"

ClearOAMBuffer: ; 0x916
; Clears the OAM buffer by loading $f0 into all of the entries.
	ld hl, wOAMBuffer ; 0xd000
	ld b, 4 * 40  ; wOAMBuffer is 4 * 40 bytes long (40 OAM entries, 4 bytes each)
	ld a, $f0  ; byte to write
.loop
	ld [hli], a
	dec b
	jr nz, .loop
	xor a
	ld [wOAMBufferSize], a
	ret

CleanOAMBuffer: ; 0x926
; Cleans up any trailing unused oam slots in the oam buffer.
	ld a, [wOAMBufferSize]
	cp wOAMBufferEnd % $100
	jr nc, .done
	ld l, a
	ld h, wOAMBufferEnd / $100
	cpl
	add (wOAMBufferEnd + 1) % $100
	ld b, a
	ld a, $f0
.loop
	ld [hli], a
	dec b
	jr nz, .loop
.done
	xor a
	ld [wOAMBufferSize], a
	ret

AdvanceFrames: ; 0x93f
	push bc
	rst AdvanceFrame
	pop bc
	dec bc
	ld a, c
	or b
	jr nz, AdvanceFrames
	ret

SGBWait1750: ; 0x948
	ld de, 1750
.asm_94b
	nop
	nop
	nop
	dec de
	ld a, d
	or e
	jr nz, .asm_94b
	dec bc
	ld a, b
	or c
	jr nz, SGBWait1750
	ret

INCLUDE "home/random.asm"
INCLUDE "home/joypad.asm"
INCLUDE "home/palettes.asm"

MultiplyAbyL_AncientEgyptian: ; 0xdd4
; Return a * l to hl
; This is a constant-time multiplication algorithm that uses binary decomposition to achieve the result.
; See https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
	push bc
	ld c, l
	ld b, $0
	ld hl, $0000
	bit 0, a
	jr z, .asm_de0
	add hl, bc
.asm_de0
	sla c
	rl b
	bit 1, a
	jr z, .asm_de9
	add hl, bc
.asm_de9
	sla c
	rl b
	bit 2, a
	jr z, .asm_df2
	add hl, bc
.asm_df2
	sla c
	rl b
	bit 3, a
	jr z, .asm_dfb
	add hl, bc
.asm_dfb
	sla c
	rl b
	bit 4, a
	jr z, .asm_e04
	add hl, bc
.asm_e04
	sla c
	rl b
	bit 5, a
	jr z, .asm_e0d
	add hl, bc
.asm_e0d
	sla c
	rl b
	bit 6, a
	jr z, .asm_e16
	add hl, bc
.asm_e16
	sla c
	rl b
	bit 7, a
	jr z, .asm_e1f
	add hl, bc
.asm_e1f
	pop bc
	ret

ConvertHexByteToDecWord: ; 0xe21
; Convert the base-16 value in register a into a Binary Coded Decimal (base-10) word.
; Example:  If a = $97, de = $0151.
	ld b, a
	ld hl, PowersOfTwo
	ld de, $0000
.asm_e28
	srl b
	ld a, [hli]
	jr nc, .asm_e34
	add e
	daa
	ld e, a
	ld a, [hl]
	adc d
	daa
	ld d, a
.asm_e34
	inc hl
	ld a, b
	and a
	jr nz, .asm_e28
	ret

PowersOfTwo: ; 0xe3a
	dw $0001
	dw $0002
	dw $0004
	dw $0008
	dw $0016
	dw $0032
	dw $0064
	dw $0128

Increment_Max100: ; 0xe4a
; Increments the value at [hl], but caps it at 100.
; Sets carry flag if the increment happens.
	ld a, [hl]
	cp 100
	jr z, .maxValue
	inc a
	ld [hl], a
	scf
	ret

.maxValue
	and a
	ret

Modulo_C: ; 0xe55
; Calculates A modulo C
; Sets zero flag if result is zero
	cp c
	jr c, .done
	sub c
	jr Modulo_C

.done
	and a
	ret

ToggleAudioEngineUpdateMethod: ; 0xe5d
; The audio engine is normally updated once every V-Blank interrupt. However, during pinball gameplay,
; the LCD is disabled (no V-Blanks) when the pinball is transitioning between the Top- and Bottom-halfs of
; the Red and Blue Fields. Therefore, the audio engine wouldn't get updated for a fraction of a second, which
; would has a noticeable pause in the music. To solve this, the Timer interrupt is enabled while the V-Blank is
; disabled, and the audio engine gets updated during the Timer interrupt.
	ld a, $1
	ld [wToggleAudioEngineUpdateMethod], a
.wait
	ld a, [wToggleAudioEngineUpdateMethod]
	and a
	jr nz, .wait
	ret

DrawBottomMessageBox: ; 0xe69
; Draws the current scrolling bottom message box to VRAM during V-Blank.
; Note, this only applies to the 1-tile high message bar. When it displays, things like Ball Bonus summary, and
; the Save/Cancel menu, this is not used to draw the message buffer.
	ld a, [rLY]
	cp $90
	jr nc, DrawBottomMessageBox ; ensure we're in V-Blank
.asm_e6f
	ld a, [rSTAT]
	and $3
	jr nz, .asm_e6f
	ld a, $a
.asm_e77
	dec a
	jr nz, .asm_e77
	ld hl, wBottomMessageBuffer + $40
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c00
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c04
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c08
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c0c
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c10
	call Write4BottomMessageBytes
	pop hl
	ld hl, wBottomMessageBuffer + $c0
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c20
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c24
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c28
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c2c
	call Write4BottomMessageBytes
	pop hl
	call Load4BottomMessageBytes
	push hl
	ld hl, $9c30
	call Write4BottomMessageBytes
	pop hl
	ret

Load4BottomMessageBytes: ; 0xeef
	ld a, [hli]
	ld b, a
	ld a, [hli]
	ld c, a
	ld a, [hli]
	ld d, a
	ld a, [hli]
	ld e, a
	ret

Write4BottomMessageBytes: ; 0xef8
	ld a, [rSTAT]
	and $3
	jr nz, Write4BottomMessageBytes
	ld a, b
	ld [hli], a
	ld a, c
	ld [hli], a
	ld a, d
	ld [hli], a
	ld a, e
	ld [hli], a
	ld a, $a
.asm_f08
	dec a
	jr nz, .asm_f08
	ret

INCLUDE "home/save.asm"

StatIntrNothing: ; 0xfbc
	jp StatIntrDone

StatIntrTogglePinballWindow:
; Handles switching the tile data to the black status bar
; window anchored to the bottom of the screen during pinball
; gameplay.
	ld hl, hLastLYC
	ld c, [hl]
	ld a, [rLY]
	cp c
	jp c, StatIntrDone
	inc c
	inc c
	cp c
	jp nc, StatIntrDone
	ld a, [hLCDCMask]
	ld c, a
	ld a, [hLCDC]
	xor $10 ; toggle window tile data
	and c
	ld c, a
	ld hl, rSTAT
.waitForHBlank
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank
	ld a, [rLCDC]
	and $80 ; enable LCD display
	or c
	ld [rLCDC], a
	jp StatIntrDone

StatIntrTogglePokedexWindow: ; 0xfea
	ld hl, hLastLYC
	ld a, [hLYCSub]
	cp [hl]
	jr nz, .asm_1015
	ld a, [rLY]
	cp [hl]
	jp nz, StatIntrDone
	ld a, [hLCDC]
	xor $18 ; toggle window tile data and tile map
	ld c, a
	ld a, [hHBlankSCX]
	ld b, a
	ld hl, rSTAT
.waitForHBlank
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank
	ld a, [rLCDC]
	and $80 ; enable LCD display
	or c
	ld [rLCDC], a
	ld a, b
	ld [rSCY], a
	jp StatIntrDone

.asm_1015
	ld a, [rLY]
	cp [hl]
	jr nz, .asm_1037
	ld a, [hLastLYC]
	ld hl, hLYCSub
	sub [hl]
	add $40
	ld c, a
	ld a, [hLYCSub]
	ld b, a
	ld hl, rSTAT
.waitForHBlank_2
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank_2
	ld a, c
	ld [rSCY], a
	ld a, b
	ld [rLYC], a
	jp StatIntrDone

.asm_1037
	ld hl, hLYCSub
	ld a, [rLY]
	cp [hl]
	jp nz, StatIntrDone
	ld a, [hLCDC]
	xor $18 ; toggle window tile data and tile map
	ld c, a
	ld a, [hHBlankSCX]
	ld b, a
	ld hl, rSTAT
.waitForHBlank_3
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank_3
	ld a, [rLCDC]
	and $80
	or c
	ld [rLCDC], a
	ld a, b
	ld [rSCY], a
	jp StatIntrDone

StatIntrToggleHighScoresWindow:
	ld hl, hLastLYC
	ld a, [rLY]
	cp [hl]
	jr z, .asm_1069
	dec a
	cp [hl]
	jr nz, .asm_1080
.asm_1069
	ld a, [hLYCSub]
	ld c, a
	ld a, [hHBlankSCX]
	ld b, a
	ld hl, rSTAT
.waitForHBlank
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank
	ld a, b
	ld [rSCY], a
	ld a, c
	ld [rLYC], a
	jp StatIntrDone

.asm_1080
	ld hl, hLYCSub
	ld a, [rLY]
	cp [hl]
	jr z, .asm_108d
	dec a
	cp [hl]
	jp nz, StatIntrDone
.asm_108d
	ld a, [hHBlankSCY]
	ld b, a
	ld hl, rSTAT
.waitForHBlank_2
	ld a, [hl]
	and $3
	jr nz, .waitForHBlank_2
	ld a, b
	ld [rSCY], a
	jp StatIntrDone

StatIntrNothing2: ; 0x109e
	jp StatIntrDone

StatIntrNothing3: ; 0x10a1
	jp StatIntrDone

StatIntrNothing4: ; 0x10a4
	jp StatIntrDone

StatIntrNothing5: ; 0x10a7
	jp StatIntrDone

QueueGraphicsToLoad: ; 0x10aa
; Queues graphics data to be loaded into VRAM at the next available time.
; See the data/queued_tiledata/ directory to view the data that is loaded by this function.
; Input: hl = pointer to async tile data header
;			  	byte 1 = number of chunks to load
;				2*(byte 1) = list of pointers to chunks
;		  a = bank of async tile data
	ld c, a
	ld a, [hli]
	ld b, a
.loop
	push bc ;bank on c, chunks to load on b
	ld a, c ;bank moves to a
	ld c, [hl] 
	inc hl
	ld b, [hl] ;pull pointer from HL, load into BC
	inc hl
	push af ;bank on a
	ld a, [bc] 
	ld e, a
	inc bc
	ld a, [bc]
	ld d, a ;pull each pointer from [bc], place in de, then run graphics loading
	inc bc
	pop af ;bank on a
	push hl
	call QueueGraphicsToLoadWithFunc
	pop hl
	pop bc ;bank on c, chunks to load on b
	dec b
	jr nz, .loop
	ret

QueueGraphicsToLoadWithFunc: ; 0x10c5
; Queues graphics data to be loaded into VRAM with the given function at the next available time.
; See the data/queued_tiledata/ directory to view the data that is loaded by this function.
; Input: de: function that is responsible for loading the chunk of VRAM data
;		 bc: pointer to data
;		  a: bank of data
	push af
	ld a, [rLCDC]
	bit 7, a
	jr z, .skip_wait_ly
.wait_ly
	ld a, [rLY]
	cp $88
	jr nc, .wait_ly
.skip_wait_ly
	pop af
	ld hl, wd7fb
	ld l, [hl]
	ld h, wcb00 / $100
	inc bc ;load bc+1 then a then de into the queue at position wd7fb
	ld [hl], c
	inc h
	ld [hl], b
	inc h
	ld [hl], a
	inc h
	ld [hl], e
	inc h
	ld [hl], d
	ld e, $ff
	ld [hROMBankBuffer], a
	ld a, [hLoadedROMBank]
	push af ;put current bank on the stack, then switch to that bank
	ld a, [hROMBankBuffer]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	dec bc
	ld a, [bc] ;load [bc] and add wd7fa
	ld hl, wd7fa
	add [hl]
	cp $30
	jr c, .size_okay ;if more than $30, load [bc] again and set e to 0 instead of $ff
	ld a, [bc]
	ld e, $0
.size_okay
	add $4 ;add 4 more, then load back into wd7fa
	ld [hl], a
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a ;swap bank back
	ld hl, wd7fb
	ld l, [hl]
	ld h, wca00 / $100 ;load queue position in ???
	inc l
	ld [hl], $0 ;insert 0 (a terminator?) after it 
	dec l
	ld [hl], e ;load FF if less than $30, 0 if more
	ld hl, wd7fb ;inc queue position
	inc [hl]
	ld a, [rLCDC]
	bit 7, a
	ret nz ;return if LCD on
	ld a, [rIE] ;disable vblank interupt
	push af
	res 0, a
	ld [rIE], a
	call Func_113a
	pop af
	ld [rIE], a ;renable vblank interupt
	ret

Func_1129: ; 0x1129
	ld a, [wd7fb]
	ld [wd7fc], a
	ret

Func_1130: ; 0x1130
	push hl
	ld a, [wd7fb]
	ld hl, wd7fc
	cp [hl]
	pop hl
	ret

Func_113a: ; 0x113a
	ld hl, wd7fc
	ld a, [wd7fb]
	cp [hl] ;if ??? = queue length, ret
	ret z
	ld l, [hl] 
	ld h, $ca
	ld [hl], $ff ;place FF into ca??
.loop
	ld a, [hl]
	and a
	jr z, .done ;loop 255 times
	push hl
	inc h ;move to cb
	ld e, [hl] ;load pointer to data into de
	inc h
	ld d, [hl]
	inc h
	ld a, [hLoadedROMBank]
	push af ;put current bank on the stack
	ld a, [hl] ;changed to the stored bank
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	inc h
	ld a, [hl]
	inc h
	ld h, [hl] ;load function to run into HL
	ld l, a
	call JumpToHL ;run the function
	pop af
	ld [hLoadedROMBank], a ;return to old bank
	ld [MBC5RomBank], a
	pop hl
	inc l
	jr .loop

.done
	ld a, l
	ld [wd7fc], a
	ld hl, wd7fb
	cp [hl]
	ret nz
	xor a
	ld [wd7fa], a
	ret

JumpToHL: ; 0x117a
	jp hl

LoadTileLists: ; 0x117b
; Loads a series of defined tile ids into VRAM
; input:  de = pointer to data structure
; data structure:  list of VRAM tile data with the following format
;        [num tiles][destination pointer][list of tile ids]
	ld h, d
	ld l, e
.loadTileSequence
	ld a, [hli]
	and a
	ret z
	ld b, a
	ld a, [hli]
	ld e, a
	ld a, [hli]
	ld d, a  ; de = destination for tile data
.loadTileData
	ld a, [hli]
	ld [de], a
	inc de
	dec b
	jr nz, .loadTileData
	jr .loadTileSequence

LoadTileListsBank1: ; 0x118d
	ld a, $1
	ld [rVBK], a
	call LoadTileLists
	xor a
	ld [rVBK], a
	ret

Func_1198:
	ld h, d
	ld l, e
.asm_119a
	ld a, [hli]
	and a
	ret z
	ld b, a
	ld a, [hli]
	ld e, a
	ld a, [hli]
	ld d, a
	srl b
	jr nc, .asm_11a8
	ld a, [hli]
	ld c, a
.asm_11a8
	push hl
	ld h, d
	ld l, e
	ld a, c
.asm_11ac
	ld [hli], a
	inc a
	dec b
	jr nz, .asm_11ac
	ld c, a
	pop hl
	jr .asm_119a

Func_11b5: ; 11b5 (0:11b5)
	ld h, d
	ld l, e
.asm_11b7
	ld a, [hli]
	and a
	ret z
	ld b, a
	ld a, [hli]
	ld e, a
	ld a, [hli]
	ld d, a
	ld a, [hli]
.asm_11c0
	ld [de], a
	inc de
	dec b
	jr nz, .asm_11c0
	jr .asm_11b7

Func_11c7:
	ld a, $1
	ld [rVBK], a
	call Func_11b5
	xor a
	ld [rVBK], a
	ret

Func_11d2:
;de = pointer to data
;data structure: byte1 * 16 = bytes to load, destination start, source start, source bank
	ld h, d ;load pointer into hl
	ld l, e
	ld a, [hLoadedROMBank]
	ld [$ff94], a ;store current bank in ff94
.asm_11d8
	ld a, [hli] ;if first byte is 0, exit
	and a
	ret z
	ld [$ff95], a ;otherwise, store in FF95
	ld a, [hli]
	ld e, a
	ld a, [hli] ;load data into e,d,c,b and then the bank 
	ld d, a
	ld a, [hli]
	ld c, a
	ld a, [hli]
	ld b, a
	ld a, [hli]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	push hl ;store hl on the stack
	ld h, b ;load bc into hl, then ff95 into b
	ld l, c
	ld a, [$ff95]
	ld b, a
.asm_11f1
	ld a, [hli] ;load 16 bytes from hl to de b times
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc e
	ld a, [hli]
	ld [de], a
	inc de
	dec b 
	jr nz, .asm_11f1
	pop hl ;recover hl from the stack and switch back to the original bank
	ld a, [$ff94]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	jr .asm_11d8 ;loop

Func_122e:
	ld a, $1
	ld [rVBK], a
	ld h, d
	ld l, e
	ld a, [hLoadedROMBank]
	ld [$ff94], a
.asm_1238
	ld a, [hli]
	and a
	jr nz, .asm_1240
	xor a
	ld [rVBK], a
	ret

.asm_1240
	ld [$ff95], a
	ld a, [hli]
	ld e, a
	ld a, [hli]
	ld d, a
	ld a, [hli]
	ld c, a
	ld a, [hli]
	ld b, a
	ld a, [hli]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	push hl
	ld h, b
	ld l, c
	ld a, [$ff95]
	ld b, a
.asm_1256
	ld a, [hli]
	ld [de], a
	inc de
	dec b
	jr nz, .asm_1256
	pop hl
	ld a, [$ff94]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	jr .asm_1238

LoadPalettes:
; Loads either BG or OAM palette data
; de = pointer to palette data
;      first byte is number of colors to load
;      second byte bit 6 determines BG or OAM palette data. set = OAM, unset = BG. other bits are the PAL number
;      third and fourth byte are a pointer to actual color data
;      fifth byte is Bank of actual color data
;      $00 marks the end of the list
	ld h, d
	ld l, e
.loop
	ld a, [hli]
	and a
	ret z
	ld [$ff94], a
	ld a, [hli]
	bit 6, a
	ld de, rBGPI
	jr z, .asm_127a
	res 6, a
	ld de, rOBPI
.asm_127a
	set 7, a
	ld [de], a
	inc de
	ld a, [hli]
	ld c, a
	ld a, [hli]
	ld b, a
	ld a, [hLoadedROMBank]
	push af
	ld a, [hli]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	push hl
	ld h, b
	ld l, c
	ld a, [$ff94]
	ld b, a
.loadColor
	ld a, [hli]
	ld [de], a
	ld a, [hli]
	ld [de], a
	dec b
	jr nz, .loadColor
	pop hl
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	jr .loop

INCLUDE "home/sgb.asm"
INCLUDE "home/serial.asm"

Func_1a21: ; 0x1a21
	call Func_1a59
	call Func_1a89
	jr c, .asm_1a3f
.asm_1a29
	call Func_1aa9
	call Func_1b3d
	jr c, .asm_1a3f
	ld a, [wd86c]
	and a
	jr z, .asm_1a29
	call Func_1b60
	jr c, .asm_1a3f
	call Func_1b88
.asm_1a3f
	call Func_1ba7
	ret

Func_1a43: ; 0x1a43
	xor a
	ld [wd86e], a
	call Func_1a59
	call Func_1a89
	jr c, .asm_1a54
	ld a, $1
	ld [wd86e], a
.asm_1a54
	call Func_1ba7
	ret

	ret ; unused instruction?

Func_1a59: ; 0x1a59
	ld [wd86a], a
	ld a, h
	ld [wd869], a
	ld a, l
	ld [wd868], a
	ld a, $80
	ld [wd866], a
	ld a, $c2
	ld [wd867], a
	xor a
	ld [wd86b], a
	ld [wd86c], a
	ld [wd86d], a
	call Func_16a2
	ld hl, rIE
	set 3, [hl]
	xor a
	ld [$ffb1], a
	ld a, $1
	ld [wd8e1], a
	ret

Func_1a89: ; 0x1a89
	call Func_16e2
	cp $f0
	jr z, .asm_1a9f
	cp $ff
	jp z, Func_1bb2
	ld a, [wd8c8]
	cp $81
	jp nz, Func_1bb2
	and a
	ret

.asm_1a9f
	ld a, [hNewlyPressedButtons]
	bit 1, a
	jp nz, Func_1bd3
	rst AdvanceFrame
	jr Func_1a89

Func_1aa9: ; 0x1aa9
	ld a, [wd866]
	ld l, a
	ld a, [wd867]
	ld h, a
	ld de, wc000
	ld b, $2
.asm_1ab6
	ld c, $14
.asm_1ab8
	ld a, [hli]
	call Func_1ae2
	dec c
	jr nz, .asm_1ab8
	ld a, l
	add $c
	ld l, a
	jr nc, .asm_1ac6
	inc h
.asm_1ac6
	dec b
	jr nz, .asm_1ab6
	ld a, l
	ld [wd866], a
	ld a, h
	ld [wd867], a
	ld a, [wd86b]
	inc a
	ld [wd86b], a
	cp $9
	jr nz, .asm_1ae1
	ld a, $1
	ld [wd86c], a
.asm_1ae1
	ret

Func_1ae2: ; 0x1ae2
	push bc
	push hl
	xor $80
	swap a
	ld c, a
	and $f
	ld b, a
	ld a, c
	and $f0
	ld c, a
	ld a, [wd868]
	ld l, a
	ld a, [wd869]
	ld h, a
	add hl, bc
	ld a, [hLoadedROMBank]
	push af
	ld a, [wd86a]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
REPT 15
	ld a, [hli]
	ld [de], a
	inc e
ENDR
	ld a, [hli]
	ld [de], a
	inc de
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	pop hl
	pop bc
	ret

Func_1b3d: ; 0x1b3d
	ld a, [wd86c]
	ld [wd8dd], a
	ld hl, wc000
	ld a, $1
	call Func_1779
	cp $ff
	jp z, Func_1bb2
	cp $f0
	jr z, .asm_1b56
	and a
	ret

.asm_1b56
	ld a, [hNewlyPressedButtons]
	bit BIT_B_BUTTON, a
	jp nz, Func_1bd3
	rst AdvanceFrame
	jr Func_1b3d

Func_1b60: ; 0x1b60
	ld a, $1
	ld [wd8a8], a
	ld a, $13
	ld [wd8a9], a
	call Func_1740
	cp $ff
	jp z, Func_1bb2
	cp $f0
	jr z, .asm_1b7e
	ld bc, $001e
	call AdvanceFrames
	and a
	ret

.asm_1b7e
	ld a, [hNewlyPressedButtons]
	bit BIT_B_BUTTON, a
	jp nz, Func_1bd3
	rst AdvanceFrame
	jr Func_1b60

Func_1b88: ; 0x1b88
	ld a, [wd8c7]
	ld b, a
	cp $ff
	jr z, Func_1bb2
	and $f0
	jr nz, Func_1bb2
	bit 1, b
	jr nz, .asm_1b9d
	call Func_16a2
	and a
	ret

.asm_1b9d
	ld a, [hNewlyPressedButtons]
	bit BIT_B_BUTTON, a
	jp nz, Func_1bd3
	rst AdvanceFrame
	jr Func_1b88

Func_1ba7: ; 0x1ba7
	ld hl, rIE
	res 3, [hl]
	xor a
	ld [wd8e1], a
	and a
	ret

Func_1bb2: ; 0x1bb2
	ld hl, Data_1bcf
	ld a, [wd8c7]
	cp $ff
	jr z, .asm_1bc6
	ld b, $3
.asm_1bbe
	inc hl
	sla a
	jr c, .asm_1bc6
	dec b
	jr nz, .asm_1bbe
.asm_1bc6
	ld a, [hl]
	ld [wd86d], a
	call Func_16a2
	scf
	ret

Data_1bcf:
	;  $ff, >$7f, >$3f, else
	db $02,  $01,  $04,  $03

Func_1bd3: ; 0x1bd3
	lb de, $00, $01
	call PlaySoundEffect
	ld a, $5
	ld [wd86d], a
	call Func_16a2
	scf
	ret

INCLUDE "home/ir.asm"

LoadOAMData2: ; 0x1f0b
; This function loads OAM data, but it adds b and c to the x and y values
; input:  a = OAM data id (see OAMDataPointers2)
	push bc
	push de
	push hl
	ld e, a
	ld d, $0
	sla e
	rl d
	ld a, [hLoadedROMBank]
	push af
	ld a, Bank(OAMDataPointers2)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld hl, OAMDataPointers2
	jr asm_1f3b

LoadOAMData: ; 0x1f24
; This function loads OAM data, but it adds b and c to the x and y values
; input:  a = OAM data id (see OAMDataPointers)
	push bc
	push de
	push hl
	ld e, a
	ld d, $0
	sla e
	rl d  ; multiply de by 2
	ld a, [hLoadedROMBank]
	push af
	ld a, Bank(OAMDataPointers)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld hl, OAMDataPointers
asm_1f3b: ; 0x1f3b
	add hl, de  ; hl points to oam pointer in OAMDataPointers
	ld a, [hli]
	ld e, a
	ld a, [hl]
	ld d, a  ; de points to OAM data
	ld a, [wOAMBufferSize]
	ld l, a
	ld h, (wOAMBuffer >> 8)
.loadOAMDataLoop
	ld a, [de]
	cp $80  ; OAM data list terminator
	jr z, .doneReadingOAMData
	add c
	ld [hli], a
	inc de
	ld a, [de]
	add b
	ld [hli], a
	inc de
	ld a, [de]
	ld [hli], a
	inc de
	ld a, [de]
	ld [hli], a
	inc de
	jr .loadOAMDataLoop

.doneReadingOAMData
	ld a, l
	ld [wOAMBufferSize], a
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	pop hl
	pop de
	pop bc
	ret

Func_1f9a:
	push bc
	push de
	push hl
	ld e, a
	ld d, $0
	sla e
	rl d
	ld a, [hLoadedROMBank]
	push af
	ld a, BANK(OAMDataPointers2)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld hl, OAMDataPointers2
	jr asm_1fca

Func_1fb3:
	push bc
	push de
	push hl
	ld e, a
	ld d, $0
	sla e
	rl d
	ld a, [hLoadedROMBank]
	push af
	ld a, BANK(OAMDataPointers)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld hl, OAMDataPointers
asm_1fca:
	add hl, de
	ld a, [hli]
	ld e, a
	ld a, [hl]
	ld d, a
	ld a, [wOAMBufferSize]
	ld l, a
	ld h, $d0
.asm_1fd5
	ld a, [de]
	cp $80
	jr z, .asm_1fee
	add c
	ld [hli], a
	inc de
	ld a, [de]
	add b
	ld [hli], a
	inc de
	ld a, [de]
	push hl
	ld hl, sp+$7
	add [hl]
	pop hl
	ld [hli], a
	inc de
	ld a, [de]
	ld [hli], a
	inc de
	jr .asm_1fd5

.asm_1fee
	ld a, l
	ld [wOAMBufferSize], a
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	pop hl
	pop de
	pop bc
	ret

Main: ; 0x1ffc
; This is the main game loop.
	ld a, $b
	ld [wd806], a
	ld a, $4
	ld [wd807], a
	callba Func_3c000
	ld a, $1
	ld [wAudioEngineEnabled], a
	ld a, $37 ; space character for player high scores name
	ld [wPlayerName], a
	ld [wPlayerName + 1], a
	ld [wPlayerName + 2], a
	ld a, SCREEN_ERASE_ALL_DATA
	ld [wCurrentScreen], a
.master_loop
	call TickRumbleDuration
	call DoScreenLogic
	call CleanOAMBuffer
	call ClearPersistentJoypadStates
	rst AdvanceFrame
	jr .master_loop

TickRumbleDuration: ; 0x2034
; Decrements the Gameboy rumble duration.
; Turns off rumble when it gets to 0.
	ld a, [wRumbleDuration]
	and a
	jr z, .rumbleOff
	dec a
	ld [wRumbleDuration], a
	ret

.rumbleOff
	ld [wRumblePattern], a
	ret

DoScreenLogic: ; 0x2043
	ld a, [wCurrentScreen]
	call CallInFollowingTable
CallTable_2049: ; 0x2049
	padded_dab HandleSelectGameboyTargetMenu ; SCREEN_SELECT_GAMEBOY_TARGET
	padded_dab HandleEraseAllDataMenu        ; SCREEN_ERASE_ALL_DATA
	padded_dab HandleCopyrightScreen         ; SCREEN_COPYRIGHT
	padded_dab HandleTitlescreen             ; SCREEN_TITLESCREEN
	padded_dab HandlePinballGame             ; SCREEN_PINBALL_GAME
	padded_dab HandlePokedexScreen           ; SCREEN_POKEDEX
	padded_dab HandleOptionsScreen           ; SCREEN_OPTIONS
	padded_dab HandleHighScoresScreen        ; SCREEN_HIGH_SCORES
	padded_dab HandleFieldSelectScreen       ; SCREEN_FIELD_SELECT

LoadDexVWFCharacter: ; 0x206d
; Loads a single variable-width-font character used in various parts of the Pokedex screen.
	ld a, [hLoadedROMBank]
	push af
	ld a, Bank(LoadDexVWFCharacter_)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	call LoadDexVWFCharacter_
	jr c, .asm_2084
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	and a
	ret

.asm_2084
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	scf
	ret

Func_208c: ; 0x208c
	ld a, [hLoadedROMBank]
	push af
	ld a, Bank(Func_8ee0)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	call Func_8ee0
	jr c, .asm_20a3
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	and a
	ret

.asm_20a3
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	scf
	ret

MultiplyBbyCUnsigned: ; 0x20ab
	; u16 bc = (u8)b * (u8)c
	push af
	xor a
	ld [hSignedMathSignBuffer], a
	jr MultiplyBbyCSigned.asm_20c6

MultiplyBbyCSigned:
	; s16 bc = (s8)b * (s8)c
	push af
	ld a, b
	xor c
	ld [hSignedMathSignBuffer], a
	bit 7, b
	jr z, .asm_20be
	ld a, b
	cpl
	inc a
	ld b, a
.asm_20be
	bit 7, c
	jr z, .asm_20c6
	ld a, c
	cpl
	inc a
	ld c, a
.asm_20c6
	; b*c == (b**2 + c**2 - (b - c)**2) / 2
	push de
	push hl
	ld a, b
	cp c
	jr nc, .c_le_b
	ld b, c
	ld c, a
.c_le_b
	; hl = b**2 + c**2
	ld h, SquaresLow / $100
	ld l, c
	ld e, [hl]
	inc h ; SquaresHigh / $100
	ld d, [hl]
	ld l, b
	ld a, [hl]
	dec h
	ld l, [hl]
	ld h, a
	add hl, de
	push af
	; hl -= (b - c) ** 2
	ld d, SquaresLow / $100
	ld a, b
	sub c
	ld e, a
	ld a, [de]
	ld c, a
	inc d ; SquaresHigh / $100
	ld a, [de]
	ld b, a
	ld a, l
	sub c
	ld l, a
	ld a, h
	sbc b
	ld h, a
	jr nc, .no_carry
	pop af
	ccf
	jr .check_sign

.no_carry
	pop af
.check_sign
	; hl /= 2
	rr h
	rr l
	ld b, h
	ld c, l
	ld a, [hSignedMathSignBuffer]
	rlca
	jr nc, .done
	ld a, c
	cpl
	add $1
	ld c, a
	ld a, b
	cpl
	adc $0
	ld b, a
.done
	pop hl
	pop de
	pop af
	ret

MultiplyVectorComponentByAngleFactor: ; 0x210b
; Input: bc = velocity
;         e = multiplier
;         d = $0 if multiplier is treated as positive, $ff if multiplier is treated as negative
; Output: bc = bc * e / 256
	push af
	push hl
	ld a, b
	xor d
	ld [hSignedMathSignBuffer2], a
	bit 7, b
	jr z, .positive
	; negate bc
	ld a, c
	cpl
	add 1
	ld c, a
	ld a, b
	cpl
	adc 0
	ld b, a
.positive
	push bc
	; first, multiply the lo byte of the vector
	ld b, e
	call MultiplyBbyCUnsigned
	ld l, c
	ld h, b
	; round to nearest hi byte
	ld bc, $0080
	add hl, bc
	ld l, h
	ld h, $0
	pop bc
	; then, multiply the hi byte of the vector
	ld c, e
	call MultiplyBbyCUnsigned
	add hl, bc
	ld a, [hSignedMathSignBuffer2]
	rlca
	jr nc, .positive2
	; negate hl
	ld a, l
	cpl
	add 1
	ld l, a
	ld a, h
	cpl
	adc 0
	ld h, a
.positive2
	ld c, l
	ld b, h
	pop hl
	pop af
	ret

Cosine: ; 0x2147
	; Input:  a = angle
	; Output: e = cos(a)
	;         d = 0 if cos(a) is positive, $ff if cos(a) is negative
	add $40
	; fall through
Sine: ; 0x2149
	; Input:  a = angle
	; Output: e = sin(a)
	;         d = 0 if sin(a) is positive, $ff if sin(a) is negative
	push hl
	ld [hSignedMathSignBuffer], a
	and $7f ; ensure angle is between 0 and 180 degrees
	cp $40
	jr c, .firstQuadrant
	; convert angle so it's between 0 and 90 degrees
	cpl
	add $80+1
.firstQuadrant
	ld hl, SineTable
	ld e, a
	ld d, $0
	add hl, de
	ld e, [hl]
	pop hl
	ld d, $0
	ld a, [hSignedMathSignBuffer]
	sla a
	ret nc
	ld d, $ff
	ret

ApplyGravityToBall: ; 0x2168
; Adds a constant to the pinball's y velocity.
	ld a, [wEnableBallGravityAndTilt]
	and a
	ret z
	ld de, $000b  ; gravity added to y velocity every frame
	ld hl, wBallYVelocity
	ld a, [hli]
	ld h, [hl]
	ld l, a
	add hl, de
	ld a, l
	ld [wBallYVelocity], a
	ld a, h
	ld [wBallYVelocity + 1], a
	ret

LimitBallVelocity: ; 0x2180
; Ensures that the ball's x and y velocity are kept under a threshold.
; The ball can travel at a greater max speed when moving diagonally, since it
; limits the x and y components independently.
	ld hl, wBallXVelocity + 1
	call _LimitBallVelocity
	ld hl, wBallYVelocity + 1
	; fall through
_LimitBallVelocity: ; 0x2189
	ld a, [hl]
	bit 7, a  ; is it negative velocity?  (left or up)
	jr nz, .negativeVelocity
	cp 8
	ret c
	ld a, 7  ; max positive velocity
	ld [hl], a
	ret

.negativeVelocity
	cp -7
	ret nc
	ld a, -7  ; max negative velocity
	ld [hl], a
	ret

MoveBallPosition: ; 0x219c
; Updates the ball's position according to its velocity
	ld a, [wBallXPos]
	ld [wPreviousBallXPos], a
	ld a, [wBallXPos + 1]
	ld [wPreviousBallXPos + 1], a
	ld a, [wBallYPos]
	ld [wPreviousBallYPos], a
	ld a, [wBallYPos + 1]
	ld [wPreviousBallYPos + 1], a
	ld de, wBallXVelocity + 1
	ld hl, wBallXPos
	call AddVelocityToPosition
	ld de, wBallYVelocity + 1
	ld hl, wBallYPos
	; fall through

AddVelocityToPosition: ; 0x21c3
	ld a, [de]
	bit 7, a
	jr nz, .negativeVelocity
	cp 5
	jr c, .readLoByte
	ld bc, $04ff
	jr .updateBallPos

.negativeVelocity
	cp -4
	jr nc, .readLoByte
	ld bc, -$04ff
	jr .updateBallPos

.readLoByte
	ld b, a
	dec de
	ld a, [de]
	ld c, a
.updateBallPos
	ld a, [hl]
	add c
	ld [hli], a
	ld a, [hl]
	adc b
	ld [hl], a
	ret

NegateAngleAndRotateVector: ; 0x21e5
	cpl
	inc a
	; fall through
RotateVector: ; 0x21e7
; Rotates a vector by an angle using the standard rotation matrix calculation. Note, that
; the matrix is inverted vertically to account for negative y values mean "up" in this world.
; Input:   bc = vector x component
;          de = vector y component
;           a = rotation angle
; Returns: bc = resulting x component = xComponent * cos(angle) + yComponent * sin(angle)
;          de = resulting y component = yComponent * cos(angle) - xComponent * sin(angle)
	push hl
	push bc
	push de
	ld [hRotationAngleBuffer], a
	call Cosine
	ld a, e
	ld [hCosineResultBuffer], a
	ld a, d
	ld [hCosineResultBuffer + 1], a
	; xComponent * cos(angle)
	call MultiplyVectorComponentByAngleFactor
	ld l, c
	ld h, b
	pop bc
	push bc
	ld a, [hRotationAngleBuffer]
	call Sine
	ld a, e
	ld [hSineResultBuffer], a
	ld a, d
	ld [hSineResultBuffer + 1], a
	; yComponent * sin(angle)
	call MultiplyVectorComponentByAngleFactor
	add hl, bc  ; hl = xComponent * cos(angle) + yComponent * sin(angle)
	pop de
	pop bc
	push hl
	push de
	ld a, [hSineResultBuffer]
	ld e, a
	ld a, [hSineResultBuffer + 1]
	cpl
	ld d, a
	; xComponent * -sin(angle)
	call MultiplyVectorComponentByAngleFactor
	ld l, c
	ld h, b
	pop bc
	ld a, [hCosineResultBuffer]
	ld e, a
	ld a, [hCosineResultBuffer + 1]
	ld d, a
	; yComponent * cos(angle)
	call MultiplyVectorComponentByAngleFactor
	add hl, bc  ; hl = yComponent * cos(angle) - xComponent * sin(angle)
	ld d, h
	ld e, l
	pop bc
	pop hl
	ret

ApplyCollisionForces: ; 0x222b
; Applies the collision force to the ball's velocity and spin.
; When this function is called, the ball's velocity has already been rotated
; by the collision's normal angle, so that the velocity components are in a
; standardized coordinate system, where the normal is pointing directly upward
; at the colliding ball. If the ball is traveling away from the normal, then this
; function is a no-op.
; Input: bc = ball x velocity in rotated coordinate system
;        de = ball y velocity in rotated coordinate system
; Output: bc = updated ball x velocity in rotated coordinate system
;         de = updated ball y velocity in rotated coordinate system
	push hl
	ld hl, wNoCollisionApplied
	ld [hl], $ff
	; Early exit if the ball is traveling away from the collision normal.
	bit 7, d
	jr nz, .exit
	ld [hl], $0
	ld a, d
	cp 3
	jr c, .applyforces
	; The ball collided hard because its y velocity is a
	; large value. Shake the rumble pack, and play a little
	; sound effect to enhance the collision.
	ld a, $ff
	ld [wRumblePattern], a
	ld a, 1
	ld [wRumbleDuration], a
	ld a, [wFlipperCollision]
	and a
	jr nz, .applyforces
	push de
	ld de, $0008
	call PlaySFXIfNoneActive
	pop de
.applyforces
	; First, apply some dampening of the vertical
	; velocity component, so that walls absorb some
	; of the ball's speed. Colliding with certain objects
	; dampen the speed less that normal, like the flippers
	; and Poliwag button.
	; Divide y velocity by 4.
	srl d
	rr e
	srl d
	rr e
	ld h, d
	ld l, e  ; hl = yVelocity / 4
	srl d
	rr e  ; de = yVelocity / 8
	ld a, [wCollisionForceAmplification]
	and a
	jr z, .updateYVelocity
.amplify
	add hl, de
	dec a
	jr nz, .amplify
.updateYVelocity
	; Negate the dampened dampened y velocity so that the ball
	; bounces off.
	ld d, h
	ld e, l
	ld a, e
	cpl
	add 1
	ld e, a
	ld a, d
	cpl
	adc 0
	ld d, a
	ld a, [wBallSpin]
	; Divide ball spin by 2, signed, and extend to 2 bytes (hl)
	sra a
	ld l, a
	ld h, $0
	bit 7, l
	jr z, .updateXVelocity
	ld h, $ff
.updateXVelocity
	; hl = ballSpin / 2
	; bc = x velocity component in rotated coordinate system
	; Add the spin to the x velocity component.
	add hl, bc
	ld b, h
	ld c, l
	push bc
	; Recalculate ball spin.
	; new ball spin = (xVelocity * 4) >> 8
	sla c
	rl b
	sla c
	rl b
	ld a, b
	ld [wBallSpin], a
	pop bc
.exit
	pop hl
	ret

LoadBallVelocity: ; 0x2299
; Loads velocity of the ball into bc and de
; bc = x velocity
; de = y velocity
	push hl
	ld hl, wBallXVelocity
	ld a, [hli]
	ld c, a
	ld a, [hli]
	ld b, a
	ld a, [hli]
	ld e, a
	ld a, [hl]
	ld d, a
	pop hl
	ret

SetBallVelocity: ; 0x22a7
; Sets the x and y velocities of the ball.
; bc = x velocity
; de = y velocity
	push hl
	ld hl, wBallXVelocity
	ld a, c
	ld [hli], a
	ld a, b
	ld [hli], a
	ld a, e
	ld [hli], a
	ld a, d
	ld [hl], a
	pop hl
	ret

CheckStageCollision: ; 0x22b5
	ld a, [wBallXPos + 1]
	sub 4
	push af
	and $7
	ld [wSubTileBallXPos], a ; sub-tile position
	pop af
	and $f8
	ld c, a ; c = tile x pos
	ld a, [wBallYPos + 1]
	sub 4
	push af
	and $7
	ld [wSubTileBallYPos], a
	pop af
	and $f8
	ld b, a ; b = tile y pos
	ld l, b  ; bc contains tile coords of ball position
	; Calculate the tile offset for the ball's position, as if the
	; board was composed of a 1D array starting from the top-left
	; going left-to-right.
	ld h, $0
	sla l
	rl h
	sla l
	rl h  ; b was multiplied by 4 ((y tile) * 32)
	srl c
	srl c
	srl c  ; c was divided by 8 (x tile)
	ld b, $0
	add hl, bc
	ld a, l
	ld [wBallPositionTileOffset], a
	ld a, h
	ld [wBallPositionTileOffset + 1], a
	ld a, [wStageCollisionMapPointer]
	ld c, a
	ld a, [wStageCollisionMapPointer + 1]
	ld b, a
	add hl, bc  ; hl = address of ball tile's collision attribute
	ld a, [hLoadedROMBank]
	push af
	ld a, [wStageCollisionMapBank]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld bc, 32 - 1  ; stage's number of tiles wide - 1
	ld a, [hli]
	ld [wUpperLeftCollisionAttribute], a
	ld a, [hl]
	ld [wUpperRightCollisionAttribute], a
	add hl, bc
	ld a, [hli]
	ld [wLowerLeftCollisionAttribute], a
	ld a, [hl]
	ld [wLowerRightCollisionAttribute], a
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld a, [hLoadedROMBank]
	push af
	ld a, [wStageCollisionMasksBank]
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld a, [wSubTileBallXPos]
	sla a
	ld c, a
	ld b, 0
	ld hl, CollisionTests
	add hl, bc
	ld e, [hl]
	inc hl
	ld d, [hl]
	ld a, [wSubTileBallYPos]
	ld c, a
	ld b, 16  ; length of wCollisionPointTests
.testCollisionPointLoop
	push bc
	ld a, [de]
	inc de
	add c  ; add the sub tile y pos
	push af
	srl a
	srl a
	srl a
	ld c, a
	ld b, $0
	ld hl, wUpperLeftCollisionAttribute
	add hl, bc
	ld a, [hl]
	call TryLoadSpecialCollisionMask
	jr nc, .staticMask
	; Load the special mask pointer.
	; hl = pointer to array of collision masks.
	pop af
	and $7
	ld c, a
	ld b, $0
	add hl, bc
	jr .testCollisionPoint

.staticMask
	ld c, a
	ld b, $0 ; bc = static collision mask id
	; Multiply bc by 8. Each collision mask is an
	; 8x8-tile of 1-bit-per-pixel data. Therefore,
	; each collision mask tile is 8 bytes.
	sla c
	rl b
	sla c
	rl b
	sla c
	rl b
	ld hl, wStageCollisionMasksPointer
	ld a, [hli]
	ld h, [hl]
	ld l, a
	add hl, bc ; hl = pointer to collision mask tile
	pop af
	and $7     ; a = y subtile offset
	ld c, a
	ld b, $0
	add hl, bc
.testCollisionPoint
	; hl = pointer to the collision mask row for the y position
	ld a, [de]
	inc de
	and [hl]
	push af
	ld a, [de]
	inc de
	ld c, a
	ld hl, wCollisionPointTests
	add hl, bc
	pop af
	ld [hl], a
	pop bc
	dec b
	jr nz, .testCollisionPointLoop
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld hl, wCollisionPointTests
	ld de, wd7d9
	ld b, 4
.copyLoop
	ld a, [hli]
	ld [de], a
	inc de
	ld a, [hli]
	ld [de], a
	inc de
	ld a, [hli]
	ld [de], a
	inc de
	ld a, [hli]
	ld [de], a
	inc de
	dec b
	jr nz, .copyLoop
	ld hl, wCollisionPointTests
	ld de, $0000
	ld b, 0
	ld a, [hl]
	and a
	jr z, .findNextCollisionPoint
.findNextCollisionPoint2
	; This loop is nearly identical to .findNextCollisionPoint
	; It exists to handle the improbable (impossible?) event that
	; the ball could be completely inside a collision wall.
	ld a, [hli]
	inc b
	and a
	jr z, .findNextCollisionPoint
	ld a, b
	cp 16 + 1
	jr nc, .determinedCollisionStatus
	jr .findNextCollisionPoint2

.findNextCollisionPoint
	ld a, [hli]
	inc b
	and a
	jr nz, .handleCollisionPoint
	ld a, b
	cp 16 + 1
	jr nc, .determinedCollisionStatus
	jr .findNextCollisionPoint

.handleCollisionPoint
	push de
	ld d, $1
	ld c, b
	dec c
.findNextFailedCollisionPoint
	ld a, [hli]
	inc b
	inc d
	and a
	jr nz, .findNextFailedCollisionPoint
	dec d
	ld a, b
	dec a
	dec a
	and $f
	swap c
	or c
	ld c, a ; c = (index of first collision point << 8) | (index of last consecutive collision point)
	ld a, d ; d = number of consecutive successful collision points
	cp e
	pop de
	jr c, .updatedMaxConsecutivePoints
	ld e, a ; e = max number of consecutive successful collision points
	ld d, c ; d = (index of first collision point << 8) | (index of last consecutive collision point)
.updatedMaxConsecutivePoints
	ld a, b
	cp 16
	jr c, .findNextCollisionPoint
.determinedCollisionStatus
	; e = maximum number of consecutive successful collision points
	; d = the start and last index of that consecutive string of collision points (wCollisionPointTests)
	;     the hi nybble is the start index, lo nybble is the end index
	ld a, e
	ld [wIsBallColliding], a
	and a
	ret z
	ld a, [hLoadedROMBank]
	push af
	ld a, Bank(CollisionForceAngles)
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	push de
	; Based on the start and end collision point indices, look
	; up the corresponding normal angle for them.
	ld e, d
	ld d, 0
	ld hl, CollisionForceAngles
	add hl, de
	ld a, [hl]
	ld [wCollisionNormalAngle], a
	; Again, based on the start and end collision point indices, apply
	; position offsets to position the ball safely outside of the wall
	; it collided with.
	sla e
	rl d
	ld hl, CollisionYDeltas
	add hl, de
	ld a, [wBallYPos]
	add [hl]
	ld [wBallYPos], a
	inc hl
	ld a, [wBallYPos + 1]
	adc [hl]
	ld [wBallYPos + 1], a
	ld hl, CollisionXDeltas
	add hl, de
	ld a, [wBallXPos]
	add [hl]
	ld [wBallXPos], a
	inc hl
	ld a, [wBallXPos + 1]
	adc [hl]
	ld [wBallXPos + 1], a
	pop de
	pop af
	ld [hLoadedROMBank], a
	ld [MBC5RomBank], a
	ld a, d
	swap a
	and $f
	ld e, a
	ld a, d
	and $f
	sub e
	jr nc, .loadTestPointOffsets
	add 16
.loadTestPointOffsets
	; e = start index of consecutive collision points
	; a = length of consecutive collision points
	; The rest of the code here determines which of
	; the collision mask tiles was the one that triggered
	; the collision. It can be multiple tiles, so it uses
	; first collision test point that was colliding. Then,
	; that tile's collision attribute is saved in wCurCollisionAttribute.
	add e
	add e
	inc a
	and $1e
	ld c, a
	ld b, 0
	ld hl, BallCollisionTestPointOffsets
	add hl, bc
	ld a, [wSubTileBallXPos]
	add 4
	add [hl]
	bit 3, a
	ld c, b
	jr z, .checkY
	ld c, 2
.checkY
	ld a, [wSubTileBallYPos]
	add 4
	inc hl
	add [hl]
	bit 3, a
	jr z, .saveCollisionAttribute
	inc c
.saveCollisionAttribute
	ld hl, wUpperLeftCollisionAttribute
	add hl, bc
	ld a, [hl]
	ld [wCurCollisionAttribute], a
	ld hl, BallPositionPointerOffsetDeltas
	add hl, bc
	ld a, [wBallPositionTileOffset]
	add [hl]
	ld [wCurCollisionTileOffset], a
	ld a, [wBallPositionTileOffset + 1]
	adc 0
	ld [wCurCollisionTileOffset + 1], a
	ret

TryLoadSpecialCollisionMask: ; 0x248a
; If it's a special collision mask attribute, load the
; pointer to its mask and set the carry flag.
; If it's a regular static collision mask attribute, simply
; reset the carry flag and don't return anything.
	push af
	ld a, [wCurrentStage]
	bit 0, a
	jr nz, .bottomStage
	pop af
	and a
	ret

.bottomStage
	pop af
	cp $d0
	ccf
	ret nc
	cp $e0
	jr nc, .notAnimatedMonCollision
	sub $d0
	sla a
	sla a
	sla a
	ld l, a
	ld h, (wMonAnimatedCollisionMask >> 8)
	scf
	ret

.notAnimatedMonCollision
	push de
	sub $e0
	ld b, a
	ld a, [wCurrentStage]
	cp FIRST_BONUS_STAGE
	jr nc, .bonusStage
	bit 4, b
	ld hl, BottomLeftCollisionMasks
	ld a, [wLeftFlipperState + 1]
	jr z, .checkFlipperPosition
	res 4, b
	ld hl, BottomRightCollisionMasks
	ld a, [wRightFlipperState + 1]
.checkFlipperPosition
	ld de, $0080
	cp 7
	jr c, .loadMaskPointer
	add hl, de
	cp 14
	jr c, .loadMaskPointer
	add hl, de
.loadMaskPointer
	ld e, b
	sla e
	sla e
	sla e
	add hl, de
	pop de
	scf
	ret

.bonusStage
	bit 4, b
	ld hl, BottomLeftBonusStageCollisionMasks
	ld a, [wLeftFlipperState + 1]
	jr z, .checkFlipperPosition2
	res 4, b
	ld hl, BottomRightBonusStageCollisionMasks
	ld a, [wRightFlipperState + 1]
.checkFlipperPosition2
	ld de, $0080
	cp 7
	jr c, .loadMaskPointer2
	add hl, de
	cp 14
	jr c, .loadMaskPointer2
	add hl, de
.loadMaskPointer2
	ld e, b
	sla e
	sla e
	sla e
	add hl, de
	pop de
	scf
	ret

BallPositionPointerOffsetDeltas:
	db $00, $20
	db $01, $21

BallCollisionTestPointOffsets:
	db  4,  0
	db  4,  1
	db  3,  3
	db  1,  4
	db  0,  4
	db -1,  4
	db -3,  3
	db -4,  1
	db -4,  0
	db -4, -1
	db -3, -3
	db -1, -4
	db  0, -4
	db  1, -4
	db  3, -3
	db  4, -1

; These tables facilitate testing the 16 collision points around the ball's origin.
; Each table corresponds to an x sub-tile offset for a base point 4 pixels up and left of
; the ball's origin.
;
; First byte:  y sub-tile offset, if its greater than 8, it bleeds over to the next tile in the 2x2 tile region
; Second byte: bitmask to test against the 1bpp collision mask's row
; Third byte:  index of wCollisionPointTests to save the result in
CollisionTests: ; 0x252e
	dw CollisionTest_X0
	dw CollisionTest_X1
	dw CollisionTest_X2
	dw CollisionTest_X3
	dw CollisionTest_X4
	dw CollisionTest_X5
	dw CollisionTest_X6
	dw CollisionTest_X7

CollisionTest_X0: ; 0x253e
	db $00, $10, $0B
	db $00, $08, $0C
	db $00, $04, $0D
	db $01, $40, $0A
	db $01, $01, $0E
	db $03, $80, $09
	db $13, $80, $0F
	db $04, $80, $08
	db $14, $80, $00
	db $05, $80, $07
	db $15, $80, $01
	db $07, $40, $06
	db $07, $01, $02
	db $08, $10, $05
	db $08, $08, $04
	db $08, $04, $03

CollisionTest_X1: ; 0x256e
	db $00, $08, $0B
	db $00, $04, $0C
	db $00, $02, $0D
	db $01, $20, $0A
	db $11, $80, $0E
	db $03, $40, $09
	db $13, $40, $0F
	db $04, $40, $08
	db $14, $40, $00
	db $05, $40, $07
	db $15, $40, $01
	db $07, $20, $06
	db $17, $80, $02
	db $08, $08, $05
	db $08, $04, $04
	db $08, $02, $03

CollisionTest_X2: ; 0x259e
	db $00, $04, $0B
	db $00, $02, $0C
	db $00, $01, $0D
	db $01, $10, $0A
	db $11, $40, $0E
	db $03, $20, $09
	db $13, $20, $0F
	db $04, $20, $08
	db $14, $20, $00
	db $05, $20, $07
	db $15, $20, $01
	db $07, $10, $06
	db $17, $40, $02
	db $08, $04, $05
	db $08, $02, $04
	db $08, $01, $03

CollisionTest_X3: ; 0x25ce
	db $00, $02, $0B
	db $00, $01, $0C
	db $10, $80, $0D
	db $01, $08, $0A
	db $11, $20, $0E
	db $03, $10, $09
	db $13, $10, $0F
	db $04, $10, $08
	db $14, $10, $00
	db $05, $10, $07
	db $15, $10, $01
	db $07, $08, $06
	db $17, $20, $02
	db $08, $02, $05
	db $08, $01, $04
	db $18, $80, $03

CollisionTest_X4: ; 0x25fe
	db $00, $01, $0B
	db $10, $80, $0C
	db $10, $40, $0D
	db $01, $04, $0A
	db $11, $10, $0E
	db $03, $08, $09
	db $13, $08, $0F
	db $04, $08, $08
	db $14, $08, $00
	db $05, $08, $07
	db $15, $08, $01
	db $07, $04, $06
	db $17, $10, $02
	db $08, $01, $05
	db $18, $80, $04
	db $18, $40, $03

CollisionTest_X5: ; 0x262e
	db $10, $80, $0B
	db $10, $40, $0C
	db $10, $20, $0D
	db $01, $02, $0A
	db $11, $08, $0E
	db $03, $04, $09
	db $13, $04, $0F
	db $04, $04, $08
	db $14, $04, $00
	db $05, $04, $07
	db $15, $04, $01
	db $07, $02, $06
	db $17, $08, $02
	db $18, $80, $05
	db $18, $40, $04
	db $18, $20, $03

CollisionTest_X6: ; 0x265e
	db $10, $40, $0B
	db $10, $20, $0C
	db $10, $10, $0D
	db $01, $01, $0A
	db $11, $04, $0E
	db $03, $02, $09
	db $13, $02, $0F
	db $04, $02, $08
	db $14, $02, $00
	db $05, $02, $07
	db $15, $02, $01
	db $07, $01, $06
	db $17, $04, $02
	db $18, $40, $05
	db $18, $20, $04
	db $18, $10, $03

CollisionTest_X7: ; 0x268e
	db $10, $20, $0B
	db $10, $10, $0C
	db $10, $08, $0D
	db $11, $80, $0A
	db $11, $02, $0E
	db $03, $01, $09
	db $13, $01, $0F
	db $04, $01, $08
	db $14, $01, $00
	db $05, $01, $07
	db $15, $01, $01
	db $17, $80, $06
	db $17, $02, $02
	db $18, $20, $05
	db $18, $10, $04
	db $18, $08, $03

INCLUDE "data/sine_table.asm"
INCLUDE "engine/pinball_game/object_collision/object_collision.asm"

LoadFlippersPalette: ; 0x2862
; Loads the flippers' color palette.
; When the flippers are active, they are white/blue. When disabled at the end of a Bonus Stage, they turn Red.
	ld a, [wFlippersDisabled]
	and a
	jr nz, .flippersDisabled
	ld a, [hGameBoyColorFlag]
	and a
	jr z, .done
	ld a, Bank(ActiveFlippersPalette)
	ld hl, ActiveFlippersPalette
	ld de, $0052
	ld bc, $0004
	call FarCopyPalettes
.done
	ret

.flippersDisabled
	ld a, [hGameBoyColorFlag]
	and a
	jr z, .done2
	ld a, Bank(DisabledFlippersPalette)
	ld hl, DisabledFlippersPalette
	ld de, $0052
	ld bc, $0004
	call FarCopyPalettes
.done2
	ret

ActiveFlippersPalette:
; The white/blue color when the flippers are active.
	RGB 31, 31, 31
	RGB 21, 21, 27

DisabledFlippersPalette:
; The reddish colors when the flippers are disabled at the end of a Bonus Stage.
	RGB 27, 10, 10
	RGB 20, 04, 04

CatchBarTiles:
; The tile ids for the blue CATCH! bar under the wild pokemon
	db $80, $AE, $AF, $B0, $B1, $B2, $B3, $80
CatchBarTilesEnd:

INCLUDE "home/animation.asm"
INCLUDE "home/text.asm"
INCLUDE "home/bcd.asm"
INCLUDE "home/tilt.asm"

SetDoubleSpeedMode:
	ld a, [rKEY1]
	bit 7, a
	ret nz
	jp ToggleDoubleSpeedMode

SetNormalSpeedMode:
	ld a, [rKEY1]
	bit 7, a
	ret z
	; fall through
ToggleDoubleSpeedMode:
	ld a, [rIE]
	push af
	xor a
	ld [rIE], a
	ld a, $30
	ld [rJOYP], a
	ld a, 1
	ld [rKEY1], a
	stop
	pop af
	ld [rIE], a
	ret

SECTION "bank0.2", ROM0

BottomLeftCollisionMasks:
	INCBIN "data/collision/masks/bottom_left_masks.masks"

BottomRightCollisionMasks:
	INCBIN "data/collision/masks/bottom_right_masks.masks"

BottomLeftBonusStageCollisionMasks:
	INCBIN "data/collision/masks/bottom_left_bonus_stage_masks.masks"

BottomRightBonusStageCollisionMasks:
	INCBIN "data/collision/masks/bottom_right_bonus_stage_masks.masks"

; These two squares data arrays must be aligned to $100 bytes and appear contiguously.
SquaresLow:
FOR X, 256
	db (X * X) % $100
ENDR

SquaresHigh:
FOR X, 256
	db (X * X) / $100
ENDR

