Zelda - A Link to the Past Decompression Code
Commented by Peekin - Feburary 22nd, 2001
; 65816 SNES Disassembler v2.0a (C)opyright 1994 by John Corey
; Begin: $00E7A3 End: $00E852
; Hirom: Yes Quiet: Yes Comments: 2 DCB: Yes Symbols: Yes 65816: Yes
00E7A3 20 43 E8 JSR $E843 ; get next byte
00E7A6 C9 FF CMP #$FF ; end of compressed stream if code = 0FFh
00E7A8 D0 03 BNE $E7AD ; continue if any other code
00E7AA E2 10 SEP #$10 ; Index (8 bit)
00E7AC 60 RTS ; end of compression routine
; get code and length (code is upper 3 bits, length is lower 5)
00E7AD 85 CD STA $CD ; save byte
00E7AF 29 E0 AND #$E0 ; get code (upper 3 bits)
00E7B1 C9 E0 CMP #$E0 ; special code for longer run count
00E7B3 F0 0A BEQ $E7BF
00E7B5 48 PHA ; save code
00E7B6 A5 CD LDA $CD ; retrieve byte
00E7B8 C2 20 REP #$20 ; Accum (16 bit)
00E7BA 29 1F 00 AND #$001F ; mask byte to get length (lower 5 bits)
00E7BD 80 12 BRA $E7D1
; long run count
00E7BF A5 CD LDA $CD ; retrieve byte
00E7C1 0A ASL ; shift byte left 3 times for new code
00E7C2 0A ASL
00E7C3 0A ASL
00E7C4 29 E0 AND #$E0 ; get new code (upper 3 bits)
00E7C6 48 PHA ; save code
00E7C7 A5 CD LDA $CD ; retrieve original byte
00E7C9 29 03 AND #$03 ; select lowest two bits for count
00E7CB EB XBA ; save count's two msb in AH (*256)
00E7CC 20 43 E8 JSR $E843 ; read next byte for a total 10bit count
00E7CF C2 20 REP #$20 ; Accum (16 bit)
; depending on if the code was E0, the count will either be 0-63 or 0-1023
00E7D1 1A INC ; count++
00E7D2 85 CB STA $CB ; store count
00E7D4 E2 20 SEP #$20 ; Accum (8 bit)
00E7D6 68 PLA ; retrieve code
00E7D7 F0 16 BEQ $E7EF ; transfer bytes from source
00E7D9 30 4A BMI $E825 ; transfer bytes from output buffer
00E7DB 0A ASL
00E7DC 10 20 BPL $E7FE ; repeat single byte
00E7DE 0A ASL
00E7DF 10 2A BPL $E80B ; repeat two alternating bytes
; repeat single incrementing byte??
00E7E1 20 43 E8 JSR $E843 ; read single byte
00E7E4 A6 CB LDX $CB ; load count
00E7E6 97 00 STA [$00],Y ; write byte
00E7E8 1A INC ; increment byte value??
00E7E9 C8 INY ; destination ptr++
00E7EA CA DEX ; count--
00E7EB D0 F9 BNE $E7E6 ; loop while count <> 0
00E7ED 80 B4 BRA $E7A3 ; go to top of loop for next code
; transfer bytes directly
00E7EF 20 43 E8 JSR $E843 ; read next byte to transfer
00E7F2 97 00 STA [$00],Y ; write byte
00E7F4 C8 INY ; destination ptr++
00E7F5 A6 CB LDX $CB ; reload count (since ReadByte changed it)
00E7F7 CA DEX ; count--
00E7F8 86 CB STX $CB ; store count
00E7FA D0 F3 BNE $E7EF ; loop while count <> 0
00E7FC 80 A5 BRA $E7A3 ; go to top of loop for next code
; repeat single byte
00E7FE 20 43 E8 JSR $E843 ; read single byte to repeat
00E801 A6 CB LDX $CB ; load count
00E803 97 00 STA [$00],Y ; write byte
00E805 C8 INY ; destination ptr++
00E806 CA DEX ; count--
00E807 D0 FA BNE $E803 ; loop while count <> 0
00E809 80 98 BRA $E7A3 ; go to top of loop for next code
; repeat run of alternating even/odd bytes
00E80B 20 43 E8 JSR $E843 ; read first byte
00E80E EB XBA ; save first byte into AH
00E80F 20 43 E8 JSR $E843 ; read second byte
00E812 A6 CB LDX $CB ; load count
00E814 EB XBA ; swap first byte with second
00E815 97 00 STA [$00],Y ; write first byte
00E817 C8 INY ; destination ptr++
00E818 CA DEX ; count--
00E819 F0 07 BEQ $E822 ; exit loop if count = 0
00E81B EB XBA ; swap first byte with second
00E81C 97 00 STA [$00],Y ; write second byte
00E81E C8 INY ; destination ptr++
00E81F CA DEX ; count--
00E820 D0 F2 BNE $E814 ; loop while count <> 0
00E822 4C A3 E7 JMP $E7A3 ; go to top of loop for next code
; copy run of bytes already in output buffer to end
00E825 20 43 E8 JSR $E843 ; read low byte ptr
00E828 EB XBA
00E829 20 43 E8 JSR $E843 ; read high byte ptr
00E82C EB XBA
00E82D AA TAX ; copy buffer source to X
00E82E 5A PHY ; save destination ptr
00E82F 9B TXY ; move buffer source to Y for indexing
00E830 B7 00 LDA [$00],Y ; read existing buffer byte
00E832 BB TYX ; copy back to X, why??
00E833 7A PLY ; retrieve destination ptr
00E834 97 00 STA [$00],Y ; write byte
00E836 C8 INY ; destination ptr++
00E837 E8 INX ; buffer source++
00E838 C2 20 REP #$20 ; Accum (16 bit)
00E83A C6 CB DEC $CB ; count--
00E83C E2 20 SEP #$20 ; Accum (8 bit)
00E83E D0 EE BNE $E82E ; loop while count <> 0
00E840 4C A3 E7 JMP $E7A3 ; go to top of loop for next code
; read next byte
00E843 A7 C8 LDA [$C8] ; read single byte from ROM
00E845 A6 C8 LDX $C8 ; load source ptr
00E847 E8 INX ; source ptr++
00E848 D0 05 BNE $E84F ; if not beyond end of bank
00E84A A2 00 80 LDX #$8000 ; wrap source to beginning of next bank
00E84D E6 CA INC $CA ; increment to next source bank
00E84F 86 C8 STX $C8 ; store source ptr
00E851 60 RTS ; end of read byte
00E852 FF FF FF FF SBC $FFFFFF,X ; those familiar separating FF's
Compression Codes
Code | Address | Description |
---|---|---|
000..... |
00E7EF |
transfer bytes from source transfer Count bytes after code to buffer |
001..... |
00E7FE |
repeat single byte read next byte and repeat Count times |
010..... |
00E80B |
repeat two alternating bytes read next two bytes and alternately repeat |
011..... |
00E7E1 |
repeat single incrementing byte read next byte and repeat incrementing each time |
111..... |
00E7BF |
long count bits 2-4 become 5-7 for the new code. bottom 2 bits become top two bits of count. read next byte for lower 8 bits of count +1. |
1xx..... |
00E825 |
transfer bytes from output buffer read next two bytes for buffer source pointer (low/high) |
11111111 |
00E7AA |
end of compressed stream |
Examples
Transfer bytes from source
02 12 34 56 -> 12 34 56
Repeat single byte
22 12 -> 12 12 12
Repeat two alternating bytes
44 12 34 -> 12 34 12 34 12
Repeat single incrementing byte
62 12 -> 12 13 14
Long count (followed by source transfer of 302h bytes)
E3 01 12 34 56 .. -> 12 34 56 78 90 12 34 ...
Transfer byte from output buffer (starting at offset 4)
82 04 00 -> 90 12 34