1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
; -----------------------------------------------------------------------------
; aplib_6502_b.s - fast aPLib backward decompressor for 6502 - 253 bytes
; written for the ACME assembler
;
; jsr apl_decompress to unpack data backwards.
; create backwards compressed data with apultra -b or oapack -b
;
; in:
; * apl_srcptr (low and high byte) = last byte of compressed data
; * apl_dstptr (low and high byte) = last byte of decompression buffer
;
; out:
; * apl_dstptr (low and high byte) = first byte of decompressed data
;
; Copyright (C) 2020 Emmanuel Marty
; With parts of the code inspired by John Brandwood, Peter Ferrie
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software
; in a product, an acknowledgment in the product documentation would be
; appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
; misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.
; -----------------------------------------------------------------------------
; Zero page locations
apl_gamma2_hi = $F6
apl_bitbuf = $F7
apl_offset = $F8
apl_winptr = $FA
apl_srcptr = $FC
apl_dstptr = $FE
; Read a byte from the source into A. Trashes X
!macro APL_GET_SRC {
lda (apl_srcptr),y
ldx <apl_srcptr+0
bne .src_page_done
dec <apl_srcptr+1
.src_page_done: dec <apl_srcptr+0
}
; Write a byte to the destinatipn
!macro APL_PUT_DST {
sta (apl_dstptr),y
lda <apl_dstptr+0
bne .dst_page_done
dec <apl_dstptr+1
.dst_page_done: dec <apl_dstptr+0
}
; Read one bit from the source into the carry, trash A
!macro APL_GET_BIT {
asl <apl_bitbuf
bne .has_bits
jsr apl_load_bits
.has_bits:
}
; Read one bit from the source into the carry, preserve A
!macro APL_GET_BIT_SAVEA {
asl <apl_bitbuf
bne .has_bits
pha
jsr apl_load_bits
pla
.has_bits:
}
; Decompress aPLib data backwards
apl_decompress: lda #$80 ; initialize empty bit queue
sta <apl_bitbuf ; plus bit to roll into carry
ldy #$00 ; clear Y for indirect addr
.copy_literal: +APL_GET_SRC ; read literal from source
.write_literal: +APL_PUT_DST ; write literal to destination
ldx #$00 ; clear 'follows match' flag
.next_token: +APL_GET_BIT ; read 'literal or match' bit
bcc .copy_literal ; if 0: literal
+APL_GET_BIT ; read '8+n bits or other' bit
bcc .long_match ; if 10x: long 8+n bits match
; 11x: other type of match
+APL_GET_BIT ; read '7+1 match or short literal' bit
bcs .short_match ; if 111: 4 bit offset for 1-byte copy
+APL_GET_SRC ; read low byte of offset + length bit
lsr ; shift offset into place, len bit into carry
beq .done ; check for EOD
sta <apl_offset+0 ; store low byte of offset
sty <apl_offset+1 ; set high byte of offset to 0
tya ; set A to 0
sty <apl_gamma2_hi ; set high byte of len to 0
adc #$02 ; add 2 or 3 depending on len bit in carry
; now, low part of len is in A
; high part of len in apl_gamma2_hi is 0
; offset is written to apl_offset
bne .got_len ; go copy matched bytes
.long_match: jsr .get_gamma2 ; 10: read gamma2 high offset bits in A
sty <apl_gamma2_hi ; zero out high byte of gamma2
cpx #$01 ; set carry if following literal
sbc #$02 ; substract 3 if following literal, 2 otherwise
bcs .no_repmatch
jsr .get_gamma2 ; read repmatch length: low part in A
bcc .got_len ; go copy large match
; (carry is always clear after .get_gamma2)
.short_match: lda #$10 ; clear offset, load end bit into place
.read_short_offs: +APL_GET_BIT_SAVEA ; read one bit of offset into carry
rol ; shift into A, shift end bit as well
bcc .read_short_offs ; loop until end bit is shifted out into carry
beq .write_literal ; zero offset means write a 0
tay
lda (apl_dstptr),y ; load backreferenced byte
ldy #$00 ; clear Y again
beq .write_literal ; go write byte to destination
.get_gamma2: lda #$01 ; 1 so it gets shifted to 2
.gamma2_loop: +APL_GET_BIT_SAVEA ; read data bit
rol ; shift into low byte
rol <apl_gamma2_hi ; shift into high byte
+APL_GET_BIT_SAVEA ; read continuation bit
bcs .gamma2_loop ; loop until a zero continuation bit is read
.done: rts
.no_repmatch: sta <apl_offset+1 ; write high byte of offset
+APL_GET_SRC ; read low byte of offset from source
sta <apl_offset+0 ; store low byte of offset
jsr .get_gamma2 ; read match length: low part in A
ldx <apl_offset+1 ; high offset byte is zero?
beq .offset_1byte ; if so, offset < 256
; offset is >= 256.
cpx #$7d ; offset >= 32000 (7d00) ?
bcs .offset_incby2 ; if so, increase match len by 2
cpx #$05 ; offset >= 1280 (0500) ?
bcs .offset_incby1 ; if so, increase match len by 1
bcc .got_len ; length is fine, go copy
.offset_1byte: ldx <apl_offset+0 ; offset < 128 ?
bmi .got_len ; if so, increase match len by 2
sec ; carry must be set below
.offset_incby2: adc #$01 ; add 1 + set carry (from bcs or sec)
bcs .len_inchi ; go add 256 to len if overflow
; carry clear: fall through for no-op
.offset_incby1: adc #$00 ; add 1 + carry
bcc .got_len
.len_inchi: inc <apl_gamma2_hi ; add 256 to len if low byte overflows
.got_len: tax ; transfer low byte of len into X
beq .add_offset
inc <apl_gamma2_hi
.add_offset: clc ; add dest + match offset
lda <apl_dstptr+0 ; low 8 bits
adc <apl_offset+0
sta <apl_winptr+0 ; store back reference address
lda <apl_dstptr+1 ; high 8 bits
adc <apl_offset+1
sta <apl_winptr+1 ; store high 8 bits of address
.copy_match_loop: lda (apl_winptr),y ; read one byte of backreference
+APL_PUT_DST ; write byte to destination
lda <apl_winptr+0 ; decrement backreference address
bne .backref_page_done
dec <apl_winptr+1
.backref_page_done:
dec <apl_winptr+0
dex ; loop to copy all matched bytes
bne .copy_match_loop
dec <apl_gamma2_hi
bne .copy_match_loop
; X is 0 when exiting the loop above
inx ; set 'follows match' flag
jmp .next_token ; go decode next token
apl_load_bits: lda (apl_srcptr),y ; read 8 bits from source
rol ; shift bit queue, and high bit into carry
sta <apl_bitbuf ; save bit queue
lda <apl_srcptr+0
bne .bits_page_done
dec <apl_srcptr+1
.bits_page_done: dec <apl_srcptr+0
rts
|