; Sonic 1 tile decompressor ; ; Needs 8 bytes of RAM for temporary storage. Define Sonic1TileLoaderMemory as the start address of the RAM to use. .block "TileLoaderSonic1" .section "Tile loader (Sonic 1)" free ; RAM usage .enum Sonic1TileLoaderMemory export Sonic1TileLoader_StartOfData dw Sonic1TileLoader_RowCount dw Sonic1TileLoader_UniqueRowsData dw Sonic1TileLoader_ArtData dw .ende ; A definition .define SMS_VDP_DATA $be Sonic1TileLoader_Decompress: ; Arguments: ; hl = data address ; VDP write address should be already set ; Uses af,bc,de,bc',de',hl' ; Note: this has been slightly modified from what is found in Sonic 1, for size and speed ; See http://info.sonicretro.org/SCHG:Sonic_the_Hedgehog_%288-bit%29#Header for details on the format ld (Sonic1TileLoader_StartOfData),hl ; Skip the "48 59" art header marker inc hl inc hl ; Read in the various offsets and convert to pointers: ; - BC is the row count, this counts down to zero as we process each row ; - DE points into the art data ; - DE' points into the duplicate rows data (per-tile bitmasks) ; - HL' points at the start of the art data ; Read the DuplicateRows offset into DE and save for later ld e,(hl) inc hl ld d,(hl) inc hl push de ; Read the ArtData offset into DE and save for later ld e,(hl) inc hl ld d,(hl) push de ; Read the row count into BC inc hl ld c,(hl) inc hl ld b,(hl) inc hl ld (Sonic1TileLoader_RowCount),bc ; Store the row count ld (Sonic1TileLoader_UniqueRowsData),hl ; Where the UniqueRows list begins ; swap BC/DE/HL with their shadow values exx ; load DE with the absolute starting address of the art header; the DuplicateRows and ArtData values are always relative to this ld de,(Sonic1TileLoader_StartOfData) pop hl ; pull the ArtData value from the stack add hl,de ; get the absolute address of ArtData ld (Sonic1TileLoader_ArtData),hl ; and save it ; copy it to BC. this will be used to produce a counter from 0 to RowCount ld c,l ld b,h pop hl ; load HL with the DuplicateRows offset add hl,de ; get the absolute address of DuplicateRows ; swap DE & HL. DE will now be the DuplicateRows absolute address, ; and HL will be the absolute address of the art header ex de,hl ; now swap the original values back, ; BC will be the row counter ; DE will be the ArtData value exx _processRow: ld hl,(Sonic1TileLoader_RowCount) ; load HL with the original row count number xor a ; set A to 0 (Carry is reset) sbc hl,bc ; subtract current counter from the row count - that is, count upwards from 0 push hl ; get the row number in the current tile (0-7): ld d,a ; zero-out D ld a,l ; load A with the lo-byte of the counter and %00000111 ; clip to the first three bits, that is, "mod 8" it so it counts 0-7 ld e,a ; load E with this value, making it a 16-bit number in DE ld hl,_rowIndexTable add hl,de ld a,(hl) ; get the bit mask for the particular row pop de ; divide the counter by 4 srl d rr e srl d rr e srl d rr e ld hl,(Sonic1TileLoader_UniqueRowsData) ; the absolute address where the UniqueRows list begins add hl,de ; add the counter, so move along to the DE'th byte in the UniqueRows list ld e,a ld a,(hl) ; read the current byte in the UniqueRows list and e ; test if the masked bit is set jr nz,_duplicateRow ; if the bit is set, it's a duplicate row, otherwise continue for a unique row _uniqueRow: ; swap back the BC/DE/HL shadow values ; BC will be the pointer to the ArtData exx ; write 1 row of pixels (4 bytes) to the VDP ld a,(bc) out (SMS_VDP_DATA),a inc bc ld a,(bc) out (SMS_VDP_DATA),a inc bc ld a,(bc) out (SMS_VDP_DATA),a inc bc ld a,(bc) out (SMS_VDP_DATA),a inc bc exx jr _endOfRow _duplicateRow: ; swap in the BC/DE/HL shadow values ; DE will be the pointer to the DuplicateRows data exx ld a,(de) ; read a byte from the duplicate rows list inc de ; move to the next byte exx ; HL will be re-purposed as the index into the art data ld h,0 ; Check if the byte from the duplicate rows list begins with $f. This is used as a marker to specify a two-byte number for indexes over 256. cp $f0 jr c,+ ; if less than $f0, use it as-is sub $f0 ; else, strip the $f0, i.e $f3 -> $03 ld h,a ; and set as the hi-byte for the art data index exx ; fetch the next byte into A ld a,(de) inc de exx +: ; multiply the duplicate row's index number to the art data by 4 - each row of art data is 4 bytes ld l,a add hl,hl add hl,hl ld de,(Sonic1TileLoader_ArtData) ; get the absolute address to the art data add hl,de ; add the index from the duplicate row list ; write 1 row of pixels (4 bytes) to the VDP ld a,(hl) out (SMS_VDP_DATA),a inc hl ld a,(hl) out (SMS_VDP_DATA),a inc hl ld a,(hl) out (SMS_VDP_DATA),a inc hl ld a,(hl) out (SMS_VDP_DATA),a inc hl _endOfRow: ; decrease the remaining row count dec bc ; check if all rows have been done ld a,b or c jr nz,_processRow ret _rowIndexTable: .db %00000001 .db %00000010 .db %00000100 .db %00001000 .db %00010000 .db %00100000 .db %01000000 .db %10000000 .ends .endb