This repository has been archived by the owner on Aug 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
witchcraft.asm
335 lines (284 loc) · 9.98 KB
/
witchcraft.asm
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#import "common.inc"
#import "basic.inc"
.const uncompressed_start_addr = demo_entry
.pc = start_addr "autostart"
autostart:
line_10:
// Call into entry subroutine
StartBasicLine(line_20, 10, basic_token_sys)
.text "2061"
EndBasicLine()
line_20:
EndBasicProgram()
.const reloc_addr = $9000
.pc = * "stub entry"
entry:
// Disable interrupts
// Technically we should be enabling them again after the decompression routine,
// but since we know we'll jump back into code that'll disable them anyways, it
// should be fine.
sei
// Bank out BASIC and kernal ROMs
lda #$35
sta $01
// Place @ character at end of screen (we'll use this as a progress indicator)
lda #$00
sta $0400 + 999
// Copy 12kb block starting at the packed data to $9000-$bfff in 48 256-byte chunks
ldy #$00
ldx #$30
reloc_block_loop:
reloc_block_load_instr:
!: lda decomp_start, y
reloc_block_store_instr:
sta reloc_addr, y
iny
bne !-
inc reloc_block_load_instr + 2
inc reloc_block_store_instr + 2
dex
bne reloc_block_loop
jmp decomp_entry
.var uncompressed_data = LoadBinary("build/demo.bin");
.var uncompressed_demo_end = uncompressed_start_addr + uncompressed_data.getSize()
// Available zp addrs:
// $02 (unused)
// $92-97 (only used during either rs232 or datasette io)
// $a3-$b1 (only used during either rs232 or datasette io)
// $f7-$fa (only used during rs232 io)
// $fb-$fe (unused)
// $ff (only used during fp -> string conversion)
.const table_base = $c000
.const table_offset_low = table_base
.const table_offset_high = table_base + $30
.const table_additional_bits = table_base + $60
.const length_table_offset_low = table_offset_low
.const length_table_offset_high = table_offset_high
.const length_table_additional_bits = table_additional_bits
.const distance_table_offset_low = table_offset_low + $10
.const distance_table_offset_high = table_offset_high + $10
.const distance_table_additional_bits = table_additional_bits + $10
.const table_offset_low_temp = $92
.const table_offset_high_temp = $93
.const table_offset_add_low_temp = $94
.const table_offset_add_high_temp = $95
.const bit_buffer = $fb
.const bit_index = $fc
.const backref_length_low = $92
.const backref_length_high = $93
// These addr's have to be consecutive
.const backref_ptr_low = $fd
.const backref_ptr_high = $fe
.const read_bit_temp = $02
.const read_n_bit_value_low = $a3
.const read_n_bit_value_high = $a4
.pc = * "decompressor and packed demo"
decomp_start:
.pseudopc reloc_addr {
decomp_entry:
// Expects y to be 0 on entry
sty bit_buffer
sty bit_index
// Decode table entries
decomp_decode_table:
// Reset current offset add amount
ldx #$00
stx table_offset_add_high_temp
inx
stx table_offset_add_low_temp
// if (i & 0x0f) == 0 then reset current offset to 1
tya
and #$0f
bne !+
sta table_offset_high_temp
txa
sta table_offset_low_temp
// Read additional bit count
!: lda #$04
jsr decomp_read_n_bit_value
// Store additional bit count into table
sta table_additional_bits, y
// Shift current offset add amount (should be 1) by number of additional bits
tax
beq decomp_decode_table_store_and_add
!: asl table_offset_add_low_temp
rol table_offset_add_high_temp
dex
bne !-
// Store current offset into tables, and add shifted amount
decomp_decode_table_store_and_add:
lda table_offset_low_temp
sta table_offset_low, y
clc
adc table_offset_add_low_temp
sta table_offset_low_temp
lda table_offset_high_temp
sta table_offset_high, y
adc table_offset_add_high_temp
sta table_offset_high_temp
iny
cpy #$30
bne decomp_decode_table
// Reset y, as the rest of the decompressor assumes its value is always 0
ldy #$00
decomp_packet:
// Read packet bit
jsr decomp_read_bit
beq decomp_literal
// Backreference
// Read length
// Read length index
jsr decomp_read_unary
// Read additional bits
pha
lda length_table_additional_bits, x
jsr decomp_read_n_bit_value
// Add offset to additional bits, and store in backref_length
pla
tax
lda length_table_offset_low, x
clc
adc read_n_bit_value_low
sta backref_length_low
lda length_table_offset_high, x
adc read_n_bit_value_high
sta backref_length_high
// Read distance
// Read distance index
jsr decomp_read_unary
// if length == 1 add 16 to distance index
cpy backref_length_high
bne !+
ldx backref_length_low
dex
bne !+
clc
adc #$10
// Subtract table offset from current output pointer and store into backref_ptr
!: tax
lda decomp_write_instr + 1
sec
sbc distance_table_offset_low, x
sta backref_ptr_low
lda decomp_write_instr + 2
sbc distance_table_offset_high, x
sta backref_ptr_high
// Read additional bits
lda distance_table_additional_bits, x
jsr decomp_read_n_bit_value
// Subtract additional bits from backref_ptr
lda backref_ptr_low
sec
sbc read_n_bit_value_low
sta backref_ptr_low
lda backref_ptr_high
sbc read_n_bit_value_high
sta backref_ptr_high
// Copy backref_length bytes from backref_ptr to output
ldx backref_length_low
decomp_copy_bytes:
// Copy and output byte
lda (backref_ptr_low), y
jsr decomp_write_byte
// Increment backref_ptr
inc backref_ptr_low
bne !+
inc backref_ptr_high
// Decrement backref_length
// Unfortunately, dex doesn't set carry, so we have to detect it ourselves manually
!: cpx #$00
bne !+
dec backref_length_high
!: dex
// Check backref_length for 0. If we're not 0, copy some more bytes
bne decomp_copy_bytes
cpy backref_length_high
bne decomp_copy_bytes
beq decomp_next // Logically this should be jmp, but beq is equivalent here (due to cpy above) and 1 byte smaller
decomp_literal:
// Literal
// Read byte
tya
ldx #$08
!: asl
sta read_bit_temp
jsr decomp_read_bit
ora read_bit_temp
dex
bne !-
jsr decomp_write_byte
decomp_next:
lda decomp_write_instr + 1
cmp #<uncompressed_demo_end
bne !+
lda decomp_write_instr + 2
cmp #>uncompressed_demo_end
bne !+
jmp demo_entry
!: jmp decomp_packet
// Ensures n and z flags are set along with returning the bit in a
decomp_read_bit:
lda bit_index
and #$07
bne !+
decomp_read_instr:
lda packed_demo_start
sta bit_buffer
inc decomp_read_instr + 1
bne !+
inc decomp_read_instr + 2
!: lda bit_buffer
lsr bit_buffer
inc bit_index
and #$01
rts
// Reads unary value into a and x
decomp_read_unary:
ldx #$00
!: jsr decomp_read_bit
bne !+
inx
bne !- // Logically this should be jmp, but bne is equivalent here (due to inx before) and 1 byte smaller
!: txa
rts
// Expects number of bits to read in a
// Reads into read_n_bit_value_low/high, and leaves read_n_bit_value_low in a
// Does not _expect_ y to be zero since it's also called when decoding the table, but doesn't touch y either
decomp_read_n_bit_value:
ldx #$00
stx read_n_bit_value_low
stx read_n_bit_value_high
tax
beq decomp_read_n_bit_value_done
!: asl read_n_bit_value_low
rol read_n_bit_value_high
jsr decomp_read_bit
ora read_n_bit_value_low
sta read_n_bit_value_low
dex
bne !-
decomp_read_n_bit_value_done:
rts
decomp_write_byte:
decomp_write_instr:
sta uncompressed_start_addr
inc decomp_write_instr + 1
bne !+
inc decomp_write_instr + 2
// Progress indicator
inc $d800 + 999
!: rts
packed_demo_start:
.import binary "build/packed-demo.bin"
packed_demo_end:
}
.const target_size = $3000
.const max_size = $3000
.var total_size = * - start_addr + 2 // + 2 to include the program load address
.if(total_size > max_size) {
.error "Total size (" + total_size + " bytes) is more than the max size (" + max_size + " bytes)!"
}
.print "Total size: " + total_size + " bytes"
.if(total_size > target_size) {
.print "WARNING: Total size greater than target size (" + target_size + " bytes) by " + (total_size - target_size) + " bytes!"
}