summaryrefslogtreecommitdiff
path: root/home
diff options
context:
space:
mode:
Diffstat (limited to 'home')
-rw-r--r--home/audio.asm183
-rw-r--r--home/fade.asm73
-rw-r--r--home/init.asm139
-rw-r--r--home/pic.asm591
-rw-r--r--home/predef.asm50
-rw-r--r--home/vblank.asm105
-rw-r--r--home/vcopy.asm450
7 files changed, 1591 insertions, 0 deletions
diff --git a/home/audio.asm b/home/audio.asm
new file mode 100644
index 00000000..724b8c5f
--- /dev/null
+++ b/home/audio.asm
@@ -0,0 +1,183 @@
+Func_2307:: ; 2307 (0:2307)
+ call WaitForSoundToFinish
+ xor a
+ ld c, a
+ ld d, a
+ ld [wcfca], a
+ jr asm_2324
+
+Func_2312:: ; 2312 (0:2312)
+ ld c, $a
+ ld d, $0
+ ld a, [wd72e]
+ bit 5, a
+ jr z, asm_2324
+ xor a
+ ld [wcfca], a
+ ld c, $8
+ ld d, c
+asm_2324:: ; 2324 (0:2324)
+ ld a, [wd700]
+ and a
+ jr z, .asm_2343
+ cp $2
+ jr z, .asm_2332
+ ld a, MUSIC_BIKE_RIDING
+ jr .asm_2334
+.asm_2332
+ ld a, MUSIC_SURFING
+.asm_2334
+ ld b, a
+ ld a, d
+ and a
+ ld a, Bank(Func_7d8ea)
+ jr nz, .asm_233e
+ ld [wc0ef], a
+.asm_233e
+ ld [wc0f0], a
+ jr .asm_234c
+.asm_2343
+ ld a, [wd35b]
+ ld b, a
+ call Func_2385
+ jr c, .asm_2351
+.asm_234c
+ ld a, [wcfca]
+ cp b
+ ret z
+.asm_2351
+ ld a, c
+ ld [wMusicHeaderPointer], a
+ ld a, b
+ ld [wcfca], a
+ ld [wc0ee], a
+ jp PlaySound
+
+Func_235f:: ; 235f (0:235f)
+ ld a, [wc0ef]
+ ld b, a
+ cp $2
+ jr nz, .checkForBank08
+.bank02
+ ld hl, Func_9103
+ jr .asm_2378
+.checkForBank08
+ cp $8
+ jr nz, .bank1F
+.bank08
+ ld hl, Func_21879
+ jr .asm_2378
+.bank1F
+ ld hl, Func_7d177
+.asm_2378
+ ld c, $6
+.asm_237a
+ push bc
+ push hl
+ call Bankswitch
+ pop hl
+ pop bc
+ dec c
+ jr nz, .asm_237a
+ ret
+
+Func_2385:: ; 2385 (0:2385)
+ ld a, [wd35c]
+ ld e, a
+ ld a, [wc0ef]
+ cp e
+ jr nz, .asm_2394
+ ld [wc0f0], a
+ and a
+ ret
+.asm_2394
+ ld a, c
+ and a
+ ld a, e
+ jr nz, .asm_239c
+ ld [wc0ef], a
+.asm_239c
+ ld [wc0f0], a
+ scf
+ ret
+
+PlayMusic:: ; 23a1 (0:23a1)
+ ld b, a
+ ld [wc0ee], a
+ xor a
+ ld [wMusicHeaderPointer], a
+ ld a, c
+ ld [wc0ef], a
+ ld [wc0f0], a
+ ld a, b
+
+; plays music specified by a. If value is $ff, music is stopped
+PlaySound:: ; 23b1 (0:23b1)
+ push hl
+ push de
+ push bc
+ ld b, a
+ ld a, [wc0ee]
+ and a
+ jr z, .asm_23c8
+ xor a
+ ld [wc02a], a
+ ld [wc02b], a
+ ld [wc02c], a
+ ld [wc02d], a
+.asm_23c8
+ ld a, [wMusicHeaderPointer]
+ and a
+ jr z, .asm_23e3
+ ld a, [wc0ee]
+ and a
+ jr z, .asm_2425
+ xor a
+ ld [wc0ee], a
+ ld a, [wcfca]
+ cp $ff
+ jr nz, .asm_2414
+ xor a
+ ld [wMusicHeaderPointer], a
+.asm_23e3
+ xor a
+ ld [wc0ee], a
+ ld a, [H_LOADEDROMBANK]
+ ld [$ffb9], a
+ ld a, [wc0ef]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ cp $2
+ jr nz, .checkForBank08
+.bank02
+ ld a, b
+ call Func_9876
+ jr .asm_240b
+.checkForBank08
+ cp $8
+ jr nz, .bank1F
+.bank08
+ ld a, b
+ call Func_22035
+ jr .asm_240b
+.bank1F
+ ld a, b
+ call Func_7d8ea
+.asm_240b
+ ld a, [$ffb9]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ jr .asm_2425
+.asm_2414
+ ld a, b
+ ld [wcfca], a
+ ld a, [wMusicHeaderPointer]
+ ld [wcfc8], a
+ ld [wcfc9], a
+ ld a, b
+ ld [wMusicHeaderPointer], a
+.asm_2425
+ pop bc
+ pop de
+ pop hl
+ ret
diff --git a/home/fade.asm b/home/fade.asm
new file mode 100644
index 00000000..9b55eaf2
--- /dev/null
+++ b/home/fade.asm
@@ -0,0 +1,73 @@
+; These routines manage gradual fading
+; (e.g., entering a doorway)
+LoadGBPal::
+ ld a, [wd35d] ;tells if cur.map is dark (requires HM5_FLASH?)
+ ld b, a
+ ld hl, FadePal4
+ ld a, l
+ sub b
+ ld l, a
+ jr nc, .ok
+ dec h
+.ok
+ ld a, [hli]
+ ld [rBGP], a
+ ld a, [hli]
+ ld [rOBP0], a
+ ld a, [hli]
+ ld [rOBP1], a
+ ret
+
+GBFadeOut1::
+ ld hl, FadePal1
+ ld b, 4
+ jr GBFadeOutCommon
+
+GBFadeOut2::
+ ld hl, FadePal6
+ ld b, 3
+
+GBFadeOutCommon::
+ ld a, [hli]
+ ld [rBGP], a
+ ld a, [hli]
+ ld [rOBP0], a
+ ld a, [hli]
+ ld [rOBP1], a
+ ld c, 8
+ call DelayFrames
+ dec b
+ jr nz, GBFadeOutCommon
+ ret
+
+GBFadeIn1::
+ ld hl, FadePal4 + 2
+ ld b, 4
+ jr GBFadeInCommon
+
+GBFadeIn2::
+ ld hl, FadePal7 + 2
+ ld b, 3
+
+GBFadeInCommon::
+ ld a, [hld]
+ ld [rOBP1], a
+ ld a, [hld]
+ ld [rOBP0], a
+ ld a, [hld]
+ ld [rBGP], a
+ ld c, 8
+ call DelayFrames
+ dec b
+ jr nz, GBFadeInCommon
+ ret
+
+FadePal1:: db %11111111, %11111111, %11111111
+FadePal2:: db %11111110, %11111110, %11111000
+FadePal3:: db %11111001, %11100100, %11100100
+FadePal4:: db %11100100, %11010000, %11100000
+; rBGP rOBP0 rOBP1
+FadePal5:: db %11100100, %11010000, %11100000
+FadePal6:: db %10010000, %10000000, %10010000
+FadePal7:: db %01000000, %01000000, %01000000
+FadePal8:: db %00000000, %00000000, %00000000
diff --git a/home/init.asm b/home/init.asm
new file mode 100644
index 00000000..939e81b5
--- /dev/null
+++ b/home/init.asm
@@ -0,0 +1,139 @@
+SoftReset::
+ call StopAllSounds
+ call GBPalWhiteOut
+ ld c, $20
+ call DelayFrames
+ ; fallthrough
+
+Init::
+; Program init.
+
+rLCDC_DEFAULT EQU %11100011
+; * LCD enabled
+; * Window tile map at $9C00
+; * Window display enabled
+; * BG and window tile data at $8800
+; * BG tile map at $9800
+; * 8x8 OBJ size
+; * OBJ display enabled
+; * BG display enabled
+
+ di
+
+ xor a
+ ld [rIF], a
+ ld [rIE], a
+ ld [$ff43], a
+ ld [$ff42], a
+ ld [$ff01], a
+ ld [$ff02], a
+ ld [$ff4b], a
+ ld [$ff4a], a
+ ld [$ff06], a
+ ld [$ff07], a
+ ld [$ff47], a
+ ld [$ff48], a
+ ld [$ff49], a
+
+ ld a, rLCDC_ENABLE_MASK
+ ld [rLCDC], a
+ call DisableLCD
+
+ ld sp, wStack
+
+ ld hl, $c000 ; start of WRAM
+ ld bc, $2000 ; size of WRAM
+.loop
+ ld [hl], 0
+ inc hl
+ dec bc
+ ld a, b
+ or c
+ jr nz, .loop
+
+ call ClearVram
+
+ ld hl, $ff80
+ ld bc, $ffff - $ff80
+ call FillMemory
+
+ call ClearSprites
+
+ ld a, Bank(WriteDMACodeToHRAM)
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+ call WriteDMACodeToHRAM
+
+ xor a
+ ld [$ffd7], a
+ ld [$ff41], a
+ ld [$ffae], a
+ ld [$ffaf], a
+ ld [$ff0f], a
+ ld a, 1 << VBLANK + 1 << TIMER + 1 << SERIAL
+ ld [rIE], a
+
+ ld a, 144 ; move the window off-screen
+ ld [$ffb0], a
+ ld [rWY], a
+ ld a, 7
+ ld [rWX], a
+
+ ld a, $ff
+ ld [$ffaa], a
+
+ ld h, vBGMap0 / $100
+ call ClearBgMap
+ ld h, vBGMap1 / $100
+ call ClearBgMap
+
+ ld a, rLCDC_DEFAULT
+ ld [rLCDC], a
+ ld a, 16
+ ld [hSoftReset], a
+ call StopAllSounds
+
+ ei
+
+ ld a, $40 ; PREDEF_SGB_BORDER
+ call Predef
+
+ ld a, $1f
+ ld [wc0ef], a
+ ld [wc0f0], a
+ ld a, $9c
+ ld [$ffbd], a
+ xor a
+ ld [$ffbc], a
+ dec a
+ ld [wcfcb], a
+
+ ld a, $32 ; PREDEF_INTRO
+ call Predef
+
+ call DisableLCD
+ call ClearVram
+ call GBPalNormal
+ call ClearSprites
+ ld a, rLCDC_DEFAULT
+ ld [rLCDC], a
+
+ jp SetDefaultNamesBeforeTitlescreen
+
+ClearVram:
+ ld hl, $8000
+ ld bc, $2000
+ xor a
+ jp FillMemory
+
+
+StopAllSounds::
+ ld a, Bank(Func_9876)
+ ld [wc0ef], a
+ ld [wc0f0], a
+ xor a
+ ld [wMusicHeaderPointer], a
+ ld [wc0ee], a
+ ld [wcfca], a
+ dec a
+ jp PlaySound
diff --git a/home/pic.asm b/home/pic.asm
new file mode 100644
index 00000000..6aa2e5c0
--- /dev/null
+++ b/home/pic.asm
@@ -0,0 +1,591 @@
+; bankswitches and runs _UncompressSpriteData
+; bank is given in a, sprite input stream is pointed to in W_SPRITEINPUTPTR
+UncompressSpriteData:: ; 24fd (0:24fd)
+ ld b, a
+ ld a, [H_LOADEDROMBANK]
+ push af
+ ld a, b
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ld a, $a
+ ld [$0], a
+ xor a
+ ld [$4000], a
+ call _UncompressSpriteData
+ pop af
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ret
+
+; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop
+_UncompressSpriteData:: ; 251a (0:251a)
+ ld hl, S_SPRITEBUFFER1
+ ld c, (2*SPRITEBUFFERSIZE) % $100
+ ld b, (2*SPRITEBUFFERSIZE) / $100
+ xor a
+ call FillMemory ; clear sprite buffer 1 and 2
+ ld a, $1
+ ld [W_SPRITEINPUTBITCOUNTER], a
+ ld a, $3
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ ld [W_SPRITELOADFLAGS], a ; wd0a8
+ call ReadNextInputByte ; first byte of input determines sprite width (high nybble) and height (low nybble) in tiles (8x8 pixels)
+ ld b, a
+ and $f
+ add a
+ add a
+ add a
+ ld [W_SPRITEHEIGHT], a
+ ld a, b
+ swap a
+ and $f
+ add a
+ add a
+ add a
+ ld [W_SPRITEWITDH], a
+ call ReadNextInputBit
+ ld [W_SPRITELOADFLAGS], a ; initialite bit1 to 0 and bit0 to the first input bit
+ ; this will load two chunks of data to S_SPRITEBUFFER1 and S_SPRITEBUFFER2
+ ; bit 0 decides in which one the first chunk is placed
+ ; fall through
+
+; uncompresses a chunk from the sprite input data stream (pointed to at wd0da) into S_SPRITEBUFFER1 or S_SPRITEBUFFER2
+; each chunk is a 1bpp sprite. A 2bpp sprite consist of two chunks which are merged afterwards
+; note that this is an endless loop which is terminated during a call to MoveToNextBufferPosition by manipulating the stack
+UncompressSpriteDataLoop:: ; 2556 (0:2556)
+ ld hl, S_SPRITEBUFFER1
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 0, a
+ jr z, .useSpriteBuffer1 ; check which buffer to use
+ ld hl, S_SPRITEBUFFER2
+.useSpriteBuffer1
+ call StoreSpriteOutputPointer
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 1, a
+ jr z, .startDecompression ; check if last iteration
+ call ReadNextInputBit ; if last chunk, read 1-2 bit unpacking mode
+ and a
+ jr z, .unpackingMode0 ; 0 -> mode 0
+ call ReadNextInputBit ; 1 0 -> mode 1
+ inc a ; 1 1 -> mode 2
+.unpackingMode0
+ ld [W_SPRITEUNPACKMODE], a
+.startDecompression
+ call ReadNextInputBit
+ and a
+ jr z, .readRLEncodedZeros ; if first bit is 0, the input starts with zeroes, otherwise with (non-zero) input
+.readNextInput
+ call ReadNextInputBit
+ ld c, a
+ call ReadNextInputBit
+ sla c
+ or c ; read next two bits into c
+ and a
+ jr z, .readRLEncodedZeros ; 00 -> RLEncoded zeroes following
+ call WriteSpriteBitsToBuffer ; otherwise write input to output and repeat
+ call MoveToNextBufferPosition
+ jr .readNextInput
+.readRLEncodedZeros
+ ld c, $0 ; number of zeroes it length encoded, the number
+.countConsecutiveOnesLoop ; of consecutive ones determines the number of bits the number has
+ call ReadNextInputBit
+ and a
+ jr z, .countConsecutiveOnesFinished
+ inc c
+ jr .countConsecutiveOnesLoop
+.countConsecutiveOnesFinished
+ ld a, c
+ add a
+ ld hl, LengthEncodingOffsetList
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [hli] ; read offset that is added to the number later on
+ ld e, a ; adding an offset of 2^length - 1 makes every integer uniquely
+ ld d, [hl] ; representable in the length encoding and saves bits
+ push de
+ inc c
+ ld e, $0
+ ld d, e
+.readNumberOfZerosLoop ; reads the next c+1 bits of input
+ call ReadNextInputBit
+ or e
+ ld e, a
+ dec c
+ jr z, .readNumberOfZerosDone
+ sla e
+ rl d
+ jr .readNumberOfZerosLoop
+.readNumberOfZerosDone
+ pop hl ; add the offset
+ add hl, de
+ ld e, l
+ ld d, h
+.writeZerosLoop
+ ld b, e
+ xor a ; write 00 to buffer
+ call WriteSpriteBitsToBuffer
+ ld e, b
+ call MoveToNextBufferPosition
+ dec de
+ ld a, d
+ and a
+ jr nz, .continueLoop
+ ld a, e
+ and a
+.continueLoop
+ jr nz, .writeZerosLoop
+ jr .readNextInput
+
+; moves output pointer to next position
+; also cancels the calling function if the all output is done (by removing the return pointer from stack)
+; and calls postprocessing functions according to the unpack mode
+MoveToNextBufferPosition:: ; 25d8 (0:25d8)
+ ld a, [W_SPRITEHEIGHT]
+ ld b, a
+ ld a, [W_SPRITECURPOSY]
+ inc a
+ cp b
+ jr z, .curColumnDone
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITEOUTPUTPTR]
+ inc a
+ ld [W_SPRITEOUTPUTPTR], a
+ ret nz
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ inc a
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ret
+.curColumnDone
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITEOUTPUTBITOFFSET]
+ and a
+ jr z, .bitOffsetsDone
+ dec a
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ ld hl, W_SPRITEOUTPUTPTRCACHED
+ ld a, [hli]
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, [hl]
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ret
+.bitOffsetsDone
+ ld a, $3
+ ld [W_SPRITEOUTPUTBITOFFSET], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr z, .allColumnsDone
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ inc hl
+ jp StoreSpriteOutputPointer
+.allColumnsDone
+ pop hl
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 1, a
+ jr nz, .done ; test if there is one more sprite to go
+ xor $1
+ set 1, a
+ ld [W_SPRITELOADFLAGS], a ; wd0a8
+ jp UncompressSpriteDataLoop
+.done
+ jp UnpackSprite
+
+; writes 2 bits (from a) to the output buffer (pointed to from W_SPRITEOUTPUTPTR)
+WriteSpriteBitsToBuffer:: ; 2649 (0:2649)
+ ld e, a
+ ld a, [W_SPRITEOUTPUTBITOFFSET]
+ and a
+ jr z, .offset0
+ cp $2
+ jr c, .offset1
+ jr z, .offset2
+ rrc e ; offset 3
+ rrc e
+ jr .offset0
+.offset1
+ sla e
+ sla e
+ jr .offset0
+.offset2
+ swap e
+.offset0
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [hl]
+ or e
+ ld [hl], a
+ ret
+
+; reads next bit from input stream and returns it in a
+ReadNextInputBit:: ; 2670 (0:2670)
+ ld a, [W_SPRITEINPUTBITCOUNTER]
+ dec a
+ jr nz, .curByteHasMoreBitsToRead
+ call ReadNextInputByte
+ ld [W_SPRITEINPUTCURBYTE], a
+ ld a, $8
+.curByteHasMoreBitsToRead
+ ld [W_SPRITEINPUTBITCOUNTER], a
+ ld a, [W_SPRITEINPUTCURBYTE]
+ rlca
+ ld [W_SPRITEINPUTCURBYTE], a
+ and $1
+ ret
+
+; reads next byte from input stream and returns it in a
+ReadNextInputByte:: ; 268b (0:268b)
+ ld a, [W_SPRITEINPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEINPUTPTR+1]
+ ld h, a
+ ld a, [hli]
+ ld b, a
+ ld a, l
+ ld [W_SPRITEINPUTPTR], a
+ ld a, h
+ ld [W_SPRITEINPUTPTR+1], a
+ ld a, b
+ ret
+
+; the nth item is 2^n - 1
+LengthEncodingOffsetList:: ; 269f (0:269f)
+ dw %0000000000000001
+ dw %0000000000000011
+ dw %0000000000000111
+ dw %0000000000001111
+ dw %0000000000011111
+ dw %0000000000111111
+ dw %0000000001111111
+ dw %0000000011111111
+ dw %0000000111111111
+ dw %0000001111111111
+ dw %0000011111111111
+ dw %0000111111111111
+ dw %0001111111111111
+ dw %0011111111111111
+ dw %0111111111111111
+ dw %1111111111111111
+
+; unpacks the sprite data depending on the unpack mode
+UnpackSprite:: ; 26bf (0:26bf)
+ ld a, [W_SPRITEUNPACKMODE]
+ cp $2
+ jp z, UnpackSpriteMode2
+ and a
+ jp nz, XorSpriteChunks
+ ld hl, S_SPRITEBUFFER1
+ call SpriteDifferentialDecode
+ ld hl, S_SPRITEBUFFER2
+ ; fall through
+
+; decodes differential encoded sprite data
+; input bit value 0 preserves the current bit value and input bit value 1 toggles it (starting from initial value 0).
+SpriteDifferentialDecode:: ; 26d4 (0:26d4)
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ call StoreSpriteOutputPointer
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped
+ ld hl, DecodeNybble0TableFlipped
+ ld de, DecodeNybble1TableFlipped
+ jr .storeDecodeTablesPointers
+.notFlipped
+ ld hl, DecodeNybble0Table
+ ld de, DecodeNybble1Table
+.storeDecodeTablesPointers
+ ld a, l
+ ld [W_SPRITEDECODETABLE0PTR], a
+ ld a, h
+ ld [W_SPRITEDECODETABLE0PTR+1], a
+ ld a, e
+ ld [W_SPRITEDECODETABLE1PTR], a
+ ld a, d
+ ld [W_SPRITEDECODETABLE1PTR+1], a
+ ld e, $0 ; last decoded nybble, initialized to 0
+.decodeNextByteLoop
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [hl]
+ ld b, a
+ swap a
+ and $f
+ call DifferentialDecodeNybble ; decode high nybble
+ swap a
+ ld d, a
+ ld a, b
+ and $f
+ call DifferentialDecodeNybble ; decode low nybble
+ or d
+ ld b, a
+ ld a, [W_SPRITEOUTPUTPTR]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, b
+ ld [hl], a ; write back decoded data
+ ld a, [W_SPRITEHEIGHT]
+ add l ; move on to next column
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr nz, .decodeNextByteLoop ; test if current row is done
+ xor a
+ ld e, a
+ ld [W_SPRITECURPOSX], a
+ ld a, [W_SPRITECURPOSY] ; move on to next row
+ inc a
+ ld [W_SPRITECURPOSY], a
+ ld b, a
+ ld a, [W_SPRITEHEIGHT]
+ cp b
+ jr z, .done ; test if all rows finished
+ ld a, [W_SPRITEOUTPUTPTRCACHED]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld h, a
+ inc hl
+ call StoreSpriteOutputPointer
+ jr .decodeNextByteLoop
+.done
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ret
+
+; decodes the nybble stored in a. Last decoded data is assumed to be in e (needed to determine if initial value is 0 or 1)
+DifferentialDecodeNybble:: ; 276d (0:276d)
+ srl a ; c=a%2, a/=2
+ ld c, $0
+ jr nc, .evenNumber
+ ld c, $1
+.evenNumber
+ ld l, a
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped ; determine if initial value is 0 or one
+ bit 3, e ; if flipped, consider MSB of last data
+ jr .selectLookupTable
+.notFlipped
+ bit 0, e ; else consider LSB
+.selectLookupTable
+ ld e, l
+ jr nz, .initialValue1 ; load the appropriate table
+ ld a, [W_SPRITEDECODETABLE0PTR]
+ ld l, a
+ ld a, [W_SPRITEDECODETABLE0PTR+1]
+ jr .tableLookup
+.initialValue1
+ ld a, [W_SPRITEDECODETABLE1PTR]
+ ld l, a
+ ld a, [W_SPRITEDECODETABLE1PTR+1]
+.tableLookup
+ ld h, a
+ ld a, e
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [hl]
+ bit 0, c
+ jr nz, .selectLowNybble
+ swap a ; select high nybble
+.selectLowNybble
+ and $f
+ ld e, a ; update last decoded data
+ ret
+
+DecodeNybble0Table:: ; 27a7 (0:27a7)
+ dn $0, $1
+ dn $3, $2
+ dn $7, $6
+ dn $4, $5
+ dn $f, $e
+ dn $c, $d
+ dn $8, $9
+ dn $b, $a
+DecodeNybble1Table:: ; 27af (0:27af)
+ dn $f, $e
+ dn $c, $d
+ dn $8, $9
+ dn $b, $a
+ dn $0, $1
+ dn $3, $2
+ dn $7, $6
+ dn $4, $5
+DecodeNybble0TableFlipped:: ; 27b7 (0:27b7)
+ dn $0, $8
+ dn $c, $4
+ dn $e, $6
+ dn $2, $a
+ dn $f, $7
+ dn $3, $b
+ dn $1, $9
+ dn $d, $5
+DecodeNybble1TableFlipped:: ; 27bf (0:27bf)
+ dn $f, $7
+ dn $3, $b
+ dn $1, $9
+ dn $d, $5
+ dn $0, $8
+ dn $c, $4
+ dn $e, $6
+ dn $2, $a
+
+; combines the two loaded chunks with xor (the chunk loaded second is the destination). The source chunk is differeintial decoded beforehand.
+XorSpriteChunks:: ; 27c7 (0:27c7)
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ld [W_SPRITECURPOSY], a
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEOUTPUTPTR] ; points to buffer 1 or 2, depending on flags
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEOUTPUTPTR] ; source buffer, points to buffer 1 or 2, depending on flags
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTR+1]
+ ld h, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED] ; destination buffer, points to buffer 2 or 1, depending on flags
+ ld e, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld d, a
+.xorChunksLoop
+ ld a, [W_SPRITEFLIPPED]
+ and a
+ jr z, .notFlipped
+ push de
+ ld a, [de]
+ ld b, a
+ swap a
+ and $f
+ call ReverseNybble ; if flipped reverse the nybbles in the destination buffer
+ swap a
+ ld c, a
+ ld a, b
+ and $f
+ call ReverseNybble
+ or c
+ pop de
+ ld [de], a
+.notFlipped
+ ld a, [hli]
+ ld b, a
+ ld a, [de]
+ xor b
+ ld [de], a
+ inc de
+ ld a, [W_SPRITECURPOSY]
+ inc a
+ ld [W_SPRITECURPOSY], a ; go to next row
+ ld b, a
+ ld a, [W_SPRITEHEIGHT]
+ cp b
+ jr nz, .xorChunksLoop ; test if column finished
+ xor a
+ ld [W_SPRITECURPOSY], a
+ ld a, [W_SPRITECURPOSX]
+ add $8
+ ld [W_SPRITECURPOSX], a ; go to next column
+ ld b, a
+ ld a, [W_SPRITEWITDH]
+ cp b
+ jr nz, .xorChunksLoop ; test if all columns finished
+ xor a
+ ld [W_SPRITECURPOSX], a
+ ret
+
+; reverses the bits in the nybble given in register a
+ReverseNybble:: ; 2837 (0:2837)
+ ld de, NybbleReverseTable
+ add e
+ ld e, a
+ jr nc, .asm_283f
+ inc d
+.asm_283f
+ ld a, [de]
+ ret
+
+; resets sprite buffer pointers to buffer 1 and 2, depending on W_SPRITELOADFLAGS
+ResetSpriteBufferPointers:: ; 2841 (0:2841)
+ ld a, [W_SPRITELOADFLAGS] ; wd0a8
+ bit 0, a
+ jr nz, .buffer2Selected
+ ld de, S_SPRITEBUFFER1
+ ld hl, S_SPRITEBUFFER2
+ jr .storeBufferPointers
+.buffer2Selected
+ ld de, S_SPRITEBUFFER2
+ ld hl, S_SPRITEBUFFER1
+.storeBufferPointers
+ ld a, l
+ ld [W_SPRITEOUTPUTPTR], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld a, e
+ ld [W_SPRITEOUTPUTPTRCACHED], a
+ ld a, d
+ ld [W_SPRITEOUTPUTPTRCACHED+1], a
+ ret
+
+; maps each nybble to its reverse
+NybbleReverseTable:: ; 2867 (0:2867)
+ db $0, $8, $4, $c, $2, $a, $6 ,$e, $1, $9, $5, $d, $3, $b, $7 ,$f
+
+; combines the two loaded chunks with xor (the chunk loaded second is the destination). Both chunks are differeintial decoded beforehand.
+UnpackSpriteMode2:: ; 2877 (0:2877)
+ call ResetSpriteBufferPointers
+ ld a, [W_SPRITEFLIPPED]
+ push af
+ xor a
+ ld [W_SPRITEFLIPPED], a ; temporarily clear flipped flag for decoding the destination chunk
+ ld a, [W_SPRITEOUTPUTPTRCACHED]
+ ld l, a
+ ld a, [W_SPRITEOUTPUTPTRCACHED+1]
+ ld h, a
+ call SpriteDifferentialDecode
+ call ResetSpriteBufferPointers
+ pop af
+ ld [W_SPRITEFLIPPED], a
+ jp XorSpriteChunks
+
+; stores hl into the output pointers
+StoreSpriteOutputPointer:: ; 2897 (0:2897)
+ ld a, l
+ ld [W_SPRITEOUTPUTPTR], a
+ ld [W_SPRITEOUTPUTPTRCACHED], a
+ ld a, h
+ ld [W_SPRITEOUTPUTPTR+1], a
+ ld [W_SPRITEOUTPUTPTRCACHED+1], a
+ ret
diff --git a/home/predef.asm b/home/predef.asm
new file mode 100644
index 00000000..1777d09f
--- /dev/null
+++ b/home/predef.asm
@@ -0,0 +1,50 @@
+Predef::
+; Call predefined function a.
+; To preserve other registers, have the
+; destination call GetPredefRegisters.
+
+ ; Save the predef id for GetPredefPointer.
+ ld [wPredefID], a
+
+ ; A hack for LoadDestinationWarpPosition.
+ ; See Func_c754 (predef $19).
+ ld a, [H_LOADEDROMBANK]
+ ld [wPredefParentBank], a
+
+ push af
+ ld a, BANK(GetPredefPointer)
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+
+ call GetPredefPointer
+
+ ld a, [wPredefBank]
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+
+ ld de, .done
+ push de
+ jp [hl]
+.done
+
+ pop af
+ ld [H_LOADEDROMBANK], a
+ ld [$2000], a
+ ret
+
+GetPredefRegisters::
+; Restore the contents of register pairs
+; when GetPredefPointer was called.
+ ld a, [wPredefRegisters + 0]
+ ld h, a
+ ld a, [wPredefRegisters + 1]
+ ld l, a
+ ld a, [wPredefRegisters + 2]
+ ld d, a
+ ld a, [wPredefRegisters + 3]
+ ld e, a
+ ld a, [wPredefRegisters + 4]
+ ld b, a
+ ld a, [wPredefRegisters + 5]
+ ld c, a
+ ret
diff --git a/home/vblank.asm b/home/vblank.asm
new file mode 100644
index 00000000..ec82eb7d
--- /dev/null
+++ b/home/vblank.asm
@@ -0,0 +1,105 @@
+VBlank::
+
+ push af
+ push bc
+ push de
+ push hl
+
+ ld a, [H_LOADEDROMBANK]
+ ld [wd122], a
+
+ ld a, [$ffae]
+ ld [rSCX], a
+ ld a, [$ffaf]
+ ld [rSCY], a
+
+ ld a, [wd0a0]
+ and a
+ jr nz, .ok
+ ld a, [$ffb0]
+ ld [rWY], a
+.ok
+
+ call AutoBgMapTransfer
+ call VBlankCopyBgMap
+ call RedrawExposedScreenEdge
+ call VBlankCopy
+ call VBlankCopyDouble
+ call UpdateMovingBgTiles
+ call $ff80 ; hOAMDMA
+ ld a, Bank(PrepareOAMData)
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+ call PrepareOAMData
+
+ ; VBlank-sensitive operations end.
+
+ call Random
+
+ ld a, [H_VBLANKOCCURRED]
+ and a
+ jr z, .vblanked
+ xor a
+ ld [H_VBLANKOCCURRED], a
+.vblanked
+
+ ld a, [H_FRAMECOUNTER]
+ and a
+ jr z, .decced
+ dec a
+ ld [H_FRAMECOUNTER], a
+.decced
+
+ call Func_28cb
+
+ ld a, [wc0ef] ; music ROM bank
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+
+ cp BANK(Func_9103)
+ jr nz, .notbank2
+.bank2
+ call Func_9103
+ jr .afterMusic
+.notbank2
+ cp 8
+ jr nz, .bank1F
+.bank8
+ call Func_2136e
+ call Func_21879
+ jr .afterMusic
+.bank1F
+ call Func_7d177
+.afterMusic
+
+ callba Func_18dee ; keep track of time played
+
+ ld a, [$fff9]
+ and a
+ call z, ReadJoypad
+
+ ld a, [wd122]
+ ld [H_LOADEDROMBANK], a
+ ld [MBC3RomBank], a
+
+ pop hl
+ pop de
+ pop bc
+ pop af
+ reti
+
+
+DelayFrame::
+; Wait for the next vblank interrupt.
+; As a bonus, this saves battery.
+
+NOT_VBLANKED EQU 1
+
+ ld a, NOT_VBLANKED
+ ld [H_VBLANKOCCURRED], a
+.halt
+ halt
+ ld a, [H_VBLANKOCCURRED]
+ and a
+ jr nz, .halt
+ ret
diff --git a/home/vcopy.asm b/home/vcopy.asm
new file mode 100644
index 00000000..9f152841
--- /dev/null
+++ b/home/vcopy.asm
@@ -0,0 +1,450 @@
+; this function seems to be used only once
+; it store the address of a row and column of the VRAM background map in hl
+; INPUT: h - row, l - column, b - high byte of background tile map address in VRAM
+GetRowColAddressBgMap:: ; 1cdd (0:1cdd)
+ xor a
+ srl h
+ rr a
+ srl h
+ rr a
+ srl h
+ rr a
+ or l
+ ld l,a
+ ld a,b
+ or h
+ ld h,a
+ ret
+
+; clears a VRAM background map with blank space tiles
+; INPUT: h - high byte of background tile map address in VRAM
+ClearBgMap:: ; 1cf0 (0:1cf0)
+ ld a," "
+ jr .next
+ ld a,l
+.next
+ ld de,$400 ; size of VRAM background map
+ ld l,e
+.loop
+ ld [hli],a
+ dec e
+ jr nz,.loop
+ dec d
+ jr nz,.loop
+ ret
+
+; When the player takes a step, a row or column of 2x2 tile blocks at the edge
+; of the screen toward which they moved is exposed and has to be redrawn.
+; This function does the redrawing.
+RedrawExposedScreenEdge:: ; 1d01 (0:1d01)
+ ld a,[H_SCREENEDGEREDRAW]
+ and a
+ ret z
+ ld b,a
+ xor a
+ ld [H_SCREENEDGEREDRAW],a
+ dec b
+ jr nz,.redrawRow
+.redrawColumn
+ ld hl,wScreenEdgeTiles
+ ld a,[H_SCREENEDGEREDRAWADDR]
+ ld e,a
+ ld a,[H_SCREENEDGEREDRAWADDR + 1]
+ ld d,a
+ ld c,18 ; screen height
+.loop1
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hli]
+ ld [de],a
+ ld a,31
+ add e
+ ld e,a
+ jr nc,.noCarry
+ inc d
+.noCarry
+; the following 4 lines wrap us from bottom to top if necessary
+ ld a,d
+ and a,$03
+ or a,$98
+ ld d,a
+ dec c
+ jr nz,.loop1
+ xor a
+ ld [H_SCREENEDGEREDRAW],a
+ ret
+.redrawRow
+ ld hl,wScreenEdgeTiles
+ ld a,[H_SCREENEDGEREDRAWADDR]
+ ld e,a
+ ld a,[H_SCREENEDGEREDRAWADDR + 1]
+ ld d,a
+ push de
+ call .drawHalf ; draw upper half
+ pop de
+ ld a,32 ; width of VRAM background map
+ add e
+ ld e,a
+ ; draw lower half
+.drawHalf
+ ld c,10
+.loop2
+ ld a,[hli]
+ ld [de],a
+ inc de
+ ld a,[hli]
+ ld [de],a
+ ld a,e
+ inc a
+; the following 6 lines wrap us from the right edge to the left edge if necessary
+ and a,$1f
+ ld b,a
+ ld a,e
+ and a,$e0
+ or b
+ ld e,a
+ dec c
+ jr nz,.loop2
+ ret
+
+; This function automatically transfers tile number data from the tile map at
+; wTileMap to VRAM during V-blank. Note that it only transfers one third of the
+; background per V-blank. It cycles through which third it draws.
+; This transfer is turned off when walking around the map, but is turned
+; on when talking to sprites, battling, using menus, etc. This is because
+; the above function, RedrawExposedScreenEdge, is used when walking to
+; improve efficiency.
+AutoBgMapTransfer:: ; 1d57 (0:1d57)
+ ld a,[H_AUTOBGTRANSFERENABLED]
+ and a
+ ret z
+ ld hl,[sp + 0]
+ ld a,h
+ ld [H_SPTEMP],a
+ ld a,l
+ ld [H_SPTEMP + 1],a ; save stack pinter
+ ld a,[H_AUTOBGTRANSFERPORTION]
+ and a
+ jr z,.transferTopThird
+ dec a
+ jr z,.transferMiddleThird
+.transferBottomThird
+ FuncCoord 0,12
+ ld hl,Coord
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld de,(12 * 32)
+ add hl,de
+ xor a ; TRANSFERTOP
+ jr .doTransfer
+.transferTopThird
+ FuncCoord 0,0
+ ld hl,Coord
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld a,TRANSFERMIDDLE
+ jr .doTransfer
+.transferMiddleThird
+ FuncCoord 0,6
+ ld hl,Coord
+ ld sp,hl
+ ld a,[H_AUTOBGTRANSFERDEST + 1]
+ ld h,a
+ ld a,[H_AUTOBGTRANSFERDEST]
+ ld l,a
+ ld de,(6 * 32)
+ add hl,de
+ ld a,TRANSFERBOTTOM
+.doTransfer
+ ld [H_AUTOBGTRANSFERPORTION],a ; store next portion
+ ld b,6
+
+TransferBgRows:: ; 1d9e (0:1d9e)
+; unrolled loop and using pop for speed
+
+ rept 20 / 2 - 1
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+
+i ld a, 32 - (20 - 1)
+ add l
+ ld l, a
+ jr nc, .ok
+ inc h
+.ok
+ dec b
+ jr nz, TransferBgRows
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+ ret
+
+; Copies [H_VBCOPYBGNUMROWS] rows from H_VBCOPYBGSRC to H_VBCOPYBGDEST.
+; If H_VBCOPYBGSRC is XX00, the transfer is disabled.
+VBlankCopyBgMap:: ; 1de1 (0:1de1)
+ ld a,[H_VBCOPYBGSRC] ; doubles as enabling byte
+ and a
+ ret z
+ ld hl,[sp + 0]
+ ld a,h
+ ld [H_SPTEMP],a
+ ld a,l
+ ld [H_SPTEMP + 1],a ; save stack pointer
+ ld a,[H_VBCOPYBGSRC]
+ ld l,a
+ ld a,[H_VBCOPYBGSRC + 1]
+ ld h,a
+ ld sp,hl
+ ld a,[H_VBCOPYBGDEST]
+ ld l,a
+ ld a,[H_VBCOPYBGDEST + 1]
+ ld h,a
+ ld a,[H_VBCOPYBGNUMROWS]
+ ld b,a
+ xor a
+ ld [H_VBCOPYBGSRC],a ; disable transfer so it doesn't continue next V-blank
+ jr TransferBgRows
+
+
+VBlankCopyDouble::
+; Copy [H_VBCOPYDOUBLESIZE] 1bpp tiles
+; from H_VBCOPYDOUBLESRC to H_VBCOPYDOUBLEDEST.
+
+; While we're here, convert to 2bpp.
+; The process is straightforward:
+; copy each byte twice.
+
+ ld a, [H_VBCOPYDOUBLESIZE]
+ and a
+ ret z
+
+ ld hl, [sp + 0]
+ ld a, h
+ ld [H_SPTEMP], a
+ ld a, l
+ ld [H_SPTEMP + 1], a
+
+ ld a, [H_VBCOPYDOUBLESRC]
+ ld l, a
+ ld a, [H_VBCOPYDOUBLESRC + 1]
+ ld h, a
+ ld sp, hl
+
+ ld a, [H_VBCOPYDOUBLEDEST]
+ ld l, a
+ ld a, [H_VBCOPYDOUBLEDEST + 1]
+ ld h, a
+
+ ld a, [H_VBCOPYDOUBLESIZE]
+ ld b, a
+ xor a ; transferred
+ ld [H_VBCOPYDOUBLESIZE], a
+
+.loop
+ rept 3
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ ld [hl], d
+ inc hl
+ dec b
+ jr nz, .loop
+
+ ld a, l
+ ld [H_VBCOPYDOUBLEDEST], a
+ ld a, h
+ ld [H_VBCOPYDOUBLEDEST + 1], a
+
+ ld hl, [sp + 0]
+ ld a, l
+ ld [H_VBCOPYDOUBLESRC], a
+ ld a, h
+ ld [H_VBCOPYDOUBLESRC + 1], a
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+
+ ret
+
+
+VBlankCopy::
+; Copy [H_VBCOPYSIZE] 2bpp tiles
+; from H_VBCOPYSRC to H_VBCOPYDEST.
+
+; Source and destination addresses
+; are updated, so transfer can
+; continue in subsequent calls.
+
+ ld a, [H_VBCOPYSIZE]
+ and a
+ ret z
+
+ ld hl, [sp + 0]
+ ld a, h
+ ld [H_SPTEMP], a
+ ld a, l
+ ld [H_SPTEMP + 1], a
+
+ ld a, [H_VBCOPYSRC]
+ ld l, a
+ ld a, [H_VBCOPYSRC + 1]
+ ld h, a
+ ld sp, hl
+
+ ld a, [H_VBCOPYDEST]
+ ld l, a
+ ld a, [H_VBCOPYDEST + 1]
+ ld h, a
+
+ ld a, [H_VBCOPYSIZE]
+ ld b, a
+ xor a ; transferred
+ ld [H_VBCOPYSIZE], a
+
+.loop
+ rept 7
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc l
+ endr
+
+ pop de
+ ld [hl], e
+ inc l
+ ld [hl], d
+ inc hl
+ dec b
+ jr nz, .loop
+
+ ld a, l
+ ld [H_VBCOPYDEST], a
+ ld a, h
+ ld [H_VBCOPYDEST + 1], a
+
+ ld hl, [sp + 0]
+ ld a, l
+ ld [H_VBCOPYSRC], a
+ ld a, h
+ ld [H_VBCOPYSRC + 1], a
+
+ ld a, [H_SPTEMP]
+ ld h, a
+ ld a, [H_SPTEMP + 1]
+ ld l, a
+ ld sp, hl
+
+ ret
+
+
+UpdateMovingBgTiles::
+; Animate water and flower
+; tiles in the overworld.
+
+ ld a, [$ffd7]
+ and a
+ ret z
+
+ ld a, [$ffd8]
+ inc a
+ ld [$ffd8], a
+ cp $14
+ ret c
+ cp $15
+ jr z, .flower
+
+ ld hl, vTileset + $14 * $10
+ ld c, $10
+
+ ld a, [wd085]
+ inc a
+ and 7
+ ld [wd085], a
+
+ and 4
+ jr nz, .left
+.right
+ ld a, [hl]
+ rrca
+ ld [hli], a
+ dec c
+ jr nz, .right
+ jr .done
+.left
+ ld a, [hl]
+ rlca
+ ld [hli], a
+ dec c
+ jr nz, .left
+.done
+ ld a, [$ffd7]
+ rrca
+ ret nc
+ xor a
+ ld [$ffd8], a
+ ret
+
+.flower
+ xor a
+ ld [$ffd8], a
+
+ ld a, [wd085]
+ and 3
+ cp 2
+ ld hl, FlowerTile1
+ jr c, .copy
+ ld hl, FlowerTile2
+ jr z, .copy
+ ld hl, FlowerTile3
+.copy
+ ld de, vTileset + $3 * $10
+ ld c, $10
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+ ret
+
+FlowerTile1: INCBIN "gfx/tilesets/flower/flower1.2bpp"
+FlowerTile2: INCBIN "gfx/tilesets/flower/flower2.2bpp"
+FlowerTile3: INCBIN "gfx/tilesets/flower/flower3.2bpp"