aboutsummaryrefslogtreecommitdiff
path: root/tools/apultra/asm/6502/aplib_6502_b.asm
blob: 7963e0231c6ea555d437346bb8dc58f5f345c875 (plain)
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