shithub: pokered

Download patch

ref: d266f788b4ba251c5a41f47745c941d81e5d7d88
parent: 683a62cc1cb7d07b4c88eeec9333c415da8f0bdc
parent: fc123e360b147221b5b44d265cdf38feabe99bbe
author: U-Fish-PC\Daniel <corrnondacqb@yahoo.com>
date: Mon Jun 9 11:10:49 EDT 2014

Merge branch 'master' of https://github.com/yenatch/pokered

diff: cannot open b/home//null: file does not exist: 'b/home//null'
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@
 	@$(gfx) 2bpp $(2bppq);    $(eval 2bppq :=)
 	@$(gfx) 1bpp $(1bppq);    $(eval 1bppq :=)
 	@$(pic) compress $(picq); $(eval picq  :=)
-	rgbasm -o $@ $*.tx
+	rgbasm -h -o $@ $*.tx
 
 
 link    := rgblink -n $*.sym -m $*.map
--- a/constants/pokemon_constants.asm
+++ b/constants/pokemon_constants.asm
@@ -28,6 +28,8 @@
 BLASTOISE  EQU $1C
 PINSIR     EQU $1D
 TANGELA    EQU $1E
+
+
 GROWLITHE  EQU $21
 ONIX       EQU $22
 FEAROW     EQU $23
@@ -45,17 +47,26 @@
 PSYDUCK    EQU $2F
 DROWZEE    EQU $30
 GOLEM      EQU $31
+
 MAGMAR     EQU $33
+
 ELECTABUZZ EQU $35
 MAGNETON   EQU $36
 KOFFING    EQU $37
+
 MANKEY     EQU $39
 SEEL       EQU $3A
 DIGLETT    EQU $3B
 TAUROS     EQU $3C
+
+
+
 FARFETCH_D EQU $40
 VENONAT    EQU $41
 DRAGONITE  EQU $42
+
+
+
 DODUO      EQU $46
 POLIWAG    EQU $47
 JYNX       EQU $48
@@ -65,10 +76,15 @@
 DITTO      EQU $4C
 MEOWTH     EQU $4D
 KRABBY     EQU $4E
+
+
+
 VULPIX     EQU $52
 NINETALES  EQU $53
 PIKACHU    EQU $54
 RAICHU     EQU $55
+
+
 DRATINI    EQU $58
 DRAGONAIR  EQU $59
 KABUTO     EQU $5A
@@ -75,6 +91,8 @@
 KABUTOPS   EQU $5B
 HORSEA     EQU $5C
 SEADRA     EQU $5D
+
+
 SANDSHREW  EQU $60
 SANDSLASH  EQU $61
 OMANYTE    EQU $62
@@ -94,15 +112,19 @@
 WEEDLE     EQU $70
 KAKUNA     EQU $71
 BEEDRILL   EQU $72
+
 DODRIO     EQU $74
 PRIMEAPE   EQU $75
 DUGTRIO    EQU $76
 VENOMOTH   EQU $77
 DEWGONG    EQU $78
+
+
 CATERPIE   EQU $7B
 METAPOD    EQU $7C
 BUTTERFREE EQU $7D
 MACHAMP    EQU $7E
+
 GOLDUCK    EQU $80
 HYPNO      EQU $81
 GOLBAT     EQU $82
@@ -109,14 +131,19 @@
 MEWTWO     EQU $83
 SNORLAX    EQU $84
 MAGIKARP   EQU $85
+
+
 MUK        EQU $88
+
 KINGLER    EQU $8A
 CLOYSTER   EQU $8B
+
 ELECTRODE  EQU $8D
 CLEFABLE   EQU $8E
 WEEZING    EQU $8F
 PERSIAN    EQU $90
 MAROWAK    EQU $91
+
 HAUNTER    EQU $93
 ABRA       EQU $94
 ALAKAZAM   EQU $95
@@ -126,8 +153,13 @@
 BULBASAUR  EQU $99
 VENUSAUR   EQU $9A
 TENTACRUEL EQU $9B
+
 GOLDEEN    EQU $9D
 SEAKING    EQU $9E
+
+
+
+
 PONYTA     EQU $A3
 RAPIDASH   EQU $A4
 RATTATA    EQU $A5
@@ -137,12 +169,16 @@
 GEODUDE    EQU $A9
 PORYGON    EQU $AA
 AERODACTYL EQU $AB
+
 MAGNEMITE  EQU $AD
+
+
 CHARMANDER EQU $B0
 SQUIRTLE   EQU $B1
 CHARMELEON EQU $B2
 WARTORTLE  EQU $B3
 CHARIZARD  EQU $B4
+
 FOSSIL_KABUTOPS EQU $B6
 FOSSIL_AERODACTYL EQU $B7
 MON_GHOST  EQU $B8
@@ -151,4 +187,4 @@
 VILEPLUME  EQU $BB
 BELLSPROUT EQU $BC
 WEEPINBELL EQU $BD
-VICTREEBEL EQU $BE
\ No newline at end of file
+VICTREEBEL EQU $BE
--- /dev/null
+++ b/data/collision.asm
@@ -1,0 +1,24 @@
+Underground_Coll::  INCBIN  "gfx/tilesets/underground.tilecoll"
+Overworld_Coll::    INCBIN  "gfx/tilesets/overworld.tilecoll"
+RedsHouse1_Coll::
+RedsHouse2_Coll::   INCBIN  "gfx/tilesets/reds_house.tilecoll"
+Mart_Coll::
+Pokecenter_Coll::   INCBIN  "gfx/tilesets/pokecenter.tilecoll"
+Dojo_Coll::
+Gym_Coll::          INCBIN  "gfx/tilesets/gym.tilecoll"
+Forest_Coll::       INCBIN  "gfx/tilesets/forest.tilecoll"
+House_Coll::        INCBIN  "gfx/tilesets/house.tilecoll"
+ForestGate_Coll::
+Museum_Coll::
+Gate_Coll::         INCBIN  "gfx/tilesets/gate.tilecoll"
+Ship_Coll::         INCBIN  "gfx/tilesets/ship.tilecoll"
+ShipPort_Coll::     INCBIN  "gfx/tilesets/ship_port.tilecoll"
+Cemetery_Coll::     INCBIN  "gfx/tilesets/cemetery.tilecoll"
+Interior_Coll::     INCBIN  "gfx/tilesets/interior.tilecoll"
+Cavern_Coll::       INCBIN  "gfx/tilesets/cavern.tilecoll"
+Lobby_Coll::        INCBIN  "gfx/tilesets/lobby.tilecoll"
+Mansion_Coll::      INCBIN  "gfx/tilesets/mansion.tilecoll"
+Lab_Coll::          INCBIN  "gfx/tilesets/lab.tilecoll"
+Club_Coll::         INCBIN  "gfx/tilesets/club.tilecoll"
+Facility_Coll::     INCBIN  "gfx/tilesets/facility.tilecoll"
+Plateau_Coll::      INCBIN  "gfx/tilesets/plateau.tilecoll"
--- a/data/sgb_packets.asm
+++ b/data/sgb_packets.asm
@@ -1,142 +1,298 @@
-BlkPacket_WholeScreen: ; 7219e (1c:619e)
-	db $21,$01,$03,$00,$00,$00,$13,$11,$00,$00,$00,$00,$00,$00,$00,$00
-	db $03,$00,$00,$13,$11,$00,$00
+ATTR_BLK: MACRO
+; This is a command macro.
+; Use ATTR_BLK_DATA for data sets.
+	db ($4 << 3) + ((\1 * 6) / 16 + 1)
+	db \1
+ENDM
+ATTR_BLK_DATA: MACRO
+	db \1 ; which regions are affected
+	db \2 + (\3 << 2) + (\4 << 4) ; palette for each region
+	db \5, \6, \7, \8 ; x1, y1, x2, y2
+ENDM
 
-BlkPacket_Battle: ; 721b5 (1c:61b5)
-	db $22,$05,$07,$0a,$00,$0c,$13,$11,$03,$05,$01,$00,$0a,$03,$03,$00
-	db $0a,$07,$13,$0a,$03,$0a,$00,$04,$08,$0b,$03,$0f,$0b,$00,$13,$06
-	db $03,$00,$00,$13,$0b,$00,$03,$00,$0c,$13,$11,$02,$03,$01,$00,$0a
-	db $03,$01,$03,$0a,$08,$13,$0a,$00,$03,$00,$04,$08,$0b,$02,$03,$0b
-	db $00,$13,$07,$03,$00
+PAL_SET: MACRO
+	db ($a << 3) + 1
+	dw \1, \2, \3, \4
+	ds 7
+ENDM
 
-BlkPacket_StatusScreen: ; 721fa (1c:61fa)
-	db $21,$01,$07,$05,$01,$00,$07,$06,$00,$00,$00,$00,$00,$00,$00,$00
-	db $02,$00,$00,$11,$00,$03,$01,$00,$07,$06,$01,$03,$01,$07,$13,$11
-	db $00,$03,$08,$00,$13,$06,$00,$00
+PAL_TRN: MACRO
+	db ($b<< 3) + 1
+	ds 15
+ENDM
 
-BlkPacket_Pokedex: ; 72222 (1c:6222)
-	db $21,$01,$07,$05,$01,$01,$08,$08,$00,$00,$00,$00,$00,$00,$00,$00
-	db $02,$00,$00,$11,$00,$01,$00,$01,$13,$00,$03,$01,$01,$08,$08,$01
-	db $03,$01,$09,$08,$11,$00,$03,$09,$01,$13,$11,$00,$00
+MLT_REQ: MACRO
+	db ($11 << 3) + 1
+	db \1 - 1
+	ds 14
+ENDM
 
-BlkPacket_Slots: ; 7224f (1c:624f)
-	db $22,$05,$03,$05,$00,$00,$13,$0b,$03,$0a,$00,$04,$13,$09,$02,$0f
-	db $00,$06,$13,$07,$03,$00,$04,$04,$0f,$09,$03,$00,$00,$0c,$13,$11
-	db $03,$00,$00,$13,$0b,$01,$03,$00,$04,$13,$09,$02,$03,$00,$06,$13
-	db $07,$03,$03,$04,$04,$0f,$09,$00,$03,$00,$0c,$13,$11,$00,$00
+CHR_TRN: MACRO
+	db ($13 << 3) + 1
+	db \1 + (\2 << 1)
+	ds 14
+ENDM
 
-BlkPacket_Titlescreen: ; 7228e (1c:628e)
-	db $22,$03,$03,$00,$00,$00,$13,$07,$02,$05,$00,$08,$13,$09,$03,$0a
-	db $00,$0a,$13,$11,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
-	db $03,$00,$00,$13,$07,$00,$03,$00,$08,$13,$09,$01,$03,$00,$0a,$13
-	db $11,$02,$00
+PCT_TRN: MACRO
+	db ($14 << 3) + 1
+	ds 15
+ENDM
 
-BlkPacket_NidorinoIntro: ; 722c1 (1c:62c1)
-	db $22,$03,$03,$05,$00,$00,$13,$03,$03,$00,$00,$04,$13,$0d,$03,$05
-	db $00,$0e,$13,$11,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
-	db $03,$00,$00,$13,$03,$01,$03,$00,$04,$13,$0d,$00,$03,$00,$0e,$13
-	db $11,$01,$00
+MASK_EN: MACRO
+	db ($17 << 3) + 1
+	db \1
+	ds 14
+ENDM
 
-BlkPacket_PartyMenu: ; 722f4 (1c:62f4)
-	db $23,$07,$06,$10,$01,$00,$02,$0c,$02,$00,$05,$01,$0b,$01,$02,$00
-	db $05,$03,$0b,$03,$02,$00,$05,$05,$0b,$05,$02,$00,$05,$07,$0b,$07
-	db $02,$00,$05,$09,$0b,$09,$02,$00,$05,$0b,$0b,$0b,$00,$00,$00,$00
-	db $02,$00,$00,$11,$01,$03,$01,$00,$02,$0c,$00,$03,$01,$0d,$02,$11
-	db $01,$03,$03,$00,$13,$11,$01,$03,$0c,$00,$12,$01,$00,$03,$0c,$02
-	db $12,$03,$00,$03,$0c,$04,$12,$05,$00,$03,$0c,$06,$12,$07,$00,$03
-	db $0c,$08,$12,$09,$00,$03,$0c,$0a,$12,$0b,$00,$00
+DATA_SND: MACRO
+	db ($f << 3) + 1
+	dw \1 ; address
+	db \2 ; bank
+	db \3 ; length (1-11)
+ENDM
 
-BlkPacket_TrainerCard: ; 72360 (1c:6360)
-	db $24,$0a,$02,$00,$03,$0c,$04,$0d,$02,$05,$07,$0c,$08,$0d,$02,$0f
-	db $0b,$0c,$0c,$0d,$02,$0a,$10,$0b,$11,$0c,$02,$05,$0e,$0d,$0f,$0e
-	db $02,$0f,$10,$0d,$11,$0e,$02,$0a,$03,$0f,$04,$10,$02,$0f,$07,$0f
-	db $08,$10,$02,$0a,$0b,$0f,$0c,$10,$02,$05,$0f,$0f,$10,$10,$00,$00
-	db $03,$03,$0c,$04,$0d,$00,$03,$07,$0c,$08,$0d,$01,$03,$0b,$0c,$0c
-	db $0d,$03,$03,$10,$0b,$11,$0c,$02,$03,$0e,$0d,$0f,$0e,$01,$03,$10
-	db $0d,$11,$0e,$03,$03,$03,$0f,$04,$10,$02,$03,$07,$0f,$08,$10,$03
-	db $03,$0b,$0f,$0c,$10,$02,$03,$0f,$0f,$10,$10,$01,$00
+BlkPacket_WholeScreen: ; 7219e (1c:619e)
+	ATTR_BLK 1
+	ATTR_BLK_DATA %011, 0,0,0, 00,00, 19,17
+	ds 8
 
-BlkPacket_GameFreakIntro: ; 723dd (1c:63dd)
-	db $22,$03,$07,$05,$05,$0b,$07,$0d,$02,$0a,$08,$0b,$09,$0d,$03,$0f
-	db $0c,$0b,$0e,$0d,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
-	db $03,$00,$00,$13,$0a,$00,$03,$00,$0b,$04,$0d,$00,$03,$05,$0b,$07
-	db $0d,$01,$03,$08,$0b,$13,$0d,$00,$03,$00,$0e,$13,$11,$00,$03,$08
-	db $0b,$09,$0d,$02,$03,$0c,$0b,$0e,$0d,$03,$00
+	db $03,$00,$00,$13,$11,$00,$00
 
-PalPacket_Empty: ; 72428 (1c:6428)
-	db $51,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_Battle: ; 721b5 (1c:61b5)
+	ATTR_BLK 5
+	ATTR_BLK_DATA %111, 2,2,0, 00,12, 19,17
+	ATTR_BLK_DATA %011, 1,1,0, 01,00, 10,03
+	ATTR_BLK_DATA %011, 0,0,0, 10,07, 19,10
+	ATTR_BLK_DATA %011, 2,2,0, 00,04, 08,11
+	ATTR_BLK_DATA %011, 3,3,0, 11,00, 19,06
 
-PalPacket_PartyMenu: ; 72438 (1c:6438)
-	db $51,PAL_MEWMON,$00,PAL_GREENBAR,$00,PAL_YELLOWBAR,$00,PAL_REDBAR,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$00,$00,$13,$0b,$00
+	db $03,$00,$0c,$13,$11,$02
+	db $03,$01,$00,$0a,$03,$01
+	db $03,$0a,$08,$13,$0a,$00
+	db $03,$00,$04,$08,$0b,$02
+	db $03,$0b,$00,$13,$07,$03
+	db $00
 
-PalPacket_Black: ; 72448 (1c:6448)
-	db $51,PAL_BLACK,$00,PAL_BLACK,$00,PAL_BLACK,$00,PAL_BLACK,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_StatusScreen: ; 721fa (1c:61fa)
+	ATTR_BLK 1
+	ATTR_BLK_DATA %111, 1,1,0, 01,00, 07,06
+	ds 8
 
-PalPacket_TownMap: ; 72458 (1c:6458)
-	db $51,PAL_TOWNMAP,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $02,$00,$00,$11,$00,$03
+	db $01,$00,$07,$06,$01,$03
+	db $01,$07,$13,$11,$00,$03
+	db $08,$00,$13,$06,$00,$00
 
-PalPacket_Pokedex: ; 72468 (1c:6468)
-	db $51,PAL_BROWNMON,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_Pokedex: ; 72222 (1c:6222)
+	ATTR_BLK 1
+	ATTR_BLK_DATA %111, 1,1,0, 01,01, 08,08
+	ds 8
 
-PalPacket_Slots: ; 72478 (1c:6478)
-	db $51,PAL_SLOTS1,$00,PAL_SLOTS2,$00,PAL_SLOTS3,$00,PAL_SLOTS4,$00,$00,$00,$00,$00,$00,$00,$00
+	db $02,$00,$00,$11,$00,$01
+	db $00,$01,$13,$00,$03,$01
+	db $01,$08,$08,$01,$03,$01
+	db $09,$08,$11,$00,$03,$09
+	db $01,$13,$11,$00,$00
 
-PalPacket_Titlescreen: ; 72488 (1c:6488)
-	db $51,PAL_LOGO2,$00,PAL_LOGO1,$00,PAL_MEWMON,$00,PAL_PURPLEMON,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_Slots: ; 7224f (1c:624f)
+	ATTR_BLK 5
+	ATTR_BLK_DATA %011, 1,1,0, 00,00, 19,11
+	ATTR_BLK_DATA %011, 2,2,0, 00,04, 19,09
+	ATTR_BLK_DATA %010, 3,3,0, 00,06, 19,07
+	ATTR_BLK_DATA %011, 0,0,0, 04,04, 15,09
+	ATTR_BLK_DATA %011, 0,0,0, 00,12, 19,17
 
-PalPacket_TrainerCard: ; 72498 (1c:6498)
-	db $51,PAL_MEWMON,$00,PAL_BADGE,$00,PAL_REDMON,$00,PAL_YELLOWMON,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$00,$00,$13,$0b,$01
+	db $03,$00,$04,$13,$09,$02
+	db $03,$00,$06,$13,$07,$03
+	db $03,$04,$04,$0f,$09,$00
+	db $03,$00,$0c,$13,$11,$00
+	db $00
 
-PalPacket_Generic: ; 724a8 (1c:64a8)
-	db $51,PAL_MEWMON,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_Titlescreen: ; 7228e (1c:628e)
+	ATTR_BLK 3
+	ATTR_BLK_DATA %011, 0,0,0, 00,00, 19,07
+	ATTR_BLK_DATA %010, 1,1,0, 00,08, 19,09
+	ATTR_BLK_DATA %011, 2,2,0, 00,10, 19,17
+	ds 12
 
-PalPacket_NidorinoIntro: ; 724b8 (1c:64b8)
-	db $51,PAL_PURPLEMON,$00,PAL_BLACK,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$00,$00,$13,$07,$00
+	db $03,$00,$08,$13,$09,$01
+	db $03,$00,$0a,$13,$11,$02
+	db $00
 
-PalPacket_GameFreakIntro: ; 724c8 (1c:64c8)
-	db $51,PAL_GAMEFREAK,$00,PAL_REDMON,$00,PAL_VIRIDIAN,$00,PAL_BLUEMON,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_NidorinoIntro: ; 722c1 (1c:62c1)
+	ATTR_BLK 3
+	ATTR_BLK_DATA %011, 1,1,0, 00,00, 19,03
+	ATTR_BLK_DATA %011, 0,0,0, 00,04, 19,13
+	ATTR_BLK_DATA %011, 1,1,0, 00,14, 19,17
+	ds 12
 
-PalPacket_724d8: ; 724d8 (1c:64d8)
-	db $59,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$00,$00,$13,$03,$01
+	db $03,$00,$04,$13,$0d,$00
+	db $03,$00,$0e,$13,$11,$01
+	db $00
 
-PalPacket_724e8: ; 724e8 (1c:64e8)
-	db $89,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_PartyMenu: ; 722f4 (1c:62f4)
+	ATTR_BLK 7
+	ATTR_BLK_DATA %110, 0,0,1, 01,00, 02,12
+	ATTR_BLK_DATA %010, 0,0,0, 05,01, 11,01
+	ATTR_BLK_DATA %010, 0,0,0, 05,03, 11,03
+	ATTR_BLK_DATA %010, 0,0,0, 05,05, 11,05
+	ATTR_BLK_DATA %010, 0,0,0, 05,07, 11,07
+	ATTR_BLK_DATA %010, 0,0,0, 05,09, 11,09
+	ATTR_BLK_DATA %010, 0,0,0, 05,11, 11,11
+	ds 4
 
-PalPacket_724f8: ; 724f8 (1c:64f8)
-	db $89,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $02,$00,$00,$11,$01,$03
+	db $01,$00,$02,$0c,$00,$03
+	db $01,$0d,$02,$11,$01,$03
+	db $03,$00,$13,$11,$01,$03
+	db $0c,$00,$12,$01,$00,$03
+	db $0c,$02,$12,$03,$00,$03
+	db $0c,$04,$12,$05,$00,$03
+	db $0c,$06,$12,$07,$00,$03
+	db $0c,$08,$12,$09,$00,$03
+	db $0c,$0a,$12,$0b,$00,$00
 
-PalPacket_72508: ; 72508 (1c:6508)
-	db $99,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_TrainerCard: ; 72360 (1c:6360)
+	ATTR_BLK 10
+	ATTR_BLK_DATA %010, 0,0,0, 03,12, 04,13
+	ATTR_BLK_DATA %010, 1,1,0, 07,12, 08,13
+	ATTR_BLK_DATA %010, 3,3,0, 11,12, 12,13
+	ATTR_BLK_DATA %010, 2,2,0, 16,11, 17,12
+	ATTR_BLK_DATA %010, 1,1,0, 14,13, 15,14
+	ATTR_BLK_DATA %010, 3,3,0, 16,13, 17,14
+	ATTR_BLK_DATA %010, 2,2,0, 03,15, 04,16
+	ATTR_BLK_DATA %010, 3,3,0, 07,15, 08,16
+	ATTR_BLK_DATA %010, 2,2,0, 11,15, 12,16
+	ATTR_BLK_DATA %010, 1,1,0, 15,15, 16,16
+	ds 2
 
-PalPacket_72518: ; 72518 (1c:6518)
-	db $A1,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$03,$0c,$04,$0d,$00
+	db $03,$07,$0c,$08,$0d,$01
+	db $03,$0b,$0c,$0c,$0d,$03
+	db $03,$10,$0b,$11,$0c,$02
+	db $03,$0e,$0d,$0f,$0e,$01
+	db $03,$10,$0d,$11,$0e,$03
+	db $03,$03,$0f,$04,$10,$02
+	db $03,$07,$0f,$08,$10,$03
+	db $03,$0b,$0f,$0c,$10,$02
+	db $03,$0f,$0f,$10,$10,$01
+	db $00
 
-PalPacket_72528: ; 72528 (1c:6528)
-	db $B9,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+BlkPacket_GameFreakIntro: ; 723dd (1c:63dd)
+	ATTR_BLK 3
+	ATTR_BLK_DATA %111, 1,1,0, 05,11, 07,13
+	ATTR_BLK_DATA %010, 2,2,0, 08,11, 09,13
+	ATTR_BLK_DATA %011, 3,3,0, 12,11, 14,13
+	ds 12
 
-PalPacket_72538: ; 72538 (1c:6538)
-	db $B9,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+	db $03,$00,$00,$13,$0a,$00
+	db $03,$00,$0b,$04,$0d,$00
+	db $03,$05,$0b,$07,$0d,$01
+	db $03,$08,$0b,$13,$0d,$00
+	db $03,$00,$0e,$13,$11,$00
+	db $03,$08,$0b,$09,$0d,$02
+	db $03,$0c,$0b,$0e,$0d,$03
+	db $00
 
-PalPacket_72548: ; 72548 (1c:6548)
-	db $79,$5D,$08,$00,$0B,$8C,$D0,$F4,$60,$00,$00,$00,$00,$00,$00,$00
 
-PalPacket_72558: ; 72558 (1c:6558)
-	db $79,$52,$08,$00,$0B,$A9,$E7,$9F,$01,$C0,$7E,$E8,$E8,$E8,$E8,$E0
+PalPacket_Empty:          PAL_SET 0, 0, 0, 0
+PalPacket_PartyMenu:      PAL_SET PAL_MEWMON, PAL_GREENBAR, PAL_YELLOWBAR, PAL_REDBAR
+PalPacket_Black:          PAL_SET PAL_BLACK, PAL_BLACK, PAL_BLACK, PAL_BLACK
+PalPacket_TownMap:        PAL_SET PAL_TOWNMAP, 0, 0, 0
+PalPacket_Pokedex:        PAL_SET PAL_BROWNMON, 0, 0, 0
+PalPacket_Slots:          PAL_SET PAL_SLOTS1, PAL_SLOTS2, PAL_SLOTS3, PAL_SLOTS4
+PalPacket_Titlescreen:    PAL_SET PAL_LOGO2, PAL_LOGO1, PAL_MEWMON, PAL_PURPLEMON
+PalPacket_TrainerCard:    PAL_SET PAL_MEWMON, PAL_BADGE, PAL_REDMON, PAL_YELLOWMON
+PalPacket_Generic:        PAL_SET PAL_MEWMON, 0, 0, 0
+PalPacket_NidorinoIntro:  PAL_SET PAL_PURPLEMON, PAL_BLACK, 0, 0
+PalPacket_GameFreakIntro: PAL_SET PAL_GAMEFREAK, PAL_REDMON, PAL_VIRIDIAN, PAL_BLUEMON
 
-PalPacket_72568: ; 72568 (1c:6568)
-	db $79,$47,$08,$00,$0B,$C4,$D0,$16,$A5,$CB,$C9,$05,$D0,$10,$A2,$28
+PalTrnPacket:  PAL_TRN
+MltReq1Packet: MLT_REQ 1
+MltReq2Packet: MLT_REQ 2
+ChrTrnPacket:  CHR_TRN 0, 0
+PctTrnPacket:  PCT_TRN
 
-PalPacket_72578: ; 72578 (1c:6578)
-	db $79,$3C,$08,$00,$0B,$F0,$12,$A5,$C9,$C9,$C8,$D0,$1C,$A5,$CA,$C9
+MaskEnFreezePacket: MASK_EN 1
+MaskEnCancelPacket: MASK_EN 0
 
-PalPacket_72588: ; 72588 (1c:6588)
-	db $79,$31,$08,$00,$0B,$0C,$A5,$CA,$C9,$7E,$D0,$06,$A5,$CB,$C9,$7E
 
-PalPacket_72598: ; 72598 (1c:6598)
-	db $79,$26,$08,$00,$0B,$39,$CD,$48,$0C,$D0,$34,$A5,$C9,$C9,$80,$D0
+; These are DATA_SND packets containing SNES code.
+; This set of packets is found in several Japanese SGB-compatible titles.
+; It appears to be part of NCL's SGB devkit.
 
-PalPacket_725a8: ; 725a8 (1c:65a8)
-	db $79,$1B,$08,$00,$0B,$EA,$EA,$EA,$EA,$EA,$A9,$01,$CD,$4F,$0C,$D0
+DataSnd_72548: DATA_SND $85d, $0, 11
+	db  $8C                 ; cpx #$8c (2)
+	db  $D0, $F4            ; bne -$0c
+	db  $60                 ; rts
+	ds  7
 
-PalPacket_725b8: ; 725b8 (1c:65b8)
-	db $79,$10,$08,$00,$0B,$4C,$20,$08,$EA,$EA,$EA,$EA,$EA,$60,$EA,$EA
+DataSnd_72558: DATA_SND $852, $0, 11
+	db  $A9, $E7            ; lda #$e7
+	db  $9F, $01, $C0, $7E  ; sta $7ec001, x
+	db  $E8                 ; inx
+	db  $E8                 ; inx
+	db  $E8                 ; inx
+	db  $E8                 ; inx
+	db  $E0                 ; cpx #$8c (1)
+
+DataSnd_72568: DATA_SND $847, $0, 11
+	db  $C4                 ; cmp #$c4 (2)
+	db  $D0, $16            ; bne +$16
+	db  $A5                 ; lda dp
+	db  $CB                 ; wai
+	db  $C9, $05            ; cmp #$05
+	db  $D0, $10            ; bne +$10
+	db  $A2, $28            ; ldx #$28
+
+DataSnd_72578: DATA_SND $83c, $0, 11
+	db  $F0, $12            ; beq +$12
+	db  $A5                 ; lda dp
+	db  $C9, $C9            ; cmp #$c9
+	db  $C8                 ; iny
+	db  $D0, $1C            ; bne +$1c
+	db  $A5                 ; lda dp
+	db  $CA                 ; dex
+	db  $C9                 ; cmp #$c4 (1)
+
+DataSnd_72588: DATA_SND $831, $0, 11
+	dbw $0C, $CAA5          ; tsb $caa5
+	db  $C9, $7E            ; cmp #$7e
+	db  $D0, $06            ; bne +$06
+	db  $A5                 ; lda dp
+	db  $CB                 ; wai
+	db  $C9, $7E            ; cmp #$7e
+
+DataSnd_72598: DATA_SND $826, $0, 11
+	db  $39                 ; bne +$39 (2)
+	dbw $CD, $C48           ; cmp $c48
+	db  $D0, $34            ; bne +$34
+	db  $A5                 ; lda dp
+	db  $C9, $C9            ; cmp #$c9
+	db  $80, $D0            ; bra -$30
+
+DataSnd_725a8: DATA_SND $81b, $0, 11
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	                        ; $820:
+	db  $A9,$01             ; lda #01
+	dbw $CD,$C4F            ; cmp $c4f
+	db  $D0                 ; bne +$39 (1)
+
+DataSnd_725b8: DATA_SND $810, $0, 11
+	dbw $4C, $820           ; jmp $820
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $EA                 ; nop
+	db  $60                 ; rts
+	db  $EA                 ; nop
+	db  $EA                 ; nop
--- /dev/null
+++ b/engine/joypad.asm
@@ -1,0 +1,51 @@
+_Joypad::
+	ld a, [hJoyInput]
+	cp A_BUTTON + B_BUTTON + SELECT + START ; soft reset
+	jp z, TrySoftReset
+	ld b, a
+	ld a, [hJoyHeldLast]
+	ld e, a
+	xor b
+	ld d, a
+	and e
+	ld [hJoyReleased], a
+	ld a, d
+	and b
+	ld [hJoyPressed], a
+	ld a, b
+	ld [hJoyHeldLast], a
+	ld a, [wd730]
+	bit 5, a
+	jr nz, DiscardButtonPresses
+	ld a, [hJoyHeldLast]
+	ld [hJoyHeld], a
+	ld a, [wJoyIgnore]
+	and a
+	ret z
+	cpl
+	ld b, a
+	ld a, [hJoyHeld]
+	and b
+	ld [hJoyHeld], a
+	ld a, [hJoyPressed]
+	and b
+	ld [hJoyPressed], a
+	ret
+
+DiscardButtonPresses:
+	xor a
+	ld [hJoyHeld], a
+	ld [hJoyPressed], a
+	ld [hJoyReleased], a
+	ret
+
+TrySoftReset:
+	call DelayFrame
+	; reset joypad (to make sure the
+	; player is really trying to reset)
+	ld a, $30
+	ld [rJOYP], a
+	ld hl, hSoftReset
+	dec [hl]
+	jp z, SoftReset
+	jp Joypad
--- a/engine/palettes.asm
+++ b/engine/palettes.asm
@@ -396,21 +396,21 @@
 	ei
 	ld a, $1
 	ld [wcf2d], a
-	ld de, PalPacket_72508
+	ld de, ChrTrnPacket
 	ld hl, SGBBorderGraphics
 	call Func_7210b
 	xor a
 	ld [wcf2d], a
-	ld de, PalPacket_72518
+	ld de, PctTrnPacket
 	ld hl, BorderPalettes
 	call Func_7210b
 	xor a
 	ld [wcf2d], a
-	ld de, PalPacket_724d8
+	ld de, PalTrnPacket
 	ld hl, SuperPalettes
 	call Func_7210b
 	call ClearVram
-	ld hl, PalPacket_72538
+	ld hl, MaskEnCancelPacket
 	jp SendSGBPacket
 
 Func_72075: ; 72075 (1c:6075)
@@ -431,18 +431,18 @@
 	ret
 
 PointerTable_72089: ; 72089 (1c:6089)
-	dw PalPacket_72528
-	dw PalPacket_72548
-	dw PalPacket_72558
-	dw PalPacket_72568
-	dw PalPacket_72578
-	dw PalPacket_72588
-	dw PalPacket_72598
-	dw PalPacket_725a8
-	dw PalPacket_725b8
+	dw MaskEnFreezePacket
+	dw DataSnd_72548
+	dw DataSnd_72558
+	dw DataSnd_72568
+	dw DataSnd_72578
+	dw DataSnd_72588
+	dw DataSnd_72598
+	dw DataSnd_725a8
+	dw DataSnd_725b8
 
 Func_7209b: ; 7209b (1c:609b)
-	ld hl, PalPacket_724f8
+	ld hl, MltReq2Packet
 	di
 	call SendSGBPacket
 	ld a, $1
@@ -493,7 +493,7 @@
 	ret
 
 Func_72102: ; 72102 (1c:6102)
-	ld hl, PalPacket_724e8
+	ld hl, MltReq1Packet
 	call SendSGBPacket
 	jp Wait7000
 
--- a/home.asm
+++ b/home.asm
@@ -124,2469 +124,12 @@
 	jp Init
 
 
-ReadJoypad::
-; Poll joypad input.
-; Unlike the hardware register, button
-; presses are indicated by a set bit.
+INCLUDE "home/joypad.asm"
 
-	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
-
-
 INCLUDE "data/map_header_pointers.asm"
 
-HandleMidJump::
-; Handle the player jumping down
-; a ledge in the overworld.
-	ld b, BANK(_HandleMidJump)
-	ld hl, _HandleMidJump
-	jp Bankswitch
+INCLUDE "home/overworld.asm"
 
-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
-	ld a,$35
-	call Predef ; 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
-	FuncCoord 8, 9
-	ld a,[Coord]
-	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
-	ld a,$52
-	call Predef
-	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
-	ld a,$13
-	call Predef ; 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 AnyPlayerPokemonAliveCheck ; 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 DoFlyOrTeleportAwayGraphics
-	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)
-	FuncCoord 8, 8
-	ld a,[Coord] ; 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 DoFlyOrTeleportAwayGraphics
-	ld a, Bank(Func_62ce)
-	ld [H_LOADEDROMBANK], a
-	ld [$2000], a
-	call Func_62ce
-	jp Func_5d5f
-
-DoFlyOrTeleportAwayGraphics::
-	ld b, BANK(_DoFlyOrTeleportAwayGraphics)
-	ld hl, _DoFlyOrTeleportAwayGraphics
-	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
-	ld a,$35
-	call Predef ; 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
-	ld a,$35
-	call Predef ; 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)
-	ld a,$35
-	call Predef ; 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
-	ld a,$35
-	call Predef ; 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)
-	FuncCoord 8, 9
-	ld a,[Coord] ; 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)
-	FuncCoord 0, 0
-	ld hl,Coord
-	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)
-	FuncCoord 0,16
-	ld hl,Coord
-	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)
-	FuncCoord 18,0
-	ld hl,Coord
-	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)
-	FuncCoord 0,0
-	ld hl,Coord
-	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
-	ld a,$35
-	call Predef ; 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
-	ld a,$19
-	call Predef ; 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?
-
 ; this is used to check if the player wants to interrupt the opening sequence at several points
 ; XXX is this used anywhere else?
 ; INPUT:
@@ -3365,75 +908,33 @@
 	ld b, a
 	jp CopyVideoData
 
-Underground_Coll:: ; 172f (0:172f)
-	INCBIN "gfx/tilesets/underground.tilecoll"
-Overworld_Coll:: ; 1735 (0:1735)
-	INCBIN "gfx/tilesets/overworld.tilecoll"
-RedsHouse1_Coll::
-RedsHouse2_Coll:: ; 1749 (0:1749)
-	INCBIN "gfx/tilesets/reds_house.tilecoll"
-Mart_Coll
-Pokecenter_Coll:: ; 1753 (0:1753)
-	INCBIN "gfx/tilesets/pokecenter.tilecoll"
-Dojo_Coll::
-Gym_Coll:: ; 1759 (0:1759)
-	INCBIN "gfx/tilesets/gym.tilecoll"
-Forest_Coll:: ; 1765 (0:1765)
-	INCBIN "gfx/tilesets/forest.tilecoll"
-House_Coll:: ; 1775 (0:1775)
-	INCBIN "gfx/tilesets/house.tilecoll"
-ForestGate_Coll::
-Museum_Coll::
-Gate_Coll:: ; 177f (0:177f)
-	INCBIN "gfx/tilesets/gate.tilecoll"
-Ship_Coll:: ; 178a (0:178a)
-	INCBIN "gfx/tilesets/ship.tilecoll"
-ShipPort_Coll:: ; 1795 (0:1795)
-	INCBIN "gfx/tilesets/ship_port.tilecoll"
-Cemetery_Coll:: ; 179a (0:179a)
-	INCBIN "gfx/tilesets/cemetery.tilecoll"
-Interior_Coll:: ; 17a2 (0:17a2)
-	INCBIN "gfx/tilesets/interior.tilecoll"
-Cavern_Coll:: ; 17ac (0:17ac)
-	INCBIN "gfx/tilesets/cavern.tilecoll"
-Lobby_Coll:: ; 17b8 (0:17b8)
-	INCBIN "gfx/tilesets/lobby.tilecoll"
-Mansion_Coll:: ; 17c0 (0:17c0)
-	INCBIN "gfx/tilesets/mansion.tilecoll"
-Lab_Coll:: ; 17ca (0:17ca)
-	INCBIN "gfx/tilesets/lab.tilecoll"
-Club_Coll:: ; 17d1 (0:17d1)
-	INCBIN "gfx/tilesets/club.tilecoll"
-Facility_Coll:: ; 17dd (0:17dd)
-	INCBIN "gfx/tilesets/facility.tilecoll"
-Plateau_Coll:: ; 17f0 (0:17f0)
-	INCBIN "gfx/tilesets/plateau.tilecoll"
 
-; does the same thing as FarCopyData at 009D
-; only difference is that it uses [$ff8b] instead of [wHPBarMaxHP] for a temp value
-; copy bc bytes of data from a:hl to de
-FarCopyData2:: ; 17f7 (0:17f7)
+INCLUDE "data/collision.asm"
+
+
+FarCopyData2::
+; Identical to FarCopyData, but uses $ff8b
+; as temp space instead of wBuffer.
 	ld [$ff8b],a
 	ld a,[H_LOADEDROMBANK]
 	push af
 	ld a,[$ff8b]
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 	call CopyData
 	pop af
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 	ret
 
-; does a far copy but the source is de and the destination is hl
-; copy bc bytes of data from a:de to hl
-FarCopyData3:: ; 180d (0:180d)
+FarCopyData3::
+; Copy bc bytes from a:de to hl.
 	ld [$ff8b],a
 	ld a,[H_LOADEDROMBANK]
 	push af
 	ld a,[$ff8b]
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 	push hl
 	push de
 	push de
@@ -3445,18 +946,18 @@
 	pop hl
 	pop af
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 	ret
 
-; copies each source byte to the destination twice (next to each other)
-; copy bc source bytes from a:hl to de
-FarCopyDataDouble:: ; 182b (0:182b)
+FarCopyDataDouble::
+; Expand bc bytes of 1bpp image data
+; from a:hl to 2bpp data at de.
 	ld [$ff8b],a
 	ld a,[H_LOADEDROMBANK]
 	push af
 	ld a,[$ff8b]
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 .loop
 	ld a,[hli]
 	ld [de],a
@@ -3469,1741 +970,185 @@
 	jr nz,.loop
 	pop af
 	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
+	ld [MBC3RomBank],a
 	ret
 
-; copy (c * 16) bytes from b:de to hl during V-blank
-; transfers up to 128 bytes per V-blank
-CopyVideoData:: ; 1848 (0:1848)
-	ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag
-	push af
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying
-	ld a,[H_LOADEDROMBANK]
-	ld [$ff8b],a
-	ld a,b
-	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
-	ld a,e
-	ld [H_VBCOPYSRC],a
-	ld a,d
-	ld [H_VBCOPYSRC + 1],a
-	ld a,l
-	ld [H_VBCOPYDEST],a
-	ld a,h
-	ld [H_VBCOPYDEST + 1],a
-.loop
-	ld a,c
-	cp a,8 ; are there more than 128 bytes left to copy?
-	jr nc,.copyMaxSize ; only copy up to 128 bytes at a time
-.copyRemainder
-	ld [H_VBCOPYSIZE],a
-	call DelayFrame ; wait for V-blank handler to perform the copy
-	ld a,[$ff8b]
-	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
-	pop af
-	ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag
-	ret
-.copyMaxSize
-	ld a,8 ; 128 bytes
-	ld [H_VBCOPYSIZE],a
-	call DelayFrame ; wait for V-blank handler to perform the copy
-	ld a,c
-	sub a,8
-	ld c,a
-	jr .loop
+CopyVideoData::
+; Wait for the next VBlank, then copy c 2bpp
+; tiles from b:de to hl, 8 tiles at a time.
+; This takes c/8 frames.
 
-; copy (c * 8) source bytes from b:de to hl during V-blank
-; copies each source byte to the destination twice (next to each other)
-; transfers up to 64 source bytes per V-blank
-CopyVideoDataDouble:: ; 1886 (0:1886)
-	ld a,[H_AUTOBGTRANSFERENABLED] ; save auto-transfer enabled flag
+	ld a, [H_AUTOBGTRANSFERENABLED]
 	push af
-	xor a
-	ld [H_AUTOBGTRANSFERENABLED],a ; disable auto-transfer while copying
-	ld a,[H_LOADEDROMBANK]
-	ld [$ff8b],a
-	ld a,b
-	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
-	ld a,e
-	ld [H_VBCOPYDOUBLESRC],a
-	ld a,d
-	ld [H_VBCOPYDOUBLESRC + 1],a
-	ld a,l
-	ld [H_VBCOPYDOUBLEDEST],a
-	ld a,h
-	ld [H_VBCOPYDOUBLEDEST + 1],a
-.loop
-	ld a,c
-	cp a,8 ; are there more than 64 source bytes left to copy?
-	jr nc,.copyMaxSize ; only copy up to 64 source bytes at a time
-.copyRemainder
-	ld [H_VBCOPYDOUBLESIZE],a
-	call DelayFrame ; wait for V-blank handler to perform the copy
-	ld a,[$ff8b]
-	ld [H_LOADEDROMBANK],a
-	ld [$2000],a
-	pop af
-	ld [H_AUTOBGTRANSFERENABLED],a ; restore original auto-transfer enabled flag
-	ret
-.copyMaxSize
-	ld a,8 ; 64 source bytes
-	ld [H_VBCOPYDOUBLESIZE],a
-	call DelayFrame ; wait for V-blank handler to perform the copy
-	ld a,c
-	sub a,8
-	ld c,a
-	jr .loop
+	xor a ; disable auto-transfer while copying
+	ld [H_AUTOBGTRANSFERENABLED], a
 
-; clears an area of the screen
-; INPUT:
-; hl = address of upper left corner of the area
-; b = height
-; c = width
-ClearScreenArea:: ; 18c4 (0:18c4)
-	ld   a,$7F ; blank tile
-	ld   de,20 ; screen width
-.loop
-	push hl
-	push bc
-.innerLoop
-	ld [hli],a
-	dec c
-	jr nz,.innerLoop
-	pop bc
-	pop hl
-	add hl,de
-	dec b
-	jr nz,.loop
-	ret
+	ld a, [H_LOADEDROMBANK]
+	ld [$ff8b], a
 
-; copies the screen tile buffer from WRAM to VRAM
-; copying is done in 3 chunks of 6 rows each
-; b: high byte of VRAM destination address ($98 or $9c for window tile map 0 or 1 resp.)
-CopyScreenTileBufferToVRAM:: ; 18d6 (0:18d6)
-	ld c, $6
-	ld hl, $0000
-	ld de, wTileMap
-	call InitScreenTileBufferTransferParameters
-	call DelayFrame
-	ld hl, $600
-	ld de, wTileMap + 20 * 6
-	call InitScreenTileBufferTransferParameters
-	call DelayFrame
-	ld hl, $c00
-	ld de, wTileMap + 20 * 12
-	call InitScreenTileBufferTransferParameters
-	jp DelayFrame
+	ld a, b
+	ld [H_LOADEDROMBANK], a
+	ld [MBC3RomBank], a
 
-InitScreenTileBufferTransferParameters:: ; 18fc (0:18fc)
+	ld a, e
+	ld [H_VBCOPYSRC], a
 	ld a, d
-	ld [H_VBCOPYBGSRC+1], a
-	call GetRowColAddressBgMap
+	ld [H_VBCOPYSRC + 1], a
+
 	ld a, l
-	ld [H_VBCOPYBGDEST], a ; $ffc3
+	ld [H_VBCOPYDEST], a
 	ld a, h
-	ld [H_VBCOPYBGDEST+1], a
-	ld a, c
-	ld [H_VBCOPYBGNUMROWS], a ; $ffc5
-	ld a, e
-	ld [H_VBCOPYBGSRC], a ; $ffc1
-	ret
+	ld [H_VBCOPYDEST + 1], a
 
-ClearScreen:: ; 190f (0:190f)
-; clears all tiles in the tilemap,
-; then wait three frames
-	ld bc,$0168 ; tilemap size
-	inc b
-	ld hl,wTileMap ; TILEMAP_START
-	ld a,$7F    ; $7F is blank tile
 .loop
-	ld [hli],a
-	dec c
-	jr nz,.loop
-	dec b
-	jr nz,.loop
-	jp Delay3
+	ld a, c
+	cp 8
+	jr nc, .keepgoing
 
-TextBoxBorder:: ; 1922 (0:1922)
-; draw a text box
-; upper-left corner at coordinates hl
-; height b
-; width c
-
-	; first row
-	push hl
-	ld a,"┌"
-	ld [hli],a
-	inc a    ; horizontal border ─
-	call NPlaceChar
-	inc a    ; upper-right border ┐
-	ld [hl],a
-
-	; middle rows
-	pop hl
-	ld de,20
-	add hl,de ; skip the top row
-
-.PlaceRow
-	push hl
-	ld a,"│"
-	ld [hli],a
-	ld a," "
-	call NPlaceChar
-	ld [hl],"│"
-
-	pop hl
-	ld de,20
-	add hl,de ; move to next row
-	dec b
-	jr nz,.PlaceRow
-
-	; bottom row
-	ld a,"└"
-	ld [hli],a
-	ld a,"─"
-	call NPlaceChar
-	ld [hl],"┘"
+.done
+	ld [H_VBCOPYSIZE], a
+	call DelayFrame
+	ld a, [$ff8b]
+	ld [H_LOADEDROMBANK], a
+	ld [MBC3RomBank], a
+	pop af
+	ld [H_AUTOBGTRANSFERENABLED], a
 	ret
-;
-NPlaceChar:: ; 194f (0:194f)
-; place a row of width c of identical characters
-	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]
+.keepgoing
+	ld a, 8
+	ld [H_VBCOPYSIZE], a
+	call DelayFrame
+	ld a, c
+	sub 8
+	ld c, a
+	jr .loop
 
-	cp "@"
-	jr nz,.PlaceText
-	ld b,h
-	ld c,l
-	pop hl
-	ret
+CopyVideoDataDouble::
+; Wait for the next VBlank, then copy c 1bpp
+; tiles from b:de to hl, 8 tiles at a time.
+; This takes c/8 frames.
+	ld a, [H_AUTOBGTRANSFERENABLED]
+	push af
+	xor a ; disable auto-transfer while copying
+	ld [H_AUTOBGTRANSFERENABLED], a
+	ld a, [H_LOADEDROMBANK]
+	ld [$ff8b], a
 
-.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
+	ld a, b
+	ld [H_LOADEDROMBANK], a
+	ld [MBC3RomBank], a
 
-.next
-	cp $4F
-	jr nz,.next3
-	pop hl
-	FuncCoord 1, 16
-	ld hl,Coord
-	push hl
-	jp Next19E8
+	ld a, e
+	ld [H_VBCOPYDOUBLESRC], a
+	ld a, d
+	ld [H_VBCOPYDOUBLESRC + 1], a
 
-.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
+	ld a, l
+	ld [H_VBCOPYDOUBLEDEST], a
+	ld a, h
+	ld [H_VBCOPYDOUBLEDEST + 1], a
 
-Char00:: ; 19ec (0:19ec)
-	ld b,h
-	ld c,l
-	pop hl
-	ld de,Char00Text
-	dec de
-	ret
+.loop
+	ld a, c
+	cp 8
+	jr nc, .keepgoing
 
-Char00Text:: ; 0x19f4 “%d ERROR.”
-	TX_FAR _Char00Text
-	db "@"
-
-Char52:: ; 0x19f9 player’s name
-	push de
-	ld de,W_PLAYERNAME
-	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,W_PLAYERMONNAME ; player active monster name
-	jr FinishDTE
-
-.Enemy ; 1A40
-	; print “Enemy ”
-	ld de,Char5AText
-	call PlaceString
-
-	ld h,b
-	ld l,c
-	ld de,W_ENEMYMONNAME ; 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
-	FuncCoord 18, 16
-	ld [Coord],a
-Next1AA2:: ; 1aa2 (0:1aa2)
-	call ProtectedDelay3
-	call ManualTextScroll
-	ld a,$7F
-	FuncCoord 18, 16
-	ld [Coord],a
-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
-	FuncCoord 18, 16
-	ld [Coord],a
-	call ProtectedDelay3
-	call ManualTextScroll
-	FuncCoord 1, 13
-	ld hl,Coord
-	ld bc,$0412
-	call ClearScreenArea
-	ld c,$14
-	call DelayFrames
-	pop de
-	FuncCoord 1, 14
-	ld hl,Coord
-	jp Next19E8
-
-Char49:: ; 1ad5 (0:1ad5)
-	push de
-	ld a,$EE
-	FuncCoord 18, 16
-	ld [Coord],a
-	call ProtectedDelay3
-	call ManualTextScroll
-	FuncCoord 1, 10
-	ld hl,Coord
-	ld bc,$0712
-	call ClearScreenArea
-	ld c,$14
-	call DelayFrames
-	pop de
-	pop hl
-	FuncCoord 1, 11
-	ld hl,Coord
-	push hl
-	jp Next19E8
-
-Char4B:: ; 1af8 (0:1af8)
-	ld a,$EE
-	FuncCoord 18, 16
-	ld [Coord],a
-	call ProtectedDelay3
-	push de
-	call ManualTextScroll
-	pop de
-	ld a,$7F
-	FuncCoord 18, 16
-	ld [Coord],a
-	;fall through
-Char4C:: ; 1b0a (0:1b0a)
-	push de
-	call Next1B18
-	call Next1B18
-	FuncCoord 1, 16
-	ld hl,Coord
-	pop de
-	jp Next19E8
-
-Next1B18:: ; 1b18 (0:1b18)
-	FuncCoord 0, 14
-	ld hl,Coord
-	FuncCoord 0, 13
-	ld de,Coord
-	ld b,$3C
-.next
-	ld a,[hli]
-	ld [de],a
-	inc de
-	dec b
-	jr nz,.next
-	FuncCoord 1, 16
-	ld hl,Coord
-	ld a,$7F
-	ld b,$12
-.next2
-	ld [hli],a
-	dec b
-	jr nz,.next2
-
-	; wait five frames
-	ld b,5
-.WaitFrame
+.done
+	ld [H_VBCOPYDOUBLESIZE], a
 	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
+	ld a, [$ff8b]
+	ld [H_LOADEDROMBANK], a
+	ld [MBC3RomBank], a
 	pop af
-	ld [wd358],a
+	ld [H_AUTOBGTRANSFERENABLED], 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
-	FuncCoord 1, 16
-	ld bc,Coord ; 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
-	FuncCoord 18, 16
-	ld [Coord],a ; 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," "
-	FuncCoord 18, 16
-	ld [Coord],a ; overwrite down arrow with blank space
-	pop hl
-	jp NextTextCommand
-
-; scroll text up one line
-; 07
-; (no arguments)
-TextCommand07:: ; 1be7 (0:1be7)
-	ld a," "
-	FuncCoord 18, 16
-	ld [Coord],a ; place blank space in lower right corner of dialogue text box
-	call Next1B18 ; scroll up text
-	call Next1B18
-	pop hl
-	FuncCoord 1, 16
-	ld bc,Coord ; 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
+.keepgoing
+	ld a, 8
+	ld [H_VBCOPYDOUBLESIZE], a
+	call DelayFrame
+	ld a, c
+	sub 8
+	ld c, a
 	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
+ClearScreenArea::
+; Clear tilemap area cxb at hl.
+	ld a, $7f ; blank tile
+	ld de, 20 ; screen width
+.y
 	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
+.x
+	ld [hli], a
+	dec c
+	jr nz, .x
 	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
-
-; 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
+	add hl, de
 	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
+	jr nz, .y
 	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
-	FuncCoord 0,12
-	ld hl,Coord
-	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
-	FuncCoord 0,0
-	ld hl,Coord
-	ld sp,hl
-	ld a,[H_AUTOBGTRANSFERDEST + 1]
-	ld h,a
-	ld a,[H_AUTOBGTRANSFERDEST]
-	ld l,a
-	ld a,TRANSFERMIDDLE
-	jr .doTransfer
-.transferMiddleThird
-	FuncCoord 0,6
-	ld hl,Coord
-	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
+CopyScreenTileBufferToVRAM::
+; Copy wTileMap to the BG Map starting at b * $100.
+; This is done in thirds of 6 rows, so it takes 3 frames.
 
-; unrolled loop and using pop for speed
-TransferBgRows:: ; 1d9e (0:1d9e)
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	inc l
-	pop de
-	ld [hl],e
-	inc l
-	ld [hl],d
-	ld a,13
-	add l
-	ld l,a
-	jr nc,.noCarry
-	inc h
-.noCarry
-	dec b
-	jr nz,TransferBgRows
-	ld a,[H_SPTEMP]
-	ld h,a
-	ld a,[H_SPTEMP + 1]
-	ld l,a
-	ld sp,hl ; restore stack pointer
-	ret
+	ld c, 6
 
-; 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
+	ld hl, $600 * 0
+	ld de, wTileMap + 20 * 6 * 0
+	call .setup
+	call DelayFrame
 
+	ld hl, $600 * 1
+	ld de, wTileMap + 20 * 6 * 1
+	call .setup
+	call DelayFrame
 
-VBlankCopyDouble::
-; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles
-; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST.
+	ld hl, $600 * 2
+	ld de, wTileMap + 20 * 6 * 2
+	call .setup
+	jp DelayFrame
 
-; 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
+.setup
+	ld a, d
+	ld [H_VBCOPYBGSRC+1], a
+	call GetRowColAddressBgMap
 	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 [H_VBCOPYBGDEST], 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
-
+	ld [H_VBCOPYBGDEST+1], a
+	ld a, c
+	ld [H_VBCOPYBGNUMROWS], a
+	ld a, e
+	ld [H_VBCOPYBGSRC], a
 	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
-
+ClearScreen::
+; Clear wTileMap, then wait
+; for the bg map to update.
+	ld bc, 20 * 18
+	inc b
+	ld hl, wTileMap
+	ld a, $7f
 .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 20
-	ret c
-	cp 21
-	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"
-
-
-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, wc000 ; start of WRAM
-	ld bc, $2000 ; size of WRAM
-.loop
-	ld [hl], 0
-	inc hl
-	dec bc
-	ld a, b
-	or c
+	dec b
 	jr nz, .loop
+	jp Delay3
 
-	call ClearVram
 
-	ld hl, $ff80
-	ld bc, $ffff - $ff80
-	call FillMemory
+INCLUDE "home/text.asm"
+INCLUDE "home/vcopy.asm"
+INCLUDE "home/init.asm"
+INCLUDE "home/vblank.asm"
+INCLUDE "home/fade.asm"
 
-	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
-
-	ld a, $40 ; PREDEF_SGB_BORDER
-	call Predef
-
-	ld a, $1f
-	ld [wc0ef], a
-	ld [wc0f0], a
-	ld a, $9c
-	ld [$ffbd], a
-	xor a
-	ld [$ffbc], a
-	dec a
-	ld [wcfcb], a
-
-	ld a, $32 ; PREDEF_INTRO
-	call Predef
-
-	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(Func_9876)
-	ld [wc0ef], a
-	ld [wc0f0], a
-	xor a
-	ld [wMusicHeaderPointer], a
-	ld [wc0ee], a
-	ld [wcfca], a
-	dec a
-	jp PlaySound
-
-
-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(Func_9103)
-	jr nz, .notbank2
-.bank2
-	call Func_9103
-	jr .afterMusic
-.notbank2
-	cp 8
-	jr nz, .bank1F
-.bank8
-	call Func_2136e
-	call Func_21879
-	jr .afterMusic
-.bank1F
-	call Func_7d177
-.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
-	; XXX this is a hack--rgbasm adds
-	; a nop after halts by default.
-	db $76 ; halt
-
-	ld a, [H_VBLANKOCCURRED]
-	and a
-	jr nz, .halt
-	ret
-
-
-; These routines manage gradual fading
-; (e.g., entering a doorway)
-LoadGBPal:: ; 20ba (0:20ba)
-	ld a,[wd35d] ;tells if cur.map is dark (requires HM5_FLASH?)
-	ld b,a
-	ld hl,GBPalTable_00	;16
-	ld a,l
-	sub b
-	ld l,a
-	jr nc,.jr0
-	dec h
-.jr0
-	ld a,[hli]
-	ld [rBGP],a
-	ld a,[hli]
-	ld [rOBP0],a
-	ld a,[hli]
-	ld [rOBP1],a
-	ret
-
-GBFadeOut1:: ; 20d1 (0:20d1)
-	ld hl,IncGradGBPalTable_01	;0d
-	ld b,$04
-	jr GBFadeOutCommon
-
-GBFadeOut2:: ; 20d8 (0:20d8)
-	ld hl,IncGradGBPalTable_02	;1c
-	ld b,$03
-
-GBFadeOutCommon:: ; 20dd (0:20dd)
-	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:: ; 20ef (0:20ef)
-	ld hl,DecGradGBPalTable_01	;18
-	ld b,$04
-	jr GBFadeInCommon
-
-GBFadeIn2:: ; 20f6 (0:20f6)
-	ld hl,DecGradGBPalTable_02	;21
-	ld b,$03
-
-GBFadeInCommon:: ; 20fb (0:20fb)
-	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
-
-IncGradGBPalTable_01:: ; 210d (0:210d)
-	db %11111111 ;BG Pal
-	db %11111111 ;OBJ Pal 1
-	db %11111111 ;OBJ Pal 2
-	             ;and so on...
-	db %11111110
-	db %11111110
-	db %11111000
-
-	db %11111001
-	db %11100100
-	db %11100100
-GBPalTable_00:: ; 2116 (0:2116)
-	db %11100100
-	db %11010000
-DecGradGBPalTable_01:: ; 2118 (0:2118)
-	db %11100000
-	;19
-	db %11100100
-	db %11010000
-	db %11100000
-IncGradGBPalTable_02:: ; 211c (0:211c)
-	db %10010000
-	db %10000000
-	db %10010000
-
-	db %01000000
-	db %01000000
-DecGradGBPalTable_02:: ; 2121 (0:2121)
-	db %01000000
-
-	db %00000000
-	db %00000000
-	db %00000000
-
 Serial:: ; 2125 (0:2125)
 	push af
 	push bc
@@ -5509,194 +1454,15 @@
 	ld [$ff02], a
 	ret
 
+
 ; timer interrupt is apparently not invoked anyway
 Timer:: ; 2306 (0:2306)
 	reti
 
-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(Func_7d8ea)
-	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
+INCLUDE "home/audio.asm"
 
-Func_235f:: ; 235f (0:235f)
-	ld a, [wc0ef]
-	ld b, a
-	cp $2
-	jr nz, .checkForBank08
-.bank02
-	ld hl, Func_9103
-	jr .asm_2378
-.checkForBank08
-	cp $8
-	jr nz, .bank1F
-.bank08
-	ld hl, Func_21879
-	jr .asm_2378
-.bank1F
-	ld hl, Func_7d177
-.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 $2
-	jr nz, .checkForBank08
-.bank02
-	ld a, b
-	call Func_9876
-	jr .asm_240b
-.checkForBank08
-	cp $8
-	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
-
 UpdateSprites:: ; 2429 (0:2429)
 	ld a, [wcfcb]
 	dec a
@@ -5747,598 +1513,10 @@
 	call Predef
 	jp TextScriptEnd
 
-; 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
+INCLUDE "home/pic.asm"
 
-; 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
-
 ResetPlayerSpriteData:: ; 28a6 (0:28a6)
 	ld hl, wSpriteStateData1
 	call ResetPlayerSpriteData_ClearSpriteData
@@ -9982,56 +5160,7 @@
 	ret
 
 
-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
+INCLUDE "home/predef.asm"
 
 
 Func_3ead:: ; 3ead (0:3ead)
--- /dev/null
+++ b/home/audio.asm
@@ -1,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(Func_7d8ea)
+	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 $2
+	jr nz, .checkForBank08
+.bank02
+	ld hl, Func_9103
+	jr .asm_2378
+.checkForBank08
+	cp $8
+	jr nz, .bank1F
+.bank08
+	ld hl, Func_21879
+	jr .asm_2378
+.bank1F
+	ld hl, Func_7d177
+.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 $2
+	jr nz, .checkForBank08
+.bank02
+	ld a, b
+	call Func_9876
+	jr .asm_240b
+.checkForBank08
+	cp $8
+	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
--- /dev/null
+++ b/home/fade.asm
@@ -1,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
--- /dev/null
+++ b/home/init.asm
@@ -1,0 +1,139 @@
+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
+
+	ld a, $40 ; PREDEF_SGB_BORDER
+	call Predef
+
+	ld a, $1f
+	ld [wc0ef], a
+	ld [wc0f0], a
+	ld a, $9c
+	ld [$ffbd], a
+	xor a
+	ld [$ffbc], a
+	dec a
+	ld [wcfcb], a
+
+	ld a, $32 ; PREDEF_INTRO
+	call Predef
+
+	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(Func_9876)
+	ld [wc0ef], a
+	ld [wc0f0], a
+	xor a
+	ld [wMusicHeaderPointer], a
+	ld [wc0ee], a
+	ld [wcfca], a
+	dec a
+	jp PlaySound
--- /dev/null
+++ b/home/joypad.asm
@@ -1,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
--- /dev/null
+++ b/home/overworld.asm
@@ -1,0 +1,2419 @@
+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
+	ld a,$35
+	call Predef ; 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
+	FuncCoord 8, 9
+	ld a,[Coord]
+	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
+	ld a,$52
+	call Predef
+	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
+	ld a,$13
+	call Predef ; 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 AnyPlayerPokemonAliveCheck ; 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 DoFlyOrTeleportAwayGraphics
+	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)
+	FuncCoord 8, 8
+	ld a,[Coord] ; 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 DoFlyOrTeleportAwayGraphics
+	ld a, Bank(Func_62ce)
+	ld [H_LOADEDROMBANK], a
+	ld [$2000], a
+	call Func_62ce
+	jp Func_5d5f
+
+DoFlyOrTeleportAwayGraphics::
+	ld b, BANK(_DoFlyOrTeleportAwayGraphics)
+	ld hl, _DoFlyOrTeleportAwayGraphics
+	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
+	ld a,$35
+	call Predef ; 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
+	ld a,$35
+	call Predef ; 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)
+	ld a,$35
+	call Predef ; 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
+	ld a,$35
+	call Predef ; 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)
+	FuncCoord 8, 9
+	ld a,[Coord] ; 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)
+	FuncCoord 0, 0
+	ld hl,Coord
+	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)
+	FuncCoord 0,16
+	ld hl,Coord
+	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)
+	FuncCoord 18,0
+	ld hl,Coord
+	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)
+	FuncCoord 0,0
+	ld hl,Coord
+	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
+	ld a,$35
+	call Predef ; 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
+	ld a,$19
+	call Predef ; 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?
--- /dev/null
+++ b/home/pic.asm
@@ -1,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
--- /dev/null
+++ b/home/predef.asm
@@ -1,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
--- /dev/null
+++ b/home/text.asm
@@ -1,0 +1,738 @@
+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
+	FuncCoord 1, 16
+	ld hl,Coord
+	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,W_PLAYERNAME
+	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,W_PLAYERMONNAME ; player active monster name
+	jr FinishDTE
+
+.Enemy ; 1A40
+	; print “Enemy ”
+	ld de,Char5AText
+	call PlaceString
+
+	ld h,b
+	ld l,c
+	ld de,W_ENEMYMONNAME ; 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
+	FuncCoord 18, 16
+	ld [Coord],a
+Next1AA2:: ; 1aa2 (0:1aa2)
+	call ProtectedDelay3
+	call ManualTextScroll
+	ld a,$7F
+	FuncCoord 18, 16
+	ld [Coord],a
+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
+	FuncCoord 18, 16
+	ld [Coord],a
+	call ProtectedDelay3
+	call ManualTextScroll
+	FuncCoord 1, 13
+	ld hl,Coord
+	ld bc,$0412
+	call ClearScreenArea
+	ld c,$14
+	call DelayFrames
+	pop de
+	FuncCoord 1, 14
+	ld hl,Coord
+	jp Next19E8
+
+Char49:: ; 1ad5 (0:1ad5)
+	push de
+	ld a,$EE
+	FuncCoord 18, 16
+	ld [Coord],a
+	call ProtectedDelay3
+	call ManualTextScroll
+	FuncCoord 1, 10
+	ld hl,Coord
+	ld bc,$0712
+	call ClearScreenArea
+	ld c,$14
+	call DelayFrames
+	pop de
+	pop hl
+	FuncCoord 1, 11
+	ld hl,Coord
+	push hl
+	jp Next19E8
+
+Char4B:: ; 1af8 (0:1af8)
+	ld a,$EE
+	FuncCoord 18, 16
+	ld [Coord],a
+	call ProtectedDelay3
+	push de
+	call ManualTextScroll
+	pop de
+	ld a,$7F
+	FuncCoord 18, 16
+	ld [Coord],a
+	;fall through
+Char4C:: ; 1b0a (0:1b0a)
+	push de
+	call Next1B18
+	call Next1B18
+	FuncCoord 1, 16
+	ld hl,Coord
+	pop de
+	jp Next19E8
+
+Next1B18:: ; 1b18 (0:1b18)
+	FuncCoord 0, 14
+	ld hl,Coord
+	FuncCoord 0, 13
+	ld de,Coord
+	ld b,$3C
+.next
+	ld a,[hli]
+	ld [de],a
+	inc de
+	dec b
+	jr nz,.next
+	FuncCoord 1, 16
+	ld hl,Coord
+	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
+	FuncCoord 1, 16
+	ld bc,Coord ; 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
+	FuncCoord 18, 16
+	ld [Coord],a ; 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," "
+	FuncCoord 18, 16
+	ld [Coord],a ; overwrite down arrow with blank space
+	pop hl
+	jp NextTextCommand
+
+; scroll text up one line
+; 07
+; (no arguments)
+TextCommand07:: ; 1be7 (0:1be7)
+	ld a," "
+	FuncCoord 18, 16
+	ld [Coord],a ; place blank space in lower right corner of dialogue text box
+	call Next1B18 ; scroll up text
+	call Next1B18
+	pop hl
+	FuncCoord 1, 16
+	ld bc,Coord ; 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
--- /dev/null
+++ b/home/vblank.asm
@@ -1,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(Func_9103)
+	jr nz, .notbank2
+.bank2
+	call Func_9103
+	jr .afterMusic
+.notbank2
+	cp 8
+	jr nz, .bank1F
+.bank8
+	call Func_2136e
+	call Func_21879
+	jr .afterMusic
+.bank1F
+	call Func_7d177
+.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
--- /dev/null
+++ b/home/vcopy.asm
@@ -1,0 +1,450 @@
+; 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
+	FuncCoord 0,12
+	ld hl,Coord
+	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
+	FuncCoord 0,0
+	ld hl,Coord
+	ld sp,hl
+	ld a,[H_AUTOBGTRANSFERDEST + 1]
+	ld h,a
+	ld a,[H_AUTOBGTRANSFERDEST]
+	ld l,a
+	ld a,TRANSFERMIDDLE
+	jr .doTransfer
+.transferMiddleThird
+	FuncCoord 0,6
+	ld hl,Coord
+	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
+
+i	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"
--- a/main.asm
+++ b/main.asm
@@ -1,6 +1,17 @@
 INCLUDE "constants.asm"
 
+NPC_SPRITES_1 EQU $4
+NPC_SPRITES_2 EQU $5
 
+GFX EQU $4
+
+PICS_1 EQU $9
+PICS_2 EQU $A
+PICS_3 EQU $B
+PICS_4 EQU $C
+PICS_5 EQU $D
+
+
 INCLUDE "home.asm"
 
 
@@ -1940,58 +1951,8 @@
 
 SECTION "bank3",ROMX,BANK[$3]
 
-_Joypad::
-	ld a, [hJoyInput]
-	cp A_BUTTON + B_BUTTON + SELECT + START ; soft reset
-	jp z, TrySoftReset
-	ld b, a
-	ld a, [hJoyHeldLast]
-	ld e, a
-	xor b
-	ld d, a
-	and e
-	ld [hJoyReleased], a
-	ld a, d
-	and b
-	ld [hJoyPressed], a
-	ld a, b
-	ld [hJoyHeldLast], a
-	ld a, [wd730]
-	bit 5, a
-	jr nz, DiscardButtonPresses
-	ld a, [hJoyHeldLast]
-	ld [hJoyHeld], a
-	ld a, [wJoyIgnore]
-	and a
-	ret z
-	cpl
-	ld b, a
-	ld a, [hJoyHeld]
-	and b
-	ld [hJoyHeld], a
-	ld a, [hJoyPressed]
-	and b
-	ld [hJoyPressed], a
-	ret
+INCLUDE "engine/joypad.asm"
 
-DiscardButtonPresses:
-	xor a
-	ld [hJoyHeld], a
-	ld [hJoyPressed], a
-	ld [hJoyReleased], a
-	ret
-
-TrySoftReset:
-	call DelayFrame
-	; reset joypad (to make sure the
-	; player is really trying to reset)
-	ld a, $30
-	ld [rJOYP], a
-	ld hl, hSoftReset
-	dec [hl]
-	jp z, SoftReset
-	jp Joypad
-
 INCLUDE "data/map_songs.asm"
 
 INCLUDE "data/map_header_banks.asm"
@@ -4657,7 +4618,7 @@
 INCLUDE "engine/hidden_object_functions3.asm"
 
 
-SECTION "bank4",ROMX,BANK[$4]
+SECTION "NPC Sprites 1", ROMX, BANK[NPC_SPRITES_1]
 
 OakAideSprite:         INCBIN "gfx/sprites/oak_aide.2bpp"
 RockerSprite:          INCBIN "gfx/sprites/rocker.2bpp"
@@ -4687,6 +4648,9 @@
 OldAmberSprite:        INCBIN "gfx/sprites/old_amber.2bpp"
 LyingOldManSprite:     INCBIN "gfx/sprites/lying_old_man.2bpp"
 
+
+SECTION "Graphics", ROMX, BANK[GFX]
+
 PokemonLogoGraphics:            INCBIN "gfx/pokemon_logo.2bpp"
 FontGraphics:                   INCBIN "gfx/font.1bpp"
 ABTiles:                        INCBIN "gfx/AB.2bpp"
@@ -4701,6 +4665,9 @@
 WorldMapTileGraphics:           INCBIN "gfx/town_map.2bpp"
 PlayerCharacterTitleGraphics:   INCBIN "gfx/player_title.2bpp"
 
+
+SECTION "Battle (bank 4)", ROMX, BANK[$4]
+
 INCLUDE "engine/battle/4.asm"
 INCLUDE "engine/menu/status_screen.asm"
 INCLUDE "engine/menu/party_menu.asm"
@@ -4710,17 +4677,13 @@
 ShrinkPic2::  INCBIN "pic/trainer/shrink2.pic"
 
 INCLUDE "engine/turn_sprite.asm"
-
 INCLUDE "engine/menu/start_sub_menus.asm"
-
 INCLUDE "engine/items/tms.asm"
-
 INCLUDE "engine/battle/4_2.asm"
-
 INCLUDE "engine/random.asm"
 
 
-SECTION "bank5",ROMX,BANK[$5]
+SECTION "NPC Sprites 2", ROMX, BANK[NPC_SPRITES_2]
 
 RedCyclingSprite:     INCBIN "gfx/sprites/cycling.2bpp"
 RedSprite:            INCBIN "gfx/sprites/red.2bpp"
@@ -4763,15 +4726,14 @@
 LoreleiSprite:        INCBIN "gfx/sprites/lorelei.2bpp"
 SeelSprite:           INCBIN "gfx/sprites/seel.2bpp"
 
-INCLUDE "engine/load_pokedex_tiles.asm"
 
+SECTION "Battle (bank 5)", ROMX, BANK[$5]
+
+INCLUDE "engine/load_pokedex_tiles.asm"
 INCLUDE "engine/overworld/map_sprites.asm"
 INCLUDE "engine/overworld/emotion_bubbles.asm"
-
 INCLUDE "engine/evolve_trade.asm"
-
 INCLUDE "engine/battle/5.asm"
-
 INCLUDE "engine/menu/pc.asm"
 
 
@@ -5031,7 +4993,7 @@
 INCLUDE "engine/hidden_object_functions7.asm"
 
 
-SECTION "bank9",ROMX,BANK[$9]
+SECTION "Pics 1", ROMX, BANK[PICS_1]
 
 RhydonPicFront::      INCBIN "pic/bmon/rhydon.pic"
 RhydonPicBack::       INCBIN "pic/monback/rhydonb.pic"
@@ -5092,10 +5054,12 @@
 TangelaPicFront::     INCBIN "pic/bmon/tangela.pic"
 TangelaPicBack::      INCBIN "pic/monback/tangelab.pic"
 
+
+SECTION "Battle (bank 9)", ROMX, BANK[$9]
 INCLUDE "engine/battle/9.asm"
 
 
-SECTION "bankA",ROMX,BANK[$A]
+SECTION "Pics 2", ROMX, BANK[PICS_2]
 
 GrowlithePicFront::   INCBIN "pic/bmon/growlithe.pic"
 GrowlithePicBack::    INCBIN "pic/monback/growlitheb.pic"
@@ -5162,10 +5126,12 @@
 MoltresPicFront::     INCBIN "pic/bmon/moltres.pic"
 MoltresPicBack::      INCBIN "pic/monback/moltresb.pic"
 
+
+SECTION "Battle (bank A)", ROMX, BANK[$A]
 INCLUDE "engine/battle/a.asm"
 
 
-SECTION "bankB",ROMX,BANK[$B]
+SECTION "Pics 3", ROMX, BANK[PICS_3]
 
 ArticunoPicFront::    INCBIN "pic/bmon/articuno.pic"
 ArticunoPicBack::     INCBIN "pic/monback/articunob.pic"
@@ -5238,6 +5204,9 @@
 
 FossilKabutopsPic::   INCBIN "pic/bmon/fossilkabutops.pic"
 
+
+SECTION "Battle (bank B)", ROMX, BANK[$B]
+
 INCLUDE "engine/battle/b.asm"
 
 TrainerInfoTextBoxTileGraphics:  INCBIN "gfx/trainer_info.2bpp"
@@ -5250,7 +5219,7 @@
 INCLUDE "engine/game_corner_slots2.asm"
 
 
-SECTION "bankC",ROMX,BANK[$C]
+SECTION "Pics 4", ROMX, BANK[PICS_4]
 
 DodrioPicFront::       INCBIN "pic/bmon/dodrio.pic"
 DodrioPicBack::        INCBIN "pic/monback/dodriob.pic"
@@ -5314,10 +5283,12 @@
 RedPicBack::           INCBIN "pic/trainer/redb.pic"
 OldManPic::            INCBIN "pic/trainer/oldman.pic"
 
+
+SECTION "Battle (bank C)", ROMX, BANK[$C]
 INCLUDE "engine/battle/c.asm"
 
 
-SECTION "bankD",ROMX,BANK[$D]
+SECTION "Pics 5", ROMX, BANK[PICS_5]
 
 BulbasaurPicFront::    INCBIN "pic/bmon/bulbasaur.pic"
 BulbasaurPicBack::     INCBIN "pic/monback/bulbasaurb.pic"
@@ -5373,6 +5344,9 @@
 WeepinbellPicBack::    INCBIN "pic/monback/weepinbellb.pic"
 VictreebelPicFront::   INCBIN "pic/bmon/victreebel.pic"
 VictreebelPicBack::    INCBIN "pic/monback/victreebelb.pic"
+
+
+SECTION "Battle (bank D)", ROMX, BANK[$D]
 
 INCLUDE "engine/titlescreen2.asm"
 INCLUDE "engine/battle/d.asm"