code:
	.align 2
gTileset_General:: @ 83DF704
	.byte TRUE @ is compressed
	.byte FALSE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_General
	.4byte gTilesetPalettes_General
	.4byte gMetatiles_General
	.4byte gMetatileAttributes_General
	.4byte InitTilesetAnim_General

	.align 2
gTileset_Petalburg:: @ 83DF71C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Petalburg
	.4byte gTilesetPalettes_Petalburg
	.4byte gMetatiles_Petalburg
	.4byte gMetatileAttributes_Petalburg
	.4byte InitTilesetAnim_Petalburg

	.align 2
gTileset_Rustboro:: @ 83DF734
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Rustboro
	.4byte gTilesetPalettes_Rustboro
	.4byte gMetatiles_Rustboro
	.4byte gMetatileAttributes_Rustboro
	.4byte InitTilesetAnim_Rustboro

	.align 2
gTileset_Dewford:: @ 83DF74C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Dewford
	.4byte gTilesetPalettes_Dewford
	.4byte gMetatiles_Dewford
	.4byte gMetatileAttributes_Dewford
	.4byte InitTilesetAnim_Dewford

	.align 2
gTileset_Slateport:: @ 83DF764
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Slateport
	.4byte gTilesetPalettes_Slateport
	.4byte gMetatiles_Slateport
	.4byte gMetatileAttributes_Slateport
	.4byte InitTilesetAnim_Slateport

	.align 2
gTileset_Mauville:: @ 83DF77C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Mauville
	.4byte gTilesetPalettes_Mauville
	.4byte gMetatiles_Mauville
	.4byte gMetatileAttributes_Mauville
	.4byte InitTilesetAnim_Mauville

	.align 2
gTileset_Lavaridge:: @ 83DF794
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Lavaridge
	.4byte gTilesetPalettes_Lavaridge
	.4byte gMetatiles_Lavaridge
	.4byte gMetatileAttributes_Lavaridge
	.4byte InitTilesetAnim_Lavaridge

	.align 2
gTileset_Fallarbor:: @ 83DF7AC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Fallarbor
	.4byte gTilesetPalettes_Fallarbor
	.4byte gMetatiles_Fallarbor
	.4byte gMetatileAttributes_Fallarbor
	.4byte InitTilesetAnim_Fallarbor

	.align 2
gTileset_Fortree:: @ 83DF7C4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Fortree
	.4byte gTilesetPalettes_Fortree
	.4byte gMetatiles_Fortree
	.4byte gMetatileAttributes_Fortree
	.4byte InitTilesetAnim_Fortree

	.align 2
gTileset_Lilycove:: @ 83DF7DC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Lilycove
	.4byte gTilesetPalettes_Lilycove
	.4byte gMetatiles_Lilycove
	.4byte gMetatileAttributes_Lilycove
	.4byte InitTilesetAnim_Lilycove

	.align 2
gTileset_Mossdeep:: @ 83DF7F4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Mossdeep
	.4byte gTilesetPalettes_Mossdeep
	.4byte gMetatiles_Mossdeep
	.4byte gMetatileAttributes_Mossdeep
	.4byte InitTilesetAnim_Mossdeep

	.align 2
gTileset_EverGrande:: @ 83DF80C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_EverGrande
	.4byte gTilesetPalettes_EverGrande
	.4byte gMetatiles_EverGrande
	.4byte gMetatileAttributes_EverGrande
	.4byte InitTilesetAnim_EverGrande

	.align 2
gTileset_Pacifidlog:: @ 83DF824
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Pacifidlog
	.4byte gTilesetPalettes_Pacifidlog
	.4byte gMetatiles_Pacifidlog
	.4byte gMetatileAttributes_Pacifidlog
	.4byte InitTilesetAnim_Pacifidlog

	.align 2
gTileset_Sootopolis:: @ 83DF83C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Sootopolis
	.4byte gTilesetPalettes_Sootopolis
	.4byte gMetatiles_Sootopolis
	.4byte gMetatileAttributes_Sootopolis
	.4byte InitTilesetAnim_Sootopolis

	.align 2
gTileset_BattleFrontierOutsideWest:: @ 83DF854
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleFrontierOutsideWest
	.4byte gTilesetPalettes_BattleFrontierOutsideWest
	.4byte gMetatiles_BattleFrontierOutsideWest
	.4byte gMetatileAttributes_BattleFrontierOutsideWest
	.4byte InitTilesetAnim_BattleFrontierOutsideWest

	.align 2
gTileset_BattleFrontierOutsideEast:: @ 83DF86C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleFrontierOutsideEast
	.4byte gTilesetPalettes_BattleFrontierOutsideEast
	.4byte gMetatiles_BattleFrontierOutsideEast
	.4byte gMetatileAttributes_BattleFrontierOutsideEast
	.4byte InitTilesetAnim_BattleFrontierOutsideEast

	.align 2
gTileset_Building:: @ 83DF884
	.byte TRUE @ is compressed
	.byte FALSE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_InsideBuilding
	.4byte gTilesetPalettes_InsideBuilding
	.4byte gMetatiles_InsideBuilding
	.4byte gMetatileAttributes_InsideBuilding
	.4byte InitTilesetAnim_Building

	.align 2
gTileset_Shop:: @ 83DF89C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Shop
	.4byte gTilesetPalettes_Shop
	.4byte gMetatiles_Shop
	.4byte gMetatileAttributes_Shop
	.4byte NULL @ animation callback

	.align 2
gTileset_PokemonCenter:: @ 83DF8B4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PokemonCenter
	.4byte gTilesetPalettes_PokemonCenter
	.4byte gMetatiles_PokemonCenter
	.4byte gMetatileAttributes_PokemonCenter
	.4byte NULL @ animation callback

	.align 2
gTileset_Cave:: @ 83DF8CC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Cave
	.4byte gTilesetPalettes_Cave
	.4byte gMetatiles_Cave
	.4byte gMetatileAttributes_Cave
	.4byte InitTilesetAnim_Cave

	.align 2
gTileset_PokemonSchool:: @ 83DF8E4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PokemonSchool
	.4byte gTilesetPalettes_PokemonSchool
	.4byte gMetatiles_PokemonSchool
	.4byte gMetatileAttributes_PokemonSchool
	.4byte NULL @ animation callback

	.align 2
gTileset_PokemonFanClub:: @ 83DF8FC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PokemonFanClub
	.4byte gTilesetPalettes_PokemonFanClub
	.4byte gMetatiles_PokemonFanClub
	.4byte gMetatileAttributes_PokemonFanClub
	.4byte NULL @ animation callback

	.align 2
gTileset_Unused1:: @ 83DF914
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Unused1
	.4byte gTilesetPalettes_Unused1
	.4byte gMetatiles_Unused1
	.4byte gMetatileAttributes_Unused1
	.4byte NULL @ animation callback

	.align 2
gTileset_MeteorFalls:: @ 83DF92C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MeteorFalls
	.4byte gTilesetPalettes_MeteorFalls
	.4byte gMetatiles_MeteorFalls
	.4byte gMetatileAttributes_MeteorFalls
	.4byte NULL @ animation callback

	.align 2
gTileset_OceanicMuseum:: @ 83DF944
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_OceanicMuseum
	.4byte gTilesetPalettes_OceanicMuseum
	.4byte gMetatiles_OceanicMuseum
	.4byte gMetatileAttributes_OceanicMuseum
	.4byte NULL @ animation callback

	.align 2
gTileset_CableClub:: @ 83DF95C
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_CableClub
	.4byte gTilesetPalettes_CableClub
	.4byte gMetatiles_CableClub
	.4byte gMetatileAttributes_CableClub
	.4byte NULL @ animation callback

	.align 2
gTileset_SeashoreHouse:: @ 83DF974
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SeashoreHouse
	.4byte gTilesetPalettes_SeashoreHouse
	.4byte gMetatiles_SeashoreHouse
	.4byte gMetatileAttributes_SeashoreHouse
	.4byte NULL @ animation callback

	.align 2
gTileset_PrettyPetalFlowerShop:: @ 83DF98C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PrettyPetalFlowerShop
	.4byte gTilesetPalettes_PrettyPetalFlowerShop
	.4byte gMetatiles_PrettyPetalFlowerShop
	.4byte gMetatileAttributes_PrettyPetalFlowerShop
	.4byte NULL @ animation callback

	.align 2
gTileset_PokemonDayCare:: @ 83DF9A4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PokemonDayCare
	.4byte gTilesetPalettes_PokemonDayCare
	.4byte gMetatiles_PokemonDayCare
	.4byte gMetatileAttributes_PokemonDayCare
	.4byte NULL @ animation callback

	.align 2
gTileset_Facility:: @ 83DF9BC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Facility
	.4byte gTilesetPalettes_Facility
	.4byte gMetatiles_Facility
	.4byte gMetatileAttributes_Facility
	.4byte NULL @ animation callback

	.align 2
gTileset_BikeShop:: @ 83DF9D4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BikeShop
	.4byte gTilesetPalettes_BikeShop
	.4byte gMetatiles_BikeShop
	.4byte gMetatileAttributes_BikeShop
	.4byte InitTilesetAnim_BikeShop

	.align 2
gTileset_RusturfTunnel:: @ 83DF9EC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_RusturfTunnel
	.4byte gTilesetPalettes_RusturfTunnel
	.4byte gMetatiles_RusturfTunnel
	.4byte gMetatileAttributes_RusturfTunnel
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseBrownCave:: @ 83DFA04
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseBrownCave
	.4byte gTilesetPalettes_SecretBaseBrownCave
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseTree:: @ 83DFA1C
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseTree
	.4byte gTilesetPalettes_SecretBaseTree
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseShrub:: @ 83DFA34
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseShrub
	.4byte gTilesetPalettes_SecretBaseShrub
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseBlueCave:: @ 83DFA4C
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseBlueCave
	.4byte gTilesetPalettes_SecretBaseBlueCave
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseYellowCave:: @ 83DFA64
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseYellowCave
	.4byte gTilesetPalettes_SecretBaseYellowCave
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBaseRedCave:: @ 83DFA7C
	.byte FALSE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBaseRedCave
	.4byte gTilesetPalettes_SecretBaseRedCave
	.4byte gMetatiles_SecretBaseSecondary
	.4byte gMetatileAttributes_SecretBaseSecondary
	.4byte NULL @ animation callback

	.align 2
gTileset_InsideOfTruck:: @ 83DFA94
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_InsideOfTruck
	.4byte gTilesetPalettes_InsideOfTruck
	.4byte gMetatiles_InsideOfTruck
	.4byte gMetatileAttributes_InsideOfTruck
	.4byte NULL @ animation callback

	.align 2
gTileset_Unused2:: @ 83DFAAC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Unused2
	.4byte gTilesetPalettes_Unused2
	.4byte gMetatiles_Unused2
	.4byte gMetatileAttributes_Unused2
	.4byte NULL @ animation callback

	.align 2
gTileset_Contest:: @ 83DFAC4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Contest
	.4byte gTilesetPalettes_Contest
	.4byte gMetatiles_Contest
	.4byte gMetatileAttributes_Contest
	.4byte NULL @ animation callback

	.align 2
gTileset_LilycoveMuseum:: @ 83DFADC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_LilycoveMuseum
	.4byte gTilesetPalettes_LilycoveMuseum
	.4byte gMetatiles_LilycoveMuseum
	.4byte gMetatileAttributes_LilycoveMuseum
	.4byte NULL @ animation callback

	.align 2
gTileset_BrendansMaysHouse:: @ 83DFAF4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BrendansMaysHouse
	.4byte gTilesetPalettes_BrendansMaysHouse
	.4byte gMetatiles_BrendansMaysHouse
	.4byte gMetatileAttributes_BrendansMaysHouse
	.4byte NULL @ animation callback

	.align 2
gTileset_Lab:: @ 83DFB0C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Lab
	.4byte gTilesetPalettes_Lab
	.4byte gMetatiles_Lab
	.4byte gMetatileAttributes_Lab
	.4byte NULL @ animation callback

	.align 2
gTileset_Underwater:: @ 83DFB24
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_Underwater
	.4byte gTilesetPalettes_Underwater
	.4byte gMetatiles_Underwater
	.4byte gMetatileAttributes_Underwater
	.4byte InitTilesetAnim_Underwater

	.align 2
gTileset_PetalburgGym:: @ 83DFB3C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_PetalburgGym
	.4byte gTilesetPalettes_PetalburgGym
	.4byte gMetatiles_PetalburgGym
	.4byte gMetatileAttributes_PetalburgGym
	.4byte NULL @ animation callback

	.align 2
gTileset_SootopolisGym:: @ 83DFB54
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SootopolisGym
	.4byte gTilesetPalettes_SootopolisGym
	.4byte gMetatiles_SootopolisGym
	.4byte gMetatileAttributes_SootopolisGym
	.4byte InitTilesetAnim_SootopolisGym

	.align 2
gTileset_GenericBuilding:: @ 83DFB6C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_GenericBuilding
	.4byte gTilesetPalettes_GenericBuilding
	.4byte gMetatiles_GenericBuilding
	.4byte gMetatileAttributes_GenericBuilding
	.4byte NULL @ animation callback

	.align 2
gTileset_MauvilleGameCorner:: @ 83DFB84
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MauvilleGameCorner
	.4byte gTilesetPalettes_MauvilleGameCorner
	.4byte gMetatiles_MauvilleGameCorner
	.4byte gMetatileAttributes_MauvilleGameCorner
	.4byte NULL @ animation callback

	.align 2
gTileset_RustboroGym:: @ 83DFB9C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_RustboroGym
	.4byte gTilesetPalettes_RustboroGym
	.4byte gMetatiles_RustboroGym
	.4byte gMetatileAttributes_RustboroGym
	.4byte NULL @ animation callback

	.align 2
gTileset_DewfordGym:: @ 83DFBB4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_DewfordGym
	.4byte gTilesetPalettes_DewfordGym
	.4byte gMetatiles_DewfordGym
	.4byte gMetatileAttributes_DewfordGym
	.4byte NULL @ animation callback

	.align 2
gTileset_MauvilleGym:: @ 83DFBCC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MauvilleGym
	.4byte gTilesetPalettes_MauvilleGym
	.4byte gMetatiles_MauvilleGym
	.4byte gMetatileAttributes_MauvilleGym
	.4byte InitTilesetAnim_MauvilleGym

	.align 2
gTileset_LavaridgeGym:: @ 83DFBE4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_LavaridgeGym
	.4byte gTilesetPalettes_LavaridgeGym
	.4byte gMetatiles_LavaridgeGym
	.4byte gMetatileAttributes_LavaridgeGym
	.4byte NULL @ animation callback

	.align 2
gTileset_TrickHousePuzzle:: @ 83DFBFC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_TrickHousePuzzle
	.4byte gTilesetPalettes_TrickHousePuzzle
	.4byte gMetatiles_TrickHousePuzzle
	.4byte gMetatileAttributes_TrickHousePuzzle
	.4byte NULL @ animation callback

	.align 2
gTileset_FortreeGym:: @ 83DFC14
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_FortreeGym
	.4byte gTilesetPalettes_FortreeGym
	.4byte gMetatiles_FortreeGym
	.4byte gMetatileAttributes_FortreeGym
	.4byte NULL @ animation callback

	.align 2
gTileset_MossdeepGym:: @ 83DFC2C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MossdeepGym
	.4byte gTilesetPalettes_MossdeepGym
	.4byte gMetatiles_MossdeepGym
	.4byte gMetatileAttributes_MossdeepGym
	.4byte NULL @ animation callback

	.align 2
gTileset_InsideShip:: @ 83DFC44
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_InsideShip
	.4byte gTilesetPalettes_InsideShip
	.4byte gMetatiles_InsideShip
	.4byte gMetatileAttributes_InsideShip
	.4byte NULL @ animation callback

	.align 2
gTileset_SecretBase:: @ 83DFC5C
	.byte FALSE @ is compressed
	.byte FALSE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_SecretBase
	.4byte gTilesetPalettes_SecretBase
	.4byte gMetatiles_SecretBasePrimary
	.4byte gMetatileAttributes_SecretBasePrimary
	.4byte NULL @ animation callback

	.align 2
gTilesetPointer_SecretBase:: @ 83DFC74
	.4byte gTileset_SecretBase

	.align 2
gTilesetPointer_SecretBaseRedCave:: @ 83DFC78
	.4byte gTileset_SecretBaseRedCave

	.align 2
gTileset_EliteFour:: @ 83DFC7C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_EliteFour
	.4byte gTilesetPalettes_EliteFour
	.4byte gMetatiles_EliteFour
	.4byte gMetatileAttributes_EliteFour
	.4byte InitTilesetAnim_EliteFour

	.align 2
gTileset_BattleFrontier:: @ 83DFC94
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleFrontier
	.4byte gTilesetPalettes_BattleFrontier
	.4byte gMetatiles_BattleFrontier
	.4byte gMetatileAttributes_BattleFrontier
	.4byte NULL @ animation callback

	.align 2
gTileset_BattlePalace:: @ 83DFCAC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattlePalace
	.4byte gTilesetPalettes_BattlePalace
	.4byte gMetatiles_BattlePalace
	.4byte gMetatileAttributes_BattlePalace
	.4byte NULL @ animation callback

	.align 2
gTileset_BattleDome:: @ 83DFCC4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleDome
	.4byte gTilesetPalettes_BattleDome
	.4byte gMetatiles_BattleDome
	.4byte gMetatileAttributes_BattleDome
	.4byte InitTilesetAnim_BattleDome

	.align 2
gTileset_BattleFactory:: @ 83DFCDC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleFactory
	.4byte gTilesetPalettes_BattleFactory
	.4byte gMetatiles_BattleFactory
	.4byte gMetatileAttributes_BattleFactory
	.4byte NULL @ animation callback

	.align 2
gTileset_BattlePike:: @ 83DFCF4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattlePike
	.4byte gTilesetPalettes_BattlePike
	.4byte gMetatiles_BattlePike
	.4byte gMetatileAttributes_BattlePike
	.4byte NULL @ animation callback

	.align 2
gTileset_BattleArena:: @ 83DFD0C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleArena
	.4byte gTilesetPalettes_BattleArena
	.4byte gMetatiles_BattleArena
	.4byte gMetatileAttributes_BattleArena
	.4byte NULL @ animation callback

	.align 2
gTileset_BattlePyramid:: @ 83DFD24
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattlePyramid
	.4byte gTilesetPalettes_BattlePyramid
	.4byte gMetatiles_BattlePyramid
	.4byte gMetatileAttributes_BattlePyramid
	.4byte InitTilesetAnim_BattlePyramid

	.align 2
gTileset_MirageTower:: @ 83DFD3C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MirageTower
	.4byte gTilesetPalettes_MirageTower
	.4byte gMetatiles_MirageTower
	.4byte gMetatileAttributes_MirageTower
	.4byte NULL @ animation callback

	.align 2
gTileset_MossdeepGameCorner:: @ 83DFD54
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MossdeepGameCorner
	.4byte gTilesetPalettes_MossdeepGameCorner
	.4byte gMetatiles_MossdeepGameCorner
	.4byte gMetatileAttributes_MossdeepGameCorner
	.4byte NULL @ animation callback

	.align 2
gTileset_IslandHarbor:: @ 83DFD6C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_IslandHarbor
	.4byte gTilesetPalettes_IslandHarbor
	.4byte gMetatiles_IslandHarbor
	.4byte gMetatileAttributes_IslandHarbor
	.4byte NULL @ animation callback

	.align 2
gTileset_TrainerHill:: @ 83DFD84
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_TrainerHill
	.4byte gTilesetPalettes_TrainerHill
	.4byte gMetatiles_TrainerHill
	.4byte gMetatileAttributes_TrainerHill
	.4byte NULL @ animation callback

	.align 2
gTileset_NavelRock:: @ 83DFD9C
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_NavelRock
	.4byte gTilesetPalettes_NavelRock
	.4byte gMetatiles_NavelRock
	.4byte gMetatileAttributes_NavelRock
	.4byte NULL @ animation callback

	.align 2
gTileset_BattleFrontierRankingHall:: @ 83DFDB4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleFrontierRankingHall
	.4byte gTilesetPalettes_BattleFrontierRankingHall
	.4byte gMetatiles_BattleFrontierRankingHall
	.4byte gMetatileAttributes_BattleFrontierRankingHall
	.4byte NULL @ animation callback

	.align 2
gTileset_BattleTent:: @ 83DFDCC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_BattleTent
	.4byte gTilesetPalettes_BattleTent
	.4byte gMetatiles_BattleTent
	.4byte gMetatileAttributes_BattleTent
	.4byte NULL @ animation callback

	.align 2
gTileset_MysteryEventsHouse:: @ 83DFDE4
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_MysteryEventsHouse
	.4byte gTilesetPalettes_MysteryEventsHouse
	.4byte gMetatiles_MysteryEventsHouse
	.4byte gMetatileAttributes_MysteryEventsHouse
	.4byte NULL @ animation callback

	.align 2
gTileset_UnionRoom:: @ 83DFDFC
	.byte TRUE @ is compressed
	.byte TRUE @ is secondary tileset
	.2byte 0 @ padding
	.4byte gTilesetTiles_UnionRoom
	.4byte gTilesetPalettes_UnionRoom
	.4byte gMetatiles_UnionRoom
	.4byte gMetatileAttributes_UnionRoom
	.4byte NULL @ animation callback


code:
	.align 1
gMetatiles_General:: @ 83960F0
	.incbin "data/tilesets/primary/general/metatiles.bin"

	.align 1
gMetatileAttributes_General:: @ 83980F0
	.incbin "data/tilesets/primary/general/metatile_attributes.bin"

	.align 1
gMetatiles_Petalburg:: @ 83984F0
	.incbin "data/tilesets/secondary/petalburg/metatiles.bin"

	.align 1
gMetatileAttributes_Petalburg:: @ 8398DF0
	.incbin "data/tilesets/secondary/petalburg/metatile_attributes.bin"

	.align 1
gMetatiles_Rustboro:: @ 8398F10
	.incbin "data/tilesets/secondary/rustboro/metatiles.bin"

	.align 1
gMetatileAttributes_Rustboro:: @ 839A4F0
	.incbin "data/tilesets/secondary/rustboro/metatile_attributes.bin"

	.align 1
gMetatiles_Dewford:: @ 839A7AC
	.incbin "data/tilesets/secondary/dewford/metatiles.bin"

	.align 1
gMetatileAttributes_Dewford:: @ 839BF5C
	.incbin "data/tilesets/secondary/dewford/metatile_attributes.bin"

	.align 1
gMetatiles_Slateport:: @ 839C252
	.incbin "data/tilesets/secondary/slateport/metatiles.bin"

	.align 1
gMetatileAttributes_Slateport:: @ 839DBB2
	.incbin "data/tilesets/secondary/slateport/metatile_attributes.bin"

	.align 1
gMetatiles_Mauville:: @ 839DEDE
	.incbin "data/tilesets/secondary/mauville/metatiles.bin"

	.align 1
gMetatileAttributes_Mauville:: @ 839FEBE
	.incbin "data/tilesets/secondary/mauville/metatile_attributes.bin"

	.align 1
gMetatiles_Lavaridge:: @ 83A02BA
	.incbin "data/tilesets/secondary/lavaridge/metatiles.bin"

	.align 1
gMetatileAttributes_Lavaridge:: @ 83A1E4A
	.incbin "data/tilesets/secondary/lavaridge/metatile_attributes.bin"

	.align 1
gMetatiles_Fallarbor:: @ 83A21BC
	.incbin "data/tilesets/secondary/fallarbor/metatiles.bin"

	.align 1
gMetatileAttributes_Fallarbor:: @ 83A38AC
	.incbin "data/tilesets/secondary/fallarbor/metatile_attributes.bin"

	.align 1
gMetatiles_Fortree:: @ 83A3B8A
	.incbin "data/tilesets/secondary/fortree/metatiles.bin"

	.align 1
gMetatileAttributes_Fortree:: @ 83A4D0A
	.incbin "data/tilesets/secondary/fortree/metatile_attributes.bin"

	.align 1
gMetatiles_Lilycove:: @ 83A4F3A
	.incbin "data/tilesets/secondary/lilycove/metatiles.bin"

	.align 1
gMetatileAttributes_Lilycove:: @ 83A652A
	.incbin "data/tilesets/secondary/lilycove/metatile_attributes.bin"

	.align 1
gMetatiles_Mossdeep:: @ 83A67E8
	.incbin "data/tilesets/secondary/mossdeep/metatiles.bin"

	.align 1
gMetatileAttributes_Mossdeep:: @ 83A8448
	.incbin "data/tilesets/secondary/mossdeep/metatile_attributes.bin"

	.align 1
gMetatiles_EverGrande:: @ 83A87D4
	.incbin "data/tilesets/secondary/ever_grande/metatiles.bin"

	.align 1
gMetatileAttributes_EverGrande:: @ 83A9254
	.incbin "data/tilesets/secondary/ever_grande/metatile_attributes.bin"

	.align 1
gMetatiles_Pacifidlog:: @ 83A93A4
	.incbin "data/tilesets/secondary/pacifidlog/metatiles.bin"

	.align 1
gMetatileAttributes_Pacifidlog:: @ 83AA054
	.incbin "data/tilesets/secondary/pacifidlog/metatile_attributes.bin"

	.align 1
gMetatiles_Sootopolis:: @ 83AA1EA
	.incbin "data/tilesets/secondary/sootopolis/metatiles.bin"

	.align 1
gMetatileAttributes_Sootopolis:: @ 83AB1CA
	.incbin "data/tilesets/secondary/sootopolis/metatile_attributes.bin"

	.align 1
gMetatiles_BattleFrontierOutsideWest:: @ 83AB3C6
	.incbin "data/tilesets/secondary/battle_frontier_outside_west/metatiles.bin"

	.align 1
gMetatileAttributes_BattleFrontierOutsideWest:: @ 83AD3A6
	.incbin "data/tilesets/secondary/battle_frontier_outside_west/metatile_attributes.bin"

	.align 1
gMetatiles_BattleFrontierOutsideEast:: @ 83AD7A2
	.incbin "data/tilesets/secondary/battle_frontier_outside_east/metatiles.bin"

	.align 1
gMetatileAttributes_BattleFrontierOutsideEast:: @ 83AF782
	.incbin "data/tilesets/secondary/battle_frontier_outside_east/metatile_attributes.bin"

	.align 1
gMetatiles_InsideBuilding:: @ 83AFB7E
	.incbin "data/tilesets/primary/building/metatiles.bin"

	.align 1
gMetatileAttributes_InsideBuilding:: @ 83AFBFE
	.incbin "data/tilesets/primary/building/metatile_attributes.bin"

	.align 1
gMetatiles_Shop:: @ 83AFC0E
	.incbin "data/tilesets/secondary/shop/metatiles.bin"

	.align 1
gMetatileAttributes_Shop:: @ 83B0E1E
	.incbin "data/tilesets/secondary/shop/metatile_attributes.bin"

	.align 1
gMetatiles_PokemonCenter:: @ 83B1060
	.incbin "data/tilesets/secondary/pokemon_center/metatiles.bin"

	.align 1
gMetatileAttributes_PokemonCenter:: @ 83B1EE0
	.incbin "data/tilesets/secondary/pokemon_center/metatile_attributes.bin"

	.align 1
gMetatiles_Cave:: @ 83B20B0
	.incbin "data/tilesets/secondary/cave/metatiles.bin"

	.align 1
gMetatileAttributes_Cave:: @ 83B3A90
	.incbin "data/tilesets/secondary/cave/metatile_attributes.bin"

	.align 1
gMetatiles_PokemonSchool:: @ 83B3DCC
	.incbin "data/tilesets/secondary/pokemon_school/metatiles.bin"

	.align 1
gMetatileAttributes_PokemonSchool:: @ 83B416C
	.incbin "data/tilesets/secondary/pokemon_school/metatile_attributes.bin"

	.align 1
gMetatiles_PokemonFanClub:: @ 83B41E0
	.incbin "data/tilesets/secondary/pokemon_fan_club/metatiles.bin"

	.align 1
gMetatileAttributes_PokemonFanClub:: @ 83B4860
	.incbin "data/tilesets/secondary/pokemon_fan_club/metatile_attributes.bin"

	.align 1
gMetatiles_Unused1:: @ 83B4930
	.incbin "data/tilesets/secondary/unused_1/metatiles.bin"

	.align 1
gMetatileAttributes_Unused1:: @ 83B4950
	.incbin "data/tilesets/secondary/unused_1/metatile_attributes.bin"

	.align 1
gMetatiles_MeteorFalls:: @ 83B4954
	.incbin "data/tilesets/secondary/meteor_falls/metatiles.bin"

	.align 1
gMetatileAttributes_MeteorFalls:: @ 83B5344
	.incbin "data/tilesets/secondary/meteor_falls/metatile_attributes.bin"

	.align 1
gMetatiles_OceanicMuseum:: @ 83B5482
	.incbin "data/tilesets/secondary/oceanic_museum/metatiles.bin"

	.align 1
gMetatileAttributes_OceanicMuseum:: @ 83B5C22
	.incbin "data/tilesets/secondary/oceanic_museum/metatile_attributes.bin"

	.align 1
gMetatiles_CableClub:: @ 83B5D16
	.incbin "data/tilesets/secondary/cable_club/metatiles.bin"

	.align 1
gMetatileAttributes_CableClub:: @ 83B6D16
	.incbin "data/tilesets/secondary/cable_club/metatile_attributes.bin"

	.align 1
gMetatiles_SeashoreHouse:: @ 83B6F16
	.incbin "data/tilesets/secondary/seashore_house/metatiles.bin"

	.align 1
gMetatileAttributes_SeashoreHouse:: @ 83B7296
	.incbin "data/tilesets/secondary/seashore_house/metatile_attributes.bin"

	.align 1
gMetatiles_PrettyPetalFlowerShop:: @ 83B7306
	.incbin "data/tilesets/secondary/pretty_petal_flower_shop/metatiles.bin"

	.align 1
gMetatileAttributes_PrettyPetalFlowerShop:: @ 83B7786
	.incbin "data/tilesets/secondary/pretty_petal_flower_shop/metatile_attributes.bin"

	.align 1
gMetatiles_PokemonDayCare:: @ 83B7816
	.incbin "data/tilesets/secondary/pokemon_day_care/metatiles.bin"

	.align 1
gMetatileAttributes_PokemonDayCare:: @ 83B7C56
	.incbin "data/tilesets/secondary/pokemon_day_care/metatile_attributes.bin"

	.align 1
gMetatiles_Facility:: @ 83B7CDE
	.incbin "data/tilesets/secondary/facility/metatiles.bin"

	.align 1
gMetatileAttributes_Facility:: @ 83B9CCE
	.incbin "data/tilesets/secondary/facility/metatile_attributes.bin"

	.align 1
gMetatiles_BikeShop:: @ 83BA0CC
	.incbin "data/tilesets/secondary/bike_shop/metatiles.bin"

	.align 1
gMetatileAttributes_BikeShop:: @ 83BB04C
	.incbin "data/tilesets/secondary/bike_shop/metatile_attributes.bin"

	.align 1
gMetatiles_RusturfTunnel:: @ 83BB23C
	.incbin "data/tilesets/secondary/rusturf_tunnel/metatiles.bin"

	.align 1
gMetatileAttributes_RusturfTunnel:: @ 83BB76C
	.incbin "data/tilesets/secondary/rusturf_tunnel/metatile_attributes.bin"

	.align 1
gMetatiles_SecretBaseSecondary:: @ 83BB812
	.incbin "data/tilesets/secondary/secret_base/metatiles.bin"

	.align 1
gMetatileAttributes_SecretBaseSecondary:: @ 83BCC52
	.incbin "data/tilesets/secondary/secret_base/metatile_attributes.bin"

	.align 1
gMetatiles_InsideOfTruck:: @ 83BCEDA
	.incbin "data/tilesets/secondary/inside_of_truck/metatiles.bin"

	.align 1
gMetatileAttributes_InsideOfTruck:: @ 83BD13A
	.incbin "data/tilesets/secondary/inside_of_truck/metatile_attributes.bin"

	.align 1
gMetatiles_Contest:: @ 83BD186
	.incbin "data/tilesets/secondary/contest/metatiles.bin"

	.align 1
gMetatileAttributes_Contest:: @ 83BE016
	.incbin "data/tilesets/secondary/contest/metatile_attributes.bin"

	.align 1
gMetatiles_LilycoveMuseum:: @ 83BE1E8
	.incbin "data/tilesets/secondary/lilycove_museum/metatiles.bin"

	.align 1
gMetatileAttributes_LilycoveMuseum:: @ 83BEAD8
	.incbin "data/tilesets/secondary/lilycove_museum/metatile_attributes.bin"

	.align 1
gMetatiles_BrendansMaysHouse:: @ 83BEBF6
	.incbin "data/tilesets/secondary/brendans_mays_house/metatiles.bin"

	.align 1
gMetatileAttributes_BrendansMaysHouse:: @ 83BF836
	.incbin "data/tilesets/secondary/brendans_mays_house/metatile_attributes.bin"

	.align 1
gMetatiles_Lab:: @ 83BF9BE
	.incbin "data/tilesets/secondary/lab/metatiles.bin"

	.align 1
gMetatileAttributes_Lab:: @ 83C039E
	.incbin "data/tilesets/secondary/lab/metatile_attributes.bin"

	.align 1
gMetatiles_Underwater:: @ 83C04DA
	.incbin "data/tilesets/secondary/underwater/metatiles.bin"

	.align 1
gMetatileAttributes_Underwater:: @ 83C139A
	.incbin "data/tilesets/secondary/underwater/metatile_attributes.bin"

	.align 1
gMetatiles_GenericBuilding:: @ 83C1572
	.incbin "data/tilesets/secondary/generic_building/metatiles.bin"

	.align 1
gMetatileAttributes_GenericBuilding:: @ 83C3572
	.incbin "data/tilesets/secondary/generic_building/metatile_attributes.bin"

	.align 1
gMetatiles_MauvilleGameCorner:: @ 83C3972
	.incbin "data/tilesets/secondary/mauville_game_corner/metatiles.bin"

	.align 1
gMetatileAttributes_MauvilleGameCorner:: @ 83C3F72
	.incbin "data/tilesets/secondary/mauville_game_corner/metatile_attributes.bin"

	.align 1
gMetatiles_Unused2:: @ 83C4032
	.incbin "data/tilesets/secondary/unused_2/metatiles.bin"

	.align 1
gMetatileAttributes_Unused2:: @ 83C43D2
	.incbin "data/tilesets/secondary/unused_2/metatile_attributes.bin"

	.align 1
gMetatiles_RustboroGym:: @ 83C4446
	.incbin "data/tilesets/secondary/rustboro_gym/metatiles.bin"

	.align 1
gMetatileAttributes_RustboroGym:: @ 83C47C6
	.incbin "data/tilesets/secondary/rustboro_gym/metatile_attributes.bin"

	.align 1
gMetatiles_DewfordGym:: @ 83C4836
	.incbin "data/tilesets/secondary/dewford_gym/metatiles.bin"

	.align 1
gMetatileAttributes_DewfordGym:: @ 83C4C86
	.incbin "data/tilesets/secondary/dewford_gym/metatile_attributes.bin"

	.align 1
gMetatiles_MauvilleGym:: @ 83C4D10
	.incbin "data/tilesets/secondary/mauville_gym/metatiles.bin"

	.align 1
gMetatileAttributes_MauvilleGym:: @ 83C5460
	.incbin "data/tilesets/secondary/mauville_gym/metatile_attributes.bin"

	.align 1
gMetatiles_LavaridgeGym:: @ 83C554A
	.incbin "data/tilesets/secondary/lavaridge_gym/metatiles.bin"

	.align 1
gMetatileAttributes_LavaridgeGym:: @ 83C591A
	.incbin "data/tilesets/secondary/lavaridge_gym/metatile_attributes.bin"

	.align 1
gMetatiles_PetalburgGym:: @ 83C5994
	.incbin "data/tilesets/secondary/petalburg_gym/metatiles.bin"

	.align 1
gMetatileAttributes_PetalburgGym:: @ 83C6794
	.incbin "data/tilesets/secondary/petalburg_gym/metatile_attributes.bin"

	.align 1
gMetatiles_FortreeGym:: @ 83C6954
	.incbin "data/tilesets/secondary/fortree_gym/metatiles.bin"

	.align 1
gMetatileAttributes_FortreeGym:: @ 83C6E54
	.incbin "data/tilesets/secondary/fortree_gym/metatile_attributes.bin"

	.align 1
gMetatiles_MossdeepGym:: @ 83C6EF4
	.incbin "data/tilesets/secondary/mossdeep_gym/metatiles.bin"

	.align 1
gMetatileAttributes_MossdeepGym:: @ 83C7644
	.incbin "data/tilesets/secondary/mossdeep_gym/metatile_attributes.bin"

	.align 1
gMetatiles_SootopolisGym:: @ 83C772E
	.incbin "data/tilesets/secondary/sootopolis_gym/metatiles.bin"

	.align 1
gMetatileAttributes_SootopolisGym:: @ 83C7ECE
	.incbin "data/tilesets/secondary/sootopolis_gym/metatile_attributes.bin"

	.align 1
gMetatiles_TrickHousePuzzle:: @ 83C7FC2
	.incbin "data/tilesets/secondary/trick_house_puzzle/metatiles.bin"

	.align 1
gMetatileAttributes_TrickHousePuzzle:: @ 83C8B92
	.incbin "data/tilesets/secondary/trick_house_puzzle/metatile_attributes.bin"

	.align 1
gMetatiles_InsideShip:: @ 83C8D0C
	.incbin "data/tilesets/secondary/inside_ship/metatiles.bin"

	.align 1
gMetatileAttributes_InsideShip:: @ 83C9CCC
	.incbin "data/tilesets/secondary/inside_ship/metatile_attributes.bin"

	.align 1
gMetatiles_SecretBasePrimary:: @ 83C9EC4
	.incbin "data/tilesets/primary/secret_base/metatiles.bin"

	.align 1
gMetatileAttributes_SecretBasePrimary:: @ 83C9EE4
	.incbin "data/tilesets/primary/secret_base/metatile_attributes.bin"

	.align 1
gMetatiles_EliteFour:: @ 83C9EE8
	.incbin "data/tilesets/secondary/elite_four/metatiles.bin"

	.align 1
gMetatileAttributes_EliteFour:: @ 83CB3A8
	.incbin "data/tilesets/secondary/elite_four/metatile_attributes.bin"

	.align 1
gMetatiles_BattleFrontier:: @ 83CB640
	.incbin "data/tilesets/secondary/battle_frontier/metatiles.bin"

	.align 1
gMetatileAttributes_BattleFrontier:: @ 83CD610
	.incbin "data/tilesets/secondary/battle_frontier/metatile_attributes.bin"

	.align 1
gMetatiles_BattlePalace:: @ 83CDA0A
	.incbin "data/tilesets/secondary/battle_palace/metatiles.bin"

	.align 1
gMetatileAttributes_BattlePalace:: @ 83CE4EA
	.incbin "data/tilesets/secondary/battle_palace/metatile_attributes.bin"

	.align 1
gMetatiles_BattleDome:: @ 83CE646
	.incbin "data/tilesets/secondary/battle_dome/metatiles.bin"

	.align 1
gMetatileAttributes_BattleDome:: @ 83D00F6
	.incbin "data/tilesets/secondary/battle_dome/metatile_attributes.bin"

	.align 1
gMetatiles_BattleFactory:: @ 83D044C
	.incbin "data/tilesets/secondary/battle_factory/metatiles.bin"

	.align 1
gMetatileAttributes_BattleFactory:: @ 83D1A6C
	.incbin "data/tilesets/secondary/battle_factory/metatile_attributes.bin"

	.align 1
gMetatiles_BattlePike:: @ 83D1D30
	.incbin "data/tilesets/secondary/battle_pike/metatiles.bin"

	.align 1
gMetatileAttributes_BattlePike:: @ 83D32E0
	.incbin "data/tilesets/secondary/battle_pike/metatile_attributes.bin"

	.align 1
gMetatiles_BattleArena:: @ 83D3596
	.incbin "data/tilesets/secondary/battle_arena/metatiles.bin"

	.align 1
gMetatileAttributes_BattleArena:: @ 83D40A6
	.incbin "data/tilesets/secondary/battle_arena/metatile_attributes.bin"

	.align 1
gMetatiles_BattlePyramid:: @ 83D4208
	.incbin "data/tilesets/secondary/battle_pyramid/metatiles.bin"

	.align 1
gMetatileAttributes_BattlePyramid:: @ 83D4FD8
	.incbin "data/tilesets/secondary/battle_pyramid/metatile_attributes.bin"

	.align 1
gMetatiles_MirageTower:: @ 83D5192
	.incbin "data/tilesets/secondary/mirage_tower/metatiles.bin"

	.align 1
gMetatileAttributes_MirageTower:: @ 83D6B72
	.incbin "data/tilesets/secondary/mirage_tower/metatile_attributes.bin"

	.align 1
gMetatiles_MossdeepGameCorner:: @ 83D6EAE
	.incbin "data/tilesets/secondary/mossdeep_game_corner/metatiles.bin"

	.align 1
gMetatileAttributes_MossdeepGameCorner:: @ 83D71FE
	.incbin "data/tilesets/secondary/mossdeep_game_corner/metatile_attributes.bin"

	.align 1
gMetatiles_IslandHarbor:: @ 83D7268
	.incbin "data/tilesets/secondary/island_harbor/metatiles.bin"

	.align 1
gMetatileAttributes_IslandHarbor:: @ 83D8FC8
	.incbin "data/tilesets/secondary/island_harbor/metatile_attributes.bin"

	.align 1
gMetatiles_TrainerHill:: @ 83D9374
	.incbin "data/tilesets/secondary/trainer_hill/metatiles.bin"

	.align 1
gMetatileAttributes_TrainerHill:: @ 83DB1D4
	.incbin "data/tilesets/secondary/trainer_hill/metatile_attributes.bin"

	.align 1
gMetatiles_NavelRock:: @ 83DB5A0
	.incbin "data/tilesets/secondary/navel_rock/metatiles.bin"

	.align 1
gMetatileAttributes_NavelRock:: @ 83DD1D0
	.incbin "data/tilesets/secondary/navel_rock/metatile_attributes.bin"

	.align 1
gMetatiles_BattleFrontierRankingHall:: @ 83DD556
	.incbin "data/tilesets/secondary/battle_frontier_ranking_hall/metatiles.bin"

	.align 1
gMetatileAttributes_BattleFrontierRankingHall:: @ 83DDB36
	.incbin "data/tilesets/secondary/battle_frontier_ranking_hall/metatile_attributes.bin"

	.align 1
gMetatiles_BattleTent:: @ 83DDBF2
	.incbin "data/tilesets/secondary/battle_tent/metatiles.bin"

	.align 1
gMetatileAttributes_BattleTent:: @ 83DECC2
	.incbin "data/tilesets/secondary/battle_tent/metatile_attributes.bin"

	.align 1
gMetatiles_MysteryEventsHouse:: @ 83DEEDC
	.incbin "data/tilesets/secondary/mystery_events_house/metatiles.bin"

	.align 1
gMetatileAttributes_MysteryEventsHouse:: @ 83DF30C
	.incbin "data/tilesets/secondary/mystery_events_house/metatile_attributes.bin"

	.align 1
gMetatiles_UnionRoom:: @ 83DF392
	.incbin "data/tilesets/secondary/union_room/metatiles.bin"

	.align 1
gMetatileAttributes_UnionRoom:: @ 83DF6A2
	.incbin "data/tilesets/secondary/union_room/metatile_attributes.bin"

repo_name: Lunos/pokeemerald

license: mit


code:
	.section .rodata

	.align 2
gUnknown_089A3470:: @ 89A3470
	.incbin "data/unknown_serial_data.bin"


code:
# Bugs and Glitches

These are known bugs and glitches in the original Pokémon Emerald game: code that clearly does not work as intended, or that only works in limited circumstances but has the possibility to fail or crash. Defining the `BUGFIX` preprocessor variable will fix some of these automatically.

Fixes are written in the `diff` format. If you've used Git before, this should look familiar:

```diff
 this is some code
-delete red - lines
+add green + lines
```

## Contents

- [RNG does not get seeded](#rng-does-not-get-seeded)
- [Scrolling through items in the bag causes the image to flicker](#scrolling-through-items-in-the-bag-causes-the-image-to-flicker)

## RNG does not get seeded

**Fix:** Add the following function to [src/main.c](https://github.com/pret/pokeemerald/blob/master/src/main.c):
```diff
+static void SeedRngWithRtc(void)
+{
+	u32 seed = RtcGetMinuteCount();
+	seed = (seed >> 16) ^ (seed & 0xFFFF);
+	SeedRng(seed);
+}
```

And edit `AgbMain`:

```diff
	...
	RtcInit();
	CheckForFlashMemory();
	InitMainCallbacks();
	InitMapMusic();
+	SeedRngWithRtc();
	ClearDma3Requests();
	...
```

This restores the code of Ruby/Sapphire.

**Alternate Fix:** Edit the following function in [src/title_screen.c](https://github.com/pret/pokeemerald/blob/master/src/title_screen.c):

```diff
void CB2_InitTitleScreen(void)
{
	switch (gMain.state)
	{
	default:
	case 0:
		SetVBlankCallback(NULL);
+		StartTimer1();
		SetGpuReg(REG_OFFSET_BLDCNT, 0);
		SetGpuReg(REG_OFFSET_BLDALPHA, 0);
		SetGpuReg(REG_OFFSET_BLDY, 0);
		...
```
This matches what FRLG does and obtains the seed differently than RS, independently of the RTC.

## Scrolling through items in the bag causes the image to flicker

**Fix:** Add the following function to [src/item_menu_icons.c](https://github.com/pret/pokeemerald/blob/master/src/item_menu_icons.c):
```diff
+void HideBagItemIconSprite(u8 id)
+{
+	u8 *spriteId = &gBagMenu->spriteId[10];
+	if (spriteId[id] != 0xFF)
+	{
+		gSprites[spriteId[id]].invisible = TRUE;
+	}
+}

```

and its corresponding declaration in [include/item_menu_icons.h](https://github.com/pret/pokeemerald/blob/master/include/item_menu_icons.h):

```diff
+void HideBagItemIconSprite(u8 id);

```

Then edit `BagMenu_MoveCursorCallback` in [src/item_menu.c](https://github.com/pret/pokeemerald/blob/master/src/item_menu.c):

```diff
	...
{
-	RemoveBagItemIconSprite(1 ^ gBagMenu->unk81B_1);
+	HideBagItemIconSprite(gBagMenu->unk81B_1 ^ 1);
+	RemoveBagItemIconSprite(gBagMenu->unk81B_1);
	if (a != -2)
	...
```

## Pokémon that have an affine transform as part of their entry animation glitch when going in and out of Poké Balls without a screen transition in between

**Fix:** Edit `sub_817F77C` in [src/pokemon_animation.c](https://github.com/pret/pokeemerald/blob/master/src/pokemon_animation.c#L1028):

```diff
    ...
-#ifdef BUGFIX
    else
    {
        // FIX: Reset these back to normal after they were changed so Poké Ball catch/release
        // animations without a screen transition in between don't break
        sprite->affineAnimPaused = FALSE;
        sprite->affineAnims = gUnknown_082FF694;
    }
-#endif // BUGFIX
}
```


code:
#include <limits.h>
#include "global.h"
#include "bg.h"
#include "dma3.h"
#include "gpu_regs.h"

#define DISPCNT_ALL_BG_AND_MODE_BITS    (DISPCNT_BG_ALL_ON | 0x7)

struct BgControl
{
    struct BgConfig {
        u16 visible:1;
        u16 unknown_1:1;
        u16 screenSize:2;
        u16 priority:2;
        u16 mosaic:1;
        u16 wraparound:1;

        u16 charBaseIndex:2;
        u16 mapBaseIndex:5;
        u16 paletteMode:1;

        u8 unknown_2;
        u8 unknown_3;
    } configs[4];

    u16 bgVisibilityAndMode;
};

struct BgConfig2
{
    u32 baseTile:10;
    u32 basePalette:4;
    u32 unk_3:18;

    void* tilemap;
    s32 bg_x;
    s32 bg_y;
};

static struct BgControl sGpuBgConfigs;
static struct BgConfig2 sGpuBgConfigs2[4];
static u32 sDmaBusyBitfield[4];

u32 gUnneededFireRedVariable;

static const struct BgConfig sZeroedBgControlStruct = { 0 };

void ResetBgs(void)
{
    ResetBgControlStructs();
    sGpuBgConfigs.bgVisibilityAndMode = 0;
    SetTextModeAndHideBgs();
}

static void SetBgModeInternal(u8 bgMode)
{
    sGpuBgConfigs.bgVisibilityAndMode &= 0xFFF8;
    sGpuBgConfigs.bgVisibilityAndMode |= bgMode;
}

u8 GetBgMode(void)
{
    return sGpuBgConfigs.bgVisibilityAndMode & 0x7;
}

void ResetBgControlStructs(void)
{
    struct BgConfig* bgConfigs = &sGpuBgConfigs.configs[0];
    struct BgConfig zeroedConfig = sZeroedBgControlStruct;
    int i;

    for (i = 0; i < 4; i++)
    {
        bgConfigs[i] = zeroedConfig;
    }
}

void Unused_ResetBgControlStruct(u8 bg)
{
    if (!IsInvalidBg(bg))
    {
        sGpuBgConfigs.configs[bg] = sZeroedBgControlStruct;
    }
}

enum
{
    BG_CTRL_ATTR_VISIBLE = 1,
    BG_CTRL_ATTR_CHARBASEINDEX = 2,
    BG_CTRL_ATTR_MAPBASEINDEX = 3,
    BG_CTRL_ATTR_SCREENSIZE = 4,
    BG_CTRL_ATTR_PALETTEMODE = 5,
    BG_CTRL_ATTR_PRIORITY = 6,
    BG_CTRL_ATTR_MOSAIC = 7,
    BG_CTRL_ATTR_WRAPAROUND = 8,
};

static void SetBgControlAttributes(u8 bg, u8 charBaseIndex, u8 mapBaseIndex, u8 screenSize, u8 paletteMode, u8 priority, u8 mosaic, u8 wraparound)
{
    if (!IsInvalidBg(bg))
    {
        if (charBaseIndex != 0xFF)
        {
            sGpuBgConfigs.configs[bg].charBaseIndex = charBaseIndex & 0x3;
        }

        if (mapBaseIndex != 0xFF)
        {
            sGpuBgConfigs.configs[bg].mapBaseIndex = mapBaseIndex & 0x1F;
        }

        if (screenSize != 0xFF)
        {
            sGpuBgConfigs.configs[bg].screenSize = screenSize & 0x3;
        }

        if (paletteMode != 0xFF)
        {
            sGpuBgConfigs.configs[bg].paletteMode = paletteMode;
        }

        if (priority != 0xFF)
        {
            sGpuBgConfigs.configs[bg].priority = priority & 0x3;
        }

        if (mosaic != 0xFF)
        {
            sGpuBgConfigs.configs[bg].mosaic = mosaic & 0x1;
        }

        if (wraparound != 0xFF)
        {
            sGpuBgConfigs.configs[bg].wraparound = wraparound;
        }

        sGpuBgConfigs.configs[bg].unknown_2 = 0;
        sGpuBgConfigs.configs[bg].unknown_3 = 0;

        sGpuBgConfigs.configs[bg].visible = 1;
    }
}

static u16 GetBgControlAttribute(u8 bg, u8 attributeId)
{
    if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible)
    {
        switch (attributeId)
        {
        case BG_CTRL_ATTR_VISIBLE:
            return sGpuBgConfigs.configs[bg].visible;
        case BG_CTRL_ATTR_CHARBASEINDEX:
            return sGpuBgConfigs.configs[bg].charBaseIndex;
        case BG_CTRL_ATTR_MAPBASEINDEX:
            return sGpuBgConfigs.configs[bg].mapBaseIndex;
        case BG_CTRL_ATTR_SCREENSIZE:
            return sGpuBgConfigs.configs[bg].screenSize;
        case BG_CTRL_ATTR_PALETTEMODE:
            return sGpuBgConfigs.configs[bg].paletteMode;
        case BG_CTRL_ATTR_PRIORITY:
            return sGpuBgConfigs.configs[bg].priority;
        case BG_CTRL_ATTR_MOSAIC:
            return sGpuBgConfigs.configs[bg].mosaic;
        case BG_CTRL_ATTR_WRAPAROUND:
            return sGpuBgConfigs.configs[bg].wraparound;
        }
    }

    return 0xFF;
}

u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode)
{
    u16 offset;
    s8 cursor;

    if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible)
    {
        switch (mode)
        {
        case 0x1:
            offset = sGpuBgConfigs.configs[bg].charBaseIndex * BG_CHAR_SIZE;
            break;
        case 0x2:
            offset = sGpuBgConfigs.configs[bg].mapBaseIndex * BG_SCREEN_SIZE;
            break;
        default:
            cursor = -1;
            goto end;
        }

        offset = destOffset + offset;

        cursor = RequestDma3Copy(src, (void*)(offset + BG_VRAM), size, 0);

        if (cursor == -1)
        {
            return -1;
        }
    }
    else
    {
       return -1;
    }

end:
    return cursor;
}

static void ShowBgInternal(u8 bg)
{
    u16 value;
    if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible)
    {
        value = sGpuBgConfigs.configs[bg].priority |
                (sGpuBgConfigs.configs[bg].charBaseIndex << 2) |
                (sGpuBgConfigs.configs[bg].mosaic << 6) |
                (sGpuBgConfigs.configs[bg].paletteMode << 7) |
                (sGpuBgConfigs.configs[bg].mapBaseIndex << 8) |
                (sGpuBgConfigs.configs[bg].wraparound << 13) |
                (sGpuBgConfigs.configs[bg].screenSize << 14);

        SetGpuReg((bg << 1) + REG_OFFSET_BG0CNT, value);

        sGpuBgConfigs.bgVisibilityAndMode |= 1 << (bg + 8);
        sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS;
    }
}

static void HideBgInternal(u8 bg)
{
    if (!IsInvalidBg(bg))
    {
        sGpuBgConfigs.bgVisibilityAndMode &= ~(1 << (bg + 8));
        sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS;
    }
}

static void SyncBgVisibilityAndMode(void)
{
    SetGpuReg(REG_OFFSET_DISPCNT, (GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS) | sGpuBgConfigs.bgVisibilityAndMode);
}

void SetTextModeAndHideBgs(void)
{
    SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS);
}

static void SetBgAffineInternal(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle)
{
    struct BgAffineSrcData src;
    struct BgAffineDstData dest;

    switch (sGpuBgConfigs.bgVisibilityAndMode & 0x7)
    {
    case 1:
        if (bg != 2)
            return;
        break;
    case 2:
        if (bg < 2 || bg > 3)
            return;
        break;
    case 0:
    default:
        return;
    }

    src.texX = srcCenterX;
    src.texY = srcCenterY;
    src.scrX = dispCenterX;
    src.scrY = dispCenterY;
    src.sx = scaleX;
    src.sy = scaleY;
    src.alpha = rotationAngle;

    BgAffineSet(&src, &dest, 1);

    SetGpuReg(REG_OFFSET_BG2PA, dest.pa);
    SetGpuReg(REG_OFFSET_BG2PB, dest.pb);
    SetGpuReg(REG_OFFSET_BG2PC, dest.pc);
    SetGpuReg(REG_OFFSET_BG2PD, dest.pd);
    SetGpuReg(REG_OFFSET_BG2PA, dest.pa);
    SetGpuReg(REG_OFFSET_BG2X_L, (s16)(dest.dx));
    SetGpuReg(REG_OFFSET_BG2X_H, (s16)(dest.dx >> 16));
    SetGpuReg(REG_OFFSET_BG2Y_L, (s16)(dest.dy));
    SetGpuReg(REG_OFFSET_BG2Y_H, (s16)(dest.dy >> 16));
}

bool8 IsInvalidBg(u8 bg)
{
    if (bg > 3)
        return TRUE;
    else
        return FALSE;
}

int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4)
{
    return 0;
}

void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable)
{
    int i;
    ResetBgs();

    for (i = 0; i < 4; i++)
    {
        sDmaBusyBitfield[i] = 0;
    }

    gUnneededFireRedVariable = leftoverFireRedLeafGreenVariable;
}

void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates)
{
    int i;
    u8 bg;

    SetBgModeInternal(bgMode);
    ResetBgControlStructs();

    for (i = 0; i < numTemplates; i++)
    {
        bg = templates[i].bg;
        if (bg < 4)
        {
            SetBgControlAttributes(bg,
                                   templates[i].charBaseIndex,
                                   templates[i].mapBaseIndex,
                                   templates[i].screenSize,
                                   templates[i].paletteMode,
                                   templates[i].priority,
                                   0,
                                   0);

            sGpuBgConfigs2[bg].baseTile = templates[i].baseTile;
            sGpuBgConfigs2[bg].basePalette = 0;
            sGpuBgConfigs2[bg].unk_3 = 0;

            sGpuBgConfigs2[bg].tilemap = NULL;
            sGpuBgConfigs2[bg].bg_x = 0;
            sGpuBgConfigs2[bg].bg_y = 0;
        }
    }
}

void InitBgFromTemplate(const struct BgTemplate *template)
{
    u8 bg = template->bg;

    if (bg < 4)
    {
        SetBgControlAttributes(bg,
                               template->charBaseIndex,
                               template->mapBaseIndex,
                               template->screenSize,
                               template->paletteMode,
                               template->priority,
                               0,
                               0);

        sGpuBgConfigs2[bg].baseTile = template->baseTile;
        sGpuBgConfigs2[bg].basePalette = 0;
        sGpuBgConfigs2[bg].unk_3 = 0;

        sGpuBgConfigs2[bg].tilemap = NULL;
        sGpuBgConfigs2[bg].bg_x = 0;
        sGpuBgConfigs2[bg].bg_y = 0;
    }
}

void SetBgMode(u8 bgMode)
{
    SetBgModeInternal(bgMode);
}

u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset)
{
    u16 tileOffset;
    u8 cursor;

    if (GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE) == 0)
    {
        tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x20;
    }
    else
    {
        tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x40;
    }

    cursor = LoadBgVram(bg, src, size, tileOffset, DISPCNT_MODE_1);

    if (cursor == 0xFF)
    {
        return -1;
    }

    sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));

    if (gUnneededFireRedVariable == 1)
    {
        DummiedOutFireRedLeafGreenTileAllocFunc(bg, tileOffset / 0x20, size / 0x20, 1);
    }

    return cursor;
}

u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset)
{
    u8 cursor = LoadBgVram(bg, src, size, destOffset * 2, DISPCNT_MODE_2);

    if (cursor == 0xFF)
    {
        return -1;
    }

    sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));

    return cursor;
}

u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset)
{
    s8 cursor;

    if (!IsInvalidBg32(bg))
    {
        u16 paletteOffset = (sGpuBgConfigs2[bg].basePalette * 0x20) + (destOffset * 2);
        cursor = RequestDma3Copy(src, (void*)(paletteOffset + BG_PLTT), size, 0);

        if (cursor == -1)
        {
            return -1;
        }
    }
    else
    {
        return -1;
    }

    sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));

    return (u8)cursor;
}

bool8 IsDma3ManagerBusyWithBgCopy(void)
{
    int i;

    for (i = 0; i < 0x80; i++)
    {
        u8 div = i / 0x20;
        u8 mod = i % 0x20;

        if ((sDmaBusyBitfield[div] & (1 << mod)))
        {
            s8 reqSpace = CheckForSpaceForDma3Request(i);
            if (reqSpace == -1)
            {
                return TRUE;
            }

            sDmaBusyBitfield[div] &= ~(1 << mod);
        }
    }

    return FALSE;
}

void ShowBg(u8 bg)
{
    ShowBgInternal(bg);
    SyncBgVisibilityAndMode();
}

void HideBg(u8 bg)
{
    HideBgInternal(bg);
    SyncBgVisibilityAndMode();
}

void SetBgAttribute(u8 bg, u8 attributeId, u8 value)
{
    switch (attributeId)
    {
    case BG_ATTR_CHARBASEINDEX:
        SetBgControlAttributes(bg, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
        break;
    case BG_ATTR_MAPBASEINDEX:
        SetBgControlAttributes(bg, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
        break;
    case BG_ATTR_SCREENSIZE:
        SetBgControlAttributes(bg, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF);
        break;
    case BG_ATTR_PALETTEMODE:
        SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF);
        break;
    case BG_ATTR_PRIORITY:
        SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF);
        break;
    case BG_ATTR_MOSAIC:
        SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF);
        break;
    case BG_ATTR_WRAPAROUND:
        SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value);
        break;
    }
}

u16 GetBgAttribute(u8 bg, u8 attributeId)
{
    switch (attributeId)
    {
    case BG_ATTR_CHARBASEINDEX:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_CHARBASEINDEX);
    case BG_ATTR_MAPBASEINDEX:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_MAPBASEINDEX);
    case BG_ATTR_SCREENSIZE:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
    case BG_ATTR_PALETTEMODE:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE);
    case BG_ATTR_PRIORITY:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_PRIORITY);
    case BG_ATTR_MOSAIC:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_MOSAIC);
    case BG_ATTR_WRAPAROUND:
        return GetBgControlAttribute(bg, BG_CTRL_ATTR_WRAPAROUND);
    case BG_ATTR_METRIC:
        switch (GetBgType(bg))
        {
        case 0:
            return GetBgMetricTextMode(bg, 0) * 0x800;
        case 1:
            return GetBgMetricAffineMode(bg, 0) * 0x100;
        default:
            return 0;
        }
    case BG_ATTR_TYPE:
        return GetBgType(bg);
    case BG_ATTR_BASETILE:
        return sGpuBgConfigs2[bg].baseTile;
    default:
        return -1;
    }
}

s32 ChangeBgX(u8 bg, s32 value, u8 op)
{
    u8 mode;
    u16 temp1;
    u16 temp2;

    if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
    {
        return -1;
    }

    switch (op)
    {
    case 0:
    default:
        sGpuBgConfigs2[bg].bg_x = value;
        break;
    case 1:
        sGpuBgConfigs2[bg].bg_x += value;
        break;
    case 2:
        sGpuBgConfigs2[bg].bg_x -= value;
        break;
    }

    mode = GetBgMode();

    switch (bg)
    {
    case 0:
        temp1 = sGpuBgConfigs2[0].bg_x >> 0x8;
        SetGpuReg(REG_OFFSET_BG0HOFS, temp1);
        break;
    case 1:
        temp1 = sGpuBgConfigs2[1].bg_x >> 0x8;
        SetGpuReg(REG_OFFSET_BG1HOFS, temp1);
        break;
    case 2:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[2].bg_x >> 0x8;
            SetGpuReg(REG_OFFSET_BG2HOFS, temp1);
        }
        else
        {
            temp1 = sGpuBgConfigs2[2].bg_x >> 0x10;
            temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF;
            SetGpuReg(REG_OFFSET_BG2X_H, temp1);
            SetGpuReg(REG_OFFSET_BG2X_L, temp2);
        }
        break;
    case 3:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[3].bg_x >> 0x8;
            SetGpuReg(REG_OFFSET_BG3HOFS, temp1);
        }
        else if (mode == 2)
        {
            temp1 = sGpuBgConfigs2[3].bg_x >> 0x10;
            temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF;
            SetGpuReg(REG_OFFSET_BG3X_H, temp1);
            SetGpuReg(REG_OFFSET_BG3X_L, temp2);
        }
        break;
    }

    return sGpuBgConfigs2[bg].bg_x;
}

s32 GetBgX(u8 bg)
{
    if (IsInvalidBg32(bg))
        return -1;
    else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
        return -1;
    else
        return sGpuBgConfigs2[bg].bg_x;
}

s32 ChangeBgY(u8 bg, s32 value, u8 op)
{
    u8 mode;
    u16 temp1;
    u16 temp2;

    if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
    {
        return -1;
    }

    switch (op)
    {
    case 0:
    default:
        sGpuBgConfigs2[bg].bg_y = value;
        break;
    case 1:
        sGpuBgConfigs2[bg].bg_y += value;
        break;
    case 2:
        sGpuBgConfigs2[bg].bg_y -= value;
        break;
    }

    mode = GetBgMode();

    switch (bg)
    {
    case 0:
        temp1 = sGpuBgConfigs2[0].bg_y >> 0x8;
        SetGpuReg(REG_OFFSET_BG0VOFS, temp1);
        break;
    case 1:
        temp1 = sGpuBgConfigs2[1].bg_y >> 0x8;
        SetGpuReg(REG_OFFSET_BG1VOFS, temp1);
        break;
    case 2:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[2].bg_y >> 0x8;
            SetGpuReg(REG_OFFSET_BG2VOFS, temp1);
        }
        else
        {
            temp1 = sGpuBgConfigs2[2].bg_y >> 0x10;
            temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF;
            SetGpuReg(REG_OFFSET_BG2Y_H, temp1);
            SetGpuReg(REG_OFFSET_BG2Y_L, temp2);
        }
        break;
    case 3:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[3].bg_y >> 0x8;
            SetGpuReg(REG_OFFSET_BG3VOFS, temp1);
        }
        else if (mode == 2)
        {
            temp1 = sGpuBgConfigs2[3].bg_y >> 0x10;
            temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF;
            SetGpuReg(REG_OFFSET_BG3Y_H, temp1);
            SetGpuReg(REG_OFFSET_BG3Y_L, temp2);
        }
        break;
    }

    return sGpuBgConfigs2[bg].bg_y;
}

s32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op)
{
    u8 mode;
    u16 temp1;
    u16 temp2;

    if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
    {
        return -1;
    }

    switch (op)
    {
    case 0:
    default:
        sGpuBgConfigs2[bg].bg_y = value;
        break;
    case 1:
        sGpuBgConfigs2[bg].bg_y += value;
        break;
    case 2:
        sGpuBgConfigs2[bg].bg_y -= value;
        break;
    }

    mode = GetBgMode();

    switch (bg)
    {
    case 0:
        temp1 = sGpuBgConfigs2[0].bg_y >> 0x8;
        SetGpuReg_ForcedBlank(REG_OFFSET_BG0VOFS, temp1);
        break;
    case 1:
        temp1 = sGpuBgConfigs2[1].bg_y >> 0x8;
        SetGpuReg_ForcedBlank(REG_OFFSET_BG1VOFS, temp1);
        break;
    case 2:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[2].bg_y >> 0x8;
            SetGpuReg_ForcedBlank(REG_OFFSET_BG2VOFS, temp1);

        }
        else
        {
            temp1 = sGpuBgConfigs2[2].bg_y >> 0x10;
            temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF;
            SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_H, temp1);
            SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_L, temp2);
        }
        break;
    case 3:
        if (mode == 0)
        {
            temp1 = sGpuBgConfigs2[3].bg_y >> 0x8;
            SetGpuReg_ForcedBlank(REG_OFFSET_BG3VOFS, temp1);
        }
        else if (mode == 2)
        {
            temp1 = sGpuBgConfigs2[3].bg_y >> 0x10;
            temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF;
            SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_H, temp1);
            SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_L, temp2);
        }
        break;
    }

    return sGpuBgConfigs2[bg].bg_y;
}

s32 GetBgY(u8 bg)
{
    if (IsInvalidBg32(bg))
        return -1;
    else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
        return -1;
    else
        return sGpuBgConfigs2[bg].bg_y;
}

void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle)
{
    SetBgAffineInternal(bg, srcCenterX, srcCenterY, dispCenterX, dispCenterY, scaleX, scaleY, rotationAngle);
}

u8 Unused_AdjustBgMosaic(u8 a1, u8 a2)
{
    u16 result = GetGpuReg(REG_OFFSET_MOSAIC);
    s16 test1 = result & 0xF;
    s16 test2 = (result >> 4) & 0xF;

    result &= 0xFF00;

    switch (a2)
    {
    case 0:
    default:
        test1 = a1 & 0xF;
        test2 = a1 >> 0x4;
        break;
    case 1:
        test1 = a1 & 0xF;
        break;
    case 2:
        if ((test1 + a1) > 0xF)
        {
            test1 = 0xF;
        }
        else
        {
            test1 += a1;
        }
        break;
    case 3:
        if ((test1 - a1) < 0)
        {
            test1 = 0x0;
        }
        else
        {
            test1 -= a1;
        }
        break;
    case 4:
        test2 = a1 & 0xF;
        break;
    case 5:
        if ((test2 + a1) > 0xF)
        {
            test2 = 0xF;
        }
        else
        {
            test2 += a1;
        }
        break;
    case 6:
        if ((test2 - a1) < 0)
        {
            test2 = 0x0;
        }
        else
        {
            test2 -= a1;
        }
        break;
    }

    result |= ((test2 << 0x4) & 0xF0);
    result |= (test1 & 0xF);

    SetGpuReg(REG_OFFSET_MOSAIC, result);

    return result;
}

void SetBgTilemapBuffer(u8 bg, void *tilemap)
{
    if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
    {
        sGpuBgConfigs2[bg].tilemap = tilemap;
    }
}

void UnsetBgTilemapBuffer(u8 bg)
{
    if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
    {
        sGpuBgConfigs2[bg].tilemap = NULL;
    }
}

void* GetBgTilemapBuffer(u8 bg)
{
    if (IsInvalidBg32(bg))
        return NULL;
    else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
        return NULL;
    else
        return sGpuBgConfigs2[bg].tilemap;
}

void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset)
{
    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        if (mode != 0)
            CpuCopy16(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)), mode);
        else
            LZ77UnCompWram(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)));
    }
}

void CopyBgTilemapBufferToVram(u8 bg)
{
    u16 sizeToLoad;

    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        switch (GetBgType(bg))
        {
        case 0:
            sizeToLoad = GetBgMetricTextMode(bg, 0) * 0x800;
            break;
        case 1:
            sizeToLoad = GetBgMetricAffineMode(bg, 0) * 0x100;
            break;
        default:
            sizeToLoad = 0;
            break;
        }
        LoadBgVram(bg, sGpuBgConfigs2[bg].tilemap, sizeToLoad, 0, 2);
    }
}

void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height)
{
    u16 destX16;
    u16 destY16;
    u16 mode;

    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        switch (GetBgType(bg))
        {
        case 0:
        {
            const u16 * srcCopy = src;
            for (destY16 = destY; destY16 < (destY + height); destY16++)
            {
                for (destX16 = destX; destX16 < (destX + width); destX16++)
                {
                    ((u16*)sGpuBgConfigs2[bg].tilemap)[((destY16 * 0x20) + destX16)] = *srcCopy++;
                }
            }
            break;
        }
        case 1:
        {
            const u8 * srcCopy = src;
            mode = GetBgMetricAffineMode(bg, 0x1);
            for (destY16 = destY; destY16 < (destY + height); destY16++)
            {
                for (destX16 = destX; destX16 < (destX + width); destX16++)
                {
                    ((u8*)sGpuBgConfigs2[bg].tilemap)[((destY16 * mode) + destX16)] = *srcCopy++;
                }
            }
            break;
        }
        }
    }
}

void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette)
{
    CopyRectToBgTilemapBufferRect(bg, src, 0, 0, rectWidth, rectHeight, destX, destY, rectWidth, rectHeight, palette, 0, 0);
}

void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 unused, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, s16 palette1, s16 tileOffset)
{
    u16 screenWidth, screenHeight, screenSize;
    u16 var;
    const void *srcPtr;
    u16 i, j;

    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
        screenWidth = GetBgMetricTextMode(bg, 0x1) * 0x20;
        screenHeight = GetBgMetricTextMode(bg, 0x2) * 0x20;
        switch (GetBgType(bg))
        {
        case 0:
            srcPtr = src + ((srcY * srcWidth) + srcX) * 2;
            for (i = destX; i < (destX + rectWidth); i++)
            {
                for (j = srcHeight; j < (srcHeight + destY); j++)
                {
                    u16 index = GetTileMapIndexFromCoords(j, i, screenSize, screenWidth, screenHeight);
                    CopyTileMapEntry(srcPtr, sGpuBgConfigs2[bg].tilemap + (index * 2), rectHeight, palette1, tileOffset);
                    srcPtr += 2;
                }
                srcPtr += (srcWidth - destY) * 2;
            }
            break;
        case 1:
            srcPtr = src + ((srcY * srcWidth) + srcX);
            var = GetBgMetricAffineMode(bg, 0x1);
            for (i = destX; i < (destX + rectWidth); i++)
            {
                for (j = srcHeight; j < (srcHeight + destY); j++)
                {
                    *(u8*)(sGpuBgConfigs2[bg].tilemap + ((var * i) + j)) = *(u8*)(srcPtr) + palette1;
                    srcPtr++;
                }
                srcPtr += (srcWidth - destY);
            }
            break;
        }
    }
}

void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height)
{
    u16 x16;
    u16 y16;
    u16 mode;

    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        switch (GetBgType(bg))
        {
        case 0:
            for (y16 = y; y16 < (y + height); y16++)
            {
                for (x16 = x; x16 < (x + width); x16++)
                {
                    ((u16*)sGpuBgConfigs2[bg].tilemap)[((y16 * 0x20) + x16)] = tileNum;
                }
            }
            break;
        case 1:
            mode = GetBgMetricAffineMode(bg, 0x1);
            for (y16 = y; y16 < (y + height); y16++)
            {
                for (x16 = x; x16 < (x + width); x16++)
                {
                    ((u8*)sGpuBgConfigs2[bg].tilemap)[((y16 * mode) + x16)] = tileNum;
                }
            }
            break;
        }
    }
}

void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette)
{
    WriteSequenceToBgTilemapBuffer(bg, tileNum, x, y, width, height, palette, 0);
}

void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta)
{
    u16 mode;
    u16 mode2;
    u16 attribute;
    u16 mode3;
    u16 x16, y16;

    if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
    {
        attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
        mode = GetBgMetricTextMode(bg, 0x1) * 0x20;
        mode2 = GetBgMetricTextMode(bg, 0x2) * 0x20;
        switch (GetBgType(bg))
        {
        case 0:
            for (y16 = y; y16 < (y + height); y16++)
            {
                for (x16 = x; x16 < (x + width); x16++)
                {
                    CopyTileMapEntry(&firstTileNum, &((u16*)sGpuBgConfigs2[bg].tilemap)[(u16)GetTileMapIndexFromCoords(x16, y16, attribute, mode, mode2)], paletteSlot, 0, 0);
                    firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK);
                }
            }
            break;
        case 1:
            mode3 = GetBgMetricAffineMode(bg, 0x1);
            for (y16 = y; y16 < (y + height); y16++)
            {
                for (x16 = x; x16 < (x + width); x16++)
                {
                    ((u8*)sGpuBgConfigs2[bg].tilemap)[(y16 * mode3) + x16] = firstTileNum;
                    firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK);
                }
            }
            break;
        }
    }
}

u16 GetBgMetricTextMode(u8 bg, u8 whichMetric)
{
    u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);

    switch (whichMetric)
    {
    case 0:
        switch (screenSize)
        {
        case 0:
            return 1;
        case 1:
        case 2:
            return 2;
        case 3:
            return 4;
        }
        break;
    case 1:
        switch (screenSize)
        {
        case 0:
            return 1;
        case 1:
            return 2;
        case 2:
            return 1;
        case 3:
            return 2;
        }
        break;
    case 2:
        switch (screenSize)
        {
        case 0:
        case 1:
            return 1;
        case 2:
        case 3:
            return 2;
        }
        break;
    }
    return 0;
}

u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric)
{
    u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);

    switch (whichMetric)
    {
    case 0:
        switch (screenSize)
        {
        case 0:
            return 0x1;
        case 1:
            return 0x4;
        case 2:
            return 0x10;
        case 3:
            return 0x40;
        }
        break;
    case 1:
    case 2:
        return 0x10 << screenSize;
    }
    return 0;
}

u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight)
{
    x = x & (screenWidth - 1);
    y = y & (screenHeight - 1);

    switch (screenSize)
    {
    case 0:
    case 2:
        break;
    case 3:
        if (y >= 0x20)
            y += 0x20;
    case 1:
        if (x >= 0x20)
        {
            x -= 0x20;
            y += 0x20;
        }
        break;
    }
    return (y * 0x20) + x;
}

void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2)
{
    u16 var;

    switch (palette1)
    {
    case 0 ... 15:
        var = ((*src + tileOffset) & 0xFFF) + ((palette1 + palette2) << 12);
        break;
    case 16:
        var = *dest;
        var &= 0xFC00;
        var += palette2 << 12;
        var |= (*src + tileOffset) & 0x3FF;
        break;
    default:
    case 17 ... INT_MAX:
        var = *src + tileOffset + (palette2 << 12);
        break;
    }
    *dest = var;
}

u32 GetBgType(u8 bg)
{
    u8 mode = GetBgMode();

    switch (bg)
    {
    case 0:
    case 1:
        switch (mode)
        {
        case 0:
        case 1:
            return 0;
        }
        break;
    case 2:
        switch (mode)
        {
        case 0:
            return 0;
        case 1:
        case 2:
            return 1;
        }
        break;
    case 3:
        switch (mode)
        {
        case 0:
            return 0;
        case 2:
            return 1;
        }
        break;
    }

    return 0xFFFF;
}

bool32 IsInvalidBg32(u8 bg)
{
    if (bg > 3)
        return TRUE;
    else
        return FALSE;
}

bool32 IsTileMapOutsideWram(u8 bg)
{
    if (sGpuBgConfigs2[bg].tilemap > (void*)IWRAM_END)
        return TRUE;
    else if (sGpuBgConfigs2[bg].tilemap == NULL)
        return TRUE;
    else
        return FALSE;
}


code:
#ifndef GUARD_BG_H
#define GUARD_BG_H

struct BGCntrlBitfield // for the I/O registers
{
    volatile u16 priority:2;
    volatile u16 charBaseBlock:2;
    volatile u16 field_0_2:4;
    volatile u16 field_1_0:5;
    volatile u16 areaOverflowMode:1;
    volatile u16 screenSize:2;
};

enum
{
	BG_ATTR_CHARBASEINDEX = 1,
	BG_ATTR_MAPBASEINDEX,
	BG_ATTR_SCREENSIZE,
	BG_ATTR_PALETTEMODE,
	BG_ATTR_MOSAIC,
	BG_ATTR_WRAPAROUND,
	BG_ATTR_PRIORITY,
	BG_ATTR_METRIC,
	BG_ATTR_TYPE,
	BG_ATTR_BASETILE,
};

struct BgTemplate
{
    u16 bg:2;                   // 0x1, 0x2 -> 0x3
    u16 charBaseIndex:2;        // 0x4, 0x8 -> 0xC
    u16 mapBaseIndex:5;         // 0x10, 0x20, 0x40, 0x80, 0x100 -> 0x1F0
    u16 screenSize:2;           // 0x200, 0x400 -> 0x600
    u16 paletteMode:1;          // 0x800
    u16 priority:2;             // 0x1000, 0x2000 > 0x3000
    u16 baseTile:10;
};

void ResetBgs(void);
u8 GetBgMode(void);
void ResetBgControlStructs(void);
void Unused_ResetBgControlStruct(u8 bg);
u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode);
void SetTextModeAndHideBgs(void);
bool8 IsInvalidBg(u8 bg);
int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4);
void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable);
void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates);
void InitBgFromTemplate(const struct BgTemplate *template);
void SetBgMode(u8 bgMode);
u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset);
u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset);
u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset);
bool8 IsDma3ManagerBusyWithBgCopy(void);
void ShowBg(u8 bg);
void HideBg(u8 bg);
void SetBgAttribute(u8 bg, u8 attributeId, u8 value);
u16 GetBgAttribute(u8 bg, u8 attributeId);
s32 ChangeBgX(u8 bg, s32 value, u8 op);
s32 GetBgX(u8 bg);
s32 ChangeBgY(u8 bg, s32 value, u8 op);
s32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op);
s32 GetBgY(u8 bg);
void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle);
u8 Unused_AdjustBgMosaic(u8 a1, u8 a2);
void SetBgTilemapBuffer(u8 bg, void *tilemap);
void UnsetBgTilemapBuffer(u8 bg);
void* GetBgTilemapBuffer(u8 bg);
void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset);
void CopyBgTilemapBufferToVram(u8 bg);
void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height);
void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette);
void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 unused, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, s16 palette1, s16 tileOffset);
void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height);
void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette);
void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta);
u16 GetBgMetricTextMode(u8 bg, u8 whichMetric);
u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric);
u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight);
void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2);
u32 GetBgType(u8 bg);
bool32 IsInvalidBg32(u8 bg);
bool32 IsTileMapOutsideWram(u8 bg);

#endif // GUARD_BG_H


code:
#include "global.h"
#include "blit.h"

void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height)
{
    BlitBitmapRect4Bit(src, dst, srcX, srcY, dstX, dstY, width, height, 0xFF);
}

void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey)
{
    s32 xEnd;
    s32 yEnd;
    s32 multiplierSrcY;
    s32 multiplierDstY;
    s32 loopSrcY, loopDstY;
    s32 loopSrcX, loopDstX;
    const u8 *pixelsSrc;
    u8 *pixelsDst;
    s32 toOrr;
    s32 toAnd;
    s32 toShift;

    if (dst->width - dstX < width)
        xEnd = (dst->width - dstX) + srcX;
    else
        xEnd = srcX + width;

    if (dst->height - dstY < height)
        yEnd = (dst->height - dstY) + srcY;
    else
        yEnd = height + srcY;

    multiplierSrcY = (src->width + (src->width & 7)) >> 3;
    multiplierDstY = (dst->width + (dst->width & 7)) >> 3;

    if (colorKey == 0xFF)
    {
        for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
        {
            for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
            {
                pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B);
                pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B);
                toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF);
                toShift = ((loopDstX & 1) << 2);
                toOrr <<= toShift;
                toAnd = 0xF0 >> (toShift);
                *pixelsDst = toOrr | (*pixelsDst & toAnd);
            }
        }
    }
    else
    {
        for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
        {
            for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
            {
                pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B);
                pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B);
                toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF);
                if (toOrr != colorKey)
                {
                    toShift = ((loopDstX & 1) << 2);
                    toOrr <<= toShift;
                    toAnd = 0xF0 >> (toShift);
                    *pixelsDst = toOrr | (*pixelsDst & toAnd);
                }
            }
        }
    }
}

void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue)
{
    s32 xEnd;
    s32 yEnd;
    s32 multiplierY;
    s32 loopX, loopY;
    u8 toOrr1, toOrr2;

    xEnd = x + width;
    if (xEnd > surface->width)
        xEnd = surface->width;

    yEnd = y + height;
    if (yEnd > surface->height)
        yEnd = surface->height;

    multiplierY = (surface->width + (surface->width & 7)) >> 3;
    toOrr1 = fillValue << 4;
    toOrr2 = fillValue & 0xF;

    for (loopY = y; loopY < yEnd; loopY++)
    {
        for (loopX = x; loopX < xEnd; loopX++)
        {
            u8 *pixels = surface->pixels + ((loopX >> 1) & 3) + ((loopX >> 3) << 5) + (((loopY >> 3) * multiplierY) << 5) + ((u32)(loopY << 0x1d) >> 0x1B);
            if ((loopX << 0x1F) != 0)
                *pixels = toOrr1 | (*pixels & 0xF);
            else
                *pixels = toOrr2 | (*pixels & 0xF0);
        }
    }
}

void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset)
{
    s32 palOffsetBits;
    s32 xEnd;
    s32 yEnd;
    s32 multiplierSrcY;
    s32 multiplierDstY;
    s32 loopSrcY, loopDstY;
    s32 loopSrcX, loopDstX;
    const u8 *pixelsSrc;
    u8 *pixelsDst;
    s32 colorKeyBits;

    palOffsetBits = (u32)(paletteOffset << 0x1C) >> 0x18;
    colorKeyBits = (u32)(colorKey << 0x1C) >> 0x18;

    if (dst->width - dstX < width)
        xEnd = (dst->width - dstX) + srcX;
    else
        xEnd = width + srcX;

    if (dst->height - dstY < height)
        yEnd = (srcY + dst->height) - dstY;
    else
        yEnd = srcY + height;

    multiplierSrcY = (src->width + (src->width & 7)) >> 3;
    multiplierDstY = (dst->width + (dst->width & 7)) >> 3;

    if (colorKey == 0xFF)
    {
        for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
        {
            pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
            for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
            {
                pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
                if (loopSrcX & 1)
                {
                    *pixelsDst = palOffsetBits + (*pixelsSrc >> 4);
                }
                else
                {
                    pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
                    *pixelsDst = palOffsetBits + (*pixelsSrc & 0xF);
                }
            }
        }
    }
    else
    {
        for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
        {
            pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
            for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
            {
                if (loopSrcX & 1)
                {
                    if ((*pixelsSrc & 0xF0) != colorKeyBits)
                    {
                        pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
                        *pixelsDst = palOffsetBits + (*pixelsSrc >> 4);
                    }
                }
                else
                {
                    pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
                    if ((*pixelsSrc & 0xF) != colorKey)
                    {
                        pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
                        *pixelsDst = palOffsetBits + (*pixelsSrc & 0xF);
                    }
                }
            }
        }
    }
}

void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue)
{
    s32 xEnd;
    s32 yEnd;
    s32 multiplierY;
    s32 loopX, loopY;

    xEnd = x + width;
    if (xEnd > surface->width)
        xEnd = surface->width;

    yEnd = y + height;
    if (yEnd > surface->height)
        yEnd = surface->height;

    multiplierY = (surface->width + (surface->width & 7)) >> 3;

    for (loopY = y; loopY < yEnd; loopY++)
    {
        for (loopX = x; loopX < xEnd; loopX++)
        {
            u8 *pixels = surface->pixels + (loopX & 7) + ((loopX >> 3) << 6) + (((loopY >> 3) * multiplierY) << 6) + ((u32)(loopY << 0x1d) >> 0x1a);
            *pixels = fillValue;
        }
    }
}


code:
#ifndef GUARD_BLIT_H
#define GUARD_BLIT_H

struct Bitmap
{
    u8 *pixels;
    u32 width:16;
    u32 height:16;
};

void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height);
void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey);
void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue);
void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset);
void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue);

#endif // GUARD_BLIT_H


code:
#ifndef GUARD_DMA3_H
#define GUARD_DMA3_H

// Maximum amount of data we will transfer in one operation
#define MAX_DMA_BLOCK_SIZE 0x1000

#define Dma3CopyLarge_(src, dest, size, bit)               \
{                                                          \
    const void *_src = src;                                \
    void *_dest = dest;                                    \
    u32 _size = size;                                      \
    while (1)                                              \
    {                                                      \
        if (_size <= MAX_DMA_BLOCK_SIZE)                   \
        {                                                  \
            DmaCopy##bit(3, _src, _dest, _size);           \
            break;                                         \
        }                                                  \
        DmaCopy##bit(3, _src, _dest, MAX_DMA_BLOCK_SIZE);  \
        _src += MAX_DMA_BLOCK_SIZE;                        \
        _dest += MAX_DMA_BLOCK_SIZE;                       \
        _size -= MAX_DMA_BLOCK_SIZE;                       \
    }                                                      \
}

#define Dma3CopyLarge16_(src, dest, size) Dma3CopyLarge_(src, dest, size, 16)
#define Dma3CopyLarge32_(src, dest, size) Dma3CopyLarge_(src, dest, size, 32)

#define Dma3FillLarge_(value, dest, size, bit)             \
{                                                          \
    void *_dest = dest;                                    \
    u32 _size = size;                                      \
    while (1)                                              \
    {                                                      \
        if (_size <= MAX_DMA_BLOCK_SIZE)                   \
        {                                                  \
            DmaFill##bit(3, value, _dest, _size);          \
            break;                                         \
        }                                                  \
        DmaFill##bit(3, value, _dest, MAX_DMA_BLOCK_SIZE); \
        _dest += MAX_DMA_BLOCK_SIZE;                       \
        _size -= MAX_DMA_BLOCK_SIZE;                       \
    }                                                      \
}

#define Dma3FillLarge16_(value, dest, size) Dma3FillLarge_(value, dest, size, 16)
#define Dma3FillLarge32_(value, dest, size) Dma3FillLarge_(value, dest, size, 32)

void ClearDma3Requests(void);
void ProcessDma3Requests(void);
s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode);
s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode);
s16 CheckForSpaceForDma3Request(s16 index);

#endif // GUARD_DMA3_H


code:
#include "global.h"
#include "dma3.h"

#define MAX_DMA_REQUESTS 128

#define DMA_REQUEST_COPY32 1
#define DMA_REQUEST_FILL32 2
#define DMA_REQUEST_COPY16 3
#define DMA_REQUEST_FILL16 4

BSS_DATA struct
{
    const u8 *src;
    u8 *dest;
    u16 size;
    u16 mode;
    u32 value;
} gDma3Requests[MAX_DMA_REQUESTS];

static volatile bool8 gDma3ManagerLocked;
static u8 gDma3RequestCursor;

void ClearDma3Requests(void)
{
    int i;

    gDma3ManagerLocked = TRUE;
    gDma3RequestCursor = 0;

    for (i = 0; i < MAX_DMA_REQUESTS; i++)
    {
        gDma3Requests[i].size = 0;
        gDma3Requests[i].src = NULL;
        gDma3Requests[i].dest = NULL;
    }

    gDma3ManagerLocked = FALSE;
}

void ProcessDma3Requests(void)
{
    u16 bytesTransferred;

    if (gDma3ManagerLocked)
        return;

    bytesTransferred = 0;

    // as long as there are DMA requests to process (unless size or vblank is an issue), do not exit
    while (gDma3Requests[gDma3RequestCursor].size != 0)
    {
        bytesTransferred += gDma3Requests[gDma3RequestCursor].size;

        if (bytesTransferred > 40 * 1024)
            return; // don't transfer more than 40 KiB
        if (*(u8 *)REG_ADDR_VCOUNT > 224)
            return; // we're about to leave vblank, stop

        switch (gDma3Requests[gDma3RequestCursor].mode)
        {
        case DMA_REQUEST_COPY32: // regular 32-bit copy
            Dma3CopyLarge32_(gDma3Requests[gDma3RequestCursor].src,
                             gDma3Requests[gDma3RequestCursor].dest,
                             gDma3Requests[gDma3RequestCursor].size);
            break;
        case DMA_REQUEST_FILL32: // repeat a single 32-bit value across RAM
            Dma3FillLarge32_(gDma3Requests[gDma3RequestCursor].value,
                             gDma3Requests[gDma3RequestCursor].dest,
                             gDma3Requests[gDma3RequestCursor].size);
            break;
        case DMA_REQUEST_COPY16:    // regular 16-bit copy
            Dma3CopyLarge16_(gDma3Requests[gDma3RequestCursor].src,
                             gDma3Requests[gDma3RequestCursor].dest,
                             gDma3Requests[gDma3RequestCursor].size);
            break;
        case DMA_REQUEST_FILL16: // repeat a single 16-bit value across RAM
            Dma3FillLarge16_(gDma3Requests[gDma3RequestCursor].value,
                             gDma3Requests[gDma3RequestCursor].dest,
                             gDma3Requests[gDma3RequestCursor].size);
            break;
        }

        // Free the request
        gDma3Requests[gDma3RequestCursor].src = NULL;
        gDma3Requests[gDma3RequestCursor].dest = NULL;
        gDma3Requests[gDma3RequestCursor].size = 0;
        gDma3Requests[gDma3RequestCursor].mode = 0;
        gDma3Requests[gDma3RequestCursor].value = 0;
        gDma3RequestCursor++;

        if (gDma3RequestCursor >= MAX_DMA_REQUESTS) // loop back to the first DMA request
            gDma3RequestCursor = 0;
    }
}

s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode)
{
    int cursor;
    int i = 0;

    gDma3ManagerLocked = TRUE;
    cursor = gDma3RequestCursor;

    while (i < MAX_DMA_REQUESTS)
    {
        if (gDma3Requests[cursor].size == 0) // an empty request was found.
        {
            gDma3Requests[cursor].src = src;
            gDma3Requests[cursor].dest = dest;
            gDma3Requests[cursor].size = size;

            if (mode == 1)
                gDma3Requests[cursor].mode = DMA_REQUEST_COPY32;
            else
                gDma3Requests[cursor].mode = DMA_REQUEST_COPY16;

            gDma3ManagerLocked = FALSE;
            return cursor;
        }
        if (++cursor >= MAX_DMA_REQUESTS) // loop back to start.
            cursor = 0;
        i++;
    }
    gDma3ManagerLocked = FALSE;
    return -1;  // no free DMA request was found
}

s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode)
{
    int cursor;
    int i = 0;

    cursor = gDma3RequestCursor;
    gDma3ManagerLocked = TRUE;

    while (i < MAX_DMA_REQUESTS)
    {
        if (gDma3Requests[cursor].size == 0) // an empty request was found.
        {
            gDma3Requests[cursor].dest = dest;
            gDma3Requests[cursor].size = size;
            gDma3Requests[cursor].mode = mode;
            gDma3Requests[cursor].value = value;

            if(mode == 1)
                gDma3Requests[cursor].mode = DMA_REQUEST_FILL32;
            else
                gDma3Requests[cursor].mode = DMA_REQUEST_FILL16;

            gDma3ManagerLocked = FALSE;
            return cursor;
        }
        if (++cursor >= MAX_DMA_REQUESTS) // loop back to start.
            cursor = 0;
        i++;
    }
    gDma3ManagerLocked = FALSE;
    return -1;  // no free DMA request was found
}

s16 CheckForSpaceForDma3Request(s16 index)
{
    int i = 0;

    if (index == -1)  // check if all requests are free
    {
        while (i < MAX_DMA_REQUESTS)
        {
            if (gDma3Requests[i].size != 0)
                return -1;
            i++;
        }
        return 0;
    }
    else  // check the specified request
    {
        if (gDma3Requests[index].size != 0)
            return -1;
        return 0;
    }
}


code:
#include "global.h"
#include "gpu_regs.h"

#define GPU_REG_BUF_SIZE 0x60

#define GPU_REG_BUF(offset) (*(u16 *)(&sGpuRegBuffer[offset]))
#define GPU_REG(offset) (*(vu16 *)(REG_BASE + offset))

#define EMPTY_SLOT 0xFF

static u8 sGpuRegBuffer[GPU_REG_BUF_SIZE];
static u8 sGpuRegWaitingList[GPU_REG_BUF_SIZE];
static volatile bool8 sGpuRegBufferLocked;
static volatile bool8 sShouldSyncRegIE;
static vu16 sRegIE;

static void CopyBufferedValueToGpuReg(u8 regOffset);
static void SyncRegIE(void);
static void UpdateRegDispstatIntrBits(u16 regIE);

void InitGpuRegManager(void)
{
    s32 i;

    for (i = 0; i < GPU_REG_BUF_SIZE; i++)
    {
        sGpuRegBuffer[i] = 0;
        sGpuRegWaitingList[i] = EMPTY_SLOT;
    }

    sGpuRegBufferLocked = FALSE;
    sShouldSyncRegIE = FALSE;
    sRegIE = 0;
}

static void CopyBufferedValueToGpuReg(u8 regOffset)
{
    if (regOffset == REG_OFFSET_DISPSTAT)
    {
        REG_DISPSTAT &= ~(DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR);
        REG_DISPSTAT |= GPU_REG_BUF(REG_OFFSET_DISPSTAT);
    }
    else
    {
        GPU_REG(regOffset) = GPU_REG_BUF(regOffset);
    }
}

void CopyBufferedValuesToGpuRegs(void)
{
    if (!sGpuRegBufferLocked)
    {
        s32 i;

        for (i = 0; i < GPU_REG_BUF_SIZE; i++)
        {
            u8 regOffset = sGpuRegWaitingList[i];
            if (regOffset == EMPTY_SLOT)
                return;
            CopyBufferedValueToGpuReg(regOffset);
            sGpuRegWaitingList[i] = EMPTY_SLOT;
        }
    }
}

void SetGpuReg(u8 regOffset, u16 value)
{
    if (regOffset < GPU_REG_BUF_SIZE)
    {
        u16 vcount;

        GPU_REG_BUF(regOffset) = value;
        vcount = REG_VCOUNT & 0xFF;

        if ((vcount >= 161 && vcount <= 225) || (REG_DISPCNT & DISPCNT_FORCED_BLANK))
        {
            CopyBufferedValueToGpuReg(regOffset);
        }
        else
        {
            s32 i;

            sGpuRegBufferLocked = TRUE;

            for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++)
            {
                if (sGpuRegWaitingList[i] == regOffset)
                {
                    sGpuRegBufferLocked = FALSE;
                    return;
                }
            }

            sGpuRegWaitingList[i] = regOffset;
            sGpuRegBufferLocked = FALSE;
        }
    }
}

void SetGpuReg_ForcedBlank(u8 regOffset, u16 value)
{
    if (regOffset < GPU_REG_BUF_SIZE)
    {
        GPU_REG_BUF(regOffset) = value;

        if (REG_DISPCNT & DISPCNT_FORCED_BLANK)
        {
            CopyBufferedValueToGpuReg(regOffset);
        }
        else
        {
            s32 i;

            sGpuRegBufferLocked = TRUE;

            for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++)
            {
                if (sGpuRegWaitingList[i] == regOffset)
                {
                    sGpuRegBufferLocked = FALSE;
                    return;
                }
            }

            sGpuRegWaitingList[i] = regOffset;
            sGpuRegBufferLocked = FALSE;
        }
    }
}

u16 GetGpuReg(u8 regOffset)
{
    if (regOffset == REG_OFFSET_DISPSTAT)
        return REG_DISPSTAT;

    if (regOffset == REG_OFFSET_VCOUNT)
        return REG_VCOUNT;

    return GPU_REG_BUF(regOffset);
}

void SetGpuRegBits(u8 regOffset, u16 mask)
{
    u16 regValue = GPU_REG_BUF(regOffset);
    SetGpuReg(regOffset, regValue | mask);
}

void ClearGpuRegBits(u8 regOffset, u16 mask)
{
    u16 regValue = GPU_REG_BUF(regOffset);
    SetGpuReg(regOffset, regValue & ~mask);
}

static void SyncRegIE(void)
{
    if (sShouldSyncRegIE)
    {
        u16 temp = REG_IME;
        REG_IME = 0;
        REG_IE = sRegIE;
        REG_IME = temp;
        sShouldSyncRegIE = FALSE;
    }
}

void EnableInterrupts(u16 mask)
{
    sRegIE |= mask;
    sShouldSyncRegIE = TRUE;
    SyncRegIE();
    UpdateRegDispstatIntrBits(sRegIE);
}

void DisableInterrupts(u16 mask)
{
    sRegIE &= ~mask;
    sShouldSyncRegIE = TRUE;
    SyncRegIE();
    UpdateRegDispstatIntrBits(sRegIE);
}

static void UpdateRegDispstatIntrBits(u16 regIE)
{
    u16 oldValue = GetGpuReg(REG_OFFSET_DISPSTAT) & (DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR);
    u16 newValue = 0;

    if (regIE & INTR_FLAG_VBLANK)
        newValue |= DISPSTAT_VBLANK_INTR;

    if (regIE & INTR_FLAG_HBLANK)
        newValue |= DISPSTAT_HBLANK_INTR;

    if (oldValue != newValue)
        SetGpuReg(REG_OFFSET_DISPSTAT, newValue);
}


code:
#ifndef GUARD_GPU_REGS_H
#define GUARD_GPU_REGS_H

// Exported type declarations

// Exported RAM declarations

// Exported ROM declarations
void InitGpuRegManager(void);
void CopyBufferedValuesToGpuRegs(void);
void SetGpuReg(u8 regOffset, u16 value);
void SetGpuReg_ForcedBlank(u8 regOffset, u16 value);
u16 GetGpuReg(u8 regOffset);
void SetGpuRegBits(u8 regOffset, u16 mask);
void ClearGpuRegBits(u8 regOffset, u16 mask);
void EnableInterrupts(u16 mask);
void DisableInterrupts(u16 mask);

#endif //GUARD_GPU_REGS_H


code:
#ifndef GUARD_IO_REG_H
#define GUARD_IO_REG_H

extern const u16 gOverworldBackgroundLayerFlags[];
extern const u16 gOrbEffectBackgroundLayerFlags[];

#endif // GUARD_IO_REG_H