diff options
-rw-r--r-- | home.asm | 4 | ||||
-rw-r--r-- | home/pic.asm | 591 | ||||
-rw-r--r-- | home/pics.asm | 196 | ||||
-rw-r--r-- | home/uncompress.asm | 725 |
4 files changed, 758 insertions, 758 deletions
@@ -74,7 +74,7 @@ LoadDestinationWarpPosition:: INCLUDE "home/pokemon.asm" INCLUDE "home/print_bcd.asm" -INCLUDE "home/uncompress.asm" +INCLUDE "home/pics.asm" INCLUDE "data/tilesets/collision_tile_ids.asm" INCLUDE "home/copy2.asm" INCLUDE "home/text.asm" @@ -103,7 +103,7 @@ UpdateSprites:: INCLUDE "data/items/marts.asm" INCLUDE "home/overworld_text.asm" -INCLUDE "home/pic.asm" +INCLUDE "home/uncompress.asm" ResetPlayerSpriteData:: ld hl, wSpriteStateData1 diff --git a/home/pic.asm b/home/pic.asm deleted file mode 100644 index cfc39f0f..00000000 --- a/home/pic.asm +++ /dev/null @@ -1,591 +0,0 @@ -; bankswitches and runs _UncompressSpriteData -; bank is given in a, sprite input stream is pointed to in wSpriteInputPtr -UncompressSpriteData:: - ld b, a - ld a, [hLoadedROMBank] - push af - ld a, b - ld [hLoadedROMBank], a - ld [MBC1RomBank], a - ld a, SRAM_ENABLE - ld [MBC1SRamEnable], a - xor a - ld [MBC1SRamBank], a - call _UncompressSpriteData - pop af - ld [hLoadedROMBank], a - ld [MBC1RomBank], a - ret - -; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop -_UncompressSpriteData:: - ld hl, sSpriteBuffer1 - ld c, (2*SPRITEBUFFERSIZE) % $100 - ld b, (2*SPRITEBUFFERSIZE) / $100 - xor a - call FillMemory ; clear sprite buffer 1 and 2 - ld a, $1 - ld [wSpriteInputBitCounter], a - ld a, $3 - ld [wSpriteOutputBitOffset], a - xor a - ld [wSpriteCurPosX], a - ld [wSpriteCurPosY], a - ld [wSpriteLoadFlags], a - 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 [wSpriteHeight], a - ld a, b - swap a - and $f - add a - add a - add a - ld [wSpriteWidth], a - call ReadNextInputBit - ld [wSpriteLoadFlags], a ; initialite bit1 to 0 and bit0 to the first input bit - ; this will load two chunks of data to sSpriteBuffer1 and sSpriteBuffer2 - ; 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 sSpriteBuffer1 or sSpriteBuffer2 -; 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:: - ld hl, sSpriteBuffer1 - ld a, [wSpriteLoadFlags] - bit 0, a - jr z, .useSpriteBuffer1 ; check which buffer to use - ld hl, sSpriteBuffer2 -.useSpriteBuffer1 - call StoreSpriteOutputPointer - ld a, [wSpriteLoadFlags] - 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 [wSpriteUnpackMode], 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:: - ld a, [wSpriteHeight] - ld b, a - ld a, [wSpriteCurPosY] - inc a - cp b - jr z, .curColumnDone - ld [wSpriteCurPosY], a - ld a, [wSpriteOutputPtr] - inc a - ld [wSpriteOutputPtr], a - ret nz - ld a, [wSpriteOutputPtr+1] - inc a - ld [wSpriteOutputPtr+1], a - ret -.curColumnDone - xor a - ld [wSpriteCurPosY], a - ld a, [wSpriteOutputBitOffset] - and a - jr z, .bitOffsetsDone - dec a - ld [wSpriteOutputBitOffset], a - ld hl, wSpriteOutputPtrCached - ld a, [hli] - ld [wSpriteOutputPtr], a - ld a, [hl] - ld [wSpriteOutputPtr+1], a - ret -.bitOffsetsDone - ld a, $3 - ld [wSpriteOutputBitOffset], a - ld a, [wSpriteCurPosX] - add $8 - ld [wSpriteCurPosX], a - ld b, a - ld a, [wSpriteWidth] - cp b - jr z, .allColumnsDone - ld a, [wSpriteOutputPtr] - ld l, a - ld a, [wSpriteOutputPtr+1] - ld h, a - inc hl - jp StoreSpriteOutputPointer -.allColumnsDone - pop hl - xor a - ld [wSpriteCurPosX], a - ld a, [wSpriteLoadFlags] - bit 1, a - jr nz, .done ; test if there is one more sprite to go - xor $1 - set 1, a - ld [wSpriteLoadFlags], a - jp UncompressSpriteDataLoop -.done - jp UnpackSprite - -; writes 2 bits (from a) to the output buffer (pointed to from wSpriteOutputPtr) -WriteSpriteBitsToBuffer:: - ld e, a - ld a, [wSpriteOutputBitOffset] - 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, [wSpriteOutputPtr] - ld l, a - ld a, [wSpriteOutputPtr+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:: - ld a, [wSpriteInputBitCounter] - dec a - jr nz, .curByteHasMoreBitsToRead - call ReadNextInputByte - ld [wSpriteInputCurByte], a - ld a, $8 -.curByteHasMoreBitsToRead - ld [wSpriteInputBitCounter], a - ld a, [wSpriteInputCurByte] - rlca - ld [wSpriteInputCurByte], a - and $1 - ret - -; reads next byte from input stream and returns it in a -ReadNextInputByte:: - ld a, [wSpriteInputPtr] - ld l, a - ld a, [wSpriteInputPtr+1] - ld h, a - ld a, [hli] - ld b, a - ld a, l - ld [wSpriteInputPtr], a - ld a, h - ld [wSpriteInputPtr+1], a - ld a, b - ret - -; the nth item is 2^n - 1 -LengthEncodingOffsetList:: - 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:: - ld a, [wSpriteUnpackMode] - cp $2 - jp z, UnpackSpriteMode2 - and a - jp nz, XorSpriteChunks - ld hl, sSpriteBuffer1 - call SpriteDifferentialDecode - ld hl, sSpriteBuffer2 - ; 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:: - xor a - ld [wSpriteCurPosX], a - ld [wSpriteCurPosY], a - call StoreSpriteOutputPointer - ld a, [wSpriteFlipped] - 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 [wSpriteDecodeTable0Ptr], a - ld a, h - ld [wSpriteDecodeTable0Ptr+1], a - ld a, e - ld [wSpriteDecodeTable1Ptr], a - ld a, d - ld [wSpriteDecodeTable1Ptr+1], a - ld e, $0 ; last decoded nybble, initialized to 0 -.decodeNextByteLoop - ld a, [wSpriteOutputPtr] - ld l, a - ld a, [wSpriteOutputPtr+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, [wSpriteOutputPtr] - ld l, a - ld a, [wSpriteOutputPtr+1] - ld h, a - ld a, b - ld [hl], a ; write back decoded data - ld a, [wSpriteHeight] - add l ; move on to next column - jr nc, .noCarry - inc h -.noCarry - ld [wSpriteOutputPtr], a - ld a, h - ld [wSpriteOutputPtr+1], a - ld a, [wSpriteCurPosX] - add $8 - ld [wSpriteCurPosX], a - ld b, a - ld a, [wSpriteWidth] - cp b - jr nz, .decodeNextByteLoop ; test if current row is done - xor a - ld e, a - ld [wSpriteCurPosX], a - ld a, [wSpriteCurPosY] ; move on to next row - inc a - ld [wSpriteCurPosY], a - ld b, a - ld a, [wSpriteHeight] - cp b - jr z, .done ; test if all rows finished - ld a, [wSpriteOutputPtrCached] - ld l, a - ld a, [wSpriteOutputPtrCached+1] - ld h, a - inc hl - call StoreSpriteOutputPointer - jr .decodeNextByteLoop -.done - xor a - ld [wSpriteCurPosY], 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:: - srl a ; c=a%2, a/=2 - ld c, $0 - jr nc, .evenNumber - ld c, $1 -.evenNumber - ld l, a - ld a, [wSpriteFlipped] - 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, [wSpriteDecodeTable0Ptr] - ld l, a - ld a, [wSpriteDecodeTable0Ptr+1] - jr .tableLookup -.initialValue1 - ld a, [wSpriteDecodeTable1Ptr] - ld l, a - ld a, [wSpriteDecodeTable1Ptr+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:: - 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:: - 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:: - 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:: - 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:: - xor a - ld [wSpriteCurPosX], a - ld [wSpriteCurPosY], a - call ResetSpriteBufferPointers - ld a, [wSpriteOutputPtr] ; points to buffer 1 or 2, depending on flags - ld l, a - ld a, [wSpriteOutputPtr+1] - ld h, a - call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags - call ResetSpriteBufferPointers - ld a, [wSpriteOutputPtr] ; source buffer, points to buffer 1 or 2, depending on flags - ld l, a - ld a, [wSpriteOutputPtr+1] - ld h, a - ld a, [wSpriteOutputPtrCached] ; destination buffer, points to buffer 2 or 1, depending on flags - ld e, a - ld a, [wSpriteOutputPtrCached+1] - ld d, a -.xorChunksLoop - ld a, [wSpriteFlipped] - 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, [wSpriteCurPosY] - inc a - ld [wSpriteCurPosY], a ; go to next row - ld b, a - ld a, [wSpriteHeight] - cp b - jr nz, .xorChunksLoop ; test if column finished - xor a - ld [wSpriteCurPosY], a - ld a, [wSpriteCurPosX] - add $8 - ld [wSpriteCurPosX], a ; go to next column - ld b, a - ld a, [wSpriteWidth] - cp b - jr nz, .xorChunksLoop ; test if all columns finished - xor a - ld [wSpriteCurPosX], a - ret - -; reverses the bits in the nybble given in register a -ReverseNybble:: - ld de, NybbleReverseTable - add e - ld e, a - jr nc, .noCarry - inc d -.noCarry - ld a, [de] - ret - -; resets sprite buffer pointers to buffer 1 and 2, depending on wSpriteLoadFlags -ResetSpriteBufferPointers:: - ld a, [wSpriteLoadFlags] - bit 0, a - jr nz, .buffer2Selected - ld de, sSpriteBuffer1 - ld hl, sSpriteBuffer2 - jr .storeBufferPointers -.buffer2Selected - ld de, sSpriteBuffer2 - ld hl, sSpriteBuffer1 -.storeBufferPointers - ld a, l - ld [wSpriteOutputPtr], a - ld a, h - ld [wSpriteOutputPtr+1], a - ld a, e - ld [wSpriteOutputPtrCached], a - ld a, d - ld [wSpriteOutputPtrCached+1], a - ret - -; maps each nybble to its reverse -NybbleReverseTable:: - 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:: - call ResetSpriteBufferPointers - ld a, [wSpriteFlipped] - push af - xor a - ld [wSpriteFlipped], a ; temporarily clear flipped flag for decoding the destination chunk - ld a, [wSpriteOutputPtrCached] - ld l, a - ld a, [wSpriteOutputPtrCached+1] - ld h, a - call SpriteDifferentialDecode - call ResetSpriteBufferPointers - pop af - ld [wSpriteFlipped], a - jp XorSpriteChunks - -; stores hl into the output pointers -StoreSpriteOutputPointer:: - ld a, l - ld [wSpriteOutputPtr], a - ld [wSpriteOutputPtrCached], a - ld a, h - ld [wSpriteOutputPtr+1], a - ld [wSpriteOutputPtrCached+1], a - ret diff --git a/home/pics.asm b/home/pics.asm new file mode 100644 index 00000000..02683db7 --- /dev/null +++ b/home/pics.asm @@ -0,0 +1,196 @@ +; uncompresses the front or back sprite of the specified mon +; assumes the corresponding mon header is already loaded +; hl contains offset to sprite pointer ($b for front or $d for back) +UncompressMonSprite:: + ld bc, wMonHeader + add hl, bc + ld a, [hli] + ld [wSpriteInputPtr], a ; fetch sprite input pointer + ld a, [hl] + ld [wSpriteInputPtr+1], a +; define (by index number) the bank that a pokemon's image is in +; index = Mew, bank 1 +; index = Kabutops fossil, bank $B +; index < $1F, bank 9 +; $1F ≤ index < $4A, bank $A +; $4A ≤ index < $74, bank $B +; $74 ≤ index < $99, bank $C +; $99 ≤ index, bank $D + ld a, [wcf91] ; XXX name for this ram location + ld b, a + cp MEW + ld a, BANK(MewPicFront) + jr z, .GotBank + ld a, b + cp FOSSIL_KABUTOPS + ld a, BANK(FossilKabutopsPic) + jr z, .GotBank + ld a, b + cp TANGELA + 1 + ld a, BANK(TangelaPicFront) + jr c, .GotBank + ld a, b + cp MOLTRES + 1 + ld a, BANK(MoltresPicFront) + jr c, .GotBank + ld a, b + cp BEEDRILL + 2 + ld a, BANK(BeedrillPicFront) + jr c, .GotBank + ld a, b + cp STARMIE + 1 + ld a, BANK(StarmiePicFront) + jr c, .GotBank + ld a, BANK(VictreebelPicFront) +.GotBank + jp UncompressSpriteData + +; de: destination location +LoadMonFrontSprite:: + push de + ld hl, wMonHFrontSprite - wMonHeader + call UncompressMonSprite + ld hl, wMonHSpriteDim + ld a, [hli] + ld c, a + pop de + ; fall through + +; postprocesses uncompressed sprite chunks to a 2bpp sprite and loads it into video ram +; calculates alignment parameters to place both sprite chunks in the center of the 7*7 tile sprite buffers +; de: destination location +; a,c: sprite dimensions (in tiles of 8x8 each) +LoadUncompressedSpriteData:: + push de + and $f + ld [hSpriteWidth], a ; each byte contains 8 pixels (in 1bpp), so tiles=bytes for width + ld b, a + ld a, $7 + sub b ; 7-w + inc a ; 8-w + srl a ; (8-w)/2 ; horizontal center (in tiles, rounded up) + ld b, a + add a + add a + add a + sub b ; 7*((8-w)/2) ; skip for horizontal center (in tiles) + ld [hSpriteOffset], a + ld a, c + swap a + and $f + ld b, a + add a + add a + add a ; 8*tiles is height in bytes + ld [hSpriteHeight], a + ld a, $7 + sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) + ld b, a + ld a, [hSpriteOffset] + add b ; 7*((8-w)/2) + 7-h ; combined overall offset (in tiles) + add a + add a + add a ; 8*(7*((8-w)/2) + 7-h) ; combined overall offset (in bytes) + ld [hSpriteOffset], a + xor a + ld [$4000], a + ld hl, sSpriteBuffer0 + call ZeroSpriteBuffer ; zero buffer 0 + ld de, sSpriteBuffer1 + ld hl, sSpriteBuffer0 + call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) + ld hl, sSpriteBuffer1 + call ZeroSpriteBuffer ; zero buffer 1 + ld de, sSpriteBuffer2 + ld hl, sSpriteBuffer1 + call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) + pop de + jp InterlaceMergeSpriteBuffers + +; copies and aligns the sprite data properly inside the sprite buffer +; sprite buffers are 7*7 tiles in size, the loaded sprite is centered within this area +AlignSpriteDataCentered:: + ld a, [hSpriteOffset] + ld b, $0 + ld c, a + add hl, bc + ld a, [hSpriteWidth] +.columnLoop + push af + push hl + ld a, [hSpriteHeight] + ld c, a +.columnInnerLoop + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .columnInnerLoop + pop hl + ld bc, 7*8 ; 7 tiles + add hl, bc ; advance one full column + pop af + dec a + jr nz, .columnLoop + ret + +; fills the sprite buffer (pointed to in hl) with zeros +ZeroSpriteBuffer:: + ld bc, SPRITEBUFFERSIZE +.nextByteLoop + xor a + ld [hli], a + dec bc + ld a, b + or c + jr nz, .nextByteLoop + ret + +; combines the (7*7 tiles, 1bpp) sprite chunks in buffer 0 and 1 into a 2bpp sprite located in buffer 1 through 2 +; in the resulting sprite, the rows of the two source sprites are interlaced +; de: output address +InterlaceMergeSpriteBuffers:: + xor a + ld [$4000], a + push de + ld hl, sSpriteBuffer2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 + ld de, sSpriteBuffer1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 + ld bc, sSpriteBuffer0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 + ld a, SPRITEBUFFERSIZE/2 ; $c4 + ld [hSpriteInterlaceCounter], a +.interlaceLoop + ld a, [de] + dec de + ld [hld], a ; write byte of source 2 + ld a, [bc] + dec bc + ld [hld], a ; write byte of source 1 + ld a, [de] + dec de + ld [hld], a ; write byte of source 2 + ld a, [bc] + dec bc + ld [hld], a ; write byte of source 1 + ld a, [hSpriteInterlaceCounter] + dec a + ld [hSpriteInterlaceCounter], a + jr nz, .interlaceLoop + ld a, [wSpriteFlipped] + and a + jr z, .notFlipped + ld bc, 2*SPRITEBUFFERSIZE + ld hl, sSpriteBuffer1 +.swapLoop + swap [hl] ; if flipped swap nybbles in all bytes + inc hl + dec bc + ld a, b + or c + jr nz, .swapLoop +.notFlipped + pop hl + ld de, sSpriteBuffer1 + ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied + ld a, [hLoadedROMBank] + ld b, a + jp CopyVideoData diff --git a/home/uncompress.asm b/home/uncompress.asm index 02683db7..cfc39f0f 100644 --- a/home/uncompress.asm +++ b/home/uncompress.asm @@ -1,196 +1,591 @@ -; uncompresses the front or back sprite of the specified mon -; assumes the corresponding mon header is already loaded -; hl contains offset to sprite pointer ($b for front or $d for back) -UncompressMonSprite:: - ld bc, wMonHeader - add hl, bc - ld a, [hli] - ld [wSpriteInputPtr], a ; fetch sprite input pointer - ld a, [hl] - ld [wSpriteInputPtr+1], a -; define (by index number) the bank that a pokemon's image is in -; index = Mew, bank 1 -; index = Kabutops fossil, bank $B -; index < $1F, bank 9 -; $1F ≤ index < $4A, bank $A -; $4A ≤ index < $74, bank $B -; $74 ≤ index < $99, bank $C -; $99 ≤ index, bank $D - ld a, [wcf91] ; XXX name for this ram location +; bankswitches and runs _UncompressSpriteData +; bank is given in a, sprite input stream is pointed to in wSpriteInputPtr +UncompressSpriteData:: ld b, a - cp MEW - ld a, BANK(MewPicFront) - jr z, .GotBank - ld a, b - cp FOSSIL_KABUTOPS - ld a, BANK(FossilKabutopsPic) - jr z, .GotBank - ld a, b - cp TANGELA + 1 - ld a, BANK(TangelaPicFront) - jr c, .GotBank - ld a, b - cp MOLTRES + 1 - ld a, BANK(MoltresPicFront) - jr c, .GotBank - ld a, b - cp BEEDRILL + 2 - ld a, BANK(BeedrillPicFront) - jr c, .GotBank + ld a, [hLoadedROMBank] + push af ld a, b - cp STARMIE + 1 - ld a, BANK(StarmiePicFront) - jr c, .GotBank - ld a, BANK(VictreebelPicFront) -.GotBank - jp UncompressSpriteData - -; de: destination location -LoadMonFrontSprite:: - push de - ld hl, wMonHFrontSprite - wMonHeader - call UncompressMonSprite - ld hl, wMonHSpriteDim - ld a, [hli] - ld c, a - pop de - ; fall through + ld [hLoadedROMBank], a + ld [MBC1RomBank], a + ld a, SRAM_ENABLE + ld [MBC1SRamEnable], a + xor a + ld [MBC1SRamBank], a + call _UncompressSpriteData + pop af + ld [hLoadedROMBank], a + ld [MBC1RomBank], a + ret -; postprocesses uncompressed sprite chunks to a 2bpp sprite and loads it into video ram -; calculates alignment parameters to place both sprite chunks in the center of the 7*7 tile sprite buffers -; de: destination location -; a,c: sprite dimensions (in tiles of 8x8 each) -LoadUncompressedSpriteData:: - push de - and $f - ld [hSpriteWidth], a ; each byte contains 8 pixels (in 1bpp), so tiles=bytes for width - ld b, a - ld a, $7 - sub b ; 7-w - inc a ; 8-w - srl a ; (8-w)/2 ; horizontal center (in tiles, rounded up) +; initializes necessary data to load a sprite and runs UncompressSpriteDataLoop +_UncompressSpriteData:: + ld hl, sSpriteBuffer1 + ld c, (2*SPRITEBUFFERSIZE) % $100 + ld b, (2*SPRITEBUFFERSIZE) / $100 + xor a + call FillMemory ; clear sprite buffer 1 and 2 + ld a, $1 + ld [wSpriteInputBitCounter], a + ld a, $3 + ld [wSpriteOutputBitOffset], a + xor a + ld [wSpriteCurPosX], a + ld [wSpriteCurPosY], a + ld [wSpriteLoadFlags], a + 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 - sub b ; 7*((8-w)/2) ; skip for horizontal center (in tiles) - ld [hSpriteOffset], a - ld a, c + ld [wSpriteHeight], a + ld a, b swap a and $f - ld b, a add a add a - add a ; 8*tiles is height in bytes - ld [hSpriteHeight], a - ld a, $7 - sub b ; 7-h ; skip for vertical center (in tiles, relative to current column) - ld b, a - ld a, [hSpriteOffset] - add b ; 7*((8-w)/2) + 7-h ; combined overall offset (in tiles) - add a add a - add a ; 8*(7*((8-w)/2) + 7-h) ; combined overall offset (in bytes) - ld [hSpriteOffset], a - xor a - ld [$4000], a - ld hl, sSpriteBuffer0 - call ZeroSpriteBuffer ; zero buffer 0 - ld de, sSpriteBuffer1 - ld hl, sSpriteBuffer0 - call AlignSpriteDataCentered ; copy and align buffer 1 to 0 (containing the MSB of the 2bpp sprite) - ld hl, sSpriteBuffer1 - call ZeroSpriteBuffer ; zero buffer 1 - ld de, sSpriteBuffer2 - ld hl, sSpriteBuffer1 - call AlignSpriteDataCentered ; copy and align buffer 2 to 1 (containing the LSB of the 2bpp sprite) - pop de - jp InterlaceMergeSpriteBuffers + ld [wSpriteWidth], a + call ReadNextInputBit + ld [wSpriteLoadFlags], a ; initialite bit1 to 0 and bit0 to the first input bit + ; this will load two chunks of data to sSpriteBuffer1 and sSpriteBuffer2 + ; bit 0 decides in which one the first chunk is placed + ; fall through -; copies and aligns the sprite data properly inside the sprite buffer -; sprite buffers are 7*7 tiles in size, the loaded sprite is centered within this area -AlignSpriteDataCentered:: - ld a, [hSpriteOffset] - ld b, $0 - ld c, a - add hl, bc - ld a, [hSpriteWidth] -.columnLoop - push af - push hl - ld a, [hSpriteHeight] +; uncompresses a chunk from the sprite input data stream (pointed to at wd0da) into sSpriteBuffer1 or sSpriteBuffer2 +; 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:: + ld hl, sSpriteBuffer1 + ld a, [wSpriteLoadFlags] + bit 0, a + jr z, .useSpriteBuffer1 ; check which buffer to use + ld hl, sSpriteBuffer2 +.useSpriteBuffer1 + call StoreSpriteOutputPointer + ld a, [wSpriteLoadFlags] + 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 [wSpriteUnpackMode], 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 -.columnInnerLoop - ld a, [de] - inc de - ld [hli], 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 nz, .columnInnerLoop + 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:: + ld a, [wSpriteHeight] + ld b, a + ld a, [wSpriteCurPosY] + inc a + cp b + jr z, .curColumnDone + ld [wSpriteCurPosY], a + ld a, [wSpriteOutputPtr] + inc a + ld [wSpriteOutputPtr], a + ret nz + ld a, [wSpriteOutputPtr+1] + inc a + ld [wSpriteOutputPtr+1], a + ret +.curColumnDone + xor a + ld [wSpriteCurPosY], a + ld a, [wSpriteOutputBitOffset] + and a + jr z, .bitOffsetsDone + dec a + ld [wSpriteOutputBitOffset], a + ld hl, wSpriteOutputPtrCached + ld a, [hli] + ld [wSpriteOutputPtr], a + ld a, [hl] + ld [wSpriteOutputPtr+1], a + ret +.bitOffsetsDone + ld a, $3 + ld [wSpriteOutputBitOffset], a + ld a, [wSpriteCurPosX] + add $8 + ld [wSpriteCurPosX], a + ld b, a + ld a, [wSpriteWidth] + cp b + jr z, .allColumnsDone + ld a, [wSpriteOutputPtr] + ld l, a + ld a, [wSpriteOutputPtr+1] + ld h, a + inc hl + jp StoreSpriteOutputPointer +.allColumnsDone pop hl - ld bc, 7*8 ; 7 tiles - add hl, bc ; advance one full column - pop af + xor a + ld [wSpriteCurPosX], a + ld a, [wSpriteLoadFlags] + bit 1, a + jr nz, .done ; test if there is one more sprite to go + xor $1 + set 1, a + ld [wSpriteLoadFlags], a + jp UncompressSpriteDataLoop +.done + jp UnpackSprite + +; writes 2 bits (from a) to the output buffer (pointed to from wSpriteOutputPtr) +WriteSpriteBitsToBuffer:: + ld e, a + ld a, [wSpriteOutputBitOffset] + 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, [wSpriteOutputPtr] + ld l, a + ld a, [wSpriteOutputPtr+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:: + ld a, [wSpriteInputBitCounter] dec a - jr nz, .columnLoop + jr nz, .curByteHasMoreBitsToRead + call ReadNextInputByte + ld [wSpriteInputCurByte], a + ld a, $8 +.curByteHasMoreBitsToRead + ld [wSpriteInputBitCounter], a + ld a, [wSpriteInputCurByte] + rlca + ld [wSpriteInputCurByte], a + and $1 ret -; fills the sprite buffer (pointed to in hl) with zeros -ZeroSpriteBuffer:: - ld bc, SPRITEBUFFERSIZE -.nextByteLoop - xor a - ld [hli], a - dec bc +; reads next byte from input stream and returns it in a +ReadNextInputByte:: + ld a, [wSpriteInputPtr] + ld l, a + ld a, [wSpriteInputPtr+1] + ld h, a + ld a, [hli] + ld b, a + ld a, l + ld [wSpriteInputPtr], a + ld a, h + ld [wSpriteInputPtr+1], a ld a, b - or c - jr nz, .nextByteLoop ret -; combines the (7*7 tiles, 1bpp) sprite chunks in buffer 0 and 1 into a 2bpp sprite located in buffer 1 through 2 -; in the resulting sprite, the rows of the two source sprites are interlaced -; de: output address -InterlaceMergeSpriteBuffers:: +; the nth item is 2^n - 1 +LengthEncodingOffsetList:: + 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:: + ld a, [wSpriteUnpackMode] + cp $2 + jp z, UnpackSpriteMode2 + and a + jp nz, XorSpriteChunks + ld hl, sSpriteBuffer1 + call SpriteDifferentialDecode + ld hl, sSpriteBuffer2 + ; 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:: xor a - ld [$4000], a - push de - ld hl, sSpriteBuffer2 + (SPRITEBUFFERSIZE - 1) ; destination: end of buffer 2 - ld de, sSpriteBuffer1 + (SPRITEBUFFERSIZE - 1) ; source 2: end of buffer 1 - ld bc, sSpriteBuffer0 + (SPRITEBUFFERSIZE - 1) ; source 1: end of buffer 0 - ld a, SPRITEBUFFERSIZE/2 ; $c4 - ld [hSpriteInterlaceCounter], a -.interlaceLoop - ld a, [de] - dec de - ld [hld], a ; write byte of source 2 - ld a, [bc] - dec bc - ld [hld], a ; write byte of source 1 - ld a, [de] - dec de - ld [hld], a ; write byte of source 2 - ld a, [bc] - dec bc - ld [hld], a ; write byte of source 1 - ld a, [hSpriteInterlaceCounter] - dec a - ld [hSpriteInterlaceCounter], a - jr nz, .interlaceLoop + ld [wSpriteCurPosX], a + ld [wSpriteCurPosY], a + call StoreSpriteOutputPointer ld a, [wSpriteFlipped] and a jr z, .notFlipped - ld bc, 2*SPRITEBUFFERSIZE - ld hl, sSpriteBuffer1 -.swapLoop - swap [hl] ; if flipped swap nybbles in all bytes + ld hl, DecodeNybble0TableFlipped + ld de, DecodeNybble1TableFlipped + jr .storeDecodeTablesPointers +.notFlipped + ld hl, DecodeNybble0Table + ld de, DecodeNybble1Table +.storeDecodeTablesPointers + ld a, l + ld [wSpriteDecodeTable0Ptr], a + ld a, h + ld [wSpriteDecodeTable0Ptr+1], a + ld a, e + ld [wSpriteDecodeTable1Ptr], a + ld a, d + ld [wSpriteDecodeTable1Ptr+1], a + ld e, $0 ; last decoded nybble, initialized to 0 +.decodeNextByteLoop + ld a, [wSpriteOutputPtr] + ld l, a + ld a, [wSpriteOutputPtr+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, [wSpriteOutputPtr] + ld l, a + ld a, [wSpriteOutputPtr+1] + ld h, a + ld a, b + ld [hl], a ; write back decoded data + ld a, [wSpriteHeight] + add l ; move on to next column + jr nc, .noCarry + inc h +.noCarry + ld [wSpriteOutputPtr], a + ld a, h + ld [wSpriteOutputPtr+1], a + ld a, [wSpriteCurPosX] + add $8 + ld [wSpriteCurPosX], a + ld b, a + ld a, [wSpriteWidth] + cp b + jr nz, .decodeNextByteLoop ; test if current row is done + xor a + ld e, a + ld [wSpriteCurPosX], a + ld a, [wSpriteCurPosY] ; move on to next row + inc a + ld [wSpriteCurPosY], a + ld b, a + ld a, [wSpriteHeight] + cp b + jr z, .done ; test if all rows finished + ld a, [wSpriteOutputPtrCached] + ld l, a + ld a, [wSpriteOutputPtrCached+1] + ld h, a inc hl - dec bc + call StoreSpriteOutputPointer + jr .decodeNextByteLoop +.done + xor a + ld [wSpriteCurPosY], 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:: + srl a ; c=a%2, a/=2 + ld c, $0 + jr nc, .evenNumber + ld c, $1 +.evenNumber + ld l, a + ld a, [wSpriteFlipped] + 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, [wSpriteDecodeTable0Ptr] + ld l, a + ld a, [wSpriteDecodeTable0Ptr+1] + jr .tableLookup +.initialValue1 + ld a, [wSpriteDecodeTable1Ptr] + ld l, a + ld a, [wSpriteDecodeTable1Ptr+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:: + 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:: + 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:: + 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:: + 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:: + xor a + ld [wSpriteCurPosX], a + ld [wSpriteCurPosY], a + call ResetSpriteBufferPointers + ld a, [wSpriteOutputPtr] ; points to buffer 1 or 2, depending on flags + ld l, a + ld a, [wSpriteOutputPtr+1] + ld h, a + call SpriteDifferentialDecode ; decode buffer 1 or 2, depending on flags + call ResetSpriteBufferPointers + ld a, [wSpriteOutputPtr] ; source buffer, points to buffer 1 or 2, depending on flags + ld l, a + ld a, [wSpriteOutputPtr+1] + ld h, a + ld a, [wSpriteOutputPtrCached] ; destination buffer, points to buffer 2 or 1, depending on flags + ld e, a + ld a, [wSpriteOutputPtrCached+1] + ld d, a +.xorChunksLoop + ld a, [wSpriteFlipped] + 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 - jr nz, .swapLoop + pop de + ld [de], a .notFlipped - pop hl - ld de, sSpriteBuffer1 - ld c, (2*SPRITEBUFFERSIZE)/16 ; $31, number of 16 byte chunks to be copied - ld a, [hLoadedROMBank] + ld a, [hli] + ld b, a + ld a, [de] + xor b + ld [de], a + inc de + ld a, [wSpriteCurPosY] + inc a + ld [wSpriteCurPosY], a ; go to next row + ld b, a + ld a, [wSpriteHeight] + cp b + jr nz, .xorChunksLoop ; test if column finished + xor a + ld [wSpriteCurPosY], a + ld a, [wSpriteCurPosX] + add $8 + ld [wSpriteCurPosX], a ; go to next column ld b, a - jp CopyVideoData + ld a, [wSpriteWidth] + cp b + jr nz, .xorChunksLoop ; test if all columns finished + xor a + ld [wSpriteCurPosX], a + ret + +; reverses the bits in the nybble given in register a +ReverseNybble:: + ld de, NybbleReverseTable + add e + ld e, a + jr nc, .noCarry + inc d +.noCarry + ld a, [de] + ret + +; resets sprite buffer pointers to buffer 1 and 2, depending on wSpriteLoadFlags +ResetSpriteBufferPointers:: + ld a, [wSpriteLoadFlags] + bit 0, a + jr nz, .buffer2Selected + ld de, sSpriteBuffer1 + ld hl, sSpriteBuffer2 + jr .storeBufferPointers +.buffer2Selected + ld de, sSpriteBuffer2 + ld hl, sSpriteBuffer1 +.storeBufferPointers + ld a, l + ld [wSpriteOutputPtr], a + ld a, h + ld [wSpriteOutputPtr+1], a + ld a, e + ld [wSpriteOutputPtrCached], a + ld a, d + ld [wSpriteOutputPtrCached+1], a + ret + +; maps each nybble to its reverse +NybbleReverseTable:: + 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:: + call ResetSpriteBufferPointers + ld a, [wSpriteFlipped] + push af + xor a + ld [wSpriteFlipped], a ; temporarily clear flipped flag for decoding the destination chunk + ld a, [wSpriteOutputPtrCached] + ld l, a + ld a, [wSpriteOutputPtrCached+1] + ld h, a + call SpriteDifferentialDecode + call ResetSpriteBufferPointers + pop af + ld [wSpriteFlipped], a + jp XorSpriteChunks + +; stores hl into the output pointers +StoreSpriteOutputPointer:: + ld a, l + ld [wSpriteOutputPtr], a + ld [wSpriteOutputPtrCached], a + ld a, h + ld [wSpriteOutputPtr+1], a + ld [wSpriteOutputPtrCached+1], a + ret |