1 | /***** |
2 | * SPC7110 emulator - version 0.03 (2008-08-10) |
3 | * Copyright (c) 2008, byuu and neviksti |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * The software is provided "as is" and the author disclaims all warranties |
10 | * with regard to this software including all implied warranties of |
11 | * merchantibility and fitness, in no event shall the author be liable for |
12 | * any special, direct, indirect, or consequential damages or any damages |
13 | * whatsoever resulting from loss of use, data or profits, whether in an |
14 | * action of contract, negligence or other tortious action, arising out of |
15 | * or in connection with the use or performance of this software. |
16 | *****/ |
17 | |
18 | |
19 | #ifdef _SPC7110EMU_CPP_ |
20 | |
21 | uint8 SPC7110Decomp::read() { |
22 | if(decomp_buffer_length == 0) { |
23 | //decompress at least (decomp_buffer_size / 2) bytes to the buffer |
24 | switch(decomp_mode) { |
25 | case 0: mode0(false); break; |
26 | case 1: mode1(false); break; |
27 | case 2: mode2(false); break; |
28 | default: return 0x00; |
29 | } |
30 | } |
31 | |
32 | uint8 data = decomp_buffer[decomp_buffer_rdoffset++]; |
33 | decomp_buffer_rdoffset &= decomp_buffer_size - 1; |
34 | decomp_buffer_length--; |
35 | return data; |
36 | } |
37 | |
38 | void SPC7110Decomp::write(uint8 data) { |
39 | decomp_buffer[decomp_buffer_wroffset++] = data; |
40 | decomp_buffer_wroffset &= decomp_buffer_size - 1; |
41 | decomp_buffer_length++; |
42 | } |
43 | |
44 | uint8 SPC7110Decomp::dataread() { |
45 | unsigned size = memory_cartrom_size() > 0x500000 ? memory_cartrom_size() - 0x200000 : memory_cartrom_size() - 0x100000; |
46 | while(decomp_offset >= size) decomp_offset -= size; |
47 | return memory_cartrom_read(0x100000 + decomp_offset++); |
48 | } |
49 | |
50 | void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) { |
51 | decomp_mode = mode; |
52 | decomp_offset = offset; |
53 | |
54 | decomp_buffer_rdoffset = 0; |
55 | decomp_buffer_wroffset = 0; |
56 | decomp_buffer_length = 0; |
57 | |
58 | //reset context states |
59 | for(unsigned i = 0; i < 32; i++) { |
60 | context[i].index = 0; |
61 | context[i].invert = 0; |
62 | } |
63 | |
64 | switch(decomp_mode) { |
65 | case 0: mode0(true); break; |
66 | case 1: mode1(true); break; |
67 | case 2: mode2(true); break; |
68 | } |
69 | |
70 | //decompress up to requested output data index |
71 | while(index--) read(); |
72 | } |
73 | |
74 | // |
75 | |
76 | void SPC7110Decomp::mode0(bool init) { |
77 | static uint8 val, in, span; |
78 | static int out, inverts, lps, in_count; |
79 | |
80 | if(init == true) { |
81 | out = inverts = lps = 0; |
82 | span = 0xff; |
83 | val = dataread(); |
84 | in = dataread(); |
85 | in_count = 8; |
86 | return; |
87 | } |
88 | |
89 | while(decomp_buffer_length < (decomp_buffer_size >> 1)) { |
90 | for(unsigned bit = 0; bit < 8; bit++) { |
91 | //get context |
92 | uint8 mask = (1 << (bit & 3)) - 1; |
93 | uint8 con = mask + ((inverts & mask) ^ (lps & mask)); |
94 | if(bit > 3) con += 15; |
95 | |
96 | //get prob and mps |
97 | unsigned prob = probability(con); |
98 | unsigned mps = (((out >> 15) & 1) ^ context[con].invert); |
99 | |
100 | //get bit |
101 | unsigned flag_lps; |
102 | if(val <= span - prob) { //mps |
103 | span = span - prob; |
104 | out = (out << 1) + mps; |
105 | flag_lps = 0; |
106 | } else { //lps |
107 | val = val - (span - (prob - 1)); |
108 | span = prob - 1; |
109 | out = (out << 1) + 1 - mps; |
110 | flag_lps = 1; |
111 | } |
112 | |
113 | //renormalize |
114 | unsigned shift = 0; |
115 | while(span < 0x7f) { |
116 | shift++; |
117 | |
118 | span = (span << 1) + 1; |
119 | val = (val << 1) + (in >> 7); |
120 | |
121 | in <<= 1; |
122 | if(--in_count == 0) { |
123 | in = dataread(); |
124 | in_count = 8; |
125 | } |
126 | } |
127 | |
128 | //update processing info |
129 | lps = (lps << 1) + flag_lps; |
130 | inverts = (inverts << 1) + context[con].invert; |
131 | |
132 | //update context state |
133 | if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; |
134 | if(flag_lps) context[con].index = next_lps(con); |
135 | else if(shift) context[con].index = next_mps(con); |
136 | } |
137 | |
138 | //save byte |
139 | write(out); |
140 | } |
141 | } |
142 | |
143 | void SPC7110Decomp::mode1(bool init) { |
144 | static unsigned pixelorder[4], realorder[4]; |
145 | static uint8 in, val, span; |
146 | static int out, inverts, lps, in_count; |
147 | |
148 | if(init == true) { |
149 | for(unsigned i = 0; i < 4; i++) pixelorder[i] = i; |
150 | out = inverts = lps = 0; |
151 | span = 0xff; |
152 | val = dataread(); |
153 | in = dataread(); |
154 | in_count = 8; |
155 | return; |
156 | } |
157 | |
158 | while(decomp_buffer_length < (decomp_buffer_size >> 1)) { |
159 | for(unsigned pixel = 0; pixel < 8; pixel++) { |
160 | //get first symbol context |
161 | unsigned a = ((out >> (1 * 2)) & 3); |
162 | unsigned b = ((out >> (7 * 2)) & 3); |
163 | unsigned c = ((out >> (8 * 2)) & 3); |
164 | unsigned con = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); |
165 | |
166 | //update pixel order |
167 | unsigned m, n; |
168 | for(m = 0; m < 4; m++) if(pixelorder[m] == a) break; |
169 | for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; |
170 | pixelorder[0] = a; |
171 | |
172 | //calculate the real pixel order |
173 | for(m = 0; m < 4; m++) realorder[m] = pixelorder[m]; |
174 | |
175 | //rotate reference pixel c value to top |
176 | for(m = 0; m < 4; m++) if(realorder[m] == c) break; |
177 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
178 | realorder[0] = c; |
179 | |
180 | //rotate reference pixel b value to top |
181 | for(m = 0; m < 4; m++) if(realorder[m] == b) break; |
182 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
183 | realorder[0] = b; |
184 | |
185 | //rotate reference pixel a value to top |
186 | for(m = 0; m < 4; m++) if(realorder[m] == a) break; |
187 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
188 | realorder[0] = a; |
189 | |
190 | //get 2 symbols |
191 | for(unsigned bit = 0; bit < 2; bit++) { |
192 | //get prob |
193 | unsigned prob = probability(con); |
194 | |
195 | //get symbol |
196 | unsigned flag_lps; |
197 | if(val <= span - prob) { //mps |
198 | span = span - prob; |
199 | flag_lps = 0; |
200 | } else { //lps |
201 | val = val - (span - (prob - 1)); |
202 | span = prob - 1; |
203 | flag_lps = 1; |
204 | } |
205 | |
206 | //renormalize |
207 | unsigned shift = 0; |
208 | while(span < 0x7f) { |
209 | shift++; |
210 | |
211 | span = (span << 1) + 1; |
212 | val = (val << 1) + (in >> 7); |
213 | |
214 | in <<= 1; |
215 | if(--in_count == 0) { |
216 | in = dataread(); |
217 | in_count = 8; |
218 | } |
219 | } |
220 | |
221 | //update processing info |
222 | lps = (lps << 1) + flag_lps; |
223 | inverts = (inverts << 1) + context[con].invert; |
224 | |
225 | //update context state |
226 | if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; |
227 | if(flag_lps) context[con].index = next_lps(con); |
228 | else if(shift) context[con].index = next_mps(con); |
229 | |
230 | //get next context |
231 | con = 5 + (con << 1) + ((lps ^ inverts) & 1); |
232 | } |
233 | |
234 | //get pixel |
235 | b = realorder[(lps ^ inverts) & 3]; |
236 | out = (out << 2) + b; |
237 | } |
238 | |
239 | //turn pixel data into bitplanes |
240 | unsigned data = morton_2x8(out); |
241 | write(data >> 8); |
242 | write(data >> 0); |
243 | } |
244 | } |
245 | |
246 | void SPC7110Decomp::mode2(bool init) { |
247 | static unsigned pixelorder[16], realorder[16]; |
248 | static uint8 bitplanebuffer[16], buffer_index; |
249 | static uint8 in, val, span; |
250 | static int out0, out1, inverts, lps, in_count; |
251 | |
252 | if(init == true) { |
253 | for(unsigned i = 0; i < 16; i++) pixelorder[i] = i; |
254 | buffer_index = 0; |
255 | out0 = out1 = inverts = lps = 0; |
256 | span = 0xff; |
257 | val = dataread(); |
258 | in = dataread(); |
259 | in_count = 8; |
260 | return; |
261 | } |
262 | |
263 | while(decomp_buffer_length < (decomp_buffer_size >> 1)) { |
264 | for(unsigned pixel = 0; pixel < 8; pixel++) { |
265 | //get first symbol context |
266 | unsigned a = ((out0 >> (0 * 4)) & 15); |
267 | unsigned b = ((out0 >> (7 * 4)) & 15); |
268 | unsigned c = ((out1 >> (0 * 4)) & 15); |
269 | unsigned con = 0; |
270 | unsigned refcon = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); |
271 | |
272 | //update pixel order |
273 | unsigned m, n; |
274 | for(m = 0; m < 16; m++) if(pixelorder[m] == a) break; |
275 | for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; |
276 | pixelorder[0] = a; |
277 | |
278 | //calculate the real pixel order |
279 | for(m = 0; m < 16; m++) realorder[m] = pixelorder[m]; |
280 | |
281 | //rotate reference pixel c value to top |
282 | for(m = 0; m < 16; m++) if(realorder[m] == c) break; |
283 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
284 | realorder[0] = c; |
285 | |
286 | //rotate reference pixel b value to top |
287 | for(m = 0; m < 16; m++) if(realorder[m] == b) break; |
288 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
289 | realorder[0] = b; |
290 | |
291 | //rotate reference pixel a value to top |
292 | for(m = 0; m < 16; m++) if(realorder[m] == a) break; |
293 | for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; |
294 | realorder[0] = a; |
295 | |
296 | //get 4 symbols |
297 | for(unsigned bit = 0; bit < 4; bit++) { |
298 | //get prob |
299 | unsigned prob = probability(con); |
300 | |
301 | //get symbol |
302 | unsigned flag_lps; |
303 | if(val <= span - prob) { //mps |
304 | span = span - prob; |
305 | flag_lps = 0; |
306 | } else { //lps |
307 | val = val - (span - (prob - 1)); |
308 | span = prob - 1; |
309 | flag_lps = 1; |
310 | } |
311 | |
312 | //renormalize |
313 | unsigned shift = 0; |
314 | while(span < 0x7f) { |
315 | shift++; |
316 | |
317 | span = (span << 1) + 1; |
318 | val = (val << 1) + (in >> 7); |
319 | |
320 | in <<= 1; |
321 | if(--in_count == 0) { |
322 | in = dataread(); |
323 | in_count = 8; |
324 | } |
325 | } |
326 | |
327 | //update processing info |
328 | lps = (lps << 1) + flag_lps; |
329 | unsigned invertbit = context[con].invert; |
330 | inverts = (inverts << 1) + invertbit; |
331 | |
332 | //update context state |
333 | if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; |
334 | if(flag_lps) context[con].index = next_lps(con); |
335 | else if(shift) context[con].index = next_mps(con); |
336 | |
337 | //get next context |
338 | con = mode2_context_table[con][flag_lps ^ invertbit] + (con == 1 ? refcon : 0); |
339 | } |
340 | |
341 | //get pixel |
342 | b = realorder[(lps ^ inverts) & 0x0f]; |
343 | out1 = (out1 << 4) + ((out0 >> 28) & 0x0f); |
344 | out0 = (out0 << 4) + b; |
345 | } |
346 | |
347 | //convert pixel data into bitplanes |
348 | unsigned data = morton_4x8(out0); |
349 | write(data >> 24); |
350 | write(data >> 16); |
351 | bitplanebuffer[buffer_index++] = data >> 8; |
352 | bitplanebuffer[buffer_index++] = data >> 0; |
353 | |
354 | if(buffer_index == 16) { |
355 | for(unsigned i = 0; i < 16; i++) write(bitplanebuffer[i]); |
356 | buffer_index = 0; |
357 | } |
358 | } |
359 | } |
360 | |
361 | // |
362 | |
363 | const uint8 SPC7110Decomp::evolution_table[53][4] = { |
364 | //{ prob, nextlps, nextmps, toggle invert }, |
365 | |
366 | { 0x5a, 1, 1, 1 }, |
367 | { 0x25, 6, 2, 0 }, |
368 | { 0x11, 8, 3, 0 }, |
369 | { 0x08, 10, 4, 0 }, |
370 | { 0x03, 12, 5, 0 }, |
371 | { 0x01, 15, 5, 0 }, |
372 | |
373 | { 0x5a, 7, 7, 1 }, |
374 | { 0x3f, 19, 8, 0 }, |
375 | { 0x2c, 21, 9, 0 }, |
376 | { 0x20, 22, 10, 0 }, |
377 | { 0x17, 23, 11, 0 }, |
378 | { 0x11, 25, 12, 0 }, |
379 | { 0x0c, 26, 13, 0 }, |
380 | { 0x09, 28, 14, 0 }, |
381 | { 0x07, 29, 15, 0 }, |
382 | { 0x05, 31, 16, 0 }, |
383 | { 0x04, 32, 17, 0 }, |
384 | { 0x03, 34, 18, 0 }, |
385 | { 0x02, 35, 5, 0 }, |
386 | |
387 | { 0x5a, 20, 20, 1 }, |
388 | { 0x48, 39, 21, 0 }, |
389 | { 0x3a, 40, 22, 0 }, |
390 | { 0x2e, 42, 23, 0 }, |
391 | { 0x26, 44, 24, 0 }, |
392 | { 0x1f, 45, 25, 0 }, |
393 | { 0x19, 46, 26, 0 }, |
394 | { 0x15, 25, 27, 0 }, |
395 | { 0x11, 26, 28, 0 }, |
396 | { 0x0e, 26, 29, 0 }, |
397 | { 0x0b, 27, 30, 0 }, |
398 | { 0x09, 28, 31, 0 }, |
399 | { 0x08, 29, 32, 0 }, |
400 | { 0x07, 30, 33, 0 }, |
401 | { 0x05, 31, 34, 0 }, |
402 | { 0x04, 33, 35, 0 }, |
403 | { 0x04, 33, 36, 0 }, |
404 | { 0x03, 34, 37, 0 }, |
405 | { 0x02, 35, 38, 0 }, |
406 | { 0x02, 36, 5, 0 }, |
407 | |
408 | { 0x58, 39, 40, 1 }, |
409 | { 0x4d, 47, 41, 0 }, |
410 | { 0x43, 48, 42, 0 }, |
411 | { 0x3b, 49, 43, 0 }, |
412 | { 0x34, 50, 44, 0 }, |
413 | { 0x2e, 51, 45, 0 }, |
414 | { 0x29, 44, 46, 0 }, |
415 | { 0x25, 45, 24, 0 }, |
416 | |
417 | { 0x56, 47, 48, 1 }, |
418 | { 0x4f, 47, 49, 0 }, |
419 | { 0x47, 48, 50, 0 }, |
420 | { 0x41, 49, 51, 0 }, |
421 | { 0x3c, 50, 52, 0 }, |
422 | { 0x37, 51, 43, 0 }, |
423 | }; |
424 | |
425 | const uint8 SPC7110Decomp::mode2_context_table[32][2] = { |
426 | //{ next 0, next 1 }, |
427 | |
428 | { 1, 2 }, |
429 | |
430 | { 3, 8 }, |
431 | { 13, 14 }, |
432 | |
433 | { 15, 16 }, |
434 | { 17, 18 }, |
435 | { 19, 20 }, |
436 | { 21, 22 }, |
437 | { 23, 24 }, |
438 | { 25, 26 }, |
439 | { 25, 26 }, |
440 | { 25, 26 }, |
441 | { 25, 26 }, |
442 | { 25, 26 }, |
443 | { 27, 28 }, |
444 | { 29, 30 }, |
445 | |
446 | { 31, 31 }, |
447 | { 31, 31 }, |
448 | { 31, 31 }, |
449 | { 31, 31 }, |
450 | { 31, 31 }, |
451 | { 31, 31 }, |
452 | { 31, 31 }, |
453 | { 31, 31 }, |
454 | { 31, 31 }, |
455 | { 31, 31 }, |
456 | { 31, 31 }, |
457 | { 31, 31 }, |
458 | { 31, 31 }, |
459 | { 31, 31 }, |
460 | { 31, 31 }, |
461 | { 31, 31 }, |
462 | |
463 | { 31, 31 }, |
464 | }; |
465 | |
466 | uint8 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; } |
467 | uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; } |
468 | uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; } |
469 | uint8 SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; } |
470 | |
471 | unsigned SPC7110Decomp::morton_2x8(unsigned data) { |
472 | //reverse morton lookup: de-interleave two 8-bit values |
473 | //15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8 |
474 | //14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0 |
475 | return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255]; |
476 | } |
477 | |
478 | unsigned SPC7110Decomp::morton_4x8(unsigned data) { |
479 | //reverse morton lookup: de-interleave four 8-bit values |
480 | //31, 27, 23, 19, 15, 11, 7, 3 -> 31-24 |
481 | //30, 26, 22, 18, 14, 10, 6, 2 -> 23-16 |
482 | //29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8 |
483 | //28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0 |
484 | return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255] |
485 | + morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255]; |
486 | } |
487 | |
488 | // |
489 | |
490 | void SPC7110Decomp::reset() { |
491 | //mode 3 is invalid; this is treated as a special case to always return 0x00 |
492 | //set to mode 3 so that reading decomp port before starting first decomp will return 0x00 |
493 | decomp_mode = 3; |
494 | |
495 | decomp_buffer_rdoffset = 0; |
496 | decomp_buffer_wroffset = 0; |
497 | decomp_buffer_length = 0; |
498 | } |
499 | |
500 | SPC7110Decomp::SPC7110Decomp() { |
501 | decomp_buffer = new uint8[decomp_buffer_size]; |
502 | reset(); |
503 | |
504 | //initialize reverse morton lookup tables |
505 | for(unsigned i = 0; i < 256; i++) { |
506 | #define map(x, y) (((i >> x) & 1) << y) |
507 | //2x8-bit |
508 | morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6) |
509 | + map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4); |
510 | morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2) |
511 | + map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0); |
512 | //4x8-bit |
513 | morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7) |
514 | + map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6); |
515 | morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5) |
516 | + map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4); |
517 | morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3) |
518 | + map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2); |
519 | morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1) |
520 | + map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0); |
521 | #undef map |
522 | } |
523 | } |
524 | |
525 | SPC7110Decomp::~SPC7110Decomp() { |
526 | delete[] decomp_buffer; |
527 | } |
528 | |
529 | #endif |
530 | |