_RunPaletteCommand: call GetPredefRegisters ld a, b cp SET_PAL_DEFAULT jr nz, .not_default ld a, [wDefaultPaletteCommand] .not_default cp SET_PAL_PARTY_MENU_HP_BARS jp z, UpdatePartyMenuBlkPacket ld l, a ld h, 0 add hl, hl ld de, SetPalFunctions add hl, de ld a, [hli] ld h, [hl] ld l, a ld de, SendSGBPackets push de jp hl SetPal_BattleBlack: ld hl, PalPacket_Black ld de, BlkPacket_Battle ret ; uses PalPacket_Empty to build a packet based on mon IDs and health color SetPal_Battle: ld hl, PalPacket_Empty ld de, wPalPacket ld bc, $10 call CopyData ld a, [wPlayerBattleStatus3] ld hl, wBattleMonSpecies call DeterminePaletteID ld b, a ld a, [wEnemyBattleStatus3] ld hl, wEnemyMonSpecies2 call DeterminePaletteID ld c, a ld hl, wPalPacket + 1 ld a, [wPlayerHPBarColor] add PAL_GREENBAR ld [hli], a inc hl ld a, [wEnemyHPBarColor] add PAL_GREENBAR ld [hli], a inc hl ld a, b ld [hli], a inc hl ld a, c ld [hl], a ld hl, wPalPacket ld de, BlkPacket_Battle ld a, SET_PAL_BATTLE ld [wDefaultPaletteCommand], a ret SetPal_TownMap: ld hl, PalPacket_TownMap ld de, BlkPacket_WholeScreen ret ; uses PalPacket_Empty to build a packet based the mon ID SetPal_StatusScreen: ld hl, PalPacket_Empty ld de, wPalPacket ld bc, $10 call CopyData ld a, [wcf91] cp NUM_POKEMON_INDEXES + 1 jr c, .pokemon ld a, $1 ; not pokemon .pokemon call DeterminePaletteIDOutOfBattle push af ld hl, wPalPacket + 1 ld a, [wStatusScreenHPBarColor] add PAL_GREENBAR ld [hli], a inc hl pop af ld [hl], a ld hl, wPalPacket ld de, BlkPacket_StatusScreen ret SetPal_PartyMenu: ld hl, PalPacket_PartyMenu ld de, wPartyMenuBlkPacket ret SetPal_Pokedex: ld hl, PalPacket_Pokedex ld de, wPalPacket ld bc, $10 call CopyData ld a, [wcf91] call DeterminePaletteIDOutOfBattle ld hl, wPalPacket + 3 ld [hl], a ld hl, wPalPacket ld de, BlkPacket_Pokedex ret SetPal_Slots: ld hl, PalPacket_Slots ld de, BlkPacket_Slots ret SetPal_TitleScreen: ld hl, PalPacket_Titlescreen ld de, BlkPacket_Titlescreen ret ; used mostly for menus and the Oak intro SetPal_Generic: ld hl, PalPacket_Generic ld de, BlkPacket_WholeScreen ret SetPal_NidorinoIntro: ld hl, PalPacket_NidorinoIntro ld de, BlkPacket_NidorinoIntro ret SetPal_GameFreakIntro: ld hl, PalPacket_GameFreakIntro ld de, BlkPacket_GameFreakIntro ld a, SET_PAL_GENERIC ld [wDefaultPaletteCommand], a ret ; uses PalPacket_Empty to build a packet based on the current map SetPal_Overworld: ld hl, PalPacket_Empty ld de, wPalPacket ld bc, $10 call CopyData ld a, [wCurMapTileset] cp CEMETERY jr z, .PokemonTowerOrAgatha cp CAVERN jr z, .caveOrBruno cp CELESTE jr z, .celeste cp CITRINE jr z, .citrine cp FOREST jr z, .forest cp SHIP jr z, .ship ld a, [wCurMap] cp FIRST_INDOOR_MAP jr c, .townOrRoute cp POWER_PLANT jr z, .powerPlant cp CERULEAN_CAVE_2F jr c, .normalDungeonOrBuilding cp CELESTE_HILL jr z, .celeste cp CITRINE_CITY jr z, .citrine cp BRUNOS_ROOM jr z, .caveOrBruno .normalDungeonOrBuilding ld a, [wLastMap] ; town or route that current dungeon or building is located .townOrRoute cp NUM_CITY_MAPS jr c, .town ld a, PAL_ROUTE - 1 .town inc a ; a town's palette ID is its map ID + 1 ld hl, wPalPacket + 1 ld [hld], a ld de, BlkPacket_WholeScreen ld a, SET_PAL_OVERWORLD ld [wDefaultPaletteCommand], a ret .PokemonTowerOrAgatha ld a, PAL_GREYMON - 1 jr .town .caveOrBruno ld a, [wCurMap] cp SEAFOAM_ISLANDS_1F jr z, .seafoam cp CINNABAR_VOLCANO jr z, .volcano cp SEAFOAM_ISLANDS_B1F jr c, .caveDefault cp SEAFOAM_ISLANDS_B4F + 1 jr c, .seafoam .caveDefault ld a, PAL_CAVE - 1 jr .town .celeste ld a, [wCurMap] cp FARAWAY_ISLAND_OUTSIDE jr z, .faraway cp FARAWAY_ISLAND_INSIDE jr z, .faraway cp CELESTE_HILL jr c, .celesteDefault .celesteDefault ld a, PAL_CELESTE - 1 jr .town .citrine ld a, [wCurMap] cp BRUNSWICK_TRAIL jr z, .brunswick cp CITRINE_CITY jr c, .citrineDefault .citrineDefault ld a, PAL_CITRINE - 1 jr .town .ship ld a, PAL_VERMILION - 1 jr .town .forest ld a, [wCurMap] cp VIRIDIAN_FOREST jr z, .viridian cp SILPH_GAUNTLET_1F jr z, .viridian cp MT_MOON_CRATER jr z, .faraway cp SILPH_GAUNTLET_5F jr c, .forestDefault .forestDefault ld a, PAL_FUCHSIA - 1 jr .town .powerPlant ld a, PAL_YELLOWMON - 1 jr .town .viridian ld a, PAL_VIRIDIAN - 1 jr .town .faraway ld a, PAL_ROUTE - 1 jr .town .brunswick ld a, PAL_BRUNSWICK - 1 jr .town .seafoam ld a, PAL_CYANMON - 1 jr .town .volcano ld a, PAL_REDMON - 1 jr .town ; used when a Pokemon is the only thing on the screen ; such as evolution, trading and the Hall of Fame SetPal_PokemonWholeScreen: push bc ld hl, PalPacket_Empty ld de, wPalPacket ld bc, $10 call CopyData pop bc ld a, c and a ld a, PAL_BLACK jr nz, .next ld a, [wWholeScreenPaletteMonSpecies] call DeterminePaletteIDOutOfBattle .next ld [wPalPacket + 1], a ld hl, wPalPacket ld de, BlkPacket_WholeScreen ret SetPal_TrainerCard: ld hl, BlkPacket_TrainerCard ld de, wTrainerCardBlkPacket ld bc, $40 call CopyData ld de, BadgeBlkDataLengths ld hl, wTrainerCardBlkPacket + 2 ld a, [wObtainedBadges] ld c, NUM_BADGES .badgeLoop srl a push af jr c, .haveBadge ; The player doens't have the badge, so zero the badge's blk data. push bc ld a, [de] ld c, a xor a .zeroBadgeDataLoop ld [hli], a dec c jr nz, .zeroBadgeDataLoop pop bc jr .nextBadge .haveBadge ; The player does have the badge, so skip past the badge's blk data. ld a, [de] .skipBadgeDataLoop inc hl dec a jr nz, .skipBadgeDataLoop .nextBadge pop af inc de dec c jr nz, .badgeLoop ld hl, PalPacket_TrainerCard ld de, wTrainerCardBlkPacket ret SetPalFunctions: ; entries correspond to SET_PAL_* constants dw SetPal_BattleBlack dw SetPal_Battle dw SetPal_TownMap dw SetPal_StatusScreen dw SetPal_Pokedex dw SetPal_Slots dw SetPal_TitleScreen dw SetPal_NidorinoIntro dw SetPal_Generic dw SetPal_Overworld dw SetPal_PartyMenu dw SetPal_PokemonWholeScreen dw SetPal_GameFreakIntro dw SetPal_TrainerCard ; The length of the blk data of each badge on the Trainer Card. ; The Rainbow Badge has 3 entries because of its many colors. BadgeBlkDataLengths: db 6 ; Boulder Badge db 6 ; Cascade Badge db 6 ; Thunder Badge db 6 * 3 ; Rainbow Badge db 6 ; Soul Badge db 6 ; Marsh Badge db 6 ; Volcano Badge db 6 ; Earth Badge DeterminePaletteID: bit TRANSFORMED, a ; a is battle status 3 ld a, PAL_PURPLEMON ; if the mon has used Transform, use Ditto's palette ret nz ld a, [hl] DeterminePaletteIDOutOfBattle: ld [wd11e], a and a ; is the mon index 0? jr z, .skipDexNumConversion push bc predef IndexToPokedex pop bc ld a, [wd11e] .skipDexNumConversion ld e, a ld d, 0 ld hl, MonsterPalettes ; not just for Pokemon, Trainers use it too add hl, de ld a, [hl] ret InitPartyMenuBlkPacket: ld hl, BlkPacket_PartyMenu ld de, wPartyMenuBlkPacket ld bc, $30 jp CopyData UpdatePartyMenuBlkPacket: ; Update the blk packet with the palette of the HP bar that is ; specified in [wWhichPartyMenuHPBar]. ld hl, wPartyMenuHPBarColors ld a, [wWhichPartyMenuHPBar] ld e, a ld d, 0 add hl, de ld e, l ld d, h ld a, [de] and a ld e, (1 << 2) | 1 ; green jr z, .next dec a ld e, (2 << 2) | 2 ; yellow jr z, .next ld e, (3 << 2) | 3 ; red .next push de ld hl, wPartyMenuBlkPacket + 8 + 1 ld bc, 6 ld a, [wWhichPartyMenuHPBar] call AddNTimes pop de ld [hl], e ret SendSGBPacket: ;check number of packets ld a, [hl] and $07 ret z ; store number of packets in B ld b, a .loop2 ; save B for later use push bc ; disable ReadJoypad to prevent it from interfering with sending the packet ld a, 1 ldh [hDisableJoypadPolling], a ; send RESET signal (P14=LOW, P15=LOW) xor a ldh [rJOYP], a ; set P14=HIGH, P15=HIGH ld a, $30 ldh [rJOYP], a ;load length of packets (16 bytes) ld b, $10 .nextByte ;set bit counter (8 bits per byte) ld e, $08 ; get next byte in the packet ld a, [hli] ld d, a .nextBit0 bit 0, d ; if 0th bit is not zero set P14=HIGH, P15=LOW (send bit 1) ld a, $10 jr nz, .next0 ; else (if 0th bit is zero) set P14=LOW, P15=HIGH (send bit 0) ld a, $20 .next0 ldh [rJOYP], a ; must set P14=HIGH,P15=HIGH between each "pulse" ld a, $30 ldh [rJOYP], a ; rotation will put next bit in 0th position (so we can always use command ; "bit 0, d" to fetch the bit that has to be sent) rr d ; decrease bit counter so we know when we have sent all 8 bits of current byte dec e jr nz, .nextBit0 dec b jr nz, .nextByte ; send bit 1 as a "stop bit" (end of parameter data) ld a, $20 ldh [rJOYP], a ; set P14=HIGH,P15=HIGH ld a, $30 ldh [rJOYP], a xor a ldh [hDisableJoypadPolling], a ; wait for about 70000 cycles call Wait7000 ; restore (previously pushed) number of packets pop bc dec b ; return if there are no more packets ret z ; else send 16 more bytes jr .loop2 LoadSGB: xor a ld [wOnSGB], a call CheckSGB ret nc ld a, 1 ld [wOnSGB], a ld a, [wGBC] and a jr z, .notGBC ret .notGBC di call PrepareSuperNintendoVRAMTransfer ei ld a, 1 ld [wCopyingSGBTileData], a ld de, ChrTrnPacket ld hl, SGBBorderGraphics call CopyGfxToSuperNintendoVRAM xor a ld [wCopyingSGBTileData], a ld de, PctTrnPacket ld hl, BorderPalettes call CopyGfxToSuperNintendoVRAM xor a ld [wCopyingSGBTileData], a ld de, PalTrnPacket ld hl, SuperPalettes call CopyGfxToSuperNintendoVRAM call ClearVram ld hl, MaskEnCancelPacket jp SendSGBPacket PrepareSuperNintendoVRAMTransfer: ld hl, .packetPointers ld c, 9 .loop push bc ld a, [hli] push hl ld h, [hl] ld l, a call SendSGBPacket pop hl inc hl pop bc dec c jr nz, .loop ret .packetPointers ; Only the first packet is needed. dw MaskEnFreezePacket dw DataSndPacket1 dw DataSndPacket2 dw DataSndPacket3 dw DataSndPacket4 dw DataSndPacket5 dw DataSndPacket6 dw DataSndPacket7 dw DataSndPacket8 CheckSGB: ; Returns whether the game is running on an SGB in carry. ld hl, MltReq2Packet di call SendSGBPacket ld a, 1 ldh [hDisableJoypadPolling], a ei call Wait7000 ldh a, [rJOYP] and $3 cp $3 jr nz, .isSGB ld a, $20 ldh [rJOYP], a ldh a, [rJOYP] ldh a, [rJOYP] call Wait7000 call Wait7000 ld a, $30 ldh [rJOYP], a call Wait7000 call Wait7000 ld a, $10 ldh [rJOYP], a ldh a, [rJOYP] ldh a, [rJOYP] ldh a, [rJOYP] ldh a, [rJOYP] ldh a, [rJOYP] ldh a, [rJOYP] call Wait7000 vc_hook Unknown_network_reset call Wait7000 ld a, $30 ldh [rJOYP], a ldh a, [rJOYP] ldh a, [rJOYP] ldh a, [rJOYP] call Wait7000 call Wait7000 ldh a, [rJOYP] and $3 cp $3 jr nz, .isSGB call SendMltReq1Packet and a ret .isSGB call SendMltReq1Packet scf ret SendMltReq1Packet: ld hl, MltReq1Packet call SendSGBPacket jp Wait7000 CopyGfxToSuperNintendoVRAM: di push de call DisableLCD ld a, $e4 ldh [rBGP], a ld de, vChars1 ld a, [wCopyingSGBTileData] and a jr z, .notCopyingTileData call CopySGBBorderTiles jr .next .notCopyingTileData ld bc, $1000 call CopyData .next ld hl, vBGMap0 ld de, $c ld a, $80 ld c, $d .loop ld b, $14 .innerLoop ld [hli], a inc a dec b jr nz, .innerLoop add hl, de dec c jr nz, .loop ld a, $e3 ldh [rLCDC], a pop hl call SendSGBPacket xor a ldh [rBGP], a ei ret Wait7000: ; Each loop takes 9 cycles so this routine actually waits 63000 cycles. ld de, 7000 .loop nop nop nop dec de ld a, d or e jr nz, .loop ret SendSGBPackets: ld a, [wGBC] and a jr z, .notGBC push de call InitGBCPalettes pop hl call EmptyFunc3 ret .notGBC push de call SendSGBPacket pop hl jp SendSGBPacket InitGBCPalettes: ld a, $80 ; index 0 with auto-increment ldh [rBGPI], a inc hl ld c, $20 .loop ld a, [hli] inc hl add a add a add a ld de, SuperPalettes add e jr nc, .noCarry inc d .noCarry ld a, [de] ldh [rBGPD], a dec c jr nz, .loop ret EmptyFunc3: ret CopySGBBorderTiles: ; SGB tile data is stored in a 4BPP planar format. ; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while ; the second 16 bytes contain bit planes 3 and 4. ; This function converts 2BPP planar data into this format by mapping ; 2BPP colors 0-3 to 4BPP colors 0-3. 4BPP colors 4-15 are not used. ld b, 128 .tileLoop ; Copy bit planes 1 and 2 of the tile data. ld c, 16 .copyLoop ld a, [hli] ld [de], a inc de dec c jr nz, .copyLoop ; Zero bit planes 3 and 4. ld c, 16 xor a .zeroLoop ld [de], a inc de dec c jr nz, .zeroLoop dec b jr nz, .tileLoop ret INCLUDE "data/sgb/sgb_packets.asm" INCLUDE "data/pokemon/palettes.asm" INCLUDE "data/sgb/sgb_palettes.asm" INCLUDE "data/sgb/sgb_border.asm"