1
0
mirror of https://github.com/pfusik/zlib6502.git synced 2024-06-25 22:29:30 +00:00
zlib6502/inflate.asx

536 lines
13 KiB
Plaintext
Raw Normal View History

zpage equ $f0 ; 11 bytes
2003-03-12 00:11:24 +00:00
inflate equ $8000
2002-08-06 15:03:06 +00:00
* 'Inflate'
; Uncompress data stored in the DEFLATE format.
;
; DEFLATE is a popular compression format, used e.g. in ZIP and GZIP archives,
; and the ZLIB library. The original description of this format
; is available in RFC (Request for Comments) 1951 in the file
; ftp://www.ietf.org/rfc/rfc1951.txt.
; Both compressed and uncompressed data must completely fit in the memory.
; As the compressed data is read sequentially and only once, it is possible
; for the compressed and uncompressed data to overlap, i.e. the data being
; uncompressed can be stored in place of some compressed data which has been
; already read. There is no general rule, but practically both data may
; overlap almost entirely, with the uncompressed data ending just a few
; bytes before the end of the compressed data.
; If you want to modify this code to support non-continuous memory areas
; for the output, you should note that the uncompressed data is used as
; a look-behind buffer, up to 32K.
; As the code is not self-modifying, it can be put in ROM.
; This source code is written in the 6502 assembly language,
; using syntax of xasm (http://xasm.atari.org).
; The one-character comments I use are:
; '!' branch always
; '-' C flag is zero (useful for adc)
; '+' C flag is set (useful for sbc)
; At the end of this file you can find source code of a simple C program
; that performs DEFLATE compression using the popular ZLIB library.
;
; Permission is granted to anyone to use this code for any purpose, including
; commercial applications, and redistribute it freely in its original form.
;
; Author: Piotr Fusik <fox@scene.pl>
; Last modified: 11 Oct 2003
* Constants
; Whether to support the new DEFLATE64, introduced as the compression method 9
; in PKZIP 4.0, released Nov 2000. This format is very uncommon (not even
; supported by ZLIB) and doesn't seem to be useful on 6502 systems, since
; the main change is the 64K look-behind buffer. It's here just for fun! :-)
USE_DEFLATE64 equ 0
; Maximum length of a Huffman code.
MAX_BITS equ 15
; All Huffman trees are stored in the bitsCount, bitsPointer_l
; and bitsPointer_h arrays. There may be two trees: the literal/length tree
; and the distance tree, or just one - the temporary tree.
; Index in the mentioned arrays for the beginning of the literal/length tree
; or the temporary tree.
PRIMARY_TREE equ 0
; Index in the mentioned arrays for the beginning of the distance tree.
DISTANCE_TREE equ MAX_BITS
; Size of each array.
TREES_SIZE equ 2*MAX_BITS
* Page zero
; (public) Pointer to the compressed data.
2002-08-06 15:03:06 +00:00
inputPointer equ zpage ; 2 bytes
; (public) Pointer to the uncompressed data.
2002-08-06 15:03:06 +00:00
outputPointer equ zpage+2 ; 2 bytes
; Local variables.
; As far as there is no conflict, same memory locations are used
; for different variables.
2002-08-06 15:03:06 +00:00
inflateDynamicBlock_cnt equ zpage+4 ; 1 byte
inflateCodes_src equ zpage+4 ; 2 bytes
buildHuffmanTree_src equ zpage+4 ; 2 bytes
getNextLength_last equ zpage+4 ; 1 byte
getNextLength_index equ zpage+5 ; 1 byte
buildHuffmanTree_ptr equ zpage+6 ; 2 bytes
fetchCode_ptr equ zpage+6 ; 2 bytes
getBits_tmp equ zpage+6 ; 1 byte
moveBlock_len equ zpage+8 ; 2 bytes
inflateDynamicBlock_np equ zpage+8 ; 1 byte
inflateDynamicBlock_nd equ zpage+9 ; 1 byte
2000-08-14 15:26:04 +00:00
getBit_hold equ zpage+10 ; 1 byte
* Code
2000-08-14 15:26:04 +00:00
org inflate
2000-08-14 15:26:04 +00:00
* (public) inflate
; Decompress the DEFLATE data starting from the address stored in inputPointer
; to the memory starting from the address stored in outputPointer.
2002-08-06 15:03:06 +00:00
mvy #1 getBit_hold
bne inflate_2 !
inflate_1
jsr inflate_3
inflate_2
2000-08-14 15:26:04 +00:00
; Get a bit of EOF and two bits of block type
2002-08-06 15:03:06 +00:00
ldx #3
2000-08-14 15:26:04 +00:00
lda #0
jsr getBits
lsr @
2002-08-06 15:03:06 +00:00
bcc inflate_1
inflate_3
lsr @
bne inflateDynamicBlock
; Note: inflateDynamicBlock may assume that A = 1
bcs inflateFixedBlock
; Note: inflateCopyBlock may assume that C = 0
2000-08-14 15:26:04 +00:00
* inflateCopyBlock
; Decompress a 'stored' data block.
2000-08-14 15:26:04 +00:00
inflateCopyBlock
; Ignore bits until byte boundary
2002-08-06 15:03:06 +00:00
mvy #1 getBit_hold
2000-08-14 15:26:04 +00:00
; Get 16-bit length
2002-08-06 15:03:06 +00:00
ldx #inputPointer
mva (0,x) moveBlock_len
mva (inputPointer),y moveBlock_len+1
; Skip the length and one's complement of it
2000-08-14 15:26:04 +00:00
lda #4
adc:sta inputPointer -
2000-08-14 15:26:04 +00:00
scc:inc inputPointer+1
* moveBlock
; Copy block of length moveBlock_len from (0,x) to the output.
2000-08-14 15:26:04 +00:00
moveBlock
2002-08-06 15:03:06 +00:00
ldy moveBlock_len
beq moveBlock_1
2000-08-14 15:26:04 +00:00
ldy #0
2002-08-06 15:03:06 +00:00
inc moveBlock_len+1
moveBlock_1
mva (0,x) (outputPointer),y
2000-08-14 15:26:04 +00:00
inw 0,x
inw outputPointer
2002-08-06 15:03:06 +00:00
dec moveBlock_len
bne moveBlock_1
dec moveBlock_len+1
bne moveBlock_1
2000-08-14 15:26:04 +00:00
rts
* inflateFixedBlock
; Decompress a Huffman-coded data block with default Huffman trees
; (defined by the DEFLATE format):
; literalCodeLength :144 dta 8
; :112 dta 9
; endCodeLength dta 7
; lengthCodeLength :23 dta 7
; :6 dta 8
; distanceCodeLength :30+2*USE_DEFLATE64 dta 5+DISTANCE_TREE
; :2 dta 8
; (two 8-bit codes from the primary tree are not used).
2000-08-14 15:26:04 +00:00
inflateFixedBlock
ldx #159+USE_DEFLATE64
stx distanceCodeLength+32+2*USE_DEFLATE64
2000-08-14 15:26:04 +00:00
lda #8
2002-08-06 15:03:06 +00:00
inflateFixedBlock_1
sta literalCodeLength-1,x
sta literalCodeLength+159+USE_DEFLATE64-1,x-
2002-08-06 15:03:06 +00:00
bne inflateFixedBlock_1
2000-08-14 15:26:04 +00:00
ldx #112
2002-08-06 15:03:06 +00:00
inc:rne literalCodeLength+144-1,x-
2000-08-14 15:26:04 +00:00
ldx #24
2002-08-06 15:03:06 +00:00
dec:rne endCodeLength-1,x-
ldx #30+2*USE_DEFLATE64
2000-08-14 15:26:04 +00:00
lda #5+DISTANCE_TREE
sta:rne distanceCodeLength-1,x-
2002-08-06 15:03:06 +00:00
beq inflateCodes !
2000-08-14 15:26:04 +00:00
* inflateDynamicBlock
; Decompress a Huffman-coded data block, reading Huffman trees first.
2000-08-14 15:26:04 +00:00
inflateDynamicBlock
2002-08-06 15:03:06 +00:00
; numberOfPrimaryCodes = 257 + getBits(5)
2000-08-14 15:26:04 +00:00
ldx #5
2002-08-06 15:03:06 +00:00
; lda #1
2000-08-14 15:26:04 +00:00
jsr getBits
2002-08-06 15:03:06 +00:00
sta inflateDynamicBlock_np
; numberOfDistanceCodes = 1 + getBits(5)
2000-08-14 15:26:04 +00:00
ldx #5
2002-08-06 15:03:06 +00:00
lda #1+29+1
2000-08-14 15:26:04 +00:00
jsr getBits
2002-08-06 15:03:06 +00:00
sta inflateDynamicBlock_nd
; numberOfTemporaryCodes = 4 + getBits(4)
2000-08-14 15:26:04 +00:00
lda:tax #4
jsr getBits
2002-08-06 15:03:06 +00:00
sta inflateDynamicBlock_cnt
; Get lengths of temporary codes in the order stored in tempCodeLengthOrder
2003-03-12 00:11:24 +00:00
txa:tay #0
2002-08-06 15:03:06 +00:00
inflateDynamicBlock_1
ldx #3 ; A = 0
jsr getBits ; does not change Y
2002-08-06 15:03:06 +00:00
inflateDynamicBlock_2
ldx tempCodeLengthOrder,y
2000-08-14 15:26:04 +00:00
sta literalCodeLength,x
2002-08-06 15:03:06 +00:00
lda #0
2000-08-14 15:26:04 +00:00
iny
2002-08-06 15:03:06 +00:00
cpy inflateDynamicBlock_cnt
bcc inflateDynamicBlock_1
cpy #19
bcc inflateDynamicBlock_2
2000-08-14 15:26:04 +00:00
ror literalCodeLength+19 +
; Build the tree for temporary codes
2000-08-14 15:26:04 +00:00
jsr buildHuffmanTree
; Use temporary codes to get lengths of literal/length and distance codes
2002-08-06 15:03:06 +00:00
ldx #0
2000-08-14 15:26:04 +00:00
ldy #1
2002-08-06 15:03:06 +00:00
stx getNextLength_last
inflateDynamicBlock_3
jsr getNextLength
sta literalCodeLength,x+
bne inflateDynamicBlock_3
inflateDynamicBlock_4
jsr getNextLength
inflateDynamicBlock_5
2002-08-06 15:03:06 +00:00
sta endCodeLength,x+
cpx inflateDynamicBlock_np
bcc inflateDynamicBlock_4
2000-08-14 15:26:04 +00:00
lda #0
2002-08-06 15:03:06 +00:00
cpx #1+29
bcc inflateDynamicBlock_5
inflateDynamicBlock_6
2002-08-06 15:03:06 +00:00
jsr getNextLength
cmp #0
seq:adc #DISTANCE_TREE-1 +
sta endCodeLength,x+
cpx inflateDynamicBlock_nd
bcc inflateDynamicBlock_6
2002-08-06 15:03:06 +00:00
ror endCodeLength,x +
2000-08-14 15:26:04 +00:00
* inflateCodes
; Decompress a data block using given Huffman trees.
2000-08-14 15:26:04 +00:00
inflateCodes
jsr buildHuffmanTree
2002-08-06 15:03:06 +00:00
inflateCodes_1
jsr fetchPrimaryCode
bcs inflateCodes_2
2000-08-14 15:26:04 +00:00
; Literal code
sta (outputPointer),0
2002-08-06 15:03:06 +00:00
inc outputPointer
bne inflateCodes_1
inc outputPointer+1
bcc inflateCodes_1 !
2000-08-14 15:26:04 +00:00
; End of block
2002-08-06 15:03:06 +00:00
inflateCodes_ret
rts
inflateCodes_2
beq inflateCodes_ret
; Restore a block from the look-behind buffer
2000-08-14 15:26:04 +00:00
jsr getValue
2002-08-06 15:03:06 +00:00
sta moveBlock_len
2000-08-14 15:26:04 +00:00
tya
jsr getBits
2002-08-06 15:03:06 +00:00
sta moveBlock_len+1
2000-08-14 15:26:04 +00:00
ldx #DISTANCE_TREE
jsr fetchCode
jsr getValue
2002-08-06 15:03:06 +00:00
sec
eor #$ff
adc outputPointer
sta inflateCodes_src
php
2000-08-14 15:26:04 +00:00
tya
jsr getBits
2002-08-06 15:03:06 +00:00
plp
eor #$ff
adc outputPointer+1
sta inflateCodes_src+1
ldx #inflateCodes_src
2000-08-14 15:26:04 +00:00
jsr moveBlock
2002-08-06 15:03:06 +00:00
beq inflateCodes_1 !
2000-08-14 15:26:04 +00:00
* buildHuffmanTree
; Build Huffman trees basing on code lengths (in bits)
; stored in the *CodeLength arrays.
; A byte with its highest bit set marks the end.
2000-08-14 15:26:04 +00:00
buildHuffmanTree
2002-08-06 15:03:06 +00:00
mwa #literalCodeLength buildHuffmanTree_src
; Clear bitsCount and bitsPointer_l
ldy #2*TREES_SIZE+1
2000-08-14 15:26:04 +00:00
lda #0
2002-08-06 15:03:06 +00:00
sta:rne bitsCount-1,y-
beq buildHuffmanTree_3 !
2000-08-14 15:26:04 +00:00
; Count number of codes of each length
2002-08-06 15:03:06 +00:00
buildHuffmanTree_2
tax
inc bitsPointer_l,x
iny
bne buildHuffmanTree_3
inc buildHuffmanTree_src+1
buildHuffmanTree_3
lda (buildHuffmanTree_src),y
bpl buildHuffmanTree_2
; Calculate a pointer for each length
2002-08-06 15:03:06 +00:00
ldx #0
lda #<sortedCodes
ldy #>sortedCodes
clc
buildHuffmanTree_4
sta bitsPointer_l,x
2000-08-14 15:26:04 +00:00
tya:sta bitsPointer_h,x
2002-08-06 15:03:06 +00:00
lda bitsPointer_l+1,x
adc bitsPointer_l,x -
2000-08-14 15:26:04 +00:00
scc:iny
inx
cpx #TREES_SIZE
2002-08-06 15:03:06 +00:00
bcc buildHuffmanTree_4
mva #>literalCodeLength buildHuffmanTree_src+1
ldy #0
bcs buildHuffmanTree_9 !
; Put codes into their place in the sorted array
2002-08-06 15:03:06 +00:00
buildHuffmanTree_6
beq buildHuffmanTree_7
tax
mva bitsPointer_l-1,x buildHuffmanTree_ptr
mva bitsPointer_h-1,x buildHuffmanTree_ptr+1
tya
ldy:inc bitsCount-1,x
sta (buildHuffmanTree_ptr),y
tay
buildHuffmanTree_7
iny
bne buildHuffmanTree_9
inc buildHuffmanTree_src+1
ldx #MAX_BITS-1
mva:rpl bitsCount,x literalCount,x-
buildHuffmanTree_9
lda (buildHuffmanTree_src),y
bpl buildHuffmanTree_6
rts
* getNextLength
; Decode next code length using temporary codes.
2002-08-06 15:03:06 +00:00
getNextLength
stx getNextLength_index
dey
bne getNextLength_1
; Fetch a temporary code
jsr fetchPrimaryCode
; Temporary code 0..15: put this length
ldy #1
cmp #16
bcc getNextLength_2
; Temporary code 16: repeat last length 3 + getBits(2) times
; Temporary code 17: put zero length 3 + getBits(3) times
; Temporary code 18: put zero length 11 + getBits(7) times
tay
ldx tempExtraBits-16,y
lda tempBaseValue-16,y
jsr getBits
cpy #17
tay
2003-03-12 00:11:24 +00:00
txa #0
2002-08-06 15:03:06 +00:00
bcs getNextLength_2
getNextLength_1
lda getNextLength_last
getNextLength_2
sta getNextLength_last
ldx getNextLength_index
2000-08-14 15:26:04 +00:00
rts
2002-08-06 15:03:06 +00:00
* fetchPrimaryCode
; Read a code basing on the primary tree.
2002-08-06 15:03:06 +00:00
fetchPrimaryCode
ldx #PRIMARY_TREE
2000-08-14 15:26:04 +00:00
* fetchCode
; Read a code from input basing on the tree specified in X.
; Return low byte of this code in A.
; For the literal/length tree, the C flag is set if the code is non-literal.
2000-08-14 15:26:04 +00:00
fetchCode
lda #0
2002-08-06 15:03:06 +00:00
fetchCode_1
jsr getBit
2000-08-14 15:26:04 +00:00
rol @
inx
2002-08-06 15:03:06 +00:00
sub bitsCount-1,x
bcs fetchCode_1
adc bitsCount-1,x -
cmp literalCount-1,x
sta fetchCode_ptr
ldy bitsPointer_l-1,x
mva bitsPointer_h-1,x fetchCode_ptr+1
lda (fetchCode_ptr),y
2000-08-14 15:26:04 +00:00
rts
* getValue
; Decode low byte of a value (length or distance), basing on the code in A.
; The result is the base value for this code plus some bits read from input.
2000-08-14 15:26:04 +00:00
getValue
tay
2002-08-06 15:03:06 +00:00
ldx lengthExtraBits-1,y
lda:pha lengthBaseValue_l-1,y
lda:tay lengthBaseValue_h-1,y
2000-08-14 15:26:04 +00:00
pla
* getBits
; Read X-bit number from the input and add it to A.
; Increment Y if overflow.
; If X > 8, read only 8 bits.
2000-08-14 15:26:04 +00:00
; On return X holds number of unread bits: X = (X > 8 ? X - 8 : 0);
getBits
cpx #0
2002-08-06 15:03:06 +00:00
beq getBits_ret
2000-08-14 15:26:04 +00:00
pha
mva #-1 getBits_tmp
2000-08-14 15:26:04 +00:00
pla
2002-08-06 15:03:06 +00:00
getBits_1
jsr getBit
bcc getBits_2
sbc getBits_tmp +
2000-08-14 15:26:04 +00:00
scc:iny
2002-08-06 15:03:06 +00:00
getBits_2
dex
beq getBits_ret
asl getBits_tmp
bmi getBits_1
2002-08-06 15:03:06 +00:00
getBits_ret
rts
2000-08-14 15:26:04 +00:00
* getBit
; Read a single bit from input, return it in the C flag.
2000-08-14 15:26:04 +00:00
getBit
2002-08-06 15:03:06 +00:00
lsr getBit_hold
bne getBit_ret
2000-08-14 15:26:04 +00:00
pha
sty getBit_hold
2000-08-14 15:26:04 +00:00
lda (inputPointer),0
ldy getBit_hold
2000-08-14 15:26:04 +00:00
inw inputPointer
ror @ +
2002-08-06 15:03:06 +00:00
sta getBit_hold
2000-08-14 15:26:04 +00:00
pla
2002-08-06 15:03:06 +00:00
getBit_ret
rts
2000-08-14 15:26:04 +00:00
2002-08-06 15:03:06 +00:00
* Arrays for the temporary codes.
; Order, in which lengths of the temporary codes are stored.
2002-08-06 15:03:06 +00:00
tempCodeLengthOrder
dta b(16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15)
; Base values.
2002-08-06 15:03:06 +00:00
tempBaseValue
dta b(3,3,11)
2000-08-14 15:26:04 +00:00
; Number of extra bits to read.
2002-08-06 15:03:06 +00:00
tempExtraBits
dta b(2,3,7)
2000-08-14 15:26:04 +00:00
2002-08-06 15:03:06 +00:00
* Arrays for the length and distance codes.
; Base values.
2002-08-06 15:03:06 +00:00
lengthBaseValue_l
2000-08-14 15:26:04 +00:00
dta l(3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43)
dta l(51,59,67,83,99,115,131,163,195,227)
:!USE_DEFLATE64 dta l(258)
:USE_DEFLATE64 dta l(3)
2002-08-06 15:03:06 +00:00
distanceBaseValue_l
2000-08-14 15:26:04 +00:00
dta l(1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385)
dta l(513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577)
:USE_DEFLATE64 dta l(32769,49153)
2002-08-06 15:03:06 +00:00
lengthBaseValue_h
2000-08-14 15:26:04 +00:00
dta h(3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43)
dta h(51,59,67,83,99,115,131,163,195,227)
:!USE_DEFLATE64 dta h(258)
:USE_DEFLATE64 dta h(3)
2002-08-06 15:03:06 +00:00
distanceBaseValue_h
2000-08-14 15:26:04 +00:00
dta h(1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385)
dta h(513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577)
:USE_DEFLATE64 dta h(32769,49153)
2002-08-06 15:03:06 +00:00
; Number of extra bits to read.
2000-08-14 15:26:04 +00:00
lengthExtraBits
dta b(0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4)
dta b(4,4,5,5,5,5)
:!USE_DEFLATE64 dta b(0)
:USE_DEFLATE64 dta b(16)
2000-08-14 15:26:04 +00:00
distanceExtraBits
dta b(0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10)
dta b(11,11,12,12,13,13)
:USE_DEFLATE64 dta b(14,14)
2000-08-14 15:26:04 +00:00
; Number of literal codes of each length in the primary tree
; (MAX_BITS bytes, overlap with literalCodeLength).
2002-08-06 15:03:06 +00:00
literalCount
* Data for building the primary tree.
; Lengths of literal codes.
2002-08-06 15:03:06 +00:00
literalCodeLength
org *+256
; Length of the end code.
2002-08-06 15:03:06 +00:00
endCodeLength
org *+1
; Lengths of length codes.
2002-08-06 15:03:06 +00:00
lengthCodeLength
org *+29
* Data for building the distance tree.
; Lengths of distance codes.
2002-08-06 15:03:06 +00:00
distanceCodeLength
org *+30+2*USE_DEFLATE64
; For two unused codes in the fixed trees and an 'end' mark
2002-08-06 15:03:06 +00:00
org *+3
2000-08-14 15:26:04 +00:00
2002-08-06 15:03:06 +00:00
* The Huffman trees.
; Number of codes of each length.
2002-08-06 15:03:06 +00:00
bitsCount
org *+TREES_SIZE
; Pointers to sorted codes of each length.
2002-08-06 15:03:06 +00:00
bitsPointer_l
org *+TREES_SIZE+1
bitsPointer_h
org *+TREES_SIZE
; Sorted codes.
2002-08-06 15:03:06 +00:00
sortedCodes
org *+256+1+29+30+2*USE_DEFLATE64+2
2000-08-14 15:26:04 +00:00
end