def addBank3F(rom): # Bank3F is used to initialize the tile data in VRAM:1 at the start of the rom. # The normal rom does not use this tile data to maintain GB compatibility. rom.patch(0, 0x0150, ASM(""" cp $11 ; is running on Game Boy Color? jr nz, notGBC ldh a, [$4d] and $80 ; do we need to switch the CPU speed? jr nz, speedSwitchDone ; switch to GBC speed ld a, $30 ldh [$00], a ld a, $01 ldh [$4d], a xor a ldh [$ff], a stop db $00 speedSwitchDone: xor a ldh [$70], a ld a, $01 ; isGBC = true jr Init notGBC: xor a ; isGBC = false Init: """), ASM(""" ; Check if we are a color gameboy, we require a color version now. cp $11 jr nz, notGBC ; Switch to bank $3F to run our custom initializer ld a, $3F ld [$2100], a call $4000 ; Switch back to bank 0 after loading our own initializer ld a, $01 ld [$2100], a ; set a to 1 to indicate GBC ld a, $01 jr Init notGBC: xor a Init: """), fill_nop=True) rom.patch( 0x3F, 0x0000, None, ASM(""" ; switch speed ld a, $30 ldh [$00], a ld a, $01 ldh [$4d], a xor a ldh [$ff], a stop db $00 ; Switch VRAM bank ld a, $01 ldh [$4F], a call $28CF ; display off ; Use the GBC DMA to transfer our tile data ld a, $70 ldh [$51], a ld a, $00 ldh [$52], a ld a, $80 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone: ldh a, [$55] and $80 jr z, waitTillTransferDone ld a, $78 ldh [$51], a ld a, $00 ldh [$52], a ld a, $88 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone2: ldh a, [$55] and $80 jr z, waitTillTransferDone2 ld a, $70 ldh [$51], a ld a, $00 ldh [$52], a ld a, $90 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone3: ldh a, [$55] and $80 jr z, waitTillTransferDone3 ; Switch VRAM bank back ld a, $00 ldh [$4F], a ; Switch the display back on, else the later code hangs ld a, $80 ldh [$40], a speedSwitchDone: xor a ldh [$70], a ret """)) # Copy all normal item graphics rom.banks[0x3F][0x3000:0x3300] = rom.banks[0x2C][0x0800: 0x0B00] # main items rom.banks[0x3F][0x3300:0x3400] = rom.banks[0x2C][ 0x0C00:0x0D00] # overworld key items rom.banks[0x3F][0x3400:0x3500] = rom.banks[0x32][ 0x3D00:0x3E00] # dungeon key items # Create ruppee for palettes 0-3 rom.banks[0x3F][0x3380:0x33A0] = rom.banks[0x3F][0x3260:0x3280] for n in range(0x3380, 0x33A0, 2): rom.banks[0x3F][n + 1] ^= rom.banks[0x3F][n] # Create capacity upgrade arrows rom.banks[0x3F][0x3230:0x3240] = utils.createTileData(""" 33 3113 311113 33311333 3113 3333 """) rom.banks[0x3F][0x3220:0x3230] = rom.banks[0x3F][0x3230:0x3240] for n in range(0x3220, 0x3240, 2): rom.banks[0x3F][n] |= rom.banks[0x3F][n + 1] # Add the slime key and mushroom which are not in the above sets rom.banks[0x3F][0x34C0:0x3500] = rom.banks[0x2C][0x28C0:0x2900] # Add tunic sprites as well. rom.banks[0x3F][0x3480:0x34A0] = rom.banks[0x35][0x0F00:0x0F20] # Add the bowwow sprites rom.banks[0x3F][0x3500:0x3600] = rom.banks[0x2E][0x2400:0x2500] # Zol sprites, so we can have zol anywhere from a chest rom.banks[0x3F][0x3600:0x3640] = rom.banks[0x2E][0x1120:0x1160] # Elephant statue rom.banks[0x3F][0x3640:0x3680] = rom.banks[0x2E][0x2680:0x26C0] # Cucco rom.banks[0x3F][0x3680:0x3700] = rom.banks[0x32][0x2500:0x2580] # Song symbols rom.banks[0x3F][0x3700:0x3760] = utils.createTileData( """ ... . .222 .2.2222 .22.222. .22222.3 .2..22.3 .33...3 .33.3.3 ..233.3 .22.2333 .222.233 .222... ... """ + """ .. .22 .223 ..222 .33.22 .3..22 .33.33 ..23. ..233. .22.333 .22..233 .. .23 .. """ + """ ... .222. .2.332 .23.32 .233.2 .222222 .2222222 .2..22.2 .2.3.222 .22...22 .2333.. .23333 .....""", " .23") # Patch the cucco graphics to load from 2nd vram bank rom.patch(0x05, 0x0514, "5001" "5201" "5401" "5601" "5221" "5021" "5621" "5421", "6809" "6A09" "6C09" "6E09" "6A29" "6829" "6E29" "6C29")
def addBank3F(rom): # Bank3F is used to initialize the tile data in VRAM:1 at the start of the rom. # The normal rom does not use this tile data to maintain GB compatibility. rom.patch(0, 0x0150, ASM(""" cp $11 ; is running on Game Boy Color? jr nz, notGBC ldh a, [$4d] and $80 ; do we need to switch the CPU speed? jr nz, speedSwitchDone ; switch to GBC speed ld a, $30 ldh [$00], a ld a, $01 ldh [$4d], a xor a ldh [$ff], a stop db $00 speedSwitchDone: xor a ldh [$70], a ld a, $01 ; isGBC = true jr Init notGBC: xor a ; isGBC = false Init: """), ASM(""" ; Check if we are a color gameboy, we require a color version now. cp $11 jr nz, notGBC ; Switch to bank $3F to run our custom initializer ld a, $3F ld [$2100], a call $4000 ; Switch back to bank 0 after loading our own initializer ld a, $01 ld [$2100], a ; set a to 1 to indicate GBC ld a, $01 jr Init notGBC: xor a Init: """), fill_nop=True) rom.patch( 0x3F, 0x0000, None, ASM(""" ; switch speed ld a, $30 ldh [$00], a ld a, $01 ldh [$4d], a xor a ldh [$ff], a stop db $00 ; Switch VRAM bank ld a, $01 ldh [$4F], a call $28CF ; display off ; Use the GBC DMA to transfer our tile data ld a, $70 ldh [$51], a ld a, $00 ldh [$52], a ld a, $80 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone: ldh a, [$55] and $80 jr z, waitTillTransferDone ld a, $78 ldh [$51], a ld a, $00 ldh [$52], a ld a, $88 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone2: ldh a, [$55] and $80 jr z, waitTillTransferDone2 ld a, $70 ldh [$51], a ld a, $00 ldh [$52], a ld a, $90 ldh [$53], a ld a, $00 ldh [$54], a ld a, $7F ldh [$55], a waitTillTransferDone3: ldh a, [$55] and $80 jr z, waitTillTransferDone3 ; Switch VRAM bank back ld a, $00 ldh [$4F], a ; Switch the display back on, else the later code hangs ld a, $80 ldh [$40], a speedSwitchDone: xor a ldh [$70], a ; Check if we are running on a bad emulator ldh [$02], a ldh a, [$02] and $7c cp $7c jr nz, badEmu ; Enable the timer to run 32 times per second xor a ldh [$06], a ld a, $04 ldh [$07], a ret badEmu: xor a ldh [$40], a ; switch display off ; Load some palette ld a, $80 ldh [$68], a xor a ldh [$69], a ldh [$69], a ldh [$69], a ldh [$69], a ; Load a different gfx tile for the first gfx cpl ld hl, $8000 ld c, $10 .loop: ldi [hl], a dec c jr nz, .loop ld a, $01 ld [$9800], a ld [$9820], a ld [$9840], a ld [$9860], a ld [$9880], a ld [$9801], a ld [$9841], a ld [$9881], a ld [$9822], a ld [$9862], a ld [$9824], a ld [$9844], a ld [$9864], a ld [$9884], a ld [$9805], a ld [$9845], a ld [$9826], a ld [$9846], a ld [$9866], a ld [$9886], a ld [$9808], a ld [$9828], a ld [$9848], a ld [$9868], a ld [$9888], a ld [$9809], a ld [$9889], a ld [$982A], a ld [$984A], a ld [$986A], a ld [$9900], a ld [$9920], a ld [$9940], a ld [$9960], a ld [$9980], a ld [$9901], a ld [$9941], a ld [$9981], a ld [$9903], a ld [$9923], a ld [$9943], a ld [$9963], a ld [$9983], a ld [$9904], a ld [$9925], a ld [$9906], a ld [$9907], a ld [$9927], a ld [$9947], a ld [$9967], a ld [$9987], a ld [$9909], a ld [$9929], a ld [$9949], a ld [$9969], a ld [$9989], a ld [$998A], a ld [$990B], a ld [$992B], a ld [$994B], a ld [$996B], a ld [$998B], a ; lcd on ld a, $91 ldh [$40], a blockBadEmu: di jr blockBadEmu """)) # Copy all normal item graphics rom.banks[0x3F][0x3000:0x3300] = rom.banks[0x2C][0x0800: 0x0B00] # main items rom.banks[0x3F][0x3300:0x3400] = rom.banks[0x2C][ 0x0C00:0x0D00] # overworld key items rom.banks[0x3F][0x3400:0x3500] = rom.banks[0x32][ 0x3D00:0x3E00] # dungeon key items # Create ruppee for palettes 0-3 rom.banks[0x3F][0x3380:0x33A0] = rom.banks[0x3F][0x3260:0x3280] for n in range(0x3380, 0x33A0, 2): rom.banks[0x3F][n + 1] ^= rom.banks[0x3F][n] # Create capacity upgrade arrows rom.banks[0x3F][0x3230:0x3240] = utils.createTileData(""" 33 3113 311113 33311333 3113 3333 """) rom.banks[0x3F][0x3220:0x3230] = rom.banks[0x3F][0x3230:0x3240] for n in range(0x3220, 0x3240, 2): rom.banks[0x3F][n] |= rom.banks[0x3F][n + 1] # Add the slime key and mushroom which are not in the above sets rom.banks[0x3F][0x34C0:0x3500] = rom.banks[0x2C][0x28C0:0x2900] # Add tunic sprites as well. rom.banks[0x3F][0x3480:0x34A0] = rom.banks[0x35][0x0F00:0x0F20] # Add the bowwow sprites rom.banks[0x3F][0x3500:0x3600] = rom.banks[0x2E][0x2400:0x2500] # Zol sprites, so we can have zol anywhere from a chest rom.banks[0x3F][0x3600:0x3660] = rom.banks[0x2E][0x1120:0x1180] # Patch gel(zol) entity to load sprites from the 2nd bank rom.patch(0x06, 0x3C09, "5202522254025422" "5200522054005420", "600A602A620A622A" "6008602862086228") rom.patch(0x07, 0x329B, "FFFFFFFF" "FFFFFFFF" "54005420" "52005220" "56005600", "FFFFFFFF" "FFFFFFFF" "62086228" "60086028" "64086408") rom.patch(0x06, 0x3BFA, "56025622", "640A642A") # Cucco rom.banks[0x3F][0x3680:0x3700] = rom.banks[0x32][0x2500:0x2580] # Patch the cucco graphics to load from 2nd vram bank rom.patch(0x05, 0x0514, "5001" "5201" "5401" "5601" "5221" "5021" "5621" "5421", "6809" "6A09" "6C09" "6E09" "6A29" "6829" "6E29" "6C29") # Song symbols rom.banks[0x3F][0x3700:0x3760] = utils.createTileData( """ ... . .222 .2.2222 .22.222. .22222.3 .2..22.3 .33...3 .33.3.3 ..233.3 .22.2333 .222.233 .222... ... """ + """ .. .22 .223 ..222 .33.22 .3..22 .33.33 ..23. ..233. .22.333 .22..233 .. .23 .. """ + """ ... .222. .2.332 .23.32 .233.2 .222222 .2222222 .2..22.2 .2.3.222 .22...22 .2333.. .23333 .....""", " .23") # Instruments rom.banks[0x3F][0x3800:0x3A00] = rom.banks[0x31][0x1000:0x1200] # Patch the egg song event to use the 2nd vram sprites rom.patch( 0x19, 0x0BAC, "5006520654065606" "58065A065C065E06" "6006620664066606" "68066A066C066E06", "800E820E840E860E" "880E8A0E8C0E8E0E" "900E920E940E960E" "980E9A0E9C0E9E0E")
def addFrameCounter(rom, check_count): # Patch marin giving the start the game to jump to a custom handler rom.patch(0x05, 0x1299, ASM("ld a, $01\ncall $2385"), ASM("push hl\nld a, $0D\nrst 8\npop hl"), fill_nop=True) # Add code that needs to be called every frame to tick our ingame time counter. rom.patch(0x00, 0x0091, "00" * (0x100 - 0x91), ASM(""" ld a, [$DB95] ;Get the gameplay type dec a ; and if it was 1 ret z ; we are at the credits and the counter should stop. ; Check if the timer expired ld hl, $FF0F bit 2, [hl] ret z res 2, [hl] ; Increase the "subsecond" counter, and continue if it "overflows" call $27D0 ; Enable SRAM ld hl, $B000 ld a, [hl] inc a cp $20 ld [hl], a ret nz xor a ldi [hl], a ; Increase the seconds counter/minutes/hours counter increaseSecMinHours: ld a, [hl] inc a daa ld [hl], a cp $60 ret nz xor a ldi [hl], a jr increaseSecMinHours """), fill_nop=True) # Replace a cgb check with the call to our counter code. rom.patch(0x00, 0x0367, ASM("ld a, $0C\ncall $0B0B"), ASM("call $0091\nld a, $2C")) # Do not switch to 8x8 sprite mode rom.patch(0x17, 0x2E9E, ASM("res 2, [hl]"), "", fill_nop=True) # We need to completely reorder link sitting on the raft to work with 16x8 sprites. sprites = rom.banks[0x38][0x1600:0x1800] sprites[0x1F0:0x200] = b'\x00' * 16 for index, position in enumerate(( 0, 0x1F, 1, 0x1F, 2, 0x1F, 7, 8, 3, 9, 4, 10, 5, 11, 6, 12, 3, 13, 4, 14, 5, 15, 6, 16, 3, 17, 4, 18, 5, 19, 6, 20, )): rom.banks[0x38][0x1600 + index * 0x10:0x1610 + index * 0x10] = sprites[position * 0x10:0x10 + position * 0x10] rom.patch(0x27, 0x376E, 0x3776, "00046601", fill_nop=True) rom.patch(0x27, 0x384E, ASM("ld c, $08"), ASM("ld c, $04")) rom.patch(0x27, 0x3776, 0x3826, "FA046002" "0208640402006204" "0A106E030A086C030A006A030AF86803" "FA046002" "0208640402006204" "0A1076030A0874030A0072030AF87003" "FA046002" "0208640402006204" "0A107E030A087C030A007A030AF87803", fill_nop=True) rom.patch(0x27, 0x382E, ASM("ld a, $6C"), ASM("ld a, $80")) # OAM start position rom.patch(0x27, 0x384E, ASM("ld c, $08"), ASM("ld c, $04")) # Amount of overlay OAM data rom.patch(0x27, 0x3826, 0x382E, ASM("dw $7776, $7792, $77AE, $7792")) # pointers to animation rom.patch(0x27, 0x3846, ASM("ld c, $2C"), ASM("ld c, $1C")) # Amount of OAM data # TODO: fix flying windfish # Upper line of credits roll into "TIME" rom.patch(0x17, 0x069D, 0x0713, ASM( """ ld hl, OAMData ld de, $C000 ; OAM Buffer ld bc, $0048 call $2914 ret OAMData: db $20, $18, $34, $00 ;T db $20, $20, $20, $00 ;I db $20, $28, $28, $00 ;M db $20, $30, $18, $00 ;E db $20, $70, $16, $00 ;D db $20, $78, $18, $00 ;E db $20, $80, $10, $00 ;A db $20, $88, $34, $00 ;T db $20, $90, $1E, $00 ;H db $50, $18, $14, $00 ;C db $50, $20, $1E, $00 ;H db $50, $28, $18, $00 ;E db $50, $30, $14, $00 ;C db $50, $38, $24, $00 ;K db $50, $40, $32, $00 ;S db $68, $38, $%02x, $00 ;0 db $68, $40, $%02x, $00 ;0 db $68, $48, $%02x, $00 ;0 """ % ((((check_count // 100) % 10) * 2) | 0x40, (((check_count // 10) % 10) * 2) | 0x40, ((check_count % 10) * 2) | 0x40), 0x469D), fill_nop=True) # Lower line of credits roll into XX XX XX rom.patch(0x17, 0x0784, 0x082D, ASM( """ ld hl, OAMData ld de, $C048 ; OAM Buffer ld bc, $0038 call $2914 call $27D0 ; Enable SRAM ld hl, $C04A ld a, [$B003] ; hours call updateOAM ld a, [$B002] ; minutes call updateOAM ld a, [$B001] ; seconds call updateOAM ld a, [$DB58] ; death count high call updateOAM ld a, [$DB57] ; death count low call updateOAM ld a, [$B011] ; check count high call updateOAM ld a, [$B010] ; death count low call updateOAM ret updateOAM: ld de, $0004 ld b, a swap a and $0F add a, a or $40 ld [hl], a add hl, de ld a, b and $0F add a, a or $40 ld [hl], a add hl, de ret OAMData: db $38, $18, $40, $00 ;0 (10 hours) db $38, $20, $40, $00 ;0 (1 hours) db $38, $30, $40, $00 ;0 (10 minutes) db $38, $38, $40, $00 ;0 (1 minutes) db $38, $48, $40, $00 ;0 (10 seconds) db $38, $50, $40, $00 ;0 (1 seconds) db $00, $00, $40, $00 ;0 (1000 death) db $38, $80, $40, $00 ;0 (100 death) db $38, $88, $40, $00 ;0 (10 death) db $38, $90, $40, $00 ;0 (1 death) ; checks db $00, $00, $40, $00 ;0 db $68, $18, $40, $00 ;0 db $68, $20, $40, $00 ;0 db $68, $28, $40, $00 ;0 """, 0x4784), fill_nop=True) # Grab the "mostly" complete A-Z font sprites = rom.banks[0x38][0x1100:0x1400] for index, position in enumerate(( 0x10, 0x20, # A 0x11, 0x21, # B 0x12, 0x12 | 0x100, # C 0x13, 0x23, # D 0x14, 0x24, # E 0x14, 0x25, # F 0x12, 0x22, # G 0x20 | 0x100, 0x26, # H 0x17, 0x17 | 0x100, # I 0x28, 0x28, # J 0x19, 0x29, # K 0x06, 0x07, # L 0x1A, 0x2A, # M 0x1B, 0x2B, # N 0x00, 0x00, # O? 0x00, 0x00, # P? #0x00, 0x00, # Q? 0x11, 0x18, # R 0x1C, 0x2C, # S 0x1D, 0x2D, # T 0x26, 0x10, # U 0x00, 0x00, # V? 0x1E, 0x2E, # W #0x00, 0x00, # X? #0x00, 0x00, # Y? 0x27, 0x27, # Z )): sprite = sprites[(position & 0xFF) * 0x10:0x10 + (position & 0xFF) * 0x10] if position & 0x100: for n in range(4): sprite[n * 2], sprite[14 - n * 2] = sprite[14 - n * 2], sprite[n * 2] sprite[n * 2 + 1], sprite[15 - n * 2] = sprite[15 - n * 2], sprite[n * 2 + 1] rom.banks[0x38][0x1100 + index * 0x10:0x1110 + index * 0x10] = sprite # Number graphics change for the end tile_graphics = """ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .111111. ..1111.. .111111. .111111. ..11111. 11111111 .111111. 11111111 .111111. .111111. 11333311 .11331.. 11333311 11333311 .113331. 13333331 11333311 13333331 11333311 11333311 13311331 113331.. 13311331 13311331 1133331. 13311111 13311331 11111331 13311331 13311331 13311331 133331.. 13311331 11111331 1331331. 1331.... 13311331 ...11331 13311331 13311331 13311331 133331.. 11111331 ....1331 1331331. 1331.... 13311111 ...13311 13311331 13311331 13311331 111331.. ...13311 .1111331 1331331. 1331111. 1331.... ..11331. 13311331 13311331 13311331 ..1331.. ..11331. .1333331 13313311 13333311 1331111. ..13311. 11333311 11333331 13311331 ..1331.. ..13311. .1111331 13333331 13311331 13333311 .11331.. 13311331 .1111331 13311331 ..1331.. .11331.. ....1331 11113311 11111331 13311331 .13311.. 13311331 ....1331 13311331 ..1331.. .13311.. ....1331 ...1331. ....1331 13311331 11331... 13311331 ....1331 13311331 ..1331.. 11331... 11111331 ...1331. 11111331 13311331 13311... 13311331 11111331 13311331 ..1331.. 13311111 13311331 ...1331. 13311331 13311331 1331.... 13311331 13311331 11333311 ..1331.. 13333331 11333311 ...1331. 11333311 11333311 1331.... 11333311 11333311 .111111. ..1111.. 11111111 .111111. ...1111. .111111. .111111. 1111.... .111111. .111111. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ """.strip() for n in range(10): gfx_high = "\n".join( [line.split(" ")[n] for line in tile_graphics.split("\n")[:8]]) gfx_low = "\n".join( [line.split(" ")[n] for line in tile_graphics.split("\n")[8:]]) rom.banks[0x38][0x1400 + n * 0x20:0x1410 + n * 0x20] = utils.createTileData(gfx_high) rom.banks[0x38][0x1410 + n * 0x20:0x1420 + n * 0x20] = utils.createTileData(gfx_low)