summaryrefslogtreecommitdiff
path: root/home
diff options
context:
space:
mode:
Diffstat (limited to 'home')
-rw-r--r--home/audio.asm183
-rw-r--r--home/fade.asm73
-rw-r--r--home/init.asm137
-rw-r--r--home/joypad.asm39
-rw-r--r--home/overworld.asm2403
-rw-r--r--home/pic.asm591
-rw-r--r--home/predef.asm50
-rw-r--r--home/text.asm718
-rw-r--r--home/vblank.asm105
-rw-r--r--home/vcopy.asm447
10 files changed, 4746 insertions, 0 deletions
diff --git a/home/audio.asm b/home/audio.asm
new file mode 100644
index 00000000..fa62cc8a
--- /dev/null
+++ b/home/audio.asm
@@ -0,0 +1,183 @@
+Func_2307:: ; 2307 (0:2307)
+ call WaitForSoundToFinish
+ xor a
+ ld c, a
+ ld d, a
+ ld [wcfca], a
+ jr asm_2324
+
+Func_2312:: ; 2312 (0:2312)
+ ld c, $a
+ ld d, $0
+ ld a, [wd72e]
+ bit 5, a
+ jr z, asm_2324
+ xor a
+ ld [wcfca], a
+ ld c, $8
+ ld d, c
+asm_2324:: ; 2324 (0:2324)
+ ld a, [wd700]
+ and a
+ jr z, .asm_2343
+ cp $2
+ jr z, .asm_2332
+ ld a, MUSIC_BIKE_RIDING
+ jr .asm_2334
+.asm_2332
+ ld a, MUSIC_SURFING
+.asm_2334
+ ld b, a
+ ld a, d
+ and a
+ ld a, BANK(Music_BikeRiding)
+ jr nz, .asm_233e
+ ld [wc0ef], a
+.asm_233e
+ ld [wc0f0], a
+ jr .asm_234c
+.asm_2343
+ ld a, [wd35b]
+ ld b, a
+ call Func_2385
+ jr c, .asm_2351
+.asm_234c
+ ld a, [wcfca]
+ cp b
+ ret z
+.asm_2351
+ ld a, c
+ ld [wMusicHeaderPointer], a
+ ld a, b
+ ld [wcfca], a
+ ld [wc0ee], a
+ jp PlaySound
+
+Func_235f:: ; 235f (0:235f)
+ ld a, [wc0ef]
+ ld b, a
+ cp BANK(Music2_UpdateMusic)
+ jr nz, .checkForBank08
+.bank02
+ ld hl, Music2_UpdateMusic
+ jr .asm_2378
+.checkForBank08
+ cp BANK(Music8_UpdateMusic)
+ jr nz, .bank1F
+.bank08
+ ld hl, Music8_UpdateMusic
+ jr .asm_2378
+.bank1F
+ ld hl, Music1f_UpdateMusic
+.asm_2378
+ ld c, $6
+.asm_237a
+ push bc
+ push hl
+ call Bankswitch
+ pop hl
+ pop bc
+ dec c
+ jr nz, .asm_237a
+ ret
+
+Func_2385:: ; 2385 (0:2385)
+ ld a, [wd35c]
+ ld e, a
+ ld a, [wc0ef]
+ cp e
+ jr nz, .asm_2394
+ ld [wc0f0], a
+ and a
+ ret
+.asm_2394
+ ld a, c
+ and a
+ ld a, e
+ jr nz, .asm_239c
+ ld [wc0ef], a
+.asm_239c
+ ld [wc0f0], a
+ scf
+ ret
+
+PlayMusic:: ; 23a1 (0:23a1)
+ ld b, a
+ ld [wc0ee], a
+ xor a
+ ld [wMusicHeaderPointer], a
+ ld a, c
+ ld [wc0ef], a
+ ld [wc0f0], a
+ ld a, b
+
+; plays music specified by a. If value is $ff, music is stopped
+PlaySound:: ; 23b1 (0:23b1)
+ push hl
+ push de
+ push bc
+ ld b, a
+ ld a, [wc0ee]
+ and a
+ jr z, .asm_23c8
+ xor a
+ ld [wc02a], a
+ ld [wc02b], a
+ ld [wc02c], a
+ ld [wc02d], a
+.asm_23c8
+ ld a, [wMusicHeaderPointer]
+ and a
+ jr z, .asm_23e3
+ ld a, [wc0ee]
+ and a
+ jr z, .asm_2425
+ xor a
+ ld [wc0ee], a
+ ld a, [wcfca]
+ cp $ff
+ jr nz, .asm_2414
+ xor a
+ ld [wMusicHeaderPointer], a
+.asm_23e3
+ xor a
+ ld [wc0ee], a
+ ld a, [H_LOADEDROMBANK]
+ ld [$ffb9], a
+ ld a, [wc0ef]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ cp BANK(Func_9876)
+ jr nz, .checkForBank08
+.bank02
+ ld a, b
+ call Func_9876
+ jr .asm_240b
+.checkForBank08
+ cp BANK(Func_22035)
+ jr nz, .bank1F
+.bank08
+ ld a, b
+ call Func_22035
+ jr .asm_240b
+.bank1F
+ ld a, b
+ call Func_7d8ea
+.asm_240b
+ ld a, [$ffb9]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ jr .asm_2425
+.asm_2414
+ ld a, b
+ ld [wcfca], a
+ ld a, [wMusicHeaderPointer]
+ ld [wcfc8], a
+ ld [wcfc9], a
+ ld a, b
+ ld [wMusicHeaderPointer], a
+.asm_2425
+ pop bc
+ pop de
+ pop hl
+ ret
diff --git a/home/fade.asm b/home/fade.asm
new file mode 100644
index 00000000..9b55eaf2
--- /dev/null
+++ b/home/fade.asm
@@ -0,0 +1,73 @@
+; These routines manage gradual fading
+; (e.g., entering a doorway)
+LoadGBPal::
+ ld a, [wd35d] ;tells if cur.map is dark (requires HM5_FLASH?)
+ ld b, a
+ ld hl, FadePal4
+ ld a, l
+ sub b
+ ld l, a
+ jr nc, .ok
+ dec h
+.ok
+ ld a, [hli]
+ ld [rBGP], a
+ ld a, [hli]
+ ld [rOBP0], a
+ ld a, [hli]
+ ld [rOBP1], a
+ ret
+
+GBFadeOut1::
+ ld hl, FadePal1
+ ld b, 4
+ jr GBFadeOutCommon
+
+GBFadeOut2::
+ ld hl, FadePal6
+ ld b, 3
+
+GBFadeOutCommon::
+ ld a, [hli]
+ ld [rBGP], a
+ ld a, [hli]
+ ld [rOBP0], a
+ ld a, [hli]
+ ld [rOBP1], a
+ ld c, 8
+ call DelayFrames
+ dec b
+ jr nz, GBFadeOutCommon
+ ret
+
+GBFadeIn1::
+ ld hl, FadePal4 + 2
+ ld b, 4
+ jr GBFadeInCommon
+
+GBFadeIn2::
+ ld hl, FadePal7 + 2
+ ld b, 3
+
+GBFadeInCommon::
+ ld a, [hld]
+ ld [rOBP1], a
+ ld a, [hld]
+ ld [rOBP0], a
+ ld a, [hld]
+ ld [rBGP], a
+ ld c, 8
+ call DelayFrames
+ dec b
+ jr nz, GBFadeInCommon
+ ret
+
+FadePal1:: db %11111111, %11111111, %11111111
+FadePal2:: db %11111110, %11111110, %11111000
+FadePal3:: db %11111001, %11100100, %11100100
+FadePal4:: db %11100100, %11010000, %11100000
+; rBGP rOBP0 rOBP1
+FadePal5:: db %11100100, %11010000, %11100000
+FadePal6:: db %10010000, %10000000, %10010000
+FadePal7:: db %01000000, %01000000, %01000000
+FadePal8:: db %00000000, %00000000, %00000000
diff --git a/home/init.asm b/home/init.asm
new file mode 100644
index 00000000..b79b19d9
--- /dev/null
+++ b/home/init.asm
@@ -0,0 +1,137 @@
+SoftReset::
+ call StopAllSounds
+ call GBPalWhiteOut
+ ld c, $20
+ call DelayFrames
+ ; fallthrough
+
+Init::
+; Program init.
+
+rLCDC_DEFAULT EQU %11100011
+; * LCD enabled
+; * Window tile map at $9C00
+; * Window display enabled
+; * BG and window tile data at $8800
+; * BG tile map at $9800
+; * 8x8 OBJ size
+; * OBJ display enabled
+; * BG display enabled
+
+ di
+
+ xor a
+ ld [rIF], a
+ ld [rIE], a
+ ld [$ff43], a
+ ld [$ff42], a
+ ld [$ff01], a
+ ld [$ff02], a
+ ld [$ff4b], a
+ ld [$ff4a], a
+ ld [$ff06], a
+ ld [$ff07], a
+ ld [$ff47], a
+ ld [$ff48], a
+ ld [$ff49], a
+
+ ld a, rLCDC_ENABLE_MASK
+ ld [rLCDC], a
+ call DisableLCD
+
+ ld sp, wStack
+
+ ld hl, $c000 ; start of WRAM
+ ld bc, $2000 ; size of WRAM
+.loop
+ ld [hl], 0
+ inc hl
+ dec bc
+ ld a, b
+ or c
+ jr nz, .loop
+
+ call ClearVram
+
+ ld hl, $ff80
+ ld bc, $ffff - $ff80
+ call FillMemory
+
+ call ClearSprites
+
+ ld a, Bank(WriteDMACodeToHRAM)
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+ call WriteDMACodeToHRAM
+
+ xor a
+ ld [$ffd7], a
+ ld [$ff41], a
+ ld [$ffae], a
+ ld [$ffaf], a
+ ld [$ff0f], a
+ ld a, 1 << VBLANK + 1 << TIMER + 1 << SERIAL
+ ld [rIE], a
+
+ ld a, 144 ; move the window off-screen
+ ld [$ffb0], a
+ ld [rWY], a
+ ld a, 7
+ ld [rWX], a
+
+ ld a, $ff
+ ld [$ffaa], a
+
+ ld h, vBGMap0 / $100
+ call ClearBgMap
+ ld h, vBGMap1 / $100
+ call ClearBgMap
+
+ ld a, rLCDC_DEFAULT
+ ld [rLCDC], a
+ ld a, 16
+ ld [hSoftReset], a
+ call StopAllSounds
+
+ ei
+
+ predef LoadSGB
+
+ ld a, BANK(SFX_1f_67)
+ ld [wc0ef], a
+ ld [wc0f0], a
+ ld a, $9c
+ ld [$ffbd], a
+ xor a
+ ld [$ffbc], a
+ dec a
+ ld [wcfcb], a
+
+ predef PlayIntro
+
+ call DisableLCD
+ call ClearVram
+ call GBPalNormal
+ call ClearSprites
+ ld a, rLCDC_DEFAULT
+ ld [rLCDC], a
+
+ jp SetDefaultNamesBeforeTitlescreen
+
+ClearVram:
+ ld hl, $8000
+ ld bc, $2000
+ xor a
+ jp FillMemory
+
+
+StopAllSounds::
+ ld a, BANK(Music2_UpdateMusic)
+ ld [wc0ef], a
+ ld [wc0f0], a
+ xor a
+ ld [wMusicHeaderPointer], a
+ ld [wc0ee], a
+ ld [wcfca], a
+ dec a
+ jp PlaySound
diff --git a/home/joypad.asm b/home/joypad.asm
new file mode 100644
index 00000000..2002bb29
--- /dev/null
+++ b/home/joypad.asm
@@ -0,0 +1,39 @@
+ReadJoypad::
+; Poll joypad input.
+; Unlike the hardware register, button
+; presses are indicated by a set bit.
+
+ ld a, 1 << 5 ; select direction keys
+ ld c, 0
+
+ ld [rJOYP], a
+ rept 6
+ ld a, [rJOYP]
+ endr
+ cpl
+ and %1111
+ swap a
+ ld b, a
+
+ ld a, 1 << 4 ; select button keys
+ ld [rJOYP], a
+ rept 10
+ ld a, [rJOYP]
+ endr
+ cpl
+ and %1111
+ or b
+
+ ld [hJoyInput], a
+
+ ld a, 1 << 4 + 1 << 5 ; deselect keys
+ ld [rJOYP], a
+ ret
+
+Joypad::
+; Update the joypad state variables:
+; [hJoyReleased] keys released since last time
+; [hJoyPressed] keys pressed since last time
+; [hJoyHeld] currently pressed keys
+ homecall _Joypad
+ ret
diff --git a/home/overworld.asm b/home/overworld.asm
new file mode 100644
index 00000000..cf36aba8
--- /dev/null
+++ b/home/overworld.asm
@@ -0,0 +1,2403 @@
+HandleMidJump::
+; Handle the player jumping down
+; a ledge in the overworld.
+ ld b, BANK(_HandleMidJump)
+ ld hl, _HandleMidJump
+ jp Bankswitch
+
+EnterMap::
+; Load a new map.
+ ld a, $ff
+ ld [wJoyIgnore], a
+ call LoadMapData
+ callba Func_c335 ; initialize map variables
+ ld hl, wd72c
+ bit 0, [hl]
+ jr z, .doNotCountSteps
+ ld a, 3
+ ld [wd13c], a ; some kind of step counter (counts up to 3 steps?)
+.doNotCountSteps
+ ld hl, wd72e
+ bit 5, [hl] ; did a battle happen immediately before this?
+ res 5, [hl] ; unset the "battle just happened" flag
+ call z, Func_12e7
+ call nz, MapEntryAfterBattle
+ ld hl, wd732
+ ld a, [hl]
+ and 1 << 4 | 1 << 3
+ jr z, .didNotFlyOrTeleportIn
+ res 3, [hl]
+ callba Func_70510 ; display fly/teleport in graphical effect
+ call UpdateSprites
+.didNotFlyOrTeleportIn
+ callba CheckForceBikeOrSurf ; handle currents in SF islands and forced bike riding in cycling road
+ ld hl, wd72d
+ res 5, [hl]
+ call UpdateSprites
+ ld hl, wd126
+ set 5, [hl]
+ set 6, [hl]
+ xor a
+ ld [wJoyIgnore], a
+
+OverworldLoop::
+ call DelayFrame
+OverworldLoopLessDelay::
+ call DelayFrame
+ call LoadGBPal
+ ld a,[wd736]
+ bit 6,a ; jumping down a ledge?
+ call nz, HandleMidJump
+ ld a,[wWalkCounter]
+ and a
+ jp nz,.moveAhead ; if the player sprite has not yet completed the walking animation
+ call JoypadOverworld ; get joypad state (which is possibly simulated)
+ callba SafariZoneCheck
+ ld a,[wda46]
+ and a
+ jp nz,WarpFound2
+ ld hl,wd72d
+ bit 3,[hl]
+ res 3,[hl]
+ jp nz,WarpFound2
+ ld a,[wd732]
+ and a,$18
+ jp nz,HandleFlyOrTeleportAway
+ ld a,[W_CUROPPONENT]
+ and a
+ jp nz,.newBattle
+ ld a,[wd730]
+ bit 7,a ; are we simulating button presses?
+ jr z,.notSimulating
+ ld a,[hJoyHeld]
+ jr .checkIfStartIsPressed
+.notSimulating
+ ld a,[hJoyPressed]
+.checkIfStartIsPressed
+ bit 3,a ; start button
+ jr z,.startButtonNotPressed
+; if START is pressed
+ xor a
+ ld [$ff8c],a ; the $2920 ID for the start menu is 0
+ jp .displayDialogue
+.startButtonNotPressed
+ bit 0,a ; A button
+ jp z,.checkIfDownButtonIsPressed
+; if A is pressed
+ ld a,[wd730]
+ bit 2,a
+ jp nz,.noDirectionButtonsPressed
+ call Func_30fd
+ jr nz,.checkForOpponent
+ call Func_3eb5 ; check for hidden items, PC's, etc.
+ ld a,[$ffeb]
+ and a
+ jp z,OverworldLoop
+ call IsSpriteOrSignInFrontOfPlayer ; check for sign or sprite in front of the player
+ ld a,[$ff8c] ; $2920 ID for NPC/sign text, if any
+ and a
+ jp z,OverworldLoop
+.displayDialogue
+ predef Func_c586 ; check what is in front of the player
+ call UpdateSprites ; move sprites
+ ld a,[wFlags_0xcd60]
+ bit 2,a
+ jr nz,.checkForOpponent
+ bit 0,a
+ jr nz,.checkForOpponent
+ aCoord 8, 9
+ ld [wcf0e],a
+ call DisplayTextID ; display either the start menu or the NPC/sign text
+ ld a,[wcc47]
+ and a
+ jr z,.checkForOpponent
+ dec a
+ ld a,$00
+ ld [wcc47],a
+ jr z,.changeMap
+ predef LoadSAV
+ ld a,[W_CURMAP]
+ ld [wd71a],a
+ call Func_62ce
+ ld a,[W_CURMAP]
+ call SwitchToMapRomBank ; switch to the ROM bank of the current map
+ ld hl,W_CURMAPTILESET
+ set 7,[hl]
+.changeMap
+ jp EnterMap
+.checkForOpponent
+ ld a,[W_CUROPPONENT]
+ and a
+ jp nz,.newBattle
+ jp OverworldLoop
+.noDirectionButtonsPressed
+ ld hl,wFlags_0xcd60
+ res 2,[hl]
+ call UpdateSprites ; move sprites
+ ld a,$01
+ ld [wcc4b],a
+ ld a,[wd528] ; the direction that was pressed last time
+ and a
+ jp z,OverworldLoop
+; if a direction was pressed last time
+ ld [wd529],a ; save the last direction
+ xor a
+ ld [wd528],a ; zero the direction
+ jp OverworldLoop
+.checkIfDownButtonIsPressed
+ ld a,[hJoyHeld] ; current joypad state
+ bit 7,a ; down button
+ jr z,.checkIfUpButtonIsPressed
+ ld a,$01
+ ld [wSpriteStateData1 + 3],a
+ ld a,$04
+ jr .handleDirectionButtonPress
+.checkIfUpButtonIsPressed
+ bit 6,a ; up button
+ jr z,.checkIfLeftButtonIsPressed
+ ld a,$ff
+ ld [wSpriteStateData1 + 3],a
+ ld a,$08
+ jr .handleDirectionButtonPress
+.checkIfLeftButtonIsPressed
+ bit 5,a ; left button
+ jr z,.checkIfRightButtonIsPressed
+ ld a,$ff
+ ld [wSpriteStateData1 + 5],a
+ ld a,$02
+ jr .handleDirectionButtonPress
+.checkIfRightButtonIsPressed
+ bit 4,a ; right button
+ jr z,.noDirectionButtonsPressed
+ ld a,$01
+ ld [wSpriteStateData1 + 5],a
+.handleDirectionButtonPress
+ ld [wd52a],a ; new direction
+ ld a,[wd730]
+ bit 7,a ; are we simulating button presses?
+ jr nz,.noDirectionChange ; ignore direction changes if we are
+ ld a,[wcc4b]
+ and a
+ jr z,.noDirectionChange
+ ld a,[wd52a] ; new direction
+ ld b,a
+ ld a,[wd529] ; old direction
+ cp b
+ jr z,.noDirectionChange
+; the code below is strange
+; it computes whether or not the player did a 180 degree turn, but then overwrites the result
+; also, it does a seemingly pointless loop afterwards
+ swap a ; put old direction in upper half
+ or b ; put new direction in lower half
+ cp a,$48 ; change dir from down to up
+ jr nz,.notDownToUp
+ ld a,$02
+ ld [wd528],a
+ jr .oddLoop
+.notDownToUp
+ cp a,$84 ; change dir from up to down
+ jr nz,.notUpToDown
+ ld a,$01
+ ld [wd528],a
+ jr .oddLoop
+.notUpToDown
+ cp a,$12 ; change dir from right to left
+ jr nz,.notRightToLeft
+ ld a,$04
+ ld [wd528],a
+ jr .oddLoop
+.notRightToLeft
+ cp a,$21 ; change dir from left to right
+ jr nz,.oddLoop
+ ld a,$08
+ ld [wd528],a
+.oddLoop
+ ld hl,wFlags_0xcd60
+ set 2,[hl]
+ ld hl,wcc4b
+ dec [hl]
+ jr nz,.oddLoop
+ ld a,[wd52a]
+ ld [wd528],a
+ call NewBattle
+ jp c,.battleOccurred
+ jp OverworldLoop
+.noDirectionChange
+ ld a,[wd52a] ; current direction
+ ld [wd528],a ; save direction
+ call UpdateSprites ; move sprites
+ ld a,[wd700]
+ cp a,$02 ; surfing
+ jr z,.surfing
+; not surfing
+ call CollisionCheckOnLand
+ jr nc,.noCollision
+ push hl
+ ld hl,wd736
+ bit 2,[hl]
+ pop hl
+ jp z,OverworldLoop
+ push hl
+ call ExtraWarpCheck ; sets carry if there is a potential to warp
+ pop hl
+ jp c,CheckWarpsCollision
+ jp OverworldLoop
+.surfing
+ call CollisionCheckOnWater
+ jp c,OverworldLoop
+.noCollision
+ ld a,$08
+ ld [wWalkCounter],a
+ jr .moveAhead2
+.moveAhead
+ ld a,[wd736]
+ bit 7,a
+ jr z,.noSpinning
+ callba LoadSpinnerArrowTiles ; spin while moving
+.noSpinning
+ call UpdateSprites ; move sprites
+.moveAhead2
+ ld hl,wFlags_0xcd60
+ res 2,[hl]
+ ld a,[wd700]
+ dec a ; riding a bike?
+ jr nz,.normalPlayerSpriteAdvancement
+ ld a,[wd736]
+ bit 6,a ; jumping a ledge?
+ jr nz,.normalPlayerSpriteAdvancement
+ call BikeSpeedup ; if riding a bike and not jumping a ledge
+.normalPlayerSpriteAdvancement
+ call AdvancePlayerSprite
+ ld a,[wWalkCounter]
+ and a
+ jp nz,CheckMapConnections ; it seems like this check will never succeed (the other place where CheckMapConnections is run works)
+; walking animation finished
+ ld a,[wd730]
+ bit 7,a
+ jr nz,.doneStepCounting ; if button presses are being simulated, don't count steps
+; step counting
+ ld hl,wd13b ; step counter
+ dec [hl]
+ ld a,[wd72c]
+ bit 0,a
+ jr z,.doneStepCounting
+ ld hl,wd13c
+ dec [hl]
+ jr nz,.doneStepCounting
+ ld hl,wd72c
+ res 0,[hl]
+.doneStepCounting
+ ld a,[wd790]
+ bit 7,a ; in the safari zone?
+ jr z,.notSafariZone
+ callba SafariZoneCheckSteps
+ ld a,[wda46]
+ and a
+ jp nz,WarpFound2
+.notSafariZone
+ ld a,[W_ISINBATTLE]
+ and a
+ jp nz,CheckWarpsNoCollision
+ predef Func_c69c ; decrement HP of poisoned pokemon
+ ld a,[wd12d]
+ and a
+ jp nz,HandleBlackOut ; if all pokemon fainted
+.newBattle
+ call NewBattle
+ ld hl,wd736
+ res 2,[hl]
+ jp nc,CheckWarpsNoCollision ; check for warps if there was no battle
+.battleOccurred
+ ld hl,wd72d
+ res 6,[hl]
+ ld hl,W_FLAGS_D733
+ res 3,[hl]
+ ld hl,wd126
+ set 5,[hl]
+ set 6,[hl]
+ xor a
+ ld [hJoyHeld],a ; clear joypad state
+ ld a,[W_CURMAP]
+ cp a,CINNABAR_GYM
+ jr nz,.notCinnabarGym
+ ld hl,wd79b
+ set 7,[hl]
+.notCinnabarGym
+ ld hl,wd72e
+ set 5,[hl]
+ ld a,[W_CURMAP]
+ cp a,OAKS_LAB
+ jp z,.noFaintCheck
+ callab AnyPartyAlive ; check if all the player's pokemon fainted
+ ld a,d
+ and a
+ jr z,.allPokemonFainted
+.noFaintCheck
+ ld c,$0a
+ call DelayFrames
+ jp EnterMap
+.allPokemonFainted
+ ld a,$ff
+ ld [W_ISINBATTLE],a
+ call RunMapScript
+ jp HandleBlackOut
+
+; function to determine if there will be a battle and execute it (either a trainer battle or wild battle)
+; sets carry if a battle occurred and unsets carry if not
+NewBattle:: ; 0683 (0:0683)
+ ld a,[wd72d]
+ bit 4,a
+ jr nz,.noBattle
+ call Func_30fd
+ jr nz,.noBattle
+ ld a,[wd72e]
+ bit 4,a
+ jr nz,.noBattle
+ ld b, BANK(InitBattle)
+ ld hl, InitBattle
+ jp Bankswitch ; determines if a battle will occur and runs the battle if so
+.noBattle
+ and a
+ ret
+
+; function to make bikes twice as fast as walking
+BikeSpeedup:: ; 06a0 (0:06a0)
+ ld a,[wcc57]
+ and a
+ ret nz
+ ld a,[W_CURMAP]
+ cp a,ROUTE_17 ; Cycling Road
+ jr nz,.goFaster
+ ld a,[hJoyHeld] ; current joypad state
+ and a,%01110000 ; bit mask for up, left, right buttons
+ ret nz
+.goFaster
+ jp AdvancePlayerSprite
+
+; check if the player has stepped onto a warp after having not collided
+CheckWarpsNoCollision:: ; 06b4 (0:06b4)
+ ld a,[wd3ae] ; number of warps
+ and a
+ jp z,CheckMapConnections
+ ld a,[wd3ae] ; number of warps
+ ld b,$00
+ ld c,a
+ ld a,[W_YCOORD]
+ ld d,a
+ ld a,[W_XCOORD]
+ ld e,a
+ ld hl,wd3af ; start of warp entries
+CheckWarpsNoCollisionLoop:: ; 06cc (0:06cc)
+ ld a,[hli] ; check if the warp's Y position matches
+ cp d
+ jr nz,CheckWarpsNoCollisionRetry1
+ ld a,[hli] ; check if the warp's X position matches
+ cp e
+ jr nz,CheckWarpsNoCollisionRetry2
+; if a match was found
+ push hl
+ push bc
+ ld hl,wd736
+ set 2,[hl]
+ callba Func_c49d ; check if the player sprite is standing on a "door" tile
+ pop bc
+ pop hl
+ jr c,WarpFound1 ; if it is, go to 0735
+ push hl
+ push bc
+ call ExtraWarpCheck ; sets carry if the warp is confirmed
+ pop bc
+ pop hl
+ jr nc,CheckWarpsNoCollisionRetry2
+; if the extra check passed
+ ld a,[W_FLAGS_D733]
+ bit 2,a
+ jr nz,WarpFound1
+ push de
+ push bc
+ call Joypad
+ pop bc
+ pop de
+ ld a,[hJoyHeld] ; current joypad state
+ and a,%11110000 ; bit mask for directional buttons
+ jr z,CheckWarpsNoCollisionRetry2 ; if directional buttons aren't being pressed, do not pass through the warp
+ jr WarpFound1
+
+; check if the player has stepped onto a warp after having collided
+CheckWarpsCollision:: ; 0706 (0:0706)
+ ld a,[wd3ae] ; number of warps
+ ld c,a
+ ld hl,wd3af ; start of warp entries
+.loop
+ ld a,[hli] ; Y coordinate of warp
+ ld b,a
+ ld a,[W_YCOORD]
+ cp b
+ jr nz,.retry1
+ ld a,[hli] ; X coordinate of warp
+ ld b,a
+ ld a,[W_XCOORD]
+ cp b
+ jr nz,.retry2
+ ld a,[hli]
+ ld [wd42f],a ; save target warp ID
+ ld a,[hl]
+ ld [$ff8b],a ; save target map
+ jr WarpFound2
+.retry1
+ inc hl
+.retry2
+ inc hl
+ inc hl
+ dec c
+ jr nz,.loop
+ jp OverworldLoop
+
+CheckWarpsNoCollisionRetry1:: ; 072f (0:072f)
+ inc hl
+CheckWarpsNoCollisionRetry2:: ; 0730 (0:0730)
+ inc hl
+ inc hl
+ jp ContinueCheckWarpsNoCollisionLoop
+
+WarpFound1:: ; 0735 (0:0735)
+ ld a,[hli]
+ ld [wd42f],a ; save target warp ID
+ ld a,[hli]
+ ld [$ff8b],a ; save target map
+
+WarpFound2:: ; 073c (0:073c)
+ ld a,[wd3ae] ; number of warps
+ sub c
+ ld [wd73b],a ; save ID of used warp
+ ld a,[W_CURMAP]
+ ld [wd73c],a
+ call CheckIfInOutsideMap
+ jr nz,.indoorMaps
+; this is for handling "outside" maps that can't have the 0xFF destination map
+ ld a,[W_CURMAP]
+ ld [wLastMap],a
+ ld a,[W_CURMAPWIDTH]
+ ld [wd366],a
+ ld a,[$ff8b] ; destination map number
+ ld [W_CURMAP],a ; change current map to destination map
+ cp a,ROCK_TUNNEL_1
+ jr nz,.notRockTunnel
+ ld a,$06
+ ld [wd35d],a
+ call GBFadeIn1
+.notRockTunnel
+ call PlayMapChangeSound
+ jr .done
+; for maps that can have the 0xFF destination map, which means to return to the outside map; not all these maps are necessarily indoors, though
+.indoorMaps
+ ld a,[$ff8b] ; destination map
+ cp a,$ff
+ jr z,.goBackOutside
+; if not going back to the previous map
+ ld [W_CURMAP],a ; current map number
+ callba Func_70787 ; check if the warp was a Silph Co. teleporter
+ ld a,[wcd5b]
+ dec a
+ jr nz,.notTeleporter
+; if it's a Silph Co. teleporter
+ ld hl,wd732
+ set 3,[hl]
+ call LeaveMapAnim
+ jr .skipMapChangeSound
+.notTeleporter
+ call PlayMapChangeSound
+.skipMapChangeSound
+ ld hl,wd736
+ res 0,[hl]
+ res 1,[hl]
+ jr .done
+.goBackOutside
+ ld a,[wLastMap]
+ ld [W_CURMAP],a
+ call PlayMapChangeSound
+ xor a
+ ld [wd35d],a
+.done
+ ld hl,wd736
+ set 0,[hl]
+ call Func_12da
+ jp EnterMap
+
+ContinueCheckWarpsNoCollisionLoop:: ; 07b5 (0:07b5)
+ inc b ; increment warp number
+ dec c ; decrement number of warps
+ jp nz,CheckWarpsNoCollisionLoop
+
+; if no matching warp was found
+CheckMapConnections:: ; 07ba (0:07ba)
+.checkWestMap
+ ld a,[W_XCOORD]
+ cp a,$ff
+ jr nz,.checkEastMap
+ ld a,[W_MAPCONN3PTR]
+ ld [W_CURMAP],a
+ ld a,[wd38f] ; new X coordinate upon entering west map
+ ld [W_XCOORD],a
+ ld a,[W_YCOORD]
+ ld c,a
+ ld a,[wd38e] ; Y adjustment upon entering west map
+ add c
+ ld c,a
+ ld [W_YCOORD],a
+ ld a,[wd390] ; pointer to upper left corner of map without adjustment for Y position
+ ld l,a
+ ld a,[wd391]
+ ld h,a
+ srl c
+ jr z,.savePointer1
+.pointerAdjustmentLoop1
+ ld a,[wd38d] ; width of connected map
+ add a,$06
+ ld e,a
+ ld d,$00
+ ld b,$00
+ add hl,de
+ dec c
+ jr nz,.pointerAdjustmentLoop1
+.savePointer1
+ ld a,l
+ ld [wd35f],a ; pointer to upper left corner of current tile block map section
+ ld a,h
+ ld [wd360],a
+ jp .loadNewMap
+.checkEastMap
+ ld b,a
+ ld a,[wd525] ; map width
+ cp b
+ jr nz,.checkNorthMap
+ ld a,[W_MAPCONN4PTR]
+ ld [W_CURMAP],a
+ ld a,[wd39a] ; new X coordinate upon entering east map
+ ld [W_XCOORD],a
+ ld a,[W_YCOORD]
+ ld c,a
+ ld a,[wd399] ; Y adjustment upon entering east map
+ add c
+ ld c,a
+ ld [W_YCOORD],a
+ ld a,[wd39b] ; pointer to upper left corner of map without adjustment for Y position
+ ld l,a
+ ld a,[wd39c]
+ ld h,a
+ srl c
+ jr z,.savePointer2
+.pointerAdjustmentLoop2
+ ld a,[wd398]
+ add a,$06
+ ld e,a
+ ld d,$00
+ ld b,$00
+ add hl,de
+ dec c
+ jr nz,.pointerAdjustmentLoop2
+.savePointer2
+ ld a,l
+ ld [wd35f],a ; pointer to upper left corner of current tile block map section
+ ld a,h
+ ld [wd360],a
+ jp .loadNewMap
+.checkNorthMap
+ ld a,[W_YCOORD]
+ cp a,$ff
+ jr nz,.checkSouthMap
+ ld a,[W_MAPCONN1PTR]
+ ld [W_CURMAP],a
+ ld a,[wd378] ; new Y coordinate upon entering north map
+ ld [W_YCOORD],a
+ ld a,[W_XCOORD]
+ ld c,a
+ ld a,[wd379] ; X adjustment upon entering north map
+ add c
+ ld c,a
+ ld [W_XCOORD],a
+ ld a,[wd37a] ; pointer to upper left corner of map without adjustment for X position
+ ld l,a
+ ld a,[wd37b]
+ ld h,a
+ ld b,$00
+ srl c
+ add hl,bc
+ ld a,l
+ ld [wd35f],a ; pointer to upper left corner of current tile block map section
+ ld a,h
+ ld [wd360],a
+ jp .loadNewMap
+.checkSouthMap
+ ld b,a
+ ld a,[wd524]
+ cp b
+ jr nz,.didNotEnterConnectedMap
+ ld a,[W_MAPCONN2PTR]
+ ld [W_CURMAP],a
+ ld a,[wd383] ; new Y coordinate upon entering south map
+ ld [W_YCOORD],a
+ ld a,[W_XCOORD]
+ ld c,a
+ ld a,[wd384] ; X adjustment upon entering south map
+ add c
+ ld c,a
+ ld [W_XCOORD],a
+ ld a,[wd385] ; pointer to upper left corner of map without adjustment for X position
+ ld l,a
+ ld a,[wd386]
+ ld h,a
+ ld b,$00
+ srl c
+ add hl,bc
+ ld a,l
+ ld [wd35f],a ; pointer to upper left corner of current tile block map section
+ ld a,h
+ ld [wd360],a
+.loadNewMap ; load the connected map that was entered
+ call LoadMapHeader
+ call Func_2312 ; music
+ ld b,$09
+ call GoPAL_SET
+; Since the sprite set shouldn't change, this will just update VRAM slots at
+; $C2XE without loading any tile patterns.
+ callba InitMapSprites
+ call LoadTileBlockMap
+ jp OverworldLoopLessDelay
+.didNotEnterConnectedMap
+ jp OverworldLoop
+
+; function to play a sound when changing maps
+PlayMapChangeSound:: ; 08c9 (0:08c9)
+ aCoord 8, 8 ; upper left tile of the 4x4 square the player's sprite is standing on
+ cp a,$0b ; door tile in tileset 0
+ jr nz,.didNotGoThroughDoor
+ ld a,(SFX_02_57 - SFX_Headers_02) / 3
+ jr .playSound
+.didNotGoThroughDoor
+ ld a,(SFX_02_5c - SFX_Headers_02) / 3
+.playSound
+ call PlaySound
+ ld a,[wd35d]
+ and a
+ ret nz
+ jp GBFadeIn1
+
+CheckIfInOutsideMap:: ; 08e1 (0:08e1)
+; If the player is in an outside map (a town or route), set the z flag
+ ld a, [W_CURMAPTILESET]
+ and a ; most towns/routes have tileset 0 (OVERWORLD)
+ ret z
+ cp PLATEAU ; Route 23 / Indigo Plateau
+ ret
+
+; this function is an extra check that sometimes has to pass in order to warp, beyond just standing on a warp
+; the "sometimes" qualification is necessary because of CheckWarpsNoCollision's behavior
+; depending on the map, either "function 1" or "function 2" is used for the check
+; "function 1" passes when the player is at the edge of the map and is facing towards the outside of the map
+; "function 2" passes when the the tile in front of the player is among a certain set
+; sets carry if the check passes, otherwise clears carry
+ExtraWarpCheck:: ; 08e9 (0:08e9)
+ ld a, [W_CURMAP]
+ cp SS_ANNE_3
+ jr z, .useFunction1
+ cp ROCKET_HIDEOUT_1
+ jr z, .useFunction2
+ cp ROCKET_HIDEOUT_2
+ jr z, .useFunction2
+ cp ROCKET_HIDEOUT_4
+ jr z, .useFunction2
+ cp ROCK_TUNNEL_1
+ jr z, .useFunction2
+ ld a, [W_CURMAPTILESET]
+ and a ; outside tileset (OVERWORLD)
+ jr z, .useFunction2
+ cp SHIP ; S.S. Anne tileset
+ jr z, .useFunction2
+ cp SHIP_PORT ; Vermilion Port tileset
+ jr z, .useFunction2
+ cp PLATEAU ; Indigo Plateau tileset
+ jr z, .useFunction2
+.useFunction1
+ ld hl, Func_c3ff
+ jr .doBankswitch
+.useFunction2
+ ld hl, Func_c44e
+.doBankswitch
+ ld b, BANK(Func_c44e)
+ jp Bankswitch
+
+MapEntryAfterBattle:: ; 091f (0:091f)
+ callba Func_c35f ; function that appears to disable warp testing after collisions if the player is standing on a warp
+ ld a,[wd35d]
+ and a
+ jp z,GBFadeIn2
+ jp LoadGBPal
+
+HandleBlackOut::
+; For when all the player's pokemon faint.
+; Does not print the "blacked out" message.
+
+ call GBFadeIn1
+ ld a, $08
+ call StopMusic
+ ld hl, wd72e
+ res 5, [hl]
+ ld a, Bank(Func_40b0) ; also Bank(Func_62ce) and Bank(Func_5d5f)
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+ call Func_40b0
+ call Func_62ce
+ call Func_2312
+ jp Func_5d5f
+
+StopMusic::
+ ld [wMusicHeaderPointer], a
+ ld a, $ff
+ ld [wc0ee], a
+ call PlaySound
+.wait
+ ld a, [wMusicHeaderPointer]
+ and a
+ jr nz, .wait
+ jp StopAllSounds
+
+HandleFlyOrTeleportAway::
+ call UpdateSprites
+ call Delay3
+ xor a
+ ld [wcf0b], a
+ ld [wd700], a
+ ld [W_ISINBATTLE], a
+ ld [wd35d], a
+ ld hl, wd732
+ set 2, [hl]
+ res 5, [hl]
+ call LeaveMapAnim
+ ld a, Bank(Func_62ce)
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ call Func_62ce
+ jp Func_5d5f
+
+LeaveMapAnim::
+ ld b, BANK(_LeaveMapAnim)
+ ld hl, _LeaveMapAnim
+ jp Bankswitch
+
+LoadPlayerSpriteGraphics::
+; Load sprite graphics based on whether the player is standing, biking, or surfing.
+
+ ; 0: standing
+ ; 1: biking
+ ; 2: surfing
+
+ ld a, [wd700]
+ dec a
+ jr z, .ridingBike
+
+ ld a, [$ffd7]
+ and a
+ jr nz, .determineGraphics
+ jr .startWalking
+
+.ridingBike
+ ; If the bike can't be used,
+ ; start walking instead.
+ call IsBikeRidingAllowed
+ jr c, .determineGraphics
+
+.startWalking
+ xor a
+ ld [wd700], a
+ ld [wd11a], a
+ jp LoadWalkingPlayerSpriteGraphics
+
+.determineGraphics
+ ld a, [wd700]
+ and a
+ jp z, LoadWalkingPlayerSpriteGraphics
+ dec a
+ jp z, LoadBikePlayerSpriteGraphics
+ dec a
+ jp z, LoadSurfingPlayerSpriteGraphics
+ jp LoadWalkingPlayerSpriteGraphics
+
+IsBikeRidingAllowed::
+; The bike can be used on Route 23 and Indigo Plateau,
+; or maps with tilesets in BikeRidingTilesets.
+; Return carry if biking is allowed.
+
+ ld a, [W_CURMAP]
+ cp ROUTE_23
+ jr z, .allowed
+ cp INDIGO_PLATEAU
+ jr z, .allowed
+
+ ld a, [W_CURMAPTILESET]
+ ld b, a
+ ld hl, BikeRidingTilesets
+.loop
+ ld a, [hli]
+ cp b
+ jr z, .allowed
+ inc a
+ jr nz, .loop
+ and a
+ ret
+
+.allowed
+ scf
+ ret
+
+INCLUDE "data/bike_riding_tilesets.asm"
+
+; load the tile pattern data of the current tileset into VRAM
+LoadTilesetTilePatternData:: ; 09e8 (0:09e8)
+ ld a,[W_TILESETGFXPTR]
+ ld l,a
+ ld a,[W_TILESETGFXPTR + 1]
+ ld h,a
+ ld de,vTileset
+ ld bc,$600
+ ld a,[W_TILESETBANK]
+ jp FarCopyData2
+
+; this loads the current maps complete tile map (which references blocks, not individual tiles) to C6E8
+; it can also load partial tile maps of connected maps into a border of length 3 around the current map
+LoadTileBlockMap:: ; 09fc (0:09fc)
+; fill C6E8-CBFB with the background tile
+ ld hl,wOverworldMap
+ ld a,[wd3ad] ; background tile number
+ ld d,a
+ ld bc,$0514
+.backgroundTileLoop
+ ld a,d
+ ld [hli],a
+ dec bc
+ ld a,c
+ or b
+ jr nz,.backgroundTileLoop
+; load tile map of current map (made of tile block IDs)
+; a 3-byte border at the edges of the map is kept so that there is space for map connections
+ ld hl,wOverworldMap
+ ld a,[W_CURMAPWIDTH]
+ ld [$ff8c],a
+ add a,$06 ; border (east and west)
+ ld [$ff8b],a ; map width + border
+ ld b,$00
+ ld c,a
+; make space for north border (next 3 lines)
+ add hl,bc
+ add hl,bc
+ add hl,bc
+ ld c,$03
+ add hl,bc ; this puts us past the (west) border
+ ld a,[W_MAPDATAPTR] ; tile map pointer
+ ld e,a
+ ld a,[W_MAPDATAPTR + 1]
+ ld d,a ; de = tile map pointer
+ ld a,[W_CURMAPHEIGHT]
+ ld b,a
+.rowLoop ; copy one row each iteration
+ push hl
+ ld a,[$ff8c] ; map width (without border)
+ ld c,a
+.rowInnerLoop
+ ld a,[de]
+ inc de
+ ld [hli],a
+ dec c
+ jr nz,.rowInnerLoop
+; add the map width plus the border to the base address of the current row to get the next row's address
+ pop hl
+ ld a,[$ff8b] ; map width + border
+ add l
+ ld l,a
+ jr nc,.noCarry
+ inc h
+.noCarry
+ dec b
+ jr nz,.rowLoop
+.northConnection
+ ld a,[W_MAPCONN1PTR]
+ cp a,$ff
+ jr z,.southConnection
+ call SwitchToMapRomBank
+ ld a,[wd372]
+ ld l,a
+ ld a,[wd373]
+ ld h,a
+ ld a,[wd374]
+ ld e,a
+ ld a,[wd375]
+ ld d,a
+ ld a,[wd376]
+ ld [$ff8b],a
+ ld a,[wd377]
+ ld [$ff8c],a
+ call LoadNorthSouthConnectionsTileMap
+.southConnection
+ ld a,[W_MAPCONN2PTR]
+ cp a,$ff
+ jr z,.westConnection
+ call SwitchToMapRomBank
+ ld a,[wd37d]
+ ld l,a
+ ld a,[wd37e]
+ ld h,a
+ ld a,[wd37f]
+ ld e,a
+ ld a,[wd380]
+ ld d,a
+ ld a,[wd381]
+ ld [$ff8b],a
+ ld a,[wd382]
+ ld [$ff8c],a
+ call LoadNorthSouthConnectionsTileMap
+.westConnection
+ ld a,[W_MAPCONN3PTR]
+ cp a,$ff
+ jr z,.eastConnection
+ call SwitchToMapRomBank
+ ld a,[wd388]
+ ld l,a
+ ld a,[wd389]
+ ld h,a
+ ld a,[wd38a]
+ ld e,a
+ ld a,[wd38b]
+ ld d,a
+ ld a,[wd38c]
+ ld b,a
+ ld a,[wd38d]
+ ld [$ff8b],a
+ call LoadEastWestConnectionsTileMap
+.eastConnection
+ ld a,[W_MAPCONN4PTR]
+ cp a,$ff
+ jr z,.done
+ call SwitchToMapRomBank
+ ld a,[wd393]
+ ld l,a
+ ld a,[wd394]
+ ld h,a
+ ld a,[wd395]
+ ld e,a
+ ld a,[wd396]
+ ld d,a
+ ld a,[wd397]
+ ld b,a
+ ld a,[wd398]
+ ld [$ff8b],a
+ call LoadEastWestConnectionsTileMap
+.done
+ ret
+
+LoadNorthSouthConnectionsTileMap:: ; 0ade (0:0ade)
+ ld c,$03
+.loop
+ push de
+ push hl
+ ld a,[$ff8b] ; width of connection
+ ld b,a
+.innerLoop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec b
+ jr nz,.innerLoop
+ pop hl
+ pop de
+ ld a,[$ff8c] ; width of connected map
+ add l
+ ld l,a
+ jr nc,.noCarry1
+ inc h
+.noCarry1
+ ld a,[W_CURMAPWIDTH]
+ add a,$06
+ add e
+ ld e,a
+ jr nc,.noCarry2
+ inc d
+.noCarry2
+ dec c
+ jr nz,.loop
+ ret
+
+LoadEastWestConnectionsTileMap:: ; 0b02 (0:0b02)
+ push hl
+ push de
+ ld c,$03
+.innerLoop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec c
+ jr nz,.innerLoop
+ pop de
+ pop hl
+ ld a,[$ff8b] ; width of connected map
+ add l
+ ld l,a
+ jr nc,.noCarry1
+ inc h
+.noCarry1
+ ld a,[W_CURMAPWIDTH]
+ add a,$06
+ add e
+ ld e,a
+ jr nc,.noCarry2
+ inc d
+.noCarry2
+ dec b
+ jr nz,LoadEastWestConnectionsTileMap
+ ret
+
+; function to check if there is a sign or sprite in front of the player
+; if so, it is stored in [$FF8C]
+; if not, [$FF8C] is set to 0
+IsSpriteOrSignInFrontOfPlayer:: ; 0b23 (0:0b23)
+ xor a
+ ld [$ff8c],a
+ ld a,[wd4b0] ; number of signs in the map
+ and a
+ jr z,.extendRangeOverCounter
+; if there are signs
+ predef Func_c586 ; get the coordinates in front of the player in de
+ ld hl,wd4b1 ; start of sign coordinates
+ ld a,[wd4b0] ; number of signs in the map
+ ld b,a
+ ld c,$00
+.signLoop
+ inc c
+ ld a,[hli] ; sign Y
+ cp d
+ jr z,.yCoordMatched
+ inc hl
+ jr .retry
+.yCoordMatched
+ ld a,[hli] ; sign X
+ cp e
+ jr nz,.retry
+.xCoordMatched
+; found sign
+ push hl
+ push bc
+ ld hl,wd4d1 ; start of sign text ID's
+ ld b,$00
+ dec c
+ add hl,bc
+ ld a,[hl]
+ ld [$ff8c],a ; store sign text ID
+ pop bc
+ pop hl
+ ret
+.retry
+ dec b
+ jr nz,.signLoop
+; check if the player is front of a counter in a pokemon center, pokemart, etc. and if so, extend the range at which he can talk to the NPC
+.extendRangeOverCounter
+ predef Func_c586 ; get the tile in front of the player in c
+ ld hl,W_TILESETTALKINGOVERTILES ; list of tiles that extend talking range (counter tiles)
+ ld b,$03
+ ld d,$20 ; talking range in pixels (long range)
+.counterTilesLoop
+ ld a,[hli]
+ cp c
+ jr z,IsSpriteInFrontOfPlayer2 ; jumps if the tile in front of the player is a counter tile
+ dec b
+ jr nz,.counterTilesLoop
+
+; part of the above function, but sometimes its called on its own, when signs are irrelevant
+; the caller must zero [$FF8C]
+IsSpriteInFrontOfPlayer:: ; 0b6b (0:0b6b)
+ ld d,$10 ; talking range in pixels (normal range)
+IsSpriteInFrontOfPlayer2:: ; 0b6d (0:0b6d)
+ ld bc,$3c40 ; Y and X position of player sprite
+ ld a,[wSpriteStateData1 + 9] ; direction the player is facing
+.checkIfPlayerFacingUp
+ cp a,$04
+ jr nz,.checkIfPlayerFacingDown
+; facing up
+ ld a,b
+ sub d
+ ld b,a
+ ld a,$08
+ jr .doneCheckingDirection
+.checkIfPlayerFacingDown
+ cp a,$00
+ jr nz,.checkIfPlayerFacingRight
+; facing down
+ ld a,b
+ add d
+ ld b,a
+ ld a,$04
+ jr .doneCheckingDirection
+.checkIfPlayerFacingRight
+ cp a,$0c
+ jr nz,.playerFacingLeft
+; facing right
+ ld a,c
+ add d
+ ld c,a
+ ld a,$01
+ jr .doneCheckingDirection
+.playerFacingLeft
+; facing left
+ ld a,c
+ sub d
+ ld c,a
+ ld a,$02
+.doneCheckingDirection
+ ld [wd52a],a
+ ld a,[W_NUMSPRITES] ; number of sprites
+ and a
+ ret z
+; if there are sprites
+ ld hl,wSpriteStateData1 + $10
+ ld d,a
+ ld e,$01
+.spriteLoop
+ push hl
+ ld a,[hli] ; image (0 if no sprite)
+ and a
+ jr z,.nextSprite
+ inc l
+ ld a,[hli] ; sprite visibility
+ inc a
+ jr z,.nextSprite
+ inc l
+ ld a,[hli] ; Y location
+ cp b
+ jr nz,.nextSprite
+ inc l
+ ld a,[hl] ; X location
+ cp c
+ jr z,.foundSpriteInFrontOfPlayer
+.nextSprite
+ pop hl
+ ld a,l
+ add a,$10
+ ld l,a
+ inc e
+ dec d
+ jr nz,.spriteLoop
+ ret
+.foundSpriteInFrontOfPlayer
+ pop hl
+ ld a,l
+ and a,$f0
+ inc a
+ ld l,a
+ set 7,[hl]
+ ld a,e
+ ld [$ff8c],a ; store sprite ID
+ ret
+
+; function to check if the player will jump down a ledge and check if the tile ahead is passable (when not surfing)
+; sets the carry flag if there is a collision, and unsets it if there isn't a collision
+CollisionCheckOnLand:: ; 0bd1 (0:0bd1)
+ ld a,[wd736]
+ bit 6,a ; is the player jumping?
+ jr nz,.noCollision
+; if not jumping a ledge
+ ld a,[wcd38]
+ and a
+ jr nz,.noCollision
+ ld a,[wd52a] ; the direction that the player is trying to go in
+ ld d,a
+ ld a,[wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code)
+ and d ; check if a sprite is in the direction the player is trying to go
+ jr nz,.collision
+ xor a
+ ld [$ff8c],a
+ call IsSpriteInFrontOfPlayer ; check for sprite collisions again? when does the above check fail to detect a sprite collision?
+ ld a,[$ff8c]
+ and a ; was there a sprite collision?
+ jr nz,.collision
+; if no sprite collision
+ ld hl,TilePairCollisionsLand
+ call CheckForJumpingAndTilePairCollisions
+ jr c,.collision
+ call CheckTilePassable
+ jr nc,.noCollision
+.collision
+ ld a,[wc02a]
+ cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing
+ jr z,.setCarry
+ ld a,(SFX_02_5b - SFX_Headers_02) / 3
+ call PlaySound ; play collision sound (if it's not already playing)
+.setCarry
+ scf
+ ret
+.noCollision
+ and a
+ ret
+
+; function that checks if the tile in front of the player is passable
+; clears carry if it is, sets carry if not
+CheckTilePassable:: ; 0c10 (0:0c10)
+ predef Func_c586 ; get tile in front of player
+ ld a,[wcfc6] ; tile in front of player
+ ld c,a
+ ld hl,W_TILESETCOLLISIONPTR ; pointer to list of passable tiles
+ ld a,[hli]
+ ld h,[hl]
+ ld l,a ; hl now points to passable tiles
+.loop
+ ld a,[hli]
+ cp a,$ff
+ jr z,.tileNotPassable
+ cp c
+ ret z
+ jr .loop
+.tileNotPassable
+ scf
+ ret
+
+; check if the player is going to jump down a small ledge
+; and check for collisions that only occur between certain pairs of tiles
+; Input: hl - address of directional collision data
+; sets carry if there is a collision and unsets carry if not
+CheckForJumpingAndTilePairCollisions:: ; 0c2a (0:0c2a)
+ push hl
+ predef Func_c586 ; get the tile in front of the player
+ push de
+ push bc
+ callba HandleLedges ; check if the player is trying to jump a ledge
+ pop bc
+ pop de
+ pop hl
+ and a
+ ld a,[wd736]
+ bit 6,a ; is the player jumping?
+ ret nz
+; if not jumping
+
+Func_c44:: ; 0c44 (0:0c44)
+ aCoord 8, 9 ; tile the player is on
+ ld [wcf0e],a
+
+CheckForTilePairCollisions:: ; 0c4a (0:0c4a)
+ ld a,[wcfc6] ; tile in front of the player
+ ld c,a
+.tilePairCollisionLoop
+ ld a,[W_CURMAPTILESET] ; tileset number
+ ld b,a
+ ld a,[hli]
+ cp a,$ff
+ jr z,.noMatch
+ cp b
+ jr z,.tilesetMatches
+ inc hl
+.retry
+ inc hl
+ jr .tilePairCollisionLoop
+.tilesetMatches
+ ld a,[wcf0e] ; tile the player is on
+ ld b,a
+ ld a,[hl]
+ cp b
+ jr z,.currentTileMatchesFirstInPair
+ inc hl
+ ld a,[hl]
+ cp b
+ jr z,.currentTileMatchesSecondInPair
+ jr .retry
+.currentTileMatchesFirstInPair
+ inc hl
+ ld a,[hl]
+ cp c
+ jr z,.foundMatch
+ jr .tilePairCollisionLoop
+.currentTileMatchesSecondInPair
+ dec hl
+ ld a,[hli]
+ cp c
+ inc hl
+ jr nz,.tilePairCollisionLoop
+.foundMatch
+ scf
+ ret
+.noMatch
+ and a
+ ret
+
+; FORMAT: tileset number, tile 1, tile 2
+; terminated by 0xFF
+; these entries indicate that the player may not cross between tile 1 and tile 2
+; it's mainly used to simulate differences in elevation
+
+TilePairCollisionsLand:: ; 0c7e (0:0c7e)
+ db CAVERN, $20, $05
+ db CAVERN, $41, $05
+ db FOREST, $30, $2E
+ db CAVERN, $2A, $05
+ db CAVERN, $05, $21
+ db FOREST, $52, $2E
+ db FOREST, $55, $2E
+ db FOREST, $56, $2E
+ db FOREST, $20, $2E
+ db FOREST, $5E, $2E
+ db FOREST, $5F, $2E
+ db $FF
+
+TilePairCollisionsWater:: ; 0ca0 (0:0ca0)
+ db FOREST, $14, $2E
+ db FOREST, $48, $2E
+ db CAVERN, $14, $05
+ db $FF
+
+; this builds a tile map from the tile block map based on the current X/Y coordinates of the player's character
+LoadCurrentMapView:: ; 0caa (0:0caa)
+ ld a,[H_LOADEDROMBANK]
+ push af
+ ld a,[W_TILESETBANK] ; tile data ROM bank
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a ; switch to ROM bank that contains tile data
+ ld a,[wd35f] ; address of upper left corner of current map view
+ ld e,a
+ ld a,[wd360]
+ ld d,a
+ ld hl,wTileMapBackup
+ ld b,$05
+.rowLoop ; each loop iteration fills in one row of tile blocks
+ push hl
+ push de
+ ld c,$06
+.rowInnerLoop ; loop to draw each tile block of the current row
+ push bc
+ push de
+ push hl
+ ld a,[de]
+ ld c,a ; tile block number
+ call DrawTileBlock
+ pop hl
+ pop de
+ pop bc
+ inc hl
+ inc hl
+ inc hl
+ inc hl
+ inc de
+ dec c
+ jr nz,.rowInnerLoop
+; update tile block map pointer to next row's address
+ pop de
+ ld a,[W_CURMAPWIDTH]
+ add a,$06
+ add e
+ ld e,a
+ jr nc,.noCarry
+ inc d
+.noCarry
+; update tile map pointer to next row's address
+ pop hl
+ ld a,$60
+ add l
+ ld l,a
+ jr nc,.noCarry2
+ inc h
+.noCarry2
+ dec b
+ jr nz,.rowLoop
+ ld hl,wTileMapBackup
+ ld bc,$0000
+.adjustForYCoordWithinTileBlock
+ ld a,[W_YBLOCKCOORD]
+ and a
+ jr z,.adjustForXCoordWithinTileBlock
+ ld bc,$0030
+ add hl,bc
+.adjustForXCoordWithinTileBlock
+ ld a,[W_XBLOCKCOORD]
+ and a
+ jr z,.copyToVisibleAreaBuffer
+ ld bc,$0002
+ add hl,bc
+.copyToVisibleAreaBuffer
+ ld de,wTileMap ; base address for the tiles that are directly transfered to VRAM during V-blank
+ ld b,$12
+.rowLoop2
+ ld c,$14
+.rowInnerLoop2
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec c
+ jr nz,.rowInnerLoop2
+ ld a,$04
+ add l
+ ld l,a
+ jr nc,.noCarry3
+ inc h
+.noCarry3
+ dec b
+ jr nz,.rowLoop2
+ pop af
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a ; restore previous ROM bank
+ ret
+
+AdvancePlayerSprite:: ; 0d27 (0:0d27)
+ ld a,[wSpriteStateData1 + 3] ; delta Y
+ ld b,a
+ ld a,[wSpriteStateData1 + 5] ; delta X
+ ld c,a
+ ld hl,wWalkCounter ; walking animation counter
+ dec [hl]
+ jr nz,.afterUpdateMapCoords
+; if it's the end of the animation, update the player's map coordinates
+ ld a,[W_YCOORD]
+ add b
+ ld [W_YCOORD],a
+ ld a,[W_XCOORD]
+ add c
+ ld [W_XCOORD],a
+.afterUpdateMapCoords
+ ld a,[wWalkCounter] ; walking animation counter
+ cp a,$07
+ jp nz,.scrollBackgroundAndSprites
+; if this is the first iteration of the animation
+ ld a,c
+ cp a,$01
+ jr nz,.checkIfMovingWest
+; moving east
+ ld a,[wd526]
+ ld e,a
+ and a,$e0
+ ld d,a
+ ld a,e
+ add a,$02
+ and a,$1f
+ or d
+ ld [wd526],a
+ jr .adjustXCoordWithinBlock
+.checkIfMovingWest
+ cp a,$ff
+ jr nz,.checkIfMovingSouth
+; moving west
+ ld a,[wd526]
+ ld e,a
+ and a,$e0
+ ld d,a
+ ld a,e
+ sub a,$02
+ and a,$1f
+ or d
+ ld [wd526],a
+ jr .adjustXCoordWithinBlock
+.checkIfMovingSouth
+ ld a,b
+ cp a,$01
+ jr nz,.checkIfMovingNorth
+; moving south
+ ld a,[wd526]
+ add a,$40
+ ld [wd526],a
+ jr nc,.adjustXCoordWithinBlock
+ ld a,[wd527]
+ inc a
+ and a,$03
+ or a,$98
+ ld [wd527],a
+ jr .adjustXCoordWithinBlock
+.checkIfMovingNorth
+ cp a,$ff
+ jr nz,.adjustXCoordWithinBlock
+; moving north
+ ld a,[wd526]
+ sub a,$40
+ ld [wd526],a
+ jr nc,.adjustXCoordWithinBlock
+ ld a,[wd527]
+ dec a
+ and a,$03
+ or a,$98
+ ld [wd527],a
+.adjustXCoordWithinBlock
+ ld a,c
+ and a
+ jr z,.pointlessJump ; mistake?
+.pointlessJump
+ ld hl,W_XBLOCKCOORD
+ ld a,[hl]
+ add c
+ ld [hl],a
+ cp a,$02
+ jr nz,.checkForMoveToWestBlock
+; moved into the tile block to the east
+ xor a
+ ld [hl],a
+ ld hl,wd4e3
+ inc [hl]
+ ld de,wd35f
+ call MoveTileBlockMapPointerEast
+ jr .updateMapView
+.checkForMoveToWestBlock
+ cp a,$ff
+ jr nz,.adjustYCoordWithinBlock
+; moved into the tile block to the west
+ ld a,$01
+ ld [hl],a
+ ld hl,wd4e3
+ dec [hl]
+ ld de,wd35f
+ call MoveTileBlockMapPointerWest
+ jr .updateMapView
+.adjustYCoordWithinBlock
+ ld hl,W_YBLOCKCOORD
+ ld a,[hl]
+ add b
+ ld [hl],a
+ cp a,$02
+ jr nz,.checkForMoveToNorthBlock
+; moved into the tile block to the south
+ xor a
+ ld [hl],a
+ ld hl,wd4e2
+ inc [hl]
+ ld de,wd35f
+ ld a,[W_CURMAPWIDTH]
+ call MoveTileBlockMapPointerSouth
+ jr .updateMapView
+.checkForMoveToNorthBlock
+ cp a,$ff
+ jr nz,.updateMapView
+; moved into the tile block to the north
+ ld a,$01
+ ld [hl],a
+ ld hl,wd4e2
+ dec [hl]
+ ld de,wd35f
+ ld a,[W_CURMAPWIDTH]
+ call MoveTileBlockMapPointerNorth
+.updateMapView
+ call LoadCurrentMapView
+ ld a,[wSpriteStateData1 + 3] ; delta Y
+ cp a,$01
+ jr nz,.checkIfMovingNorth2
+; if moving south
+ call ScheduleSouthRowRedraw
+ jr .scrollBackgroundAndSprites
+.checkIfMovingNorth2
+ cp a,$ff
+ jr nz,.checkIfMovingEast2
+; if moving north
+ call ScheduleNorthRowRedraw
+ jr .scrollBackgroundAndSprites
+.checkIfMovingEast2
+ ld a,[wSpriteStateData1 + 5] ; delta X
+ cp a,$01
+ jr nz,.checkIfMovingWest2
+; if moving east
+ call ScheduleEastColumnRedraw
+ jr .scrollBackgroundAndSprites
+.checkIfMovingWest2
+ cp a,$ff
+ jr nz,.scrollBackgroundAndSprites
+; if moving west
+ call ScheduleWestColumnRedraw
+.scrollBackgroundAndSprites
+ ld a,[wSpriteStateData1 + 3] ; delta Y
+ ld b,a
+ ld a,[wSpriteStateData1 + 5] ; delta X
+ ld c,a
+ sla b
+ sla c
+ ld a,[$ffaf]
+ add b
+ ld [$ffaf],a ; update background scroll Y
+ ld a,[$ffae]
+ add c
+ ld [$ffae],a ; update background scroll X
+; shift all the sprites in the direction opposite of the player's motion
+; so that the player appears to move relative to them
+ ld hl,wSpriteStateData1 + $14
+ ld a,[W_NUMSPRITES] ; number of sprites
+ and a ; are there any sprites?
+ jr z,.done
+ ld e,a
+.spriteShiftLoop
+ ld a,[hl]
+ sub b
+ ld [hli],a
+ inc l
+ ld a,[hl]
+ sub c
+ ld [hl],a
+ ld a,$0e
+ add l
+ ld l,a
+ dec e
+ jr nz,.spriteShiftLoop
+.done
+ ret
+
+; the following four functions are used to move the pointer to the upper left
+; corner of the tile block map in the direction of motion
+
+MoveTileBlockMapPointerEast:: ; 0e65 (0:0e65)
+ ld a,[de]
+ add a,$01
+ ld [de],a
+ ret nc
+ inc de
+ ld a,[de]
+ inc a
+ ld [de],a
+ ret
+
+MoveTileBlockMapPointerWest:: ; 0e6f (0:0e6f)
+ ld a,[de]
+ sub a,$01
+ ld [de],a
+ ret nc
+ inc de
+ ld a,[de]
+ dec a
+ ld [de],a
+ ret
+
+MoveTileBlockMapPointerSouth:: ; 0e79 (0:0e79)
+ add a,$06
+ ld b,a
+ ld a,[de]
+ add b
+ ld [de],a
+ ret nc
+ inc de
+ ld a,[de]
+ inc a
+ ld [de],a
+ ret
+
+MoveTileBlockMapPointerNorth:: ; 0e85 (0:0e85)
+ add a,$06
+ ld b,a
+ ld a,[de]
+ sub b
+ ld [de],a
+ ret nc
+ inc de
+ ld a,[de]
+ dec a
+ ld [de],a
+ ret
+
+; the following 6 functions are used to tell the V-blank handler to redraw
+; the portion of the map that was newly exposed due to the player's movement
+
+ScheduleNorthRowRedraw:: ; 0e91 (0:0e91)
+ hlCoord 0, 0
+ call ScheduleRowRedrawHelper
+ ld a,[wd526]
+ ld [H_SCREENEDGEREDRAWADDR],a
+ ld a,[wd527]
+ ld [H_SCREENEDGEREDRAWADDR + 1],a
+ ld a,REDRAWROW
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+
+ScheduleRowRedrawHelper:: ; 0ea6 (0:0ea6)
+ ld de,wScreenEdgeTiles
+ ld c,$28
+.loop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec c
+ jr nz,.loop
+ ret
+
+ScheduleSouthRowRedraw:: ; 0eb2 (0:0eb2)
+ hlCoord 0, 16
+ call ScheduleRowRedrawHelper
+ ld a,[wd526]
+ ld l,a
+ ld a,[wd527]
+ ld h,a
+ ld bc,$0200
+ add hl,bc
+ ld a,h
+ and a,$03
+ or a,$98
+ ld [H_SCREENEDGEREDRAWADDR + 1],a
+ ld a,l
+ ld [H_SCREENEDGEREDRAWADDR],a
+ ld a,REDRAWROW
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+
+ScheduleEastColumnRedraw:: ; 0ed3 (0:0ed3)
+ hlCoord 18, 0
+ call ScheduleColumnRedrawHelper
+ ld a,[wd526]
+ ld c,a
+ and a,$e0
+ ld b,a
+ ld a,c
+ add a,18
+ and a,$1f
+ or b
+ ld [H_SCREENEDGEREDRAWADDR],a
+ ld a,[wd527]
+ ld [H_SCREENEDGEREDRAWADDR + 1],a
+ ld a,REDRAWCOL
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+
+ScheduleColumnRedrawHelper:: ; 0ef2 (0:0ef2)
+ ld de,wScreenEdgeTiles
+ ld c,$12
+.loop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hl]
+ ld [de],a
+ inc de
+ ld a,19
+ add l
+ ld l,a
+ jr nc,.noCarry
+ inc h
+.noCarry
+ dec c
+ jr nz,.loop
+ ret
+
+ScheduleWestColumnRedraw:: ; 0f08 (0:0f08)
+ hlCoord 0, 0
+ call ScheduleColumnRedrawHelper
+ ld a,[wd526]
+ ld [H_SCREENEDGEREDRAWADDR],a
+ ld a,[wd527]
+ ld [H_SCREENEDGEREDRAWADDR + 1],a
+ ld a,REDRAWCOL
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+
+; function to write the tiles that make up a tile block to memory
+; Input: c = tile block ID, hl = destination address
+DrawTileBlock:: ; 0f1d (0:0f1d)
+ push hl
+ ld a,[W_TILESETBLOCKSPTR] ; pointer to tiles
+ ld l,a
+ ld a,[W_TILESETBLOCKSPTR + 1]
+ ld h,a
+ ld a,c
+ swap a
+ ld b,a
+ and a,$f0
+ ld c,a
+ ld a,b
+ and a,$0f
+ ld b,a ; bc = tile block ID * 0x10
+ add hl,bc
+ ld d,h
+ ld e,l ; de = address of the tile block's tiles
+ pop hl
+ ld c,$04 ; 4 loop iterations
+.loop ; each loop iteration, write 4 tile numbers
+ push bc
+ ld a,[de]
+ ld [hli],a
+ inc de
+ ld a,[de]
+ ld [hli],a
+ inc de
+ ld a,[de]
+ ld [hli],a
+ inc de
+ ld a,[de]
+ ld [hl],a
+ inc de
+ ld bc,$0015
+ add hl,bc
+ pop bc
+ dec c
+ jr nz,.loop
+ ret
+
+; function to update joypad state and simulate button presses
+JoypadOverworld:: ; 0f4d (0:0f4d)
+ xor a
+ ld [wSpriteStateData1 + 3],a
+ ld [wSpriteStateData1 + 5],a
+ call RunMapScript
+ call Joypad
+ ld a,[W_FLAGS_D733]
+ bit 3,a ; check if a trainer wants a challenge
+ jr nz,.notForcedDownwards
+ ld a,[W_CURMAP]
+ cp a,ROUTE_17 ; Cycling Road
+ jr nz,.notForcedDownwards
+ ld a,[hJoyHeld] ; current joypad state
+ and a,%11110011 ; bit mask for all directions and A/B
+ jr nz,.notForcedDownwards
+ ld a,%10000000 ; down pressed
+ ld [hJoyHeld],a ; on the cycling road, if there isn't a trainer and the player isn't pressing buttons, simulate a down press
+.notForcedDownwards
+ ld a,[wd730]
+ bit 7,a
+ ret z
+; if simulating button presses
+ ld a,[hJoyHeld] ; current joypad state
+ ld b,a
+ ld a,[wcd3b] ; bit mask for button presses that override simulated ones
+ and b
+ ret nz ; return if the simulated button presses are overridden
+ ld hl,wcd38 ; index of current simulated button press
+ dec [hl]
+ ld a,[hl]
+ cp a,$ff
+ jr z,.doneSimulating ; if the end of the simulated button presses has been reached
+ ld hl,wccd3 ; base address of simulated button presses
+; add offset to base address
+ add l
+ ld l,a
+ jr nc,.noCarry
+ inc h
+.noCarry
+ ld a,[hl]
+ ld [hJoyHeld],a ; store simulated button press in joypad state
+ and a
+ ret nz
+ ld [hJoyPressed],a
+ ld [hJoyReleased],a
+ ret
+; if done simulating button presses
+.doneSimulating
+ xor a
+ ld [wcd3a],a
+ ld [wcd38],a
+ ld [wccd3],a
+ ld [wJoyIgnore],a
+ ld [hJoyHeld],a
+ ld hl,wd736
+ ld a,[hl]
+ and a,$f8
+ ld [hl],a
+ ld hl,wd730
+ res 7,[hl]
+ ret
+
+; function to check the tile ahead to determine if the character should get on land or keep surfing
+; sets carry if there is a collision and clears carry otherwise
+; It seems that this function has a bug in it, but due to luck, it doesn't
+; show up. After detecting a sprite collision, it jumps to the code that
+; checks if the next tile is passable instead of just directly jumping to the
+; "collision detected" code. However, it doesn't store the next tile in c,
+; so the old value of c is used. 2429 is always called before this function,
+; and 2429 always sets c to 0xF0. There is no 0xF0 background tile, so it
+; is considered impassable and it is detected as a collision.
+CollisionCheckOnWater:: ; 0fb7 (0:0fb7)
+ ld a,[wd730]
+ bit 7,a
+ jp nz,.noCollision ; return and clear carry if button presses are being simulated
+ ld a,[wd52a] ; the direction that the player is trying to go in
+ ld d,a
+ ld a,[wSpriteStateData1 + 12] ; the player sprite's collision data (bit field) (set in the sprite movement code)
+ and d ; check if a sprite is in the direction the player is trying to go
+ jr nz,.checkIfNextTileIsPassable ; bug?
+ ld hl,TilePairCollisionsWater
+ call CheckForJumpingAndTilePairCollisions
+ jr c,.collision
+ predef Func_c586 ; get tile in front of player (puts it in c and [wcfc6])
+ ld a,[wcfc6] ; tile in front of player
+ cp a,$14 ; water tile
+ jr z,.noCollision ; keep surfing if it's a water tile
+ cp a,$32 ; either the left tile of the S.S. Anne boarding platform or the tile on eastern coastlines (depending on the current tileset)
+ jr z,.checkIfVermilionDockTileset
+ cp a,$48 ; tile on right on coast lines in Safari Zone
+ jr z,.noCollision ; keep surfing
+; check if the [land] tile in front of the player is passable
+.checkIfNextTileIsPassable
+ ld hl,W_TILESETCOLLISIONPTR ; pointer to list of passable tiles
+ ld a,[hli]
+ ld h,[hl]
+ ld l,a
+.loop
+ ld a,[hli]
+ cp a,$ff
+ jr z,.collision
+ cp c
+ jr z,.stopSurfing ; stop surfing if the tile is passable
+ jr .loop
+.collision
+ ld a,[wc02a]
+ cp a,(SFX_02_5b - SFX_Headers_02) / 3 ; check if collision sound is already playing
+ jr z,.setCarry
+ ld a,(SFX_02_5b - SFX_Headers_02) / 3
+ call PlaySound ; play collision sound (if it's not already playing)
+.setCarry
+ scf
+ jr .done
+.noCollision
+ and a
+.done
+ ret
+.stopSurfing
+ xor a
+ ld [wd700],a
+ call LoadPlayerSpriteGraphics
+ call Func_2307
+ jr .noCollision
+.checkIfVermilionDockTileset
+ ld a, [W_CURMAPTILESET] ; tileset
+ cp SHIP_PORT ; Vermilion Dock tileset
+ jr nz, .noCollision ; keep surfing if it's not the boarding platform tile
+ jr .stopSurfing ; if it is the boarding platform tile, stop surfing
+
+; function to run the current map's script
+RunMapScript:: ; 101b (0:101b)
+ push hl
+ push de
+ push bc
+ callba Func_f225 ; check if the player is pushing a boulder
+ ld a,[wFlags_0xcd60]
+ bit 1,a ; is the player pushing a boulder?
+ jr z,.afterBoulderEffect
+ callba Func_f2b5 ; displays dust effect when pushing a boulder
+.afterBoulderEffect
+ pop bc
+ pop de
+ pop hl
+ call Func_310e
+ ld a,[W_CURMAP] ; current map number
+ call SwitchToMapRomBank ; change to the ROM bank the map's data is in
+ ld hl,W_MAPSCRIPTPTR
+ ld a,[hli]
+ ld h,[hl]
+ ld l,a
+ ld de,.return
+ push de
+ jp [hl] ; jump to script
+.return
+ ret
+
+LoadWalkingPlayerSpriteGraphics:: ; 104d (0:104d)
+ ld de,RedSprite ; $4180
+ ld hl,vNPCSprites
+ jr LoadPlayerSpriteGraphicsCommon
+
+LoadSurfingPlayerSpriteGraphics:: ; 1055 (0:1055)
+ ld de,SeelSprite
+ ld hl,vNPCSprites
+ jr LoadPlayerSpriteGraphicsCommon
+
+LoadBikePlayerSpriteGraphics:: ; 105d (0:105d)
+ ld de,RedCyclingSprite
+ ld hl,vNPCSprites
+
+LoadPlayerSpriteGraphicsCommon:: ; 1063 (0:1063)
+ push de
+ push hl
+ ld bc,(BANK(RedSprite) << 8) + $0c
+ call CopyVideoData
+ pop hl
+ pop de
+ ld a,$c0
+ add e
+ ld e,a
+ jr nc,.noCarry
+ inc d
+.noCarry
+ set 3,h
+ ld bc,$050c
+ jp CopyVideoData
+
+; function to load data from the map header
+LoadMapHeader:: ; 107c (0:107c)
+ callba Func_f113
+ ld a,[W_CURMAPTILESET]
+ ld [wd119],a
+ ld a,[W_CURMAP]
+ call SwitchToMapRomBank
+ ld a,[W_CURMAPTILESET]
+ ld b,a
+ res 7,a
+ ld [W_CURMAPTILESET],a
+ ld [$ff8b],a
+ bit 7,b
+ ret nz
+ ld hl,MapHeaderPointers
+ ld a,[W_CURMAP]
+ sla a
+ jr nc,.noCarry1
+ inc h
+.noCarry1
+ add l
+ ld l,a
+ jr nc,.noCarry2
+ inc h
+.noCarry2
+ ld a,[hli]
+ ld h,[hl]
+ ld l,a ; hl = base of map header
+; copy the first 10 bytes (the fixed area) of the map data to D367-D370
+ ld de,W_CURMAPTILESET
+ ld c,$0a
+.copyFixedHeaderLoop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec c
+ jr nz,.copyFixedHeaderLoop
+; initialize all the connected maps to disabled at first, before loading the actual values
+ ld a,$ff
+ ld [W_MAPCONN1PTR],a
+ ld [W_MAPCONN2PTR],a
+ ld [W_MAPCONN3PTR],a
+ ld [W_MAPCONN4PTR],a
+; copy connection data (if any) to WRAM
+ ld a,[W_MAPCONNECTIONS]
+ ld b,a
+.checkNorth
+ bit 3,b
+ jr z,.checkSouth
+ ld de,W_MAPCONN1PTR
+ call CopyMapConnectionHeader
+.checkSouth
+ bit 2,b
+ jr z,.checkWest
+ ld de,W_MAPCONN2PTR
+ call CopyMapConnectionHeader
+.checkWest
+ bit 1,b
+ jr z,.checkEast
+ ld de,W_MAPCONN3PTR
+ call CopyMapConnectionHeader
+.checkEast
+ bit 0,b
+ jr z,.getObjectDataPointer
+ ld de,W_MAPCONN4PTR
+ call CopyMapConnectionHeader
+.getObjectDataPointer
+ ld a,[hli]
+ ld [wd3a9],a
+ ld a,[hli]
+ ld [wd3aa],a
+ push hl
+ ld a,[wd3a9]
+ ld l,a
+ ld a,[wd3aa]
+ ld h,a ; hl = base of object data
+ ld de,wd3ad ; background tile ID
+ ld a,[hli]
+ ld [de],a ; save background tile ID
+.loadWarpData
+ ld a,[hli] ; number of warps
+ ld [wd3ae],a ; save the number of warps
+ and a ; are there any warps?
+ jr z,.loadSignData ; if not, skip this
+ ld c,a
+ ld de,wd3af ; base address of warps
+.warpLoop ; one warp per loop iteration
+ ld b,$04
+.warpInnerLoop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec b
+ jr nz,.warpInnerLoop
+ dec c
+ jr nz,.warpLoop
+.loadSignData
+ ld a,[hli] ; number of signs
+ ld [wd4b0],a ; save the number of signs
+ and a ; are there any signs?
+ jr z,.loadSpriteData ; if not, skip this
+ ld c,a
+ ld de,wd4d1 ; base address of sign text IDs
+ ld a,d
+ ld [$ff95],a
+ ld a,e
+ ld [$ff96],a
+ ld de,wd4b1 ; base address of sign coordinates
+.signLoop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hli]
+ ld [de],a
+ inc de
+ push de
+ ld a,[$ff95]
+ ld d,a
+ ld a,[$ff96]
+ ld e,a
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,d
+ ld [$ff95],a
+ ld a,e
+ ld [$ff96],a
+ pop de
+ dec c
+ jr nz,.signLoop
+.loadSpriteData
+ ld a,[wd72e]
+ bit 5,a ; did a battle happen immediately before this?
+ jp nz,.finishUp ; if so, skip this because battles don't destroy this data
+ ld a,[hli]
+ ld [W_NUMSPRITES],a ; save the number of sprites
+ push hl
+; zero C110-C1FF and C210-C2FF
+ ld hl,wSpriteStateData1 + $10
+ ld de,wSpriteStateData2 + $10
+ xor a
+ ld b,$f0
+.zeroSpriteDataLoop
+ ld [hli],a
+ ld [de],a
+ inc e
+ dec b
+ jr nz,.zeroSpriteDataLoop
+; initialize all C100-C1FF sprite entries to disabled (other than player's)
+ ld hl,wSpriteStateData1 + $12
+ ld de,$0010
+ ld c,$0f
+.disableSpriteEntriesLoop
+ ld [hl],$ff
+ add hl,de
+ dec c
+ jr nz,.disableSpriteEntriesLoop
+ pop hl
+ ld de,wSpriteStateData1 + $10
+ ld a,[W_NUMSPRITES] ; number of sprites
+ and a ; are there any sprites?
+ jp z,.finishUp ; if there are no sprites, skip the rest
+ ld b,a
+ ld c,$00
+.loadSpriteLoop
+ ld a,[hli]
+ ld [de],a ; store picture ID at C1X0
+ inc d
+ ld a,$04
+ add e
+ ld e,a
+ ld a,[hli]
+ ld [de],a ; store Y position at C2X4
+ inc e
+ ld a,[hli]
+ ld [de],a ; store X position at C2X5
+ inc e
+ ld a,[hli]
+ ld [de],a ; store movement byte 1 at C2X6
+ ld a,[hli]
+ ld [$ff8d],a ; save movement byte 2
+ ld a,[hli]
+ ld [$ff8e],a ; save text ID and flags byte
+ push bc
+ push hl
+ ld b,$00
+ ld hl,W_MAPSPRITEDATA
+ add hl,bc
+ ld a,[$ff8d]
+ ld [hli],a ; store movement byte 2 in byte 0 of sprite entry
+ ld a,[$ff8e]
+ ld [hl],a ; this appears pointless, since the value is overwritten immediately after
+ ld a,[$ff8e]
+ ld [$ff8d],a
+ and a,$3f
+ ld [hl],a ; store text ID in byte 1 of sprite entry
+ pop hl
+ ld a,[$ff8d]
+ bit 6,a
+ jr nz,.trainerSprite
+ bit 7,a
+ jr nz,.itemBallSprite
+ jr .regularSprite
+.trainerSprite
+ ld a,[hli]
+ ld [$ff8d],a ; save trainer class
+ ld a,[hli]
+ ld [$ff8e],a ; save trainer number (within class)
+ push hl
+ ld hl,W_MAPSPRITEEXTRADATA
+ add hl,bc
+ ld a,[$ff8d]
+ ld [hli],a ; store trainer class in byte 0 of the entry
+ ld a,[$ff8e]
+ ld [hl],a ; store trainer number in byte 1 of the entry
+ pop hl
+ jr .nextSprite
+.itemBallSprite
+ ld a,[hli]
+ ld [$ff8d],a ; save item number
+ push hl
+ ld hl,W_MAPSPRITEEXTRADATA
+ add hl,bc
+ ld a,[$ff8d]
+ ld [hli],a ; store item number in byte 0 of the entry
+ xor a
+ ld [hl],a ; zero byte 1, since it is not used
+ pop hl
+ jr .nextSprite
+.regularSprite
+ push hl
+ ld hl,W_MAPSPRITEEXTRADATA
+ add hl,bc
+; zero both bytes, since regular sprites don't use this extra space
+ xor a
+ ld [hli],a
+ ld [hl],a
+ pop hl
+.nextSprite
+ pop bc
+ dec d
+ ld a,$0a
+ add e
+ ld e,a
+ inc c
+ inc c
+ dec b
+ jp nz,.loadSpriteLoop
+.finishUp
+ predef Func_c754 ; load tileset data
+ callab LoadWildData ; load wild pokemon data
+ pop hl ; restore hl from before going to the warp/sign/sprite data (this value was saved for seemingly no purpose)
+ ld a,[W_CURMAPHEIGHT] ; map height in 4x4 tile blocks
+ add a ; double it
+ ld [wd524],a ; store map height in 2x2 tile blocks
+ ld a,[W_CURMAPWIDTH] ; map width in 4x4 tile blocks
+ add a ; double it
+ ld [wd525],a ; map width in 2x2 tile blocks
+ ld a,[W_CURMAP]
+ ld c,a
+ ld b,$00
+ ld a,[H_LOADEDROMBANK]
+ push af
+ ld a, BANK(MapSongBanks)
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a
+ ld hl, MapSongBanks
+ add hl,bc
+ add hl,bc
+ ld a,[hli]
+ ld [wd35b],a ; music 1
+ ld a,[hl]
+ ld [wd35c],a ; music 2
+ pop af
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a
+ ret
+
+; function to copy map connection data from ROM to WRAM
+; Input: hl = source, de = destination
+CopyMapConnectionHeader:: ; 1238 (0:1238)
+ ld c,$0b
+.loop
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec c
+ jr nz,.loop
+ ret
+
+; function to load map data
+LoadMapData:: ; 1241 (0:1241)
+ ld a,[H_LOADEDROMBANK]
+ push af
+ call DisableLCD
+ ld a,$98
+ ld [wd527],a
+ xor a
+ ld [wd526],a
+ ld [$ffaf],a
+ ld [$ffae],a
+ ld [wWalkCounter],a
+ ld [wd119],a
+ ld [wd11a],a
+ ld [W_SPRITESETID],a
+ call LoadTextBoxTilePatterns
+ call LoadMapHeader
+ callba InitMapSprites ; load tile pattern data for sprites
+ call LoadTileBlockMap
+ call LoadTilesetTilePatternData
+ call LoadCurrentMapView
+; copy current map view to VRAM
+ ld hl,wTileMap
+ ld de,vBGMap0
+ ld b,18
+.vramCopyLoop
+ ld c,20
+.vramCopyInnerLoop
+ ld a,[hli]
+ ld [de],a
+ inc e
+ dec c
+ jr nz,.vramCopyInnerLoop
+ ld a,32 - 20
+ add e
+ ld e,a
+ jr nc,.noCarry
+ inc d
+.noCarry
+ dec b
+ jr nz,.vramCopyLoop
+ ld a,$01
+ ld [wcfcb],a
+ call EnableLCD
+ ld b,$09
+ call GoPAL_SET
+ call LoadPlayerSpriteGraphics
+ ld a,[wd732]
+ and a,$18 ; did the player fly or teleport in?
+ jr nz,.restoreRomBank
+ ld a,[W_FLAGS_D733]
+ bit 1,a
+ jr nz,.restoreRomBank
+ call Func_235f ; music related
+ call Func_2312 ; music related
+.restoreRomBank
+ pop af
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a
+ ret
+
+; function to switch to the ROM bank that a map is stored in
+; Input: a = map number
+SwitchToMapRomBank:: ; 12bc (0:12bc)
+ push hl
+ push bc
+ ld c,a
+ ld b,$00
+ ld a,Bank(MapHeaderBanks)
+ call BankswitchHome ; switch to ROM bank 3
+ ld hl,MapHeaderBanks
+ add hl,bc
+ ld a,[hl]
+ ld [$ffe8],a ; save map ROM bank
+ call BankswitchBack
+ ld a,[$ffe8]
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a ; switch to map ROM bank
+ pop bc
+ pop hl
+ ret
+
+Func_12da:: ; 12da (0:12da)
+ ld a, $1e
+ ld [wd13a], a
+ ld hl, wd730
+ ld a, [hl]
+ or $26
+ ld [hl], a
+ ret
+
+Func_12e7:: ; 12e7 (0:12e7)
+ ld hl, wd728
+ res 0, [hl]
+ ret
+
+ForceBikeOrSurf:: ; 12ed (0:12ed)
+ ld b, BANK(RedSprite)
+ ld hl, LoadPlayerSpriteGraphics
+ call Bankswitch
+ jp Func_2307 ; update map/player state?
diff --git a/home/pic.asm b/home/pic.asm
new file mode 100644
index 00000000..6aa2e5c0
--- /dev/null
+++ b/home/pic.asm
@@ -0,0 +1,591 @@
+; bankswitches and runs _UncompressSpriteData
+; bank is given in a, sprite input stream is pointed to in W_SPRITEINPUTPTR
+UncompressSpriteData:: ; 24fd (0:24fd)
+ ld b, a
+ ld a, [H_LOADEDROMBANK]
+ push af
+ ld a, b
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ld a, $a
+ ld [$0], a
+ xor a
+ ld [$4000], a
+ call _UncompressSpriteData
+ pop af
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ret
+
+; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop
+_UncompressSpriteData:: ; 251a (0:251a)
+ ld hl, S_SPRITEBUFFER1
+ ld c, (2*SPRITEBUFFERSIZE) % $100
+ ld b, (2*SPRITEBUFFERSIZE) / $100
+ xor a
+ call FillMemory ; clear sprite buffer 1 and 2
+ ld a, $1
+ ld [W_SPRITEINPUTBITCOUNTER], a
+ ld a, $3
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ ld [W_SPRITELOADFLAGS], a ; wd0a8
+ call ReadNextInputByte ; first byte of input determines sprite width (high nybble) and height (low nybble) in tiles (8x8 pixels)
+ ld b, a
+ and $f
+ add a
+ add a
+ add a
+ ld [W_SPRITEHEIGHT], a
+ ld a, b
+ swap a
+ and $f
+ add a
+ add a
+ add a
+ ld [W_SPRITEWITDH], a
+ call ReadNextInputBit
+ ld [W_SPRITELOADFLAGS], a ; initialite bit1 to 0 and bit0 to the first input bit
+ ; this will load two chunks of data to S_SPRITEBUFFER1 and S_SPRITEBUFFER2
+ ; bit 0 decides in which one the first chunk is placed
+ ; fall through
+
+; uncompresses a chunk from the sprite input data stream (pointed to at wd0da) into S_SPRITEBUFFER1 or S_SPRITEBUFFER2
+; each chunk is a 1bpp sprite. A 2bpp sprite consist of two chunks which are merged afterwards
+; note that this is an endless loop which is terminated during a call to MoveToNextBufferPosition by manipulating the stack
+UncompressSpriteDataLoop:: ; 2556 (0:2556)
+ ld hl, S_SPRITEBUFFER1
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 0, a
+ jr z, .useSpriteBuffer1 ; check which buffer to use
+ ld hl, S_SPRITEBUFFER2
+.useSpriteBuffer1
+ call StoreSpriteOutputPointer
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 1, a
+ jr z, .startDecompression ; check if last iteration
+ call ReadNextInputBit ; if last chunk, read 1-2 bit unpacking mode
+ and a
+ jr z, .unpackingMode0 ; 0 -> mode 0
+ call ReadNextInputBit ; 1 0 -> mode 1
+ inc a ; 1 1 -> mode 2
+.unpackingMode0
+ ld [W_SPRITEUNPACKMODE], a
+.startDecompression
+ call ReadNextInputBit
+ and a
+ jr z, .readRLEncodedZeros ; if first bit is 0, the input starts with zeroes, otherwise with (non-zero) input
+.readNextInput
+ call ReadNextInputBit
+ ld c, a
+ call ReadNextInputBit
+ sla c
+ or c ; read next two bits into c
+ and a
+ jr z, .readRLEncodedZeros ; 00 -> RLEncoded zeroes following
+ call WriteSpriteBitsToBuffer ; otherwise write input to output and repeat
+ call MoveToNextBufferPosition
+ jr .readNextInput
+.readRLEncodedZeros
+ ld c, $0 ; number of zeroes it length encoded, the number
+.countConsecutiveOnesLoop ; of consecutive ones determines the number of bits the number has
+ call ReadNextInputBit
+ and a
+ jr z, .countConsecutiveOnesFinished
+ inc c
+ jr .countConsecutiveOnesLoop
+.countConsecutiveOnesFinished
+ ld a, c
+ add a
+ ld hl, LengthEncodingOffsetList
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [hli] ; read offset that is added to the number later on
+ ld e, a ; adding an offset of 2^length - 1 makes every integer uniquely
+ ld d, [hl] ; representable in the length encoding and saves bits
+ push de
+ inc c
+ ld e, $0
+ ld d, e
+.readNumberOfZerosLoop ; reads the next c+1 bits of input
+ call ReadNextInputBit
+ or e
+ ld e, a
+ dec c
+ jr z, .readNumberOfZerosDone
+ sla e
+ rl d
+ jr .readNumberOfZerosLoop
+.readNumberOfZerosDone
+ pop hl ; add the offset
+ add hl, de
+ ld e, l
+ ld d, h
+.writeZerosLoop
+ ld b, e
+ xor a ; write 00 to buffer
+ call WriteSpriteBitsToBuffer
+ ld e, b
+ call MoveToNextBufferPosition
+ dec de
+ ld a, d
+ and a
+ jr nz, .continueLoop
+ ld a, e
+ and a
+.continueLoop
+ jr nz, .writeZerosLoop
+ jr .readNextInput
+
+; moves output pointer to next position
+; also cancels the calling function if the all output is done (by removing the return pointer from stack)
+; and calls postprocessing functions according to the unpack mode
+MoveToNextBufferPosition:: ; 25d8 (0:25d8)
+ ld a, [W_SPRITEHEIGHT]
+ ld b, a
+ ld a, [W_SPRITECURPOSY]
+ inc a
+ cp b
+ jr z, .curColumnDone
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITEOUTPUTPTR]
+ inc a
+ ld [W_SPRITEOUTPUTPTR], a
+ ret nz
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ inc a
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ret
+.curColumnDone
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITEOUTPUTBITOFFSET]
+ and a
+ jr z, .bitOffsetsDone
+ dec a
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ ld hl, W_SPRITEOUTPUTPTRCACHED
+ ld a, [hli]
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, [hl]
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ret
+.bitOffsetsDone
+ ld a, $3
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr z, .allColumnsDone
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ inc hl
+ jp StoreSpriteOutputPointer
+.allColumnsDone
+ pop hl
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 1, a
+ jr nz, .done ; test if there is one more sprite to go
+ xor $1
+ set 1, a
+ ld [W_SPRITELOADFLAGS], a ; wd0a8
+ jp UncompressSpriteDataLoop
+.done
+ jp UnpackSprite
+
+; writes 2 bits (from a) to the output buffer (pointed to from W_SPRITEOUTPUTPTR)
+WriteSpriteBitsToBuffer:: ; 2649 (0:2649)
+ ld e, a
+ ld a, [W_SPRITEOUTPUTBITOFFSET]
+ and a
+ jr z, .offset0
+ cp $2
+ jr c, .offset1
+ jr z, .offset2
+ rrc e ; offset 3
+ rrc e
+ jr .offset0
+.offset1
+ sla e
+ sla e
+ jr .offset0
+.offset2
+ swap e
+.offset0
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [hl]
+ or e
+ ld [hl], a
+ ret
+
+; reads next bit from input stream and returns it in a
+ReadNextInputBit:: ; 2670 (0:2670)
+ ld a, [W_SPRITEINPUTBITCOUNTER]
+ dec a
+ jr nz, .curByteHasMoreBitsToRead
+ call ReadNextInputByte
+ ld [W_SPRITEINPUTCURBYTE], a
+ ld a, $8
+.curByteHasMoreBitsToRead
+ ld [W_SPRITEINPUTBITCOUNTER], a
+ ld a, [W_SPRITEINPUTCURBYTE]
+ rlca
+ ld [W_SPRITEINPUTCURBYTE], a
+ and $1
+ ret
+
+; reads next byte from input stream and returns it in a
+ReadNextInputByte:: ; 268b (0:268b)
+ ld a, [W_SPRITEINPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEINPUTPTR+1]
+ ld h, a
+ ld a, [hli]
+ ld b, a
+ ld a, l
+ ld [W_SPRITEINPUTPTR], a
+ ld a, h
+ ld [W_SPRITEINPUTPTR+1], a
+ ld a, b
+ ret
+
+; the nth item is 2^n - 1
+LengthEncodingOffsetList:: ; 269f (0:269f)
+ dw %0000000000000001
+ dw %0000000000000011
+ dw %0000000000000111
+ dw %0000000000001111
+ dw %0000000000011111
+ dw %0000000000111111
+ dw %0000000001111111
+ dw %0000000011111111
+ dw %0000000111111111
+ dw %0000001111111111
+ dw %0000011111111111
+ dw %0000111111111111
+ dw %0001111111111111
+ dw %0011111111111111
+ dw %0111111111111111
+ dw %1111111111111111
+
+; unpacks the sprite data depending on the unpack mode
+UnpackSprite:: ; 26bf (0:26bf)
+ ld a, [W_SPRITEUNPACKMODE]
+ cp $2
+ jp z, UnpackSpriteMode2
+ and a
+ jp nz, XorSpriteChunks
+ ld hl, S_SPRITEBUFFER1
+ call SpriteDifferentialDecode
+ ld hl, S_SPRITEBUFFER2
+ ; fall through
+
+; decodes differential encoded sprite data
+; input bit value 0 preserves the current bit value and input bit value 1 toggles it (starting from initial value 0).
+SpriteDifferentialDecode:: ; 26d4 (0:26d4)
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ call StoreSpriteOutputPointer
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped
+ ld hl, DecodeNybble0TableFlipped
+ ld de, DecodeNybble1TableFlipped
+ jr .storeDecodeTablesPointers
+.notFlipped
+ ld hl, DecodeNybble0Table
+ ld de, DecodeNybble1Table
+.storeDecodeTablesPointers
+ ld a, l
+ ld [W_SPRITEDECODETABLE0PTR], a
+ ld a, h
+ ld [W_SPRITEDECODETABLE0PTR+1], a
+ ld a, e
+ ld [W_SPRITEDECODETABLE1PTR], a
+ ld a, d
+ ld [W_SPRITEDECODETABLE1PTR+1], a
+ ld e, $0 ; last decoded nybble, initialized to 0
+.decodeNextByteLoop
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [hl]
+ ld b, a
+ swap a
+ and $f
+ call DifferentialDecodeNybble ; decode high nybble
+ swap a
+ ld d, a
+ ld a, b
+ and $f
+ call DifferentialDecodeNybble ; decode low nybble
+ or d
+ ld b, a
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, b
+ ld [hl], a ; write back decoded data
+ ld a, [W_SPRITEHEIGHT]
+ add l ; move on to next column
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr nz, .decodeNextByteLoop ; test if current row is done
+ xor a
+ ld e, a
+ ld [W_SPRITECURPOSX], a
+ ld a, [W_SPRITECURPOSY] ; move on to next row
+ inc a
+ ld [W_SPRITECURPOSY], a
+ ld b, a
+ ld a, [W_SPRITEHEIGHT]
+ cp b
+ jr z, .done ; test if all rows finished
+ ld a, [W_SPRITEOUTPUTPTRCACHED]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld h, a
+ inc hl
+ call StoreSpriteOutputPointer
+ jr .decodeNextByteLoop
+.done
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ret
+
+; decodes the nybble stored in a. Last decoded data is assumed to be in e (needed to determine if initial value is 0 or 1)
+DifferentialDecodeNybble:: ; 276d (0:276d)
+ srl a ; c=a%2, a/=2
+ ld c, $0
+ jr nc, .evenNumber
+ ld c, $1
+.evenNumber
+ ld l, a
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped ; determine if initial value is 0 or one
+ bit 3, e ; if flipped, consider MSB of last data
+ jr .selectLookupTable
+.notFlipped
+ bit 0, e ; else consider LSB
+.selectLookupTable
+ ld e, l
+ jr nz, .initialValue1 ; load the appropriate table
+ ld a, [W_SPRITEDECODETABLE0PTR]
+ ld l, a
+ ld a, [W_SPRITEDECODETABLE0PTR+1]
+ jr .tableLookup
+.initialValue1
+ ld a, [W_SPRITEDECODETABLE1PTR]
+ ld l, a
+ ld a, [W_SPRITEDECODETABLE1PTR+1]
+.tableLookup
+ ld h, a
+ ld a, e
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [hl]
+ bit 0, c
+ jr nz, .selectLowNybble
+ swap a ; select high nybble
+.selectLowNybble
+ and $f
+ ld e, a ; update last decoded data
+ ret
+
+DecodeNybble0Table:: ; 27a7 (0:27a7)
+ dn $0, $1
+ dn $3, $2
+ dn $7, $6
+ dn $4, $5
+ dn $f, $e
+ dn $c, $d
+ dn $8, $9
+ dn $b, $a
+DecodeNybble1Table:: ; 27af (0:27af)
+ dn $f, $e
+ dn $c, $d
+ dn $8, $9
+ dn $b, $a
+ dn $0, $1
+ dn $3, $2
+ dn $7, $6
+ dn $4, $5
+DecodeNybble0TableFlipped:: ; 27b7 (0:27b7)
+ dn $0, $8
+ dn $c, $4
+ dn $e, $6
+ dn $2, $a
+ dn $f, $7
+ dn $3, $b
+ dn $1, $9
+ dn $d, $5
+DecodeNybble1TableFlipped:: ; 27bf (0:27bf)
+ dn $f, $7
+ dn $3, $b
+ dn $1, $9
+ dn $d, $5
+ dn $0, $8
+ dn $c, $4
+ dn $e, $6
+ dn $2, $a
+
+; combines the two loaded chunks with xor (the chunk loaded second is the destination). The source chunk is differeintial decoded beforehand.
+XorSpriteChunks:: ; 27c7 (0:27c7)
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEOUTPUTPTR] ; points to buffer 1 or 2, depending on flags
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEOUTPUTPTR] ; source buffer, points to buffer 1 or 2, depending on flags
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED] ; destination buffer, points to buffer 2 or 1, depending on flags
+ ld e, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld d, a
+.xorChunksLoop
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped
+ push de
+ ld a, [de]
+ ld b, a
+ swap a
+ and $f
+ call ReverseNybble ; if flipped reverse the nybbles in the destination buffer
+ swap a
+ ld c, a
+ ld a, b
+ and $f
+ call ReverseNybble
+ or c
+ pop de
+ ld [de], a
+.notFlipped
+ ld a, [hli]
+ ld b, a
+ ld a, [de]
+ xor b
+ ld [de], a
+ inc de
+ ld a, [W_SPRITECURPOSY]
+ inc a
+ ld [W_SPRITECURPOSY], a ; go to next row
+ ld b, a
+ ld a, [W_SPRITEHEIGHT]
+ cp b
+ jr nz, .xorChunksLoop ; test if column finished
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a ; go to next column
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr nz, .xorChunksLoop ; test if all columns finished
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ret
+
+; reverses the bits in the nybble given in register a
+ReverseNybble:: ; 2837 (0:2837)
+ ld de, NybbleReverseTable
+ add e
+ ld e, a
+ jr nc, .asm_283f
+ inc d
+.asm_283f
+ ld a, [de]
+ ret
+
+; resets sprite buffer pointers to buffer 1 and 2, depending on W_SPRITELOADFLAGS
+ResetSpriteBufferPointers:: ; 2841 (0:2841)
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 0, a
+ jr nz, .buffer2Selected
+ ld de, S_SPRITEBUFFER1
+ ld hl, S_SPRITEBUFFER2
+ jr .storeBufferPointers
+.buffer2Selected
+ ld de, S_SPRITEBUFFER2
+ ld hl, S_SPRITEBUFFER1
+.storeBufferPointers
+ ld a, l
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld a, e
+ ld [W_SPRITEOUTPUTPTRCACHED], a
+ ld a, d
+ ld [W_SPRITEOUTPUTPTRCACHED+1], a
+ ret
+
+; maps each nybble to its reverse
+NybbleReverseTable:: ; 2867 (0:2867)
+ db $0, $8, $4, $c, $2, $a, $6 ,$e, $1, $9, $5, $d, $3, $b, $7 ,$f
+
+; combines the two loaded chunks with xor (the chunk loaded second is the destination). Both chunks are differeintial decoded beforehand.
+UnpackSpriteMode2:: ; 2877 (0:2877)
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEFLIPPED]
+ push af
+ xor a
+ ld [W_SPRITEFLIPPED], a ; temporarily clear flipped flag for decoding the destination chunk
+ ld a, [W_SPRITEOUTPUTPTRCACHED]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld h, a
+ call SpriteDifferentialDecode
+ call ResetSpriteBufferPointers
+ pop af
+ ld [W_SPRITEFLIPPED], a
+ jp XorSpriteChunks
+
+; stores hl into the output pointers
+StoreSpriteOutputPointer:: ; 2897 (0:2897)
+ ld a, l
+ ld [W_SPRITEOUTPUTPTR], a
+ ld [W_SPRITEOUTPUTPTRCACHED], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld [W_SPRITEOUTPUTPTRCACHED+1], a
+ ret
diff --git a/home/predef.asm b/home/predef.asm
new file mode 100644
index 00000000..1777d09f
--- /dev/null
+++ b/home/predef.asm
@@ -0,0 +1,50 @@
+Predef::
+; Call predefined function a.
+; To preserve other registers, have the
+; destination call GetPredefRegisters.
+
+ ; Save the predef id for GetPredefPointer.
+ ld [wPredefID], a
+
+ ; A hack for LoadDestinationWarpPosition.
+ ; See Func_c754 (predef $19).
+ ld a, [H_LOADEDROMBANK]
+ ld [wPredefParentBank], a
+
+ push af
+ ld a, BANK(GetPredefPointer)
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+
+ call GetPredefPointer
+
+ ld a, [wPredefBank]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+
+ ld de, .done
+ push de
+ jp [hl]
+.done
+
+ pop af
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ret
+
+GetPredefRegisters::
+; Restore the contents of register pairs
+; when GetPredefPointer was called.
+ ld a, [wPredefRegisters + 0]
+ ld h, a
+ ld a, [wPredefRegisters + 1]
+ ld l, a
+ ld a, [wPredefRegisters + 2]
+ ld d, a
+ ld a, [wPredefRegisters + 3]
+ ld e, a
+ ld a, [wPredefRegisters + 4]
+ ld b, a
+ ld a, [wPredefRegisters + 5]
+ ld c, a
+ ret
diff --git a/home/text.asm b/home/text.asm
new file mode 100644
index 00000000..abcac9fd
--- /dev/null
+++ b/home/text.asm
@@ -0,0 +1,718 @@
+TextBoxBorder::
+; Draw a cxb text box at hl.
+
+ ; top row
+ push hl
+ ld a, "┌"
+ ld [hli], a
+ inc a ; ─
+ call NPlaceChar
+ inc a ; ┐
+ ld [hl], a
+ pop hl
+
+ ld de, 20
+ add hl, de
+
+ ; middle rows
+.next
+ push hl
+ ld a, "│"
+ ld [hli],a
+ ld a, " "
+ call NPlaceChar
+ ld [hl], "│"
+ pop hl
+
+ ld de, 20
+ add hl, de
+ dec b
+ jr nz, .next
+
+ ; bottom row
+ ld a, "└"
+ ld [hli], a
+ ld a, "─"
+ call NPlaceChar
+ ld [hl], "┘"
+ ret
+
+NPlaceChar::
+; Place char a c times.
+ ld d, c
+.loop
+ ld [hli], a
+ dec d
+ jr nz, .loop
+ ret
+
+PlaceString:: ; 1955 (0:1955)
+ push hl
+PlaceNextChar:: ; 1956 (0:1956)
+ ld a,[de]
+
+ cp "@"
+ jr nz,.PlaceText
+ ld b,h
+ ld c,l
+ pop hl
+ ret
+
+.PlaceText
+ cp $4E
+ jr nz,.next
+ ld bc,$0028
+ ld a,[$FFF6]
+ bit 2,a
+ jr z,.next2
+ ld bc,$14
+.next2
+ pop hl
+ add hl,bc
+ push hl
+ jp Next19E8
+
+.next
+ cp $4F
+ jr nz,.next3
+ pop hl
+ hlCoord 1, 16
+ push hl
+ jp Next19E8
+
+.next3 ; Check against a dictionary
+ and a
+ jp z,Char00
+ cp $4C
+ jp z,Char4C
+ cp $4B
+ jp z,Char4B
+ cp $51
+ jp z,Char51
+ cp $49
+ jp z,Char49
+ cp $52
+ jp z,Char52
+ cp $53
+ jp z,Char53
+ cp $54
+ jp z,Char54
+ cp $5B
+ jp z,Char5B
+ cp $5E
+ jp z,Char5E
+ cp $5C
+ jp z,Char5C
+ cp $5D
+ jp z,Char5D
+ cp $55
+ jp z,Char55
+ cp $56
+ jp z,Char56
+ cp $57
+ jp z,Char57
+ cp $58
+ jp z,Char58
+ cp $4A
+ jp z,Char4A
+ cp $5F
+ jp z,Char5F
+ cp $59
+ jp z,Char59
+ cp $5A
+ jp z,Char5A
+ ld [hli],a
+ call PrintLetterDelay
+Next19E8:: ; 19e8 (0:19e8)
+ inc de
+ jp PlaceNextChar
+
+Char00:: ; 19ec (0:19ec)
+ ld b,h
+ ld c,l
+ pop hl
+ ld de,Char00Text
+ dec de
+ ret
+
+Char00Text:: ; 0x19f4 “%d ERROR.”
+ TX_FAR _Char00Text
+ db "@"
+
+Char52:: ; 0x19f9 player’s name
+ push de
+ ld de,wPlayerName
+ jr FinishDTE
+
+Char53:: ; 19ff (0:19ff) ; rival’s name
+ push de
+ ld de,W_RIVALNAME
+ jr FinishDTE
+
+Char5D:: ; 1a05 (0:1a05) ; TRAINER
+ push de
+ ld de,Char5DText
+ jr FinishDTE
+
+Char5C:: ; 1a0b (0:1a0b) ; TM
+ push de
+ ld de,Char5CText
+ jr FinishDTE
+
+Char5B:: ; 1a11 (0:1a11) ; PC
+ push de
+ ld de,Char5BText
+ jr FinishDTE
+
+Char5E:: ; 1a17 (0:1a17) ; ROCKET
+ push de
+ ld de,Char5EText
+ jr FinishDTE
+
+Char54:: ; 1a1d (0:1a1d) ; POKé
+ push de
+ ld de,Char54Text
+ jr FinishDTE
+
+Char56:: ; 1a23 (0:1a23) ; ……
+ push de
+ ld de,Char56Text
+ jr FinishDTE
+
+Char4A:: ; 1a29 (0:1a29) ; PKMN
+ push de
+ ld de,Char4AText
+ jr FinishDTE
+
+Char59:: ; 1a2f (0:1a2f)
+; depending on whose turn it is, print
+; enemy active monster’s name, prefixed with “Enemy ”
+; or
+; player active monster’s name
+; (like Char5A but flipped)
+ ld a,[H_WHOSETURN]
+ xor 1
+ jr MonsterNameCharsCommon
+
+Char5A:: ; 1a35 (0:1a35)
+; depending on whose turn it is, print
+; player active monster’s name
+; or
+; enemy active monster’s name, prefixed with “Enemy ”
+ ld a,[H_WHOSETURN]
+MonsterNameCharsCommon:: ; 1a37 (0:1a37)
+ push de
+ and a
+ jr nz,.Enemy
+ ld de,wBattleMonNick ; player active monster name
+ jr FinishDTE
+
+.Enemy ; 1A40
+ ; print “Enemy ”
+ ld de,Char5AText
+ call PlaceString
+
+ ld h,b
+ ld l,c
+ ld de,wEnemyMonNick ; enemy active monster name
+
+FinishDTE:: ; 1a4b (0:1a4b)
+ call PlaceString
+ ld h,b
+ ld l,c
+ pop de
+ inc de
+ jp PlaceNextChar
+
+Char5CText:: ; 1a55 (0:1a55)
+ db "TM@"
+Char5DText:: ; 1a58 (0:1a58)
+ db "TRAINER@"
+Char5BText:: ; 1a60 (0:1a60)
+ db "PC@"
+Char5EText:: ; 1a63 (0:1a63)
+ db "ROCKET@"
+Char54Text:: ; 1a6a (0:1a6a)
+ db "POKé@"
+Char56Text:: ; 1a6f (0:1a6f)
+ db "……@"
+Char5AText:: ; 1a72 (0:1a72)
+ db "Enemy @"
+Char4AText:: ; 1a79 (0:1a79)
+ db $E1,$E2,"@" ; PKMN
+
+Char55:: ; 1a7c (0:1a7c)
+ push de
+ ld b,h
+ ld c,l
+ ld hl,Char55Text
+ call TextCommandProcessor
+ ld h,b
+ ld l,c
+ pop de
+ inc de
+ jp PlaceNextChar
+
+Char55Text:: ; 1a8c (0:1a8c)
+; equivalent to Char4B
+ TX_FAR _Char55Text
+ db "@"
+
+Char5F:: ; 1a91 (0:1a91)
+; ends a Pokédex entry
+ ld [hl],"."
+ pop hl
+ ret
+
+Char58:: ; 1a95 (0:1a95)
+ ld a,[W_ISLINKBATTLE]
+ cp 4
+ jp z,Next1AA2
+ ld a,$EE
+ Coorda 18, 16
+Next1AA2:: ; 1aa2 (0:1aa2)
+ call ProtectedDelay3
+ call ManualTextScroll
+ ld a,$7F
+ Coorda 18, 16
+Char57:: ; 1aad (0:1aad)
+ pop hl
+ ld de,Char58Text
+ dec de
+ ret
+
+Char58Text:: ; 1ab3 (0:1ab3)
+ db "@"
+
+Char51:: ; 1ab4 (0:1ab4)
+ push de
+ ld a,$EE
+ Coorda 18, 16
+ call ProtectedDelay3
+ call ManualTextScroll
+ hlCoord 1, 13
+ ld bc,$0412
+ call ClearScreenArea
+ ld c,$14
+ call DelayFrames
+ pop de
+ hlCoord 1, 14
+ jp Next19E8
+
+Char49:: ; 1ad5 (0:1ad5)
+ push de
+ ld a,$EE
+ Coorda 18, 16
+ call ProtectedDelay3
+ call ManualTextScroll
+ hlCoord 1, 10
+ ld bc,$0712
+ call ClearScreenArea
+ ld c,$14
+ call DelayFrames
+ pop de
+ pop hl
+ hlCoord 1, 11
+ push hl
+ jp Next19E8
+
+Char4B:: ; 1af8 (0:1af8)
+ ld a,$EE
+ Coorda 18, 16
+ call ProtectedDelay3
+ push de
+ call ManualTextScroll
+ pop de
+ ld a,$7F
+ Coorda 18, 16
+ ;fall through
+Char4C:: ; 1b0a (0:1b0a)
+ push de
+ call Next1B18
+ call Next1B18
+ hlCoord 1, 16
+ pop de
+ jp Next19E8
+
+Next1B18:: ; 1b18 (0:1b18)
+ hlCoord 0, 14
+ deCoord 0, 13
+ ld b,$3C
+.next
+ ld a,[hli]
+ ld [de],a
+ inc de
+ dec b
+ jr nz,.next
+ hlCoord 1, 16
+ ld a,$7F
+ ld b,$12
+.next2
+ ld [hli],a
+ dec b
+ jr nz,.next2
+
+ ; wait five frames
+ ld b,5
+.WaitFrame
+ call DelayFrame
+ dec b
+ jr nz,.WaitFrame
+
+ ret
+
+ProtectedDelay3:: ; 1b3a (0:1b3a)
+ push bc
+ call Delay3
+ pop bc
+ ret
+
+TextCommandProcessor:: ; 1b40 (0:1b40)
+ ld a,[wd358]
+ push af
+ set 1,a
+ ld e,a
+ ld a,[$fff4]
+ xor e
+ ld [wd358],a
+ ld a,c
+ ld [wcc3a],a
+ ld a,b
+ ld [wcc3b],a
+
+NextTextCommand:: ; 1b55 (0:1b55)
+ ld a,[hli]
+ cp a, "@" ; terminator
+ jr nz,.doTextCommand
+ pop af
+ ld [wd358],a
+ ret
+.doTextCommand
+ push hl
+ cp a,$17
+ jp z,TextCommand17
+ cp a,$0e
+ jp nc,TextCommand0B ; if a != 0x17 and a >= 0xE, go to command 0xB
+; if a < 0xE, use a jump table
+ ld hl,TextCommandJumpTable
+ push bc
+ add a
+ ld b,$00
+ ld c,a
+ add hl,bc
+ pop bc
+ ld a,[hli]
+ ld h,[hl]
+ ld l,a
+ jp [hl]
+
+; draw box
+; 04AAAABBCC
+; AAAA = address of upper left corner
+; BB = height
+; CC = width
+TextCommand04:: ; 1b78 (0:1b78)
+ pop hl
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ ld a,[hli]
+ ld b,a
+ ld a,[hli]
+ ld c,a
+ push hl
+ ld h,d
+ ld l,e
+ call TextBoxBorder
+ pop hl
+ jr NextTextCommand
+
+; place string inline
+; 00{string}
+TextCommand00:: ; 1b8a (0:1b8a)
+ pop hl
+ ld d,h
+ ld e,l
+ ld h,b
+ ld l,c
+ call PlaceString
+ ld h,d
+ ld l,e
+ inc hl
+ jr NextTextCommand
+
+; place string from RAM
+; 01AAAA
+; AAAA = address of string
+TextCommand01:: ; 1b97 (0:1b97)
+ pop hl
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ push hl
+ ld h,b
+ ld l,c
+ call PlaceString
+ pop hl
+ jr NextTextCommand
+
+; print BCD number
+; 02AAAABB
+; AAAA = address of BCD number
+; BB
+; bits 0-4 = length in bytes
+; bits 5-7 = unknown flags
+TextCommand02:: ; 1ba5 (0:1ba5)
+ pop hl
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ ld a,[hli]
+ push hl
+ ld h,b
+ ld l,c
+ ld c,a
+ call PrintBCDNumber
+ ld b,h
+ ld c,l
+ pop hl
+ jr NextTextCommand
+
+; repoint destination address
+; 03AAAA
+; AAAA = new destination address
+TextCommand03:: ; 1bb7 (0:1bb7)
+ pop hl
+ ld a,[hli]
+ ld [wcc3a],a
+ ld c,a
+ ld a,[hli]
+ ld [wcc3b],a
+ ld b,a
+ jp NextTextCommand
+
+; repoint destination to second line of dialogue text box
+; 05
+; (no arguments)
+TextCommand05:: ; 1bc5 (0:1bc5)
+ pop hl
+ bcCoord 1, 16 ; address of second line of dialogue text box
+ jp NextTextCommand
+
+; blink arrow and wait for A or B to be pressed
+; 06
+; (no arguments)
+TextCommand06:: ; 1bcc (0:1bcc)
+ ld a,[W_ISLINKBATTLE]
+ cp a,$04
+ jp z,TextCommand0D
+ ld a,$ee ; down arrow
+ Coorda 18, 16 ; place down arrow in lower right corner of dialogue text box
+ push bc
+ call ManualTextScroll ; blink arrow and wait for A or B to be pressed
+ pop bc
+ ld a," "
+ Coorda 18, 16 ; overwrite down arrow with blank space
+ pop hl
+ jp NextTextCommand
+
+; scroll text up one line
+; 07
+; (no arguments)
+TextCommand07:: ; 1be7 (0:1be7)
+ ld a," "
+ Coorda 18, 16 ; place blank space in lower right corner of dialogue text box
+ call Next1B18 ; scroll up text
+ call Next1B18
+ pop hl
+ bcCoord 1, 16 ; address of second line of dialogue text box
+ jp NextTextCommand
+
+; execute asm inline
+; 08{code}
+TextCommand08:: ; 1bf9 (0:1bf9)
+ pop hl
+ ld de,NextTextCommand
+ push de ; return address
+ jp [hl]
+
+; print decimal number (converted from binary number)
+; 09AAAABB
+; AAAA = address of number
+; BB
+; bits 0-3 = how many digits to display
+; bits 4-7 = how long the number is in bytes
+TextCommand09:: ; 1bff (0:1bff)
+ pop hl
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ ld a,[hli]
+ push hl
+ ld h,b
+ ld l,c
+ ld b,a
+ and a,$0f
+ ld c,a
+ ld a,b
+ and a,$f0
+ swap a
+ set 6,a
+ ld b,a
+ call PrintNumber
+ ld b,h
+ ld c,l
+ pop hl
+ jp NextTextCommand
+
+; wait half a second if the user doesn't hold A or B
+; 0A
+; (no arguments)
+TextCommand0A:: ; 1c1d (0:1c1d)
+ push bc
+ call Joypad
+ ld a,[hJoyHeld]
+ and a,%00000011 ; A and B buttons
+ jr nz,.skipDelay
+ ld c,30
+ call DelayFrames
+.skipDelay
+ pop bc
+ pop hl
+ jp NextTextCommand
+
+; plays sounds
+; this actually handles various command ID's, not just 0B
+; (no arguments)
+TextCommand0B:: ; 1c31 (0:1c31)
+ pop hl
+ push bc
+ dec hl
+ ld a,[hli]
+ ld b,a ; b = command number that got us here
+ push hl
+ ld hl,TextCommandSounds
+.loop
+ ld a,[hli]
+ cp b
+ jr z,.matchFound
+ inc hl
+ jr .loop
+.matchFound
+ cp a,$14
+ jr z,.pokemonCry
+ cp a,$15
+ jr z,.pokemonCry
+ cp a,$16
+ jr z,.pokemonCry
+ ld a,[hl]
+ call PlaySound
+ call WaitForSoundToFinish
+ pop hl
+ pop bc
+ jp NextTextCommand
+.pokemonCry
+ push de
+ ld a,[hl]
+ call PlayCry
+ pop de
+ pop hl
+ pop bc
+ jp NextTextCommand
+
+; format: text command ID, sound ID or cry ID
+TextCommandSounds:: ; 1c64 (0:1c64)
+ db $0B,(SFX_02_3a - SFX_Headers_02) / 3
+ db $12,(SFX_02_46 - SFX_Headers_02) / 3
+ db $0E,(SFX_02_41 - SFX_Headers_02) / 3
+ db $0F,(SFX_02_3a - SFX_Headers_02) / 3
+ db $10,(SFX_02_3b - SFX_Headers_02) / 3
+ db $11,(SFX_02_42 - SFX_Headers_02) / 3
+ db $13,(SFX_02_44 - SFX_Headers_02) / 3
+ db $14,NIDORINA ; used in OakSpeech
+ db $15,PIDGEOT ; used in SaffronCityText12
+ db $16,DEWGONG ; unused?
+
+; draw ellipses
+; 0CAA
+; AA = number of ellipses to draw
+TextCommand0C:: ; 1c78 (0:1c78)
+ pop hl
+ ld a,[hli]
+ ld d,a
+ push hl
+ ld h,b
+ ld l,c
+.loop
+ ld a,$75 ; ellipsis
+ ld [hli],a
+ push de
+ call Joypad
+ pop de
+ ld a,[hJoyHeld] ; joypad state
+ and a,%00000011 ; is A or B button pressed?
+ jr nz,.skipDelay ; if so, skip the delay
+ ld c,10
+ call DelayFrames
+.skipDelay
+ dec d
+ jr nz,.loop
+ ld b,h
+ ld c,l
+ pop hl
+ jp NextTextCommand
+
+; wait for A or B to be pressed
+; 0D
+; (no arguments)
+TextCommand0D:: ; 1c9a (0:1c9a)
+ push bc
+ call ManualTextScroll ; wait for A or B to be pressed
+ pop bc
+ pop hl
+ jp NextTextCommand
+
+; process text commands in another ROM bank
+; 17AAAABB
+; AAAA = address of text commands
+; BB = bank
+TextCommand17:: ; 1ca3 (0:1ca3)
+ pop hl
+ ld a,[H_LOADEDROMBANK]
+ push af
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ ld a,[hli]
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a
+ push hl
+ ld l,e
+ ld h,d
+ call TextCommandProcessor
+ pop hl
+ pop af
+ ld [H_LOADEDROMBANK],a
+ ld [$2000],a
+ jp NextTextCommand
+
+TextCommandJumpTable:: ; 1cc1 (0:1cc1)
+ dw TextCommand00
+ dw TextCommand01
+ dw TextCommand02
+ dw TextCommand03
+ dw TextCommand04
+ dw TextCommand05
+ dw TextCommand06
+ dw TextCommand07
+ dw TextCommand08
+ dw TextCommand09
+ dw TextCommand0A
+ dw TextCommand0B
+ dw TextCommand0C
+ dw TextCommand0D
diff --git a/home/vblank.asm b/home/vblank.asm
new file mode 100644
index 00000000..15f91437
--- /dev/null
+++ b/home/vblank.asm
@@ -0,0 +1,105 @@
+VBlank::
+
+ push af
+ push bc
+ push de
+ push hl
+
+ ld a, [H_LOADEDROMBANK]
+ ld [wd122], a
+
+ ld a, [$ffae]
+ ld [rSCX], a
+ ld a, [$ffaf]
+ ld [rSCY], a
+
+ ld a, [wd0a0]
+ and a
+ jr nz, .ok
+ ld a, [$ffb0]
+ ld [rWY], a
+.ok
+
+ call AutoBgMapTransfer
+ call VBlankCopyBgMap
+ call RedrawExposedScreenEdge
+ call VBlankCopy
+ call VBlankCopyDouble
+ call UpdateMovingBgTiles
+ call $ff80 ; hOAMDMA
+ ld a, Bank(PrepareOAMData)
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+ call PrepareOAMData
+
+ ; VBlank-sensitive operations end.
+
+ call Random
+
+ ld a, [H_VBLANKOCCURRED]
+ and a
+ jr z, .vblanked
+ xor a
+ ld [H_VBLANKOCCURRED], a
+.vblanked
+
+ ld a, [H_FRAMECOUNTER]
+ and a
+ jr z, .decced
+ dec a
+ ld [H_FRAMECOUNTER], a
+.decced
+
+ call Func_28cb
+
+ ld a, [wc0ef] ; music ROM bank
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+
+ cp BANK(Music2_UpdateMusic)
+ jr nz, .notbank2
+.bank2
+ call Music2_UpdateMusic
+ jr .afterMusic
+.notbank2
+ cp BANK(Music8_UpdateMusic)
+ jr nz, .bank1F
+.bank8
+ call Func_2136e
+ call Music8_UpdateMusic
+ jr .afterMusic
+.bank1F
+ call Music1f_UpdateMusic
+.afterMusic
+
+ callba Func_18dee ; keep track of time played
+
+ ld a, [$fff9]
+ and a
+ call z, ReadJoypad
+
+ ld a, [wd122]
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+
+ pop hl
+ pop de
+ pop bc
+ pop af
+ reti
+
+
+DelayFrame::
+; Wait for the next vblank interrupt.
+; As a bonus, this saves battery.
+
+NOT_VBLANKED EQU 1
+
+ ld a, NOT_VBLANKED
+ ld [H_VBLANKOCCURRED], a
+.halt
+ halt
+ ld a, [H_VBLANKOCCURRED]
+ and a
+ jr nz, .halt
+ ret
diff --git a/home/vcopy.asm b/home/vcopy.asm
new file mode 100644
index 00000000..81fcb991
--- /dev/null
+++ b/home/vcopy.asm
@@ -0,0 +1,447 @@
+; this function seems to be used only once
+; it store the address of a row and column of the VRAM background map in hl
+; INPUT: h - row, l - column, b - high byte of background tile map address in VRAM
+GetRowColAddressBgMap:: ; 1cdd (0:1cdd)
+ xor a
+ srl h
+ rr a
+ srl h
+ rr a
+ srl h
+ rr a
+ or l
+ ld l,a
+ ld a,b
+ or h
+ ld h,a
+ ret
+
+; clears a VRAM background map with blank space tiles
+; INPUT: h - high byte of background tile map address in VRAM
+ClearBgMap:: ; 1cf0 (0:1cf0)
+ ld a," "
+ jr .next
+ ld a,l
+.next
+ ld de,$400 ; size of VRAM background map
+ ld l,e
+.loop
+ ld [hli],a
+ dec e
+ jr nz,.loop
+ dec d
+ jr nz,.loop
+ ret
+
+; When the player takes a step, a row or column of 2x2 tile blocks at the edge
+; of the screen toward which they moved is exposed and has to be redrawn.
+; This function does the redrawing.
+RedrawExposedScreenEdge:: ; 1d01 (0:1d01)
+ ld a,[H_SCREENEDGEREDRAW]
+ and a
+ ret z
+ ld b,a
+ xor a
+ ld [H_SCREENEDGEREDRAW],a
+ dec b
+ jr nz,.redrawRow
+.redrawColumn
+ ld hl,wScreenEdgeTiles
+ ld a,[H_SCREENEDGEREDRAWADDR]
+ ld e,a
+ ld a,[H_SCREENEDGEREDRAWADDR + 1]
+ ld d,a
+ ld c,18 ; screen height
+.loop1
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hli]
+ ld [de],a
+ ld a,31
+ add e
+ ld e,a
+ jr nc,.noCarry
+ inc d
+.noCarry
+; the following 4 lines wrap us from bottom to top if necessary
+ ld a,d
+ and a,$03
+ or a,$98
+ ld d,a
+ dec c
+ jr nz,.loop1
+ xor a
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+.redrawRow
+ ld hl,wScreenEdgeTiles
+ ld a,[H_SCREENEDGEREDRAWADDR]
+ ld e,a
+ ld a,[H_SCREENEDGEREDRAWADDR + 1]
+ ld d,a
+ push de
+ call .drawHalf ; draw upper half
+ pop de
+ ld a,32 ; width of VRAM background map
+ add e
+ ld e,a
+ ; draw lower half
+.drawHalf
+ ld c,10
+.loop2
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hli]
+ ld [de],a
+ ld a,e
+ inc a
+; the following 6 lines wrap us from the right edge to the left edge if necessary
+ and a,$1f
+ ld b,a
+ ld a,e
+ and a,$e0
+ or b
+ ld e,a
+ dec c
+ jr nz,.loop2
+ ret
+
+; This function automatically transfers tile number data from the tile map at
+; wTileMap to VRAM during V-blank. Note that it only transfers one third of the
+; background per V-blank. It cycles through which third it draws.
+; This transfer is turned off when walking around the map, but is turned
+; on when talking to sprites, battling, using menus, etc. This is because
+; the above function, RedrawExposedScreenEdge, is used when walking to
+; improve efficiency.
+AutoBgMapTransfer:: ; 1d57 (0:1d57)
+ ld a,[H_AUTOBGTRANSFERENABLED]
+ and a
+ ret z
+ ld hl,[sp + 0]
+ ld a,h
+ ld [H_SPTEMP],a
+ ld a,l
+ ld [H_SPTEMP + 1],a ; save stack pinter
+ ld a,[H_AUTOBGTRANSFERPORTION]
+ and a
+ jr z,.transferTopThird
+ dec a
+ jr z,.transferMiddleThird
+.transferBottomThird
+ hlCoord 0, 12
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld de,(12 * 32)
+ add hl,de
+ xor a ; TRANSFERTOP
+ jr .doTransfer
+.transferTopThird
+ hlCoord 0, 0
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld a,TRANSFERMIDDLE
+ jr .doTransfer
+.transferMiddleThird
+ hlCoord 0, 6
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld de,(6 * 32)
+ add hl,de
+ ld a,TRANSFERBOTTOM
+.doTransfer
+ ld [H_AUTOBGTRANSFERPORTION],a ; store next portion
+ ld b,6
+
+TransferBgRows:: ; 1d9e (0:1d9e)
+; unrolled loop and using pop for speed
+
+ rept 20 / 2 - 1
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+
+ ld a, 32 - (20 - 1)
+ add l
+ ld l, a
+ jr nc, .ok
+ inc h
+.ok
+ dec b
+ jr nz, TransferBgRows
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+ ret
+
+; Copies [H_VBCOPYBGNUMROWS] rows from H_VBCOPYBGSRC to H_VBCOPYBGDEST.
+; If H_VBCOPYBGSRC is XX00, the transfer is disabled.
+VBlankCopyBgMap:: ; 1de1 (0:1de1)
+ ld a,[H_VBCOPYBGSRC] ; doubles as enabling byte
+ and a
+ ret z
+ ld hl,[sp + 0]
+ ld a,h
+ ld [H_SPTEMP],a
+ ld a,l
+ ld [H_SPTEMP + 1],a ; save stack pointer
+ ld a,[H_VBCOPYBGSRC]
+ ld l,a
+ ld a,[H_VBCOPYBGSRC + 1]
+ ld h,a
+ ld sp,hl
+ ld a,[H_VBCOPYBGDEST]
+ ld l,a
+ ld a,[H_VBCOPYBGDEST + 1]
+ ld h,a
+ ld a,[H_VBCOPYBGNUMROWS]
+ ld b,a
+ xor a
+ ld [H_VBCOPYBGSRC],a ; disable transfer so it doesn't continue next V-blank
+ jr TransferBgRows
+
+
+VBlankCopyDouble::
+; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles
+; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST.
+
+; While we're here, convert to 2bpp.
+; The process is straightforward:
+; copy each byte twice.
+
+ ld a, [H_VBCOPYDOUBLESIZE]
+ and a
+ ret z
+
+ ld hl, [sp + 0]
+ ld a, h
+ ld [H_SPTEMP], a
+ ld a, l
+ ld [H_SPTEMP + 1], a
+
+ ld a, [H_VBCOPYDOUBLESRC]
+ ld l, a
+ ld a, [H_VBCOPYDOUBLESRC + 1]
+ ld h, a
+ ld sp, hl
+
+ ld a, [H_VBCOPYDOUBLEDEST]
+ ld l, a
+ ld a, [H_VBCOPYDOUBLEDEST + 1]
+ ld h, a
+
+ ld a, [H_VBCOPYDOUBLESIZE]
+ ld b, a
+ xor a ; transferred
+ ld [H_VBCOPYDOUBLESIZE], a
+
+.loop
+ rept 3
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc hl
+ dec b
+ jr nz, .loop
+
+ ld a, l
+ ld [H_VBCOPYDOUBLEDEST], a
+ ld a, h
+ ld [H_VBCOPYDOUBLEDEST + 1], a
+
+ ld hl, [sp + 0]
+ ld a, l
+ ld [H_VBCOPYDOUBLESRC], a
+ ld a, h
+ ld [H_VBCOPYDOUBLESRC + 1], a
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+
+ ret
+
+
+VBlankCopy::
+; Copy [H_VBCOPYSIZE] 2bpp tiles
+; from H_VBCOPYSRC to H_VBCOPYDEST.
+
+; Source and destination addresses
+; are updated, so transfer can
+; continue in subsequent calls.
+
+ ld a, [H_VBCOPYSIZE]
+ and a
+ ret z
+
+ ld hl, [sp + 0]
+ ld a, h
+ ld [H_SPTEMP], a
+ ld a, l
+ ld [H_SPTEMP + 1], a
+
+ ld a, [H_VBCOPYSRC]
+ ld l, a
+ ld a, [H_VBCOPYSRC + 1]
+ ld h, a
+ ld sp, hl
+
+ ld a, [H_VBCOPYDEST]
+ ld l, a
+ ld a, [H_VBCOPYDEST + 1]
+ ld h, a
+
+ ld a, [H_VBCOPYSIZE]
+ ld b, a
+ xor a ; transferred
+ ld [H_VBCOPYSIZE], a
+
+.loop
+ rept 7
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc hl
+ dec b
+ jr nz, .loop
+
+ ld a, l
+ ld [H_VBCOPYDEST], a
+ ld a, h
+ ld [H_VBCOPYDEST + 1], a
+
+ ld hl, [sp + 0]
+ ld a, l
+ ld [H_VBCOPYSRC], a
+ ld a, h
+ ld [H_VBCOPYSRC + 1], a
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+
+ ret
+
+
+UpdateMovingBgTiles::
+; Animate water and flower
+; tiles in the overworld.
+
+ ld a, [$ffd7]
+ and a
+ ret z
+
+ ld a, [$ffd8]
+ inc a
+ ld [$ffd8], a
+ cp $14
+ ret c
+ cp $15
+ jr z, .flower
+
+ ld hl, vTileset + $14 * $10
+ ld c, $10
+
+ ld a, [wd085]
+ inc a
+ and 7
+ ld [wd085], a
+
+ and 4
+ jr nz, .left
+.right
+ ld a, [hl]
+ rrca
+ ld [hli], a
+ dec c
+ jr nz, .right
+ jr .done
+.left
+ ld a, [hl]
+ rlca
+ ld [hli], a
+ dec c
+ jr nz, .left
+.done
+ ld a, [$ffd7]
+ rrca
+ ret nc
+ xor a
+ ld [$ffd8], a
+ ret
+
+.flower
+ xor a
+ ld [$ffd8], a
+
+ ld a, [wd085]
+ and 3
+ cp 2
+ ld hl, FlowerTile1
+ jr c, .copy
+ ld hl, FlowerTile2
+ jr z, .copy
+ ld hl, FlowerTile3
+.copy
+ ld de, vTileset + $3 * $10
+ ld c, $10
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+ ret
+
+FlowerTile1: INCBIN "gfx/tilesets/flower/flower1.2bpp"
+FlowerTile2: INCBIN "gfx/tilesets/flower/flower2.2bpp"
+FlowerTile3: INCBIN "gfx/tilesets/flower/flower3.2bpp"