diff options
Diffstat (limited to 'apps/codecs/spc')
-rw-r--r-- | apps/codecs/spc/Spc_Cpu.h | 1037 | ||||
-rw-r--r-- | apps/codecs/spc/Spc_Dsp.h | 1107 | ||||
-rw-r--r-- | apps/codecs/spc/spc_profiler.h | 89 |
3 files changed, 2233 insertions, 0 deletions
diff --git a/apps/codecs/spc/Spc_Cpu.h b/apps/codecs/spc/Spc_Cpu.h new file mode 100644 index 0000000000..b931ca2a3b --- /dev/null +++ b/apps/codecs/spc/Spc_Cpu.h | |||
@@ -0,0 +1,1037 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) | ||
10 | * Copyright (C) 2004-2007 Shay Green (blargg) | ||
11 | * Copyright (C) 2002 Brad Martin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | |||
22 | /* The CPU portion (shock!) */ | ||
23 | |||
24 | #define READ( addr ) (SPC_read( this, addr, spc_time_ )) | ||
25 | #define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ )) | ||
26 | |||
27 | #define READ_DP( addr ) READ( (addr) + dp ) | ||
28 | #define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) | ||
29 | |||
30 | #define READ_PROG16( addr ) GET_LE16( RAM + (addr) ) | ||
31 | |||
32 | #define READ_PC( pc ) (*(pc)) | ||
33 | #define READ_PC16( pc ) GET_LE16( pc ) | ||
34 | |||
35 | #define SET_PC( n ) (pc = RAM + (n)) | ||
36 | #define GET_PC() (pc - RAM) | ||
37 | |||
38 | static unsigned char const cycle_table [0x100] = { | ||
39 | 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, /* 0 */ | ||
40 | 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, /* 1 */ | ||
41 | 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, /* 2 */ | ||
42 | 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, /* 3 */ | ||
43 | 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, /* 4 */ | ||
44 | 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, /* 5 */ | ||
45 | 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, /* 6 */ | ||
46 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, /* 7 */ | ||
47 | 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, /* 8 */ | ||
48 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,/* 9 */ | ||
49 | 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, /* A */ | ||
50 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, /* B */ | ||
51 | 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, /* C */ | ||
52 | 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, /* D */ | ||
53 | 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, /* E */ | ||
54 | 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 /* F */ | ||
55 | }; | ||
56 | |||
57 | #define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ ) | ||
58 | |||
59 | static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ ) | ||
60 | ICODE_ATTR; | ||
61 | |||
62 | static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ ) | ||
63 | { | ||
64 | unsigned addr = READ_PC16( pc ); | ||
65 | unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); | ||
66 | return (t << 8) & 0x100; | ||
67 | } | ||
68 | |||
69 | /* status flags */ | ||
70 | enum { st_n = 0x80 }; | ||
71 | enum { st_v = 0x40 }; | ||
72 | enum { st_p = 0x20 }; | ||
73 | enum { st_b = 0x10 }; | ||
74 | enum { st_h = 0x08 }; | ||
75 | enum { st_i = 0x04 }; | ||
76 | enum { st_z = 0x02 }; | ||
77 | enum { st_c = 0x01 }; | ||
78 | |||
79 | #define IS_NEG (nz & 0x880) | ||
80 | |||
81 | #define CALC_STATUS( out )\ | ||
82 | {\ | ||
83 | out = status & ~(st_n | st_z | st_c);\ | ||
84 | out |= (c >> 8) & st_c;\ | ||
85 | out |= (dp >> 3) & st_p;\ | ||
86 | if ( IS_NEG ) out |= st_n;\ | ||
87 | if ( !(nz & 0xFF) ) out |= st_z;\ | ||
88 | } | ||
89 | |||
90 | #define SET_STATUS( in )\ | ||
91 | {\ | ||
92 | status = in & ~(st_n | st_z | st_c | st_p);\ | ||
93 | c = in << 8;\ | ||
94 | nz = ((in << 4) & 0x800) | (~in & st_z);\ | ||
95 | dp = (in << 3) & 0x100;\ | ||
96 | } | ||
97 | |||
98 | |||
99 | /* stack */ | ||
100 | #define PUSH( v ) (*--sp = (uint8_t) (v)) | ||
101 | #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) | ||
102 | #define POP() (*sp++) | ||
103 | #define SET_SP( v ) (sp = RAM + 0x101 + (v)) | ||
104 | #define GET_SP() (sp - 0x101 - RAM) | ||
105 | |||
106 | static long CPU_run( THIS, long start_time ) ICODE_ATTR; | ||
107 | |||
108 | static long CPU_run( THIS, long start_time ) | ||
109 | { | ||
110 | #if 0 | ||
111 | ENTER_TIMER(cpu); | ||
112 | #endif | ||
113 | |||
114 | register long spc_time_ = start_time; | ||
115 | |||
116 | #ifdef CPU_ARM | ||
117 | uint8_t* const ram_ = ram.ram; | ||
118 | #undef RAM | ||
119 | #define RAM ram_ | ||
120 | #endif | ||
121 | |||
122 | int a = this->r.a; | ||
123 | int x = this->r.x; | ||
124 | int y = this->r.y; | ||
125 | |||
126 | uint8_t const* pc; | ||
127 | SET_PC( this->r.pc ); | ||
128 | |||
129 | uint8_t* sp; | ||
130 | SET_SP( this->r.sp ); | ||
131 | |||
132 | int status; | ||
133 | int c; | ||
134 | int nz; | ||
135 | unsigned dp; | ||
136 | { | ||
137 | int temp = this->r.status; | ||
138 | SET_STATUS( temp ); | ||
139 | } | ||
140 | |||
141 | goto loop; | ||
142 | |||
143 | /* main loop */ | ||
144 | cbranch_taken_loop: | ||
145 | pc += *(int8_t const*) pc; | ||
146 | spc_time_ += 2; | ||
147 | inc_pc_loop: | ||
148 | pc++; | ||
149 | loop: | ||
150 | check( (unsigned) GET_PC() < 0x10000 ); | ||
151 | check( (unsigned) GET_SP() < 0x100 ); | ||
152 | check( (unsigned) a < 0x100 ); | ||
153 | check( (unsigned) x < 0x100 ); | ||
154 | check( (unsigned) y < 0x100 ); | ||
155 | |||
156 | unsigned opcode = *pc; | ||
157 | int cycles = this->cycle_table [opcode]; | ||
158 | unsigned data = *++pc; | ||
159 | if ( (spc_time_ += cycles) > 0 ) | ||
160 | goto out_of_time; | ||
161 | switch ( opcode ) | ||
162 | { | ||
163 | |||
164 | /* Common instructions */ | ||
165 | |||
166 | #define BRANCH( cond )\ | ||
167 | {\ | ||
168 | pc++;\ | ||
169 | int offset = (int8_t) data;\ | ||
170 | if ( cond ) {\ | ||
171 | pc += offset;\ | ||
172 | spc_time_ += 2;\ | ||
173 | }\ | ||
174 | goto loop;\ | ||
175 | } | ||
176 | |||
177 | case 0xF0: /* BEQ (most common) */ | ||
178 | BRANCH( !(uint8_t) nz ) | ||
179 | |||
180 | case 0xD0: /* BNE */ | ||
181 | BRANCH( (uint8_t) nz ) | ||
182 | |||
183 | case 0x3F: /* CALL */ | ||
184 | PUSH16( GET_PC() + 2 ); | ||
185 | SET_PC( READ_PC16( pc ) ); | ||
186 | goto loop; | ||
187 | |||
188 | case 0x6F: /* RET */ | ||
189 | SET_PC( POP() ); | ||
190 | pc += POP() * 0x100; | ||
191 | goto loop; | ||
192 | |||
193 | #define CASE( n ) case n: | ||
194 | |||
195 | /* Define common address modes based on opcode for immediate mode. Execution | ||
196 | ends with data set to the address of the operand. */ | ||
197 | #define ADDR_MODES( op )\ | ||
198 | CASE( op - 0x02 ) /* (X) */\ | ||
199 | data = x + dp;\ | ||
200 | pc--;\ | ||
201 | goto end_##op;\ | ||
202 | CASE( op + 0x0F ) /* (dp)+Y */\ | ||
203 | data = READ_PROG16( data + dp ) + y;\ | ||
204 | goto end_##op;\ | ||
205 | CASE( op - 0x01 ) /* (dp+X) */\ | ||
206 | data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ | ||
207 | goto end_##op;\ | ||
208 | CASE( op + 0x0E ) /* abs+Y */\ | ||
209 | data += y;\ | ||
210 | goto abs_##op;\ | ||
211 | CASE( op + 0x0D ) /* abs+X */\ | ||
212 | data += x;\ | ||
213 | CASE( op - 0x03 ) /* abs */\ | ||
214 | abs_##op:\ | ||
215 | data += 0x100 * READ_PC( ++pc );\ | ||
216 | goto end_##op;\ | ||
217 | CASE( op + 0x0C ) /* dp+X */\ | ||
218 | data = (uint8_t) (data + x);\ | ||
219 | CASE( op - 0x04 ) /* dp */\ | ||
220 | data += dp;\ | ||
221 | end_##op: | ||
222 | |||
223 | /* 1. 8-bit Data Transmission Commands. Group I */ | ||
224 | |||
225 | ADDR_MODES( 0xE8 ) /* MOV A,addr */ | ||
226 | /*case 0xE4:*/ /* MOV a,dp (most common) */ | ||
227 | mov_a_addr: | ||
228 | a = nz = READ( data ); | ||
229 | goto inc_pc_loop; | ||
230 | case 0xBF: /* MOV A,(X)+ */ | ||
231 | data = x + dp; | ||
232 | x = (uint8_t) (x + 1); | ||
233 | pc--; | ||
234 | goto mov_a_addr; | ||
235 | |||
236 | case 0xE8: /* MOV A,imm */ | ||
237 | a = data; | ||
238 | nz = data; | ||
239 | goto inc_pc_loop; | ||
240 | |||
241 | case 0xF9: /* MOV X,dp+Y */ | ||
242 | data = (uint8_t) (data + y); | ||
243 | case 0xF8: /* MOV X,dp */ | ||
244 | data += dp; | ||
245 | goto mov_x_addr; | ||
246 | case 0xE9: /* MOV X,abs */ | ||
247 | data = READ_PC16( pc ); | ||
248 | pc++; | ||
249 | mov_x_addr: | ||
250 | data = READ( data ); | ||
251 | case 0xCD: /* MOV X,imm */ | ||
252 | x = data; | ||
253 | nz = data; | ||
254 | goto inc_pc_loop; | ||
255 | |||
256 | case 0xFB: /* MOV Y,dp+X */ | ||
257 | data = (uint8_t) (data + x); | ||
258 | case 0xEB: /* MOV Y,dp */ | ||
259 | data += dp; | ||
260 | goto mov_y_addr; | ||
261 | case 0xEC: /* MOV Y,abs */ | ||
262 | data = READ_PC16( pc ); | ||
263 | pc++; | ||
264 | mov_y_addr: | ||
265 | data = READ( data ); | ||
266 | case 0x8D: /* MOV Y,imm */ | ||
267 | y = data; | ||
268 | nz = data; | ||
269 | goto inc_pc_loop; | ||
270 | |||
271 | /* 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 */ | ||
272 | |||
273 | ADDR_MODES( 0xC8 ) /* MOV addr,A */ | ||
274 | WRITE( data, a ); | ||
275 | goto inc_pc_loop; | ||
276 | |||
277 | { | ||
278 | int temp; | ||
279 | case 0xCC: /* MOV abs,Y */ | ||
280 | temp = y; | ||
281 | goto mov_abs_temp; | ||
282 | case 0xC9: /* MOV abs,X */ | ||
283 | temp = x; | ||
284 | mov_abs_temp: | ||
285 | WRITE( READ_PC16( pc ), temp ); | ||
286 | pc += 2; | ||
287 | goto loop; | ||
288 | } | ||
289 | |||
290 | case 0xD9: /* MOV dp+Y,X */ | ||
291 | data = (uint8_t) (data + y); | ||
292 | case 0xD8: /* MOV dp,X */ | ||
293 | WRITE( data + dp, x ); | ||
294 | goto inc_pc_loop; | ||
295 | |||
296 | case 0xDB: /* MOV dp+X,Y */ | ||
297 | data = (uint8_t) (data + x); | ||
298 | case 0xCB: /* MOV dp,Y */ | ||
299 | WRITE( data + dp, y ); | ||
300 | goto inc_pc_loop; | ||
301 | |||
302 | case 0xFA: /* MOV dp,dp */ | ||
303 | data = READ( data + dp ); | ||
304 | case 0x8F: /* MOV dp,#imm */ | ||
305 | WRITE_DP( READ_PC( ++pc ), data ); | ||
306 | goto inc_pc_loop; | ||
307 | |||
308 | /* 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. */ | ||
309 | |||
310 | case 0x7D: /* MOV A,X */ | ||
311 | a = x; | ||
312 | nz = x; | ||
313 | goto loop; | ||
314 | |||
315 | case 0xDD: /* MOV A,Y */ | ||
316 | a = y; | ||
317 | nz = y; | ||
318 | goto loop; | ||
319 | |||
320 | case 0x5D: /* MOV X,A */ | ||
321 | x = a; | ||
322 | nz = a; | ||
323 | goto loop; | ||
324 | |||
325 | case 0xFD: /* MOV Y,A */ | ||
326 | y = a; | ||
327 | nz = a; | ||
328 | goto loop; | ||
329 | |||
330 | case 0x9D: /* MOV X,SP */ | ||
331 | x = nz = GET_SP(); | ||
332 | goto loop; | ||
333 | |||
334 | case 0xBD: /* MOV SP,X */ | ||
335 | SET_SP( x ); | ||
336 | goto loop; | ||
337 | |||
338 | /*case 0xC6:*/ /* MOV (X),A (handled by MOV addr,A in group 2) */ | ||
339 | |||
340 | case 0xAF: /* MOV (X)+,A */ | ||
341 | WRITE_DP( x, a ); | ||
342 | x++; | ||
343 | goto loop; | ||
344 | |||
345 | /* 5. 8-BIT LOGIC OPERATION COMMANDS */ | ||
346 | |||
347 | #define LOGICAL_OP( op, func )\ | ||
348 | ADDR_MODES( op ) /* addr */\ | ||
349 | data = READ( data );\ | ||
350 | case op: /* imm */\ | ||
351 | nz = a func##= data;\ | ||
352 | goto inc_pc_loop;\ | ||
353 | { unsigned addr;\ | ||
354 | case op + 0x11: /* X,Y */\ | ||
355 | data = READ_DP( y );\ | ||
356 | addr = x + dp;\ | ||
357 | pc--;\ | ||
358 | goto addr_##op;\ | ||
359 | case op + 0x01: /* dp,dp */\ | ||
360 | data = READ_DP( data );\ | ||
361 | case op + 0x10: /*dp,imm*/\ | ||
362 | addr = READ_PC( ++pc ) + dp;\ | ||
363 | addr_##op:\ | ||
364 | nz = data func READ( addr );\ | ||
365 | WRITE( addr, nz );\ | ||
366 | goto inc_pc_loop;\ | ||
367 | } | ||
368 | |||
369 | LOGICAL_OP( 0x28, & ); /* AND */ | ||
370 | |||
371 | LOGICAL_OP( 0x08, | ); /* OR */ | ||
372 | |||
373 | LOGICAL_OP( 0x48, ^ ); /* EOR */ | ||
374 | |||
375 | /* 4. 8-BIT ARITHMETIC OPERATION COMMANDS */ | ||
376 | |||
377 | ADDR_MODES( 0x68 ) /* CMP addr */ | ||
378 | data = READ( data ); | ||
379 | case 0x68: /* CMP imm */ | ||
380 | nz = a - data; | ||
381 | c = ~nz; | ||
382 | nz &= 0xFF; | ||
383 | goto inc_pc_loop; | ||
384 | |||
385 | case 0x79: /* CMP (X),(Y) */ | ||
386 | data = READ_DP( x ); | ||
387 | nz = data - READ_DP( y ); | ||
388 | c = ~nz; | ||
389 | nz &= 0xFF; | ||
390 | goto loop; | ||
391 | |||
392 | case 0x69: /* CMP (dp),(dp) */ | ||
393 | data = READ_DP( data ); | ||
394 | case 0x78: /* CMP dp,imm */ | ||
395 | nz = READ_DP( READ_PC( ++pc ) ) - data; | ||
396 | c = ~nz; | ||
397 | nz &= 0xFF; | ||
398 | goto inc_pc_loop; | ||
399 | |||
400 | case 0x3E: /* CMP X,dp */ | ||
401 | data += dp; | ||
402 | goto cmp_x_addr; | ||
403 | case 0x1E: /* CMP X,abs */ | ||
404 | data = READ_PC16( pc ); | ||
405 | pc++; | ||
406 | cmp_x_addr: | ||
407 | data = READ( data ); | ||
408 | case 0xC8: /* CMP X,imm */ | ||
409 | nz = x - data; | ||
410 | c = ~nz; | ||
411 | nz &= 0xFF; | ||
412 | goto inc_pc_loop; | ||
413 | |||
414 | case 0x7E: /* CMP Y,dp */ | ||
415 | data += dp; | ||
416 | goto cmp_y_addr; | ||
417 | case 0x5E: /* CMP Y,abs */ | ||
418 | data = READ_PC16( pc ); | ||
419 | pc++; | ||
420 | cmp_y_addr: | ||
421 | data = READ( data ); | ||
422 | case 0xAD: /* CMP Y,imm */ | ||
423 | nz = y - data; | ||
424 | c = ~nz; | ||
425 | nz &= 0xFF; | ||
426 | goto inc_pc_loop; | ||
427 | |||
428 | { | ||
429 | int addr; | ||
430 | case 0xB9: /* SBC (x),(y) */ | ||
431 | case 0x99: /* ADC (x),(y) */ | ||
432 | pc--; /* compensate for inc later */ | ||
433 | data = READ_DP( x ); | ||
434 | addr = y + dp; | ||
435 | goto adc_addr; | ||
436 | case 0xA9: /* SBC dp,dp */ | ||
437 | case 0x89: /* ADC dp,dp */ | ||
438 | data = READ_DP( data ); | ||
439 | case 0xB8: /* SBC dp,imm */ | ||
440 | case 0x98: /* ADC dp,imm */ | ||
441 | addr = READ_PC( ++pc ) + dp; | ||
442 | adc_addr: | ||
443 | nz = READ( addr ); | ||
444 | goto adc_data; | ||
445 | |||
446 | /* catch ADC and SBC together, then decode later based on operand */ | ||
447 | #undef CASE | ||
448 | #define CASE( n ) case n: case (n) + 0x20: | ||
449 | ADDR_MODES( 0x88 ) /* ADC/SBC addr */ | ||
450 | data = READ( data ); | ||
451 | case 0xA8: /* SBC imm */ | ||
452 | case 0x88: /* ADC imm */ | ||
453 | addr = -1; /* A */ | ||
454 | nz = a; | ||
455 | adc_data: { | ||
456 | if ( opcode & 0x20 ) | ||
457 | data ^= 0xFF; /* SBC */ | ||
458 | int carry = (c >> 8) & 1; | ||
459 | int ov = (nz ^ 0x80) + carry + (int8_t) data; /* sign-extend */ | ||
460 | int hc = (nz & 15) + carry; | ||
461 | c = nz += data + carry; | ||
462 | hc = (nz & 15) - hc; | ||
463 | status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | | ||
464 | ((hc >> 1) & st_h); | ||
465 | if ( addr < 0 ) { | ||
466 | a = (uint8_t) nz; | ||
467 | goto inc_pc_loop; | ||
468 | } | ||
469 | WRITE( addr, (uint8_t) nz ); | ||
470 | goto inc_pc_loop; | ||
471 | } | ||
472 | |||
473 | } | ||
474 | |||
475 | /* 6. ADDITION & SUBTRACTION COMMANDS */ | ||
476 | |||
477 | #define INC_DEC_REG( reg, n )\ | ||
478 | nz = reg + n;\ | ||
479 | reg = (uint8_t) nz;\ | ||
480 | goto loop; | ||
481 | |||
482 | case 0xBC: INC_DEC_REG( a, 1 ) /* INC A */ | ||
483 | case 0x3D: INC_DEC_REG( x, 1 ) /* INC X */ | ||
484 | case 0xFC: INC_DEC_REG( y, 1 ) /* INC Y */ | ||
485 | |||
486 | case 0x9C: INC_DEC_REG( a, -1 ) /* DEC A */ | ||
487 | case 0x1D: INC_DEC_REG( x, -1 ) /* DEC X */ | ||
488 | case 0xDC: INC_DEC_REG( y, -1 ) /* DEC Y */ | ||
489 | |||
490 | case 0x9B: /* DEC dp+X */ | ||
491 | case 0xBB: /* INC dp+X */ | ||
492 | data = (uint8_t) (data + x); | ||
493 | case 0x8B: /* DEC dp */ | ||
494 | case 0xAB: /* INC dp */ | ||
495 | data += dp; | ||
496 | goto inc_abs; | ||
497 | case 0x8C: /* DEC abs */ | ||
498 | case 0xAC: /* INC abs */ | ||
499 | data = READ_PC16( pc ); | ||
500 | pc++; | ||
501 | inc_abs: | ||
502 | nz = ((opcode >> 4) & 2) - 1; | ||
503 | nz += READ( data ); | ||
504 | WRITE( data, (uint8_t) nz ); | ||
505 | goto inc_pc_loop; | ||
506 | |||
507 | /* 7. SHIFT, ROTATION COMMANDS */ | ||
508 | |||
509 | case 0x5C: /* LSR A */ | ||
510 | c = 0; | ||
511 | case 0x7C:{/* ROR A */ | ||
512 | nz = ((c >> 1) & 0x80) | (a >> 1); | ||
513 | c = a << 8; | ||
514 | a = nz; | ||
515 | goto loop; | ||
516 | } | ||
517 | |||
518 | case 0x1C: /* ASL A */ | ||
519 | c = 0; | ||
520 | case 0x3C:{/* ROL A */ | ||
521 | int temp = (c >> 8) & 1; | ||
522 | c = a << 1; | ||
523 | nz = c | temp; | ||
524 | a = (uint8_t) nz; | ||
525 | goto loop; | ||
526 | } | ||
527 | |||
528 | case 0x0B: /* ASL dp */ | ||
529 | c = 0; | ||
530 | data += dp; | ||
531 | goto rol_mem; | ||
532 | case 0x1B: /* ASL dp+X */ | ||
533 | c = 0; | ||
534 | case 0x3B: /* ROL dp+X */ | ||
535 | data = (uint8_t) (data + x); | ||
536 | case 0x2B: /* ROL dp */ | ||
537 | data += dp; | ||
538 | goto rol_mem; | ||
539 | case 0x0C: /* ASL abs */ | ||
540 | c = 0; | ||
541 | case 0x2C: /* ROL abs */ | ||
542 | data = READ_PC16( pc ); | ||
543 | pc++; | ||
544 | rol_mem: | ||
545 | nz = (c >> 8) & 1; | ||
546 | nz |= (c = READ( data ) << 1); | ||
547 | WRITE( data, (uint8_t) nz ); | ||
548 | goto inc_pc_loop; | ||
549 | |||
550 | case 0x4B: /* LSR dp */ | ||
551 | c = 0; | ||
552 | data += dp; | ||
553 | goto ror_mem; | ||
554 | case 0x5B: /* LSR dp+X */ | ||
555 | c = 0; | ||
556 | case 0x7B: /* ROR dp+X */ | ||
557 | data = (uint8_t) (data + x); | ||
558 | case 0x6B: /* ROR dp */ | ||
559 | data += dp; | ||
560 | goto ror_mem; | ||
561 | case 0x4C: /* LSR abs */ | ||
562 | c = 0; | ||
563 | case 0x6C: /* ROR abs */ | ||
564 | data = READ_PC16( pc ); | ||
565 | pc++; | ||
566 | ror_mem: { | ||
567 | int temp = READ( data ); | ||
568 | nz = ((c >> 1) & 0x80) | (temp >> 1); | ||
569 | c = temp << 8; | ||
570 | WRITE( data, nz ); | ||
571 | goto inc_pc_loop; | ||
572 | } | ||
573 | |||
574 | case 0x9F: /* XCN */ | ||
575 | nz = a = (a >> 4) | (uint8_t) (a << 4); | ||
576 | goto loop; | ||
577 | |||
578 | /* 8. 16-BIT TRANSMISION COMMANDS */ | ||
579 | |||
580 | case 0xBA: /* MOVW YA,dp */ | ||
581 | a = READ_DP( data ); | ||
582 | nz = (a & 0x7F) | (a >> 1); | ||
583 | y = READ_DP( (uint8_t) (data + 1) ); | ||
584 | nz |= y; | ||
585 | goto inc_pc_loop; | ||
586 | |||
587 | case 0xDA: /* MOVW dp,YA */ | ||
588 | WRITE_DP( data, a ); | ||
589 | WRITE_DP( (uint8_t) (data + 1), y ); | ||
590 | goto inc_pc_loop; | ||
591 | |||
592 | /* 9. 16-BIT OPERATION COMMANDS */ | ||
593 | |||
594 | case 0x3A: /* INCW dp */ | ||
595 | case 0x1A:{/* DECW dp */ | ||
596 | data += dp; | ||
597 | |||
598 | /* low byte */ | ||
599 | int temp = READ( data ); | ||
600 | temp += ((opcode >> 4) & 2) - 1; /* +1 for INCW, -1 for DECW */ | ||
601 | nz = ((temp >> 1) | temp) & 0x7F; | ||
602 | WRITE( data, (uint8_t) temp ); | ||
603 | |||
604 | /* high byte */ | ||
605 | data = ((uint8_t) (data + 1)) + dp; | ||
606 | temp >>= 8; | ||
607 | temp = (uint8_t) (temp + READ( data )); | ||
608 | nz |= temp; | ||
609 | WRITE( data, temp ); | ||
610 | |||
611 | goto inc_pc_loop; | ||
612 | } | ||
613 | |||
614 | case 0x9A: /* SUBW YA,dp */ | ||
615 | case 0x7A: /* ADDW YA,dp */ | ||
616 | { | ||
617 | /* read 16-bit addend */ | ||
618 | int temp = READ_DP( data ); | ||
619 | int sign = READ_DP( (uint8_t) (data + 1) ); | ||
620 | temp += 0x100 * sign; | ||
621 | status &= ~(st_v | st_h); | ||
622 | |||
623 | /* to do: fix half-carry for SUBW (it's probably wrong) */ | ||
624 | |||
625 | /* for SUBW, negate and truncate to 16 bits */ | ||
626 | if ( opcode & 0x80 ) { | ||
627 | temp = (temp ^ 0xFFFF) + 1; | ||
628 | sign = temp >> 8; | ||
629 | } | ||
630 | |||
631 | /* add low byte (A) */ | ||
632 | temp += a; | ||
633 | a = (uint8_t) temp; | ||
634 | nz = (temp | (temp >> 1)) & 0x7F; | ||
635 | |||
636 | /* add high byte (Y) */ | ||
637 | temp >>= 8; | ||
638 | c = y + temp; | ||
639 | nz = (nz | c) & 0xFF; | ||
640 | |||
641 | /* half-carry (temporary avoids CodeWarrior optimizer bug) */ | ||
642 | unsigned hc = (c & 15) - (y & 15); | ||
643 | status |= (hc >> 4) & st_h; | ||
644 | |||
645 | /* overflow if sign of YA changed when previous sign | ||
646 | and addend sign were same */ | ||
647 | status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; | ||
648 | |||
649 | y = (uint8_t) c; | ||
650 | |||
651 | goto inc_pc_loop; | ||
652 | } | ||
653 | |||
654 | case 0x5A: { /* CMPW YA,dp */ | ||
655 | int temp = a - READ_DP( data ); | ||
656 | nz = ((temp >> 1) | temp) & 0x7F; | ||
657 | temp = y + (temp >> 8); | ||
658 | temp -= READ_DP( (uint8_t) (data + 1) ); | ||
659 | nz |= temp; | ||
660 | c = ~temp; | ||
661 | nz &= 0xFF; | ||
662 | goto inc_pc_loop; | ||
663 | } | ||
664 | |||
665 | /* 10. MULTIPLICATION & DIVISON COMMANDS */ | ||
666 | |||
667 | case 0xCF: { /* MUL YA */ | ||
668 | unsigned temp = y * a; | ||
669 | a = (uint8_t) temp; | ||
670 | nz = ((temp >> 1) | temp) & 0x7F; | ||
671 | y = temp >> 8; | ||
672 | nz |= y; | ||
673 | goto loop; | ||
674 | } | ||
675 | |||
676 | case 0x9E: /* DIV YA,X */ | ||
677 | { | ||
678 | /* behavior based on SPC CPU tests */ | ||
679 | |||
680 | status &= ~(st_h | st_v); | ||
681 | |||
682 | if ( (y & 15) >= (x & 15) ) | ||
683 | status |= st_h; | ||
684 | |||
685 | if ( y >= x ) | ||
686 | status |= st_v; | ||
687 | |||
688 | unsigned ya = y * 0x100 + a; | ||
689 | if ( y < x * 2 ) | ||
690 | { | ||
691 | a = ya / x; | ||
692 | y = ya - a * x; | ||
693 | } | ||
694 | else | ||
695 | { | ||
696 | a = 255 - (ya - x * 0x200) / (256 - x); | ||
697 | y = x + (ya - x * 0x200) % (256 - x); | ||
698 | } | ||
699 | |||
700 | nz = (uint8_t) a; | ||
701 | a = (uint8_t) a; | ||
702 | |||
703 | goto loop; | ||
704 | } | ||
705 | |||
706 | /* 11. DECIMAL COMPENSATION COMMANDS */ | ||
707 | |||
708 | /* seem unused */ | ||
709 | /* case 0xDF: */ /* DAA */ | ||
710 | /* case 0xBE: */ /* DAS */ | ||
711 | |||
712 | /* 12. BRANCHING COMMANDS */ | ||
713 | |||
714 | case 0x2F: /* BRA rel */ | ||
715 | pc += (int8_t) data; | ||
716 | goto inc_pc_loop; | ||
717 | |||
718 | case 0x30: /* BMI */ | ||
719 | BRANCH( IS_NEG ) | ||
720 | |||
721 | case 0x10: /* BPL */ | ||
722 | BRANCH( !IS_NEG ) | ||
723 | |||
724 | case 0xB0: /* BCS */ | ||
725 | BRANCH( c & 0x100 ) | ||
726 | |||
727 | case 0x90: /* BCC */ | ||
728 | BRANCH( !(c & 0x100) ) | ||
729 | |||
730 | case 0x70: /* BVS */ | ||
731 | BRANCH( status & st_v ) | ||
732 | |||
733 | case 0x50: /* BVC */ | ||
734 | BRANCH( !(status & st_v) ) | ||
735 | |||
736 | case 0x03: /* BBS dp.bit,rel */ | ||
737 | case 0x23: | ||
738 | case 0x43: | ||
739 | case 0x63: | ||
740 | case 0x83: | ||
741 | case 0xA3: | ||
742 | case 0xC3: | ||
743 | case 0xE3: | ||
744 | pc++; | ||
745 | if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) | ||
746 | goto cbranch_taken_loop; | ||
747 | goto inc_pc_loop; | ||
748 | |||
749 | case 0x13: /* BBC dp.bit,rel */ | ||
750 | case 0x33: | ||
751 | case 0x53: | ||
752 | case 0x73: | ||
753 | case 0x93: | ||
754 | case 0xB3: | ||
755 | case 0xD3: | ||
756 | case 0xF3: | ||
757 | pc++; | ||
758 | if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) | ||
759 | goto cbranch_taken_loop; | ||
760 | goto inc_pc_loop; | ||
761 | |||
762 | case 0xDE: /* CBNE dp+X,rel */ | ||
763 | data = (uint8_t) (data + x); | ||
764 | /* fall through */ | ||
765 | case 0x2E: /* CBNE dp,rel */ | ||
766 | pc++; | ||
767 | if ( READ_DP( data ) != a ) | ||
768 | goto cbranch_taken_loop; | ||
769 | goto inc_pc_loop; | ||
770 | |||
771 | case 0xFE: /* DBNZ Y,rel */ | ||
772 | y = (uint8_t) (y - 1); | ||
773 | BRANCH( y ) | ||
774 | |||
775 | case 0x6E: { /* DBNZ dp,rel */ | ||
776 | pc++; | ||
777 | unsigned temp = READ_DP( data ) - 1; | ||
778 | WRITE_DP( (uint8_t) data, (uint8_t) temp ); | ||
779 | if ( temp ) | ||
780 | goto cbranch_taken_loop; | ||
781 | goto inc_pc_loop; | ||
782 | } | ||
783 | |||
784 | case 0x1F: /* JMP (abs+X) */ | ||
785 | SET_PC( READ_PC16( pc ) + x ); | ||
786 | /* fall through */ | ||
787 | case 0x5F: /* JMP abs */ | ||
788 | SET_PC( READ_PC16( pc ) ); | ||
789 | goto loop; | ||
790 | |||
791 | /* 13. SUB-ROUTINE CALL RETURN COMMANDS */ | ||
792 | |||
793 | case 0x0F:{/* BRK */ | ||
794 | check( 0 ); /* untested */ | ||
795 | PUSH16( GET_PC() + 1 ); | ||
796 | SET_PC( READ_PROG16( 0xFFDE ) ); /* vector address verified */ | ||
797 | int temp; | ||
798 | CALC_STATUS( temp ); | ||
799 | PUSH( temp ); | ||
800 | status = (status | st_b) & ~st_i; | ||
801 | goto loop; | ||
802 | } | ||
803 | |||
804 | case 0x4F: /* PCALL offset */ | ||
805 | PUSH16( GET_PC() + 1 ); | ||
806 | SET_PC( 0xFF00 + data ); | ||
807 | goto loop; | ||
808 | |||
809 | case 0x01: /* TCALL n */ | ||
810 | case 0x11: | ||
811 | case 0x21: | ||
812 | case 0x31: | ||
813 | case 0x41: | ||
814 | case 0x51: | ||
815 | case 0x61: | ||
816 | case 0x71: | ||
817 | case 0x81: | ||
818 | case 0x91: | ||
819 | case 0xA1: | ||
820 | case 0xB1: | ||
821 | case 0xC1: | ||
822 | case 0xD1: | ||
823 | case 0xE1: | ||
824 | case 0xF1: | ||
825 | PUSH16( GET_PC() ); | ||
826 | SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); | ||
827 | goto loop; | ||
828 | |||
829 | /* 14. STACK OPERATION COMMANDS */ | ||
830 | |||
831 | { | ||
832 | int temp; | ||
833 | case 0x7F: /* RET1 */ | ||
834 | temp = POP(); | ||
835 | SET_PC( POP() ); | ||
836 | pc += POP() << 8; | ||
837 | goto set_status; | ||
838 | case 0x8E: /* POP PSW */ | ||
839 | temp = POP(); | ||
840 | set_status: | ||
841 | SET_STATUS( temp ); | ||
842 | goto loop; | ||
843 | } | ||
844 | |||
845 | case 0x0D: { /* PUSH PSW */ | ||
846 | int temp; | ||
847 | CALC_STATUS( temp ); | ||
848 | PUSH( temp ); | ||
849 | goto loop; | ||
850 | } | ||
851 | |||
852 | case 0x2D: /* PUSH A */ | ||
853 | PUSH( a ); | ||
854 | goto loop; | ||
855 | |||
856 | case 0x4D: /* PUSH X */ | ||
857 | PUSH( x ); | ||
858 | goto loop; | ||
859 | |||
860 | case 0x6D: /* PUSH Y */ | ||
861 | PUSH( y ); | ||
862 | goto loop; | ||
863 | |||
864 | case 0xAE: /* POP A */ | ||
865 | a = POP(); | ||
866 | goto loop; | ||
867 | |||
868 | case 0xCE: /* POP X */ | ||
869 | x = POP(); | ||
870 | goto loop; | ||
871 | |||
872 | case 0xEE: /* POP Y */ | ||
873 | y = POP(); | ||
874 | goto loop; | ||
875 | |||
876 | /* 15. BIT OPERATION COMMANDS */ | ||
877 | |||
878 | case 0x02: /* SET1 */ | ||
879 | case 0x22: | ||
880 | case 0x42: | ||
881 | case 0x62: | ||
882 | case 0x82: | ||
883 | case 0xA2: | ||
884 | case 0xC2: | ||
885 | case 0xE2: | ||
886 | case 0x12: /* CLR1 */ | ||
887 | case 0x32: | ||
888 | case 0x52: | ||
889 | case 0x72: | ||
890 | case 0x92: | ||
891 | case 0xB2: | ||
892 | case 0xD2: | ||
893 | case 0xF2: { | ||
894 | data += dp; | ||
895 | int bit = 1 << (opcode >> 5); | ||
896 | int mask = ~bit; | ||
897 | if ( opcode & 0x10 ) | ||
898 | bit = 0; | ||
899 | WRITE( data, (READ( data ) & mask) | bit ); | ||
900 | goto inc_pc_loop; | ||
901 | } | ||
902 | |||
903 | case 0x0E: /* TSET1 abs */ | ||
904 | case 0x4E:{/* TCLR1 abs */ | ||
905 | data = READ_PC16( pc ); | ||
906 | pc += 2; | ||
907 | unsigned temp = READ( data ); | ||
908 | nz = temp & a; | ||
909 | temp &= ~a; | ||
910 | if ( !(opcode & 0x40) ) | ||
911 | temp |= a; | ||
912 | WRITE( data, temp ); | ||
913 | goto loop; | ||
914 | } | ||
915 | |||
916 | case 0x4A: /* AND1 C,mem.bit */ | ||
917 | c &= MEM_BIT(); | ||
918 | pc += 2; | ||
919 | goto loop; | ||
920 | |||
921 | case 0x6A: /* AND1 C,/mem.bit */ | ||
922 | check( 0 ); /* untested */ | ||
923 | c &= ~MEM_BIT(); | ||
924 | pc += 2; | ||
925 | goto loop; | ||
926 | |||
927 | case 0x0A: /* OR1 C,mem.bit */ | ||
928 | check( 0 ); /* untested */ | ||
929 | c |= MEM_BIT(); | ||
930 | pc += 2; | ||
931 | goto loop; | ||
932 | |||
933 | case 0x2A: /* OR1 C,/mem.bit */ | ||
934 | check( 0 ); /* untested */ | ||
935 | c |= ~MEM_BIT(); | ||
936 | pc += 2; | ||
937 | goto loop; | ||
938 | |||
939 | case 0x8A: /* EOR1 C,mem.bit */ | ||
940 | c ^= MEM_BIT(); | ||
941 | pc += 2; | ||
942 | goto loop; | ||
943 | |||
944 | case 0xEA: { /* NOT1 mem.bit */ | ||
945 | data = READ_PC16( pc ); | ||
946 | pc += 2; | ||
947 | unsigned temp = READ( data & 0x1FFF ); | ||
948 | temp ^= 1 << (data >> 13); | ||
949 | WRITE( data & 0x1FFF, temp ); | ||
950 | goto loop; | ||
951 | } | ||
952 | |||
953 | case 0xCA: { /* MOV1 mem.bit,C */ | ||
954 | data = READ_PC16( pc ); | ||
955 | pc += 2; | ||
956 | unsigned temp = READ( data & 0x1FFF ); | ||
957 | unsigned bit = data >> 13; | ||
958 | temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); | ||
959 | WRITE( data & 0x1FFF, temp ); | ||
960 | goto loop; | ||
961 | } | ||
962 | |||
963 | case 0xAA: /* MOV1 C,mem.bit */ | ||
964 | c = MEM_BIT(); | ||
965 | pc += 2; | ||
966 | goto loop; | ||
967 | |||
968 | /* 16. PROGRAM STATUS FLAG OPERATION COMMANDS */ | ||
969 | |||
970 | case 0x60: /* CLRC */ | ||
971 | c = 0; | ||
972 | goto loop; | ||
973 | |||
974 | case 0x80: /* SETC */ | ||
975 | c = ~0; | ||
976 | goto loop; | ||
977 | |||
978 | case 0xED: /* NOTC */ | ||
979 | c ^= 0x100; | ||
980 | goto loop; | ||
981 | |||
982 | case 0xE0: /* CLRV */ | ||
983 | status &= ~(st_v | st_h); | ||
984 | goto loop; | ||
985 | |||
986 | case 0x20: /* CLRP */ | ||
987 | dp = 0; | ||
988 | goto loop; | ||
989 | |||
990 | case 0x40: /* SETP */ | ||
991 | dp = 0x100; | ||
992 | goto loop; | ||
993 | |||
994 | case 0xA0: /* EI */ | ||
995 | check( 0 ); /* untested */ | ||
996 | status |= st_i; | ||
997 | goto loop; | ||
998 | |||
999 | case 0xC0: /* DI */ | ||
1000 | check( 0 ); /* untested */ | ||
1001 | status &= ~st_i; | ||
1002 | goto loop; | ||
1003 | |||
1004 | /* 17. OTHER COMMANDS */ | ||
1005 | |||
1006 | case 0x00: /* NOP */ | ||
1007 | goto loop; | ||
1008 | |||
1009 | /*case 0xEF:*/ /* SLEEP */ | ||
1010 | /*case 0xFF:*/ /* STOP */ | ||
1011 | case 0xFF: | ||
1012 | c |= 1; /* force switch table to have 256 entries, | ||
1013 | hopefully helping optimizer */ | ||
1014 | } /* switch */ | ||
1015 | |||
1016 | /* unhandled instructions fall out of switch so emulator can catch them */ | ||
1017 | |||
1018 | out_of_time: | ||
1019 | /* undo partial execution of opcode */ | ||
1020 | spc_time_ -= this->cycle_table [*--pc]; | ||
1021 | { | ||
1022 | int temp; | ||
1023 | CALC_STATUS( temp ); | ||
1024 | this->r.status = (uint8_t) temp; | ||
1025 | } | ||
1026 | |||
1027 | this->r.pc = GET_PC(); | ||
1028 | this->r.sp = (uint8_t) GET_SP(); | ||
1029 | this->r.a = (uint8_t) a; | ||
1030 | this->r.x = (uint8_t) x; | ||
1031 | this->r.y = (uint8_t) y; | ||
1032 | |||
1033 | #if 0 | ||
1034 | EXIT_TIMER(cpu); | ||
1035 | #endif | ||
1036 | return spc_time_; | ||
1037 | } | ||
diff --git a/apps/codecs/spc/Spc_Dsp.h b/apps/codecs/spc/Spc_Dsp.h new file mode 100644 index 0000000000..b297630b42 --- /dev/null +++ b/apps/codecs/spc/Spc_Dsp.h | |||
@@ -0,0 +1,1107 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) | ||
10 | * Copyright (C) 2004-2007 Shay Green (blargg) | ||
11 | * Copyright (C) 2002 Brad Martin | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | /* The DSP portion (awe!) */ | ||
22 | |||
23 | enum { voice_count = 8 }; | ||
24 | enum { register_count = 128 }; | ||
25 | |||
26 | struct raw_voice_t | ||
27 | { | ||
28 | int8_t volume [2]; | ||
29 | uint8_t rate [2]; | ||
30 | uint8_t waveform; | ||
31 | uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */ | ||
32 | uint8_t gain; /* envelope gain (if not using ADSR) */ | ||
33 | int8_t envx; /* current envelope level */ | ||
34 | int8_t outx; /* current sample */ | ||
35 | int8_t unused [6]; | ||
36 | }; | ||
37 | |||
38 | struct globals_t | ||
39 | { | ||
40 | int8_t unused1 [12]; | ||
41 | int8_t volume_0; /* 0C Main Volume Left (-.7) */ | ||
42 | int8_t echo_feedback; /* 0D Echo Feedback (-.7) */ | ||
43 | int8_t unused2 [14]; | ||
44 | int8_t volume_1; /* 1C Main Volume Right (-.7) */ | ||
45 | int8_t unused3 [15]; | ||
46 | int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */ | ||
47 | uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */ | ||
48 | int8_t unused4 [14]; | ||
49 | int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */ | ||
50 | uint8_t noise_enables; /* 3D Noise output on/off for each voice */ | ||
51 | int8_t unused5 [14]; | ||
52 | uint8_t key_ons; /* 4C Key On for each voice */ | ||
53 | uint8_t echo_ons; /* 4D Echo on/off for each voice */ | ||
54 | int8_t unused6 [14]; | ||
55 | uint8_t key_offs; /* 5C key off for each voice | ||
56 | (instantiates release mode) */ | ||
57 | uint8_t wave_page; /* 5D source directory (wave table offsets) */ | ||
58 | int8_t unused7 [14]; | ||
59 | uint8_t flags; /* 6C flags and noise freq */ | ||
60 | uint8_t echo_page; /* 6D */ | ||
61 | int8_t unused8 [14]; | ||
62 | uint8_t wave_ended; /* 7C */ | ||
63 | uint8_t echo_delay; /* 7D ms >> 4 */ | ||
64 | char unused9 [2]; | ||
65 | }; | ||
66 | |||
67 | enum state_t { /* -1, 0, +1 allows more efficient if statements */ | ||
68 | state_decay = -1, | ||
69 | state_sustain = 0, | ||
70 | state_attack = +1, | ||
71 | state_release = 2 | ||
72 | }; | ||
73 | |||
74 | struct cache_entry_t | ||
75 | { | ||
76 | int16_t const* samples; | ||
77 | unsigned end; /* past-the-end position */ | ||
78 | unsigned loop; /* number of samples in loop */ | ||
79 | unsigned start_addr; | ||
80 | }; | ||
81 | |||
82 | enum { brr_block_size = 16 }; | ||
83 | |||
84 | struct voice_t | ||
85 | { | ||
86 | #if SPC_BRRCACHE | ||
87 | int16_t const* samples; | ||
88 | long wave_end; | ||
89 | int wave_loop; | ||
90 | #else | ||
91 | int16_t samples [3 + brr_block_size + 1]; | ||
92 | int block_header; /* header byte from current block */ | ||
93 | #endif | ||
94 | uint8_t const* addr; | ||
95 | short volume [2]; | ||
96 | long position;/* position in samples buffer, with 12-bit fraction */ | ||
97 | short envx; | ||
98 | short env_mode; | ||
99 | short env_timer; | ||
100 | short key_on_delay; | ||
101 | }; | ||
102 | |||
103 | #if SPC_BRRCACHE | ||
104 | /* a little extra for samples that go past end */ | ||
105 | static int16_t BRRcache [0x20000 + 32]; | ||
106 | #endif | ||
107 | |||
108 | enum { fir_buf_half = 8 }; | ||
109 | |||
110 | struct Spc_Dsp | ||
111 | { | ||
112 | union | ||
113 | { | ||
114 | struct raw_voice_t voice [voice_count]; | ||
115 | uint8_t reg [register_count]; | ||
116 | struct globals_t g; | ||
117 | int16_t align; | ||
118 | } r; | ||
119 | |||
120 | unsigned echo_pos; | ||
121 | int keys_down; | ||
122 | int noise_count; | ||
123 | uint16_t noise; /* also read as int16_t */ | ||
124 | |||
125 | /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ | ||
126 | int fir_pos; /* (0 to 7) */ | ||
127 | int fir_buf [fir_buf_half * 2] [2]; | ||
128 | /* copy of echo FIR constants as int, for faster access */ | ||
129 | int fir_coeff [voice_count]; | ||
130 | |||
131 | struct voice_t voice_state [voice_count]; | ||
132 | |||
133 | #if SPC_BRRCACHE | ||
134 | uint8_t oldsize; | ||
135 | struct cache_entry_t wave_entry [256]; | ||
136 | struct cache_entry_t wave_entry_old [256]; | ||
137 | #endif | ||
138 | }; | ||
139 | |||
140 | struct src_dir | ||
141 | { | ||
142 | char start [2]; | ||
143 | char loop [2]; | ||
144 | }; | ||
145 | |||
146 | static void DSP_reset( struct Spc_Dsp* this ) | ||
147 | { | ||
148 | this->keys_down = 0; | ||
149 | this->echo_pos = 0; | ||
150 | this->noise_count = 0; | ||
151 | this->noise = 2; | ||
152 | this->fir_pos = 0; | ||
153 | |||
154 | this->r.g.flags = 0xE0; /* reset, mute, echo off */ | ||
155 | this->r.g.key_ons = 0; | ||
156 | |||
157 | memset( this->voice_state, 0, sizeof this->voice_state ); | ||
158 | |||
159 | int i; | ||
160 | for ( i = voice_count; --i >= 0; ) | ||
161 | { | ||
162 | struct voice_t* v = this->voice_state + i; | ||
163 | v->env_mode = state_release; | ||
164 | v->addr = ram.ram; | ||
165 | } | ||
166 | |||
167 | #if SPC_BRRCACHE | ||
168 | this->oldsize = 0; | ||
169 | for ( i = 0; i < 256; i++ ) | ||
170 | this->wave_entry [i].start_addr = -1; | ||
171 | #endif | ||
172 | |||
173 | memset( this->fir_buf, 0, sizeof this->fir_buf ); | ||
174 | assert( offsetof (struct globals_t,unused9 [2]) == register_count ); | ||
175 | assert( sizeof (this->r.voice) == register_count ); | ||
176 | } | ||
177 | |||
178 | static void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR; | ||
179 | static void DSP_write( struct Spc_Dsp* this, int i, int data ) | ||
180 | { | ||
181 | assert( (unsigned) i < register_count ); | ||
182 | |||
183 | this->r.reg [i] = data; | ||
184 | int high = i >> 4; | ||
185 | int low = i & 0x0F; | ||
186 | if ( low < 2 ) /* voice volumes */ | ||
187 | { | ||
188 | int left = *(int8_t const*) &this->r.reg [i & ~1]; | ||
189 | int right = *(int8_t const*) &this->r.reg [i | 1]; | ||
190 | struct voice_t* v = this->voice_state + high; | ||
191 | v->volume [0] = left; | ||
192 | v->volume [1] = right; | ||
193 | } | ||
194 | else if ( low == 0x0F ) /* fir coefficients */ | ||
195 | { | ||
196 | this->fir_coeff [7 - high] = (int8_t) data; /* sign-extend */ | ||
197 | } | ||
198 | } | ||
199 | |||
200 | static inline int DSP_read( struct Spc_Dsp* this, int i ) | ||
201 | { | ||
202 | assert( (unsigned) i < register_count ); | ||
203 | return this->r.reg [i]; | ||
204 | } | ||
205 | |||
206 | /* if ( n < -32768 ) out = -32768; */ | ||
207 | /* if ( n > 32767 ) out = 32767; */ | ||
208 | #define CLAMP16( n, out )\ | ||
209 | {\ | ||
210 | if ( (int16_t) n != n )\ | ||
211 | out = 0x7FFF ^ (n >> 31);\ | ||
212 | } | ||
213 | |||
214 | #if SPC_BRRCACHE | ||
215 | static void decode_brr( struct Spc_Dsp* this, unsigned start_addr, | ||
216 | struct voice_t* voice, | ||
217 | struct raw_voice_t const* const raw_voice ) ICODE_ATTR; | ||
218 | static void decode_brr( struct Spc_Dsp* this, unsigned start_addr, | ||
219 | struct voice_t* voice, | ||
220 | struct raw_voice_t const* const raw_voice ) | ||
221 | { | ||
222 | /* setup same variables as where decode_brr() is called from */ | ||
223 | #undef RAM | ||
224 | #define RAM ram.ram | ||
225 | struct src_dir const* const sd = | ||
226 | (struct src_dir*) &RAM [this->r.g.wave_page * 0x100]; | ||
227 | struct cache_entry_t* const wave_entry = | ||
228 | &this->wave_entry [raw_voice->waveform]; | ||
229 | |||
230 | /* the following block can be put in place of the call to | ||
231 | decode_brr() below | ||
232 | */ | ||
233 | { | ||
234 | DEBUGF( "decode at %08x (wave #%d)\n", | ||
235 | start_addr, raw_voice->waveform ); | ||
236 | |||
237 | /* see if in cache */ | ||
238 | int i; | ||
239 | for ( i = 0; i < this->oldsize; i++ ) | ||
240 | { | ||
241 | struct cache_entry_t* e = &this->wave_entry_old [i]; | ||
242 | if ( e->start_addr == start_addr ) | ||
243 | { | ||
244 | DEBUGF( "found in wave_entry_old (oldsize=%d)\n", | ||
245 | this->oldsize ); | ||
246 | *wave_entry = *e; | ||
247 | goto wave_in_cache; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | wave_entry->start_addr = start_addr; | ||
252 | |||
253 | uint8_t const* const loop_ptr = | ||
254 | RAM + GET_LE16A( sd [raw_voice->waveform].loop ); | ||
255 | short* loop_start = 0; | ||
256 | |||
257 | short* out = BRRcache + start_addr * 2; | ||
258 | wave_entry->samples = out; | ||
259 | *out++ = 0; | ||
260 | int smp1 = 0; | ||
261 | int smp2 = 0; | ||
262 | |||
263 | uint8_t const* addr = RAM + start_addr; | ||
264 | int block_header; | ||
265 | do | ||
266 | { | ||
267 | if ( addr == loop_ptr ) | ||
268 | { | ||
269 | loop_start = out; | ||
270 | DEBUGF( "loop at %08x (wave #%d)\n", addr - RAM, raw_voice->waveform ); | ||
271 | } | ||
272 | |||
273 | /* header */ | ||
274 | block_header = *addr; | ||
275 | addr += 9; | ||
276 | voice->addr = addr; | ||
277 | int const filter = (block_header & 0x0C) - 0x08; | ||
278 | |||
279 | /* scaling | ||
280 | (invalid scaling gives -4096 for neg nybble, 0 for pos) */ | ||
281 | static unsigned char const right_shifts [16] = { | ||
282 | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29, | ||
283 | }; | ||
284 | static unsigned char const left_shifts [16] = { | ||
285 | 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 | ||
286 | }; | ||
287 | int const scale = block_header >> 4; | ||
288 | int const right_shift = right_shifts [scale]; | ||
289 | int const left_shift = left_shifts [scale]; | ||
290 | |||
291 | /* output position */ | ||
292 | out += brr_block_size; | ||
293 | int offset = -brr_block_size << 2; | ||
294 | |||
295 | do /* decode and filter 16 samples */ | ||
296 | { | ||
297 | /* Get nybble, sign-extend, then scale | ||
298 | get byte, select which nybble, sign-extend, then shift based | ||
299 | on scaling. also handles invalid scaling values. */ | ||
300 | int delta = (int) (int8_t) (addr [offset >> 3] << (offset & 4)) | ||
301 | >> right_shift << left_shift; | ||
302 | |||
303 | out [offset >> 2] = smp2; | ||
304 | |||
305 | if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */ | ||
306 | { | ||
307 | delta -= smp2 >> 1; | ||
308 | delta += smp2 >> 5; | ||
309 | smp2 = smp1; | ||
310 | delta += smp1; | ||
311 | delta += (-smp1 - (smp1 >> 1)) >> 5; | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | if ( filter == -4 ) /* mode 0x04 */ | ||
316 | { | ||
317 | delta += smp1 >> 1; | ||
318 | delta += (-smp1) >> 5; | ||
319 | } | ||
320 | else if ( filter > -4 ) /* mode 0x0C */ | ||
321 | { | ||
322 | delta -= smp2 >> 1; | ||
323 | delta += (smp2 + (smp2 >> 1)) >> 4; | ||
324 | delta += smp1; | ||
325 | delta += (-smp1 * 13) >> 7; | ||
326 | } | ||
327 | smp2 = smp1; | ||
328 | } | ||
329 | |||
330 | CLAMP16( delta, delta ); | ||
331 | smp1 = (int16_t) (delta * 2); /* sign-extend */ | ||
332 | } | ||
333 | while ( (offset += 4) != 0 ); | ||
334 | |||
335 | /* if next block has end flag set, this block ends early */ | ||
336 | /* (verified) */ | ||
337 | if ( (block_header & 3) != 3 && (*addr & 3) == 1 ) | ||
338 | { | ||
339 | /* skip last 9 samples */ | ||
340 | out -= 9; | ||
341 | goto early_end; | ||
342 | } | ||
343 | } | ||
344 | while ( !(block_header & 1) && addr < RAM + 0x10000 ); | ||
345 | |||
346 | out [0] = smp2; | ||
347 | out [1] = smp1; | ||
348 | |||
349 | early_end: | ||
350 | wave_entry->end = (out - 1 - wave_entry->samples) << 12; | ||
351 | |||
352 | wave_entry->loop = 0; | ||
353 | if ( (block_header & 2) ) | ||
354 | { | ||
355 | if ( loop_start ) | ||
356 | { | ||
357 | int loop = out - loop_start; | ||
358 | wave_entry->loop = loop; | ||
359 | wave_entry->end += 0x3000; | ||
360 | out [2] = loop_start [2]; | ||
361 | out [3] = loop_start [3]; | ||
362 | out [4] = loop_start [4]; | ||
363 | } | ||
364 | else | ||
365 | { | ||
366 | DEBUGF( "loop point outside initial wave\n" ); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | DEBUGF( "end at %08x (wave #%d)\n", addr - RAM, raw_voice->waveform ); | ||
371 | |||
372 | /* add to cache */ | ||
373 | this->wave_entry_old [this->oldsize++] = *wave_entry; | ||
374 | wave_in_cache:; | ||
375 | } | ||
376 | } | ||
377 | #endif | ||
378 | |||
379 | static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice, | ||
380 | struct src_dir const* const sd, | ||
381 | struct raw_voice_t const* const raw_voice, | ||
382 | const int key_on_delay, const int vbit) ICODE_ATTR; | ||
383 | static void key_on(struct Spc_Dsp* const this, struct voice_t* const voice, | ||
384 | struct src_dir const* const sd, | ||
385 | struct raw_voice_t const* const raw_voice, | ||
386 | const int key_on_delay, const int vbit) { | ||
387 | #undef RAM | ||
388 | #define RAM ram.ram | ||
389 | int const env_rate_init = 0x7800; | ||
390 | voice->key_on_delay = key_on_delay; | ||
391 | if ( key_on_delay == 0 ) | ||
392 | { | ||
393 | this->keys_down |= vbit; | ||
394 | voice->envx = 0; | ||
395 | voice->env_mode = state_attack; | ||
396 | voice->env_timer = env_rate_init; /* TODO: inaccurate? */ | ||
397 | unsigned start_addr = GET_LE16A( sd [raw_voice->waveform].start ); | ||
398 | #if !SPC_BRRCACHE | ||
399 | { | ||
400 | voice->addr = RAM + start_addr; | ||
401 | /* BRR filter uses previous samples */ | ||
402 | voice->samples [brr_block_size + 1] = 0; | ||
403 | voice->samples [brr_block_size + 2] = 0; | ||
404 | /* decode three samples immediately */ | ||
405 | voice->position = (brr_block_size + 3) * 0x1000 - 1; | ||
406 | voice->block_header = 0; /* "previous" BRR header */ | ||
407 | } | ||
408 | #else | ||
409 | { | ||
410 | voice->position = 3 * 0x1000 - 1; | ||
411 | struct cache_entry_t* const wave_entry = | ||
412 | &this->wave_entry [raw_voice->waveform]; | ||
413 | |||
414 | /* predecode BRR if not already */ | ||
415 | if ( wave_entry->start_addr != start_addr ) | ||
416 | { | ||
417 | /* the following line can be replaced by the indicated block | ||
418 | in decode_brr() */ | ||
419 | decode_brr( this, start_addr, voice, raw_voice ); | ||
420 | } | ||
421 | |||
422 | voice->samples = wave_entry->samples; | ||
423 | voice->wave_end = wave_entry->end; | ||
424 | voice->wave_loop = wave_entry->loop; | ||
425 | } | ||
426 | #endif | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) | ||
431 | ICODE_ATTR; | ||
432 | static void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) | ||
433 | { | ||
434 | #undef RAM | ||
435 | #ifdef CPU_ARM | ||
436 | uint8_t* const ram_ = ram.ram; | ||
437 | #define RAM ram_ | ||
438 | #else | ||
439 | #define RAM ram.ram | ||
440 | #endif | ||
441 | #if 0 | ||
442 | EXIT_TIMER(cpu); | ||
443 | ENTER_TIMER(dsp); | ||
444 | #endif | ||
445 | |||
446 | /* Here we check for keys on/off. Docs say that successive writes | ||
447 | to KON/KOF must be separated by at least 2 Ts periods or risk | ||
448 | being neglected. Therefore DSP only looks at these during an | ||
449 | update, and not at the time of the write. Only need to do this | ||
450 | once however, since the regs haven't changed over the whole | ||
451 | period we need to catch up with. */ | ||
452 | |||
453 | { | ||
454 | int key_ons = this->r.g.key_ons; | ||
455 | int key_offs = this->r.g.key_offs; | ||
456 | /* keying on a voice resets that bit in ENDX */ | ||
457 | this->r.g.wave_ended &= ~key_ons; | ||
458 | /* key_off bits prevent key_on from being acknowledged */ | ||
459 | this->r.g.key_ons = key_ons & key_offs; | ||
460 | |||
461 | /* process key events outside loop, since they won't re-occur */ | ||
462 | struct voice_t* voice = this->voice_state + 8; | ||
463 | int vbit = 0x80; | ||
464 | do | ||
465 | { | ||
466 | --voice; | ||
467 | if ( key_offs & vbit ) | ||
468 | { | ||
469 | voice->env_mode = state_release; | ||
470 | voice->key_on_delay = 0; | ||
471 | } | ||
472 | else if ( key_ons & vbit ) | ||
473 | { | ||
474 | voice->key_on_delay = 8; | ||
475 | } | ||
476 | } | ||
477 | while ( (vbit >>= 1) != 0 ); | ||
478 | } | ||
479 | |||
480 | struct src_dir const* const sd = | ||
481 | (struct src_dir*) &RAM [this->r.g.wave_page * 0x100]; | ||
482 | |||
483 | #if !SPC_NOINTERP | ||
484 | int const slow_gaussian = (this->r.g.pitch_mods >> 1) | | ||
485 | this->r.g.noise_enables; | ||
486 | #endif | ||
487 | /* (g.flags & 0x40) ? 30 : 14 */ | ||
488 | int const global_muting = ((this->r.g.flags & 0x40) >> 2) + 14; | ||
489 | |||
490 | /* scaling to offset quietage */ | ||
491 | int const global_vol_0 = this->r.g.volume_0 * 3; | ||
492 | int const global_vol_1 = this->r.g.volume_1 * 3; | ||
493 | |||
494 | /* each rate divides exactly into 0x7800 without remainder */ | ||
495 | int const env_rate_init = 0x7800; | ||
496 | static unsigned short const env_rates [0x20] ICONST_ATTR = | ||
497 | { | ||
498 | 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, | ||
499 | 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, | ||
500 | 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, | ||
501 | 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 | ||
502 | }; | ||
503 | |||
504 | do /* one pair of output samples per iteration */ | ||
505 | { | ||
506 | /* Noise */ | ||
507 | if ( this->r.g.noise_enables ) | ||
508 | { | ||
509 | if ( (this->noise_count -= | ||
510 | env_rates [this->r.g.flags & 0x1F]) <= 0 ) | ||
511 | { | ||
512 | this->noise_count = env_rate_init; | ||
513 | int feedback = (this->noise << 13) ^ (this->noise << 14); | ||
514 | this->noise = (feedback & 0x8000) ^ (this->noise >> 1 & ~1); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | #if !SPC_NOECHO | ||
519 | int echo_0 = 0; | ||
520 | int echo_1 = 0; | ||
521 | #endif | ||
522 | long prev_outx = 0; /* TODO: correct value for first channel? */ | ||
523 | int chans_0 = 0; | ||
524 | int chans_1 = 0; | ||
525 | /* TODO: put raw_voice pointer in voice_t? */ | ||
526 | struct raw_voice_t * raw_voice = this->r.voice; | ||
527 | struct voice_t* voice = this->voice_state; | ||
528 | int vbit = 1; | ||
529 | for ( ; vbit < 0x100; vbit <<= 1, ++voice, ++raw_voice ) | ||
530 | { | ||
531 | /* pregen involves checking keyon, etc */ | ||
532 | #if 0 | ||
533 | ENTER_TIMER(dsp_pregen); | ||
534 | #endif | ||
535 | |||
536 | /* Key on events are delayed */ | ||
537 | int key_on_delay = voice->key_on_delay; | ||
538 | |||
539 | if ( --key_on_delay >= 0 ) /* <1% of the time */ | ||
540 | { | ||
541 | key_on(this,voice,sd,raw_voice,key_on_delay,vbit); | ||
542 | } | ||
543 | |||
544 | if ( !(this->keys_down & vbit) ) /* Silent channel */ | ||
545 | { | ||
546 | silent_chan: | ||
547 | raw_voice->envx = 0; | ||
548 | raw_voice->outx = 0; | ||
549 | prev_outx = 0; | ||
550 | continue; | ||
551 | } | ||
552 | |||
553 | /* Envelope */ | ||
554 | { | ||
555 | int const env_range = 0x800; | ||
556 | int env_mode = voice->env_mode; | ||
557 | int adsr0 = raw_voice->adsr [0]; | ||
558 | int env_timer; | ||
559 | if ( env_mode != state_release ) /* 99% of the time */ | ||
560 | { | ||
561 | env_timer = voice->env_timer; | ||
562 | if ( adsr0 & 0x80 ) /* 79% of the time */ | ||
563 | { | ||
564 | int adsr1 = raw_voice->adsr [1]; | ||
565 | if ( env_mode == state_sustain ) /* 74% of the time */ | ||
566 | { | ||
567 | if ( (env_timer -= env_rates [adsr1 & 0x1F]) > 0 ) | ||
568 | goto write_env_timer; | ||
569 | |||
570 | int envx = voice->envx; | ||
571 | envx--; /* envx *= 255 / 256 */ | ||
572 | envx -= envx >> 8; | ||
573 | voice->envx = envx; | ||
574 | /* TODO: should this be 8? */ | ||
575 | raw_voice->envx = envx >> 4; | ||
576 | goto init_env_timer; | ||
577 | } | ||
578 | else if ( env_mode < 0 ) /* 25% state_decay */ | ||
579 | { | ||
580 | int envx = voice->envx; | ||
581 | if ( (env_timer -= | ||
582 | env_rates [(adsr0 >> 3 & 0x0E) + 0x10]) <= 0 ) | ||
583 | { | ||
584 | envx--; /* envx *= 255 / 256 */ | ||
585 | envx -= envx >> 8; | ||
586 | voice->envx = envx; | ||
587 | /* TODO: should this be 8? */ | ||
588 | raw_voice->envx = envx >> 4; | ||
589 | env_timer = env_rate_init; | ||
590 | } | ||
591 | |||
592 | int sustain_level = adsr1 >> 5; | ||
593 | if ( envx <= (sustain_level + 1) * 0x100 ) | ||
594 | voice->env_mode = state_sustain; | ||
595 | |||
596 | goto write_env_timer; | ||
597 | } | ||
598 | else /* state_attack */ | ||
599 | { | ||
600 | int t = adsr0 & 0x0F; | ||
601 | if ( (env_timer -= env_rates [t * 2 + 1]) > 0 ) | ||
602 | goto write_env_timer; | ||
603 | |||
604 | int envx = voice->envx; | ||
605 | |||
606 | int const step = env_range / 64; | ||
607 | envx += step; | ||
608 | if ( t == 15 ) | ||
609 | envx += env_range / 2 - step; | ||
610 | |||
611 | if ( envx >= env_range ) | ||
612 | { | ||
613 | envx = env_range - 1; | ||
614 | voice->env_mode = state_decay; | ||
615 | } | ||
616 | voice->envx = envx; | ||
617 | /* TODO: should this be 8? */ | ||
618 | raw_voice->envx = envx >> 4; | ||
619 | goto init_env_timer; | ||
620 | } | ||
621 | } | ||
622 | else /* gain mode */ | ||
623 | { | ||
624 | int t = raw_voice->gain; | ||
625 | if ( t < 0x80 ) | ||
626 | { | ||
627 | raw_voice->envx = t; | ||
628 | voice->envx = t << 4; | ||
629 | goto env_end; | ||
630 | } | ||
631 | else | ||
632 | { | ||
633 | if ( (env_timer -= env_rates [t & 0x1F]) > 0 ) | ||
634 | goto write_env_timer; | ||
635 | |||
636 | int envx = voice->envx; | ||
637 | int mode = t >> 5; | ||
638 | if ( mode <= 5 ) /* decay */ | ||
639 | { | ||
640 | int step = env_range / 64; | ||
641 | if ( mode == 5 ) /* exponential */ | ||
642 | { | ||
643 | envx--; /* envx *= 255 / 256 */ | ||
644 | step = envx >> 8; | ||
645 | } | ||
646 | if ( (envx -= step) < 0 ) | ||
647 | { | ||
648 | envx = 0; | ||
649 | if ( voice->env_mode == state_attack ) | ||
650 | voice->env_mode = state_decay; | ||
651 | } | ||
652 | } | ||
653 | else /* attack */ | ||
654 | { | ||
655 | int const step = env_range / 64; | ||
656 | envx += step; | ||
657 | if ( mode == 7 && | ||
658 | envx >= env_range * 3 / 4 + step ) | ||
659 | envx += env_range / 256 - step; | ||
660 | |||
661 | if ( envx >= env_range ) | ||
662 | envx = env_range - 1; | ||
663 | } | ||
664 | voice->envx = envx; | ||
665 | /* TODO: should this be 8? */ | ||
666 | raw_voice->envx = envx >> 4; | ||
667 | goto init_env_timer; | ||
668 | } | ||
669 | } | ||
670 | } | ||
671 | else /* state_release */ | ||
672 | { | ||
673 | int envx = voice->envx; | ||
674 | if ( (envx -= env_range / 256) > 0 ) | ||
675 | { | ||
676 | voice->envx = envx; | ||
677 | raw_voice->envx = envx >> 8; | ||
678 | goto env_end; | ||
679 | } | ||
680 | else | ||
681 | { | ||
682 | /* bit was set, so this clears it */ | ||
683 | this->keys_down ^= vbit; | ||
684 | voice->envx = 0; | ||
685 | goto silent_chan; | ||
686 | } | ||
687 | } | ||
688 | init_env_timer: | ||
689 | env_timer = env_rate_init; | ||
690 | write_env_timer: | ||
691 | voice->env_timer = env_timer; | ||
692 | env_end:; | ||
693 | } | ||
694 | #if 0 | ||
695 | EXIT_TIMER(dsp_pregen); | ||
696 | |||
697 | ENTER_TIMER(dsp_gen); | ||
698 | #endif | ||
699 | #if !SPC_BRRCACHE | ||
700 | /* Decode BRR block */ | ||
701 | if ( voice->position >= brr_block_size * 0x1000 ) | ||
702 | { | ||
703 | voice->position -= brr_block_size * 0x1000; | ||
704 | |||
705 | uint8_t const* addr = voice->addr; | ||
706 | if ( addr >= RAM + 0x10000 ) | ||
707 | addr -= 0x10000; | ||
708 | |||
709 | /* action based on previous block's header */ | ||
710 | if ( voice->block_header & 1 ) | ||
711 | { | ||
712 | addr = RAM + GET_LE16A( sd [raw_voice->waveform].loop ); | ||
713 | this->r.g.wave_ended |= vbit; | ||
714 | if ( !(voice->block_header & 2) ) /* 1% of the time */ | ||
715 | { | ||
716 | /* first block was end block; | ||
717 | don't play anything (verified) */ | ||
718 | /* bit was set, so this clears it */ | ||
719 | this->keys_down ^= vbit; | ||
720 | |||
721 | /* since voice->envx is 0, | ||
722 | samples and position don't matter */ | ||
723 | raw_voice->envx = 0; | ||
724 | voice->envx = 0; | ||
725 | goto skip_decode; | ||
726 | } | ||
727 | } | ||
728 | |||
729 | /* header */ | ||
730 | int const block_header = *addr; | ||
731 | addr += 9; | ||
732 | voice->addr = addr; | ||
733 | voice->block_header = block_header; | ||
734 | int const filter = (block_header & 0x0C) - 0x08; | ||
735 | |||
736 | /* scaling (invalid scaling gives -4096 for neg nybble, | ||
737 | 0 for pos) */ | ||
738 | static unsigned char const right_shifts [16] = { | ||
739 | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 29, | ||
740 | }; | ||
741 | static unsigned char const left_shifts [16] = { | ||
742 | 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 | ||
743 | }; | ||
744 | int const scale = block_header >> 4; | ||
745 | int const right_shift = right_shifts [scale]; | ||
746 | int const left_shift = left_shifts [scale]; | ||
747 | |||
748 | /* previous samples */ | ||
749 | int smp2 = voice->samples [brr_block_size + 1]; | ||
750 | int smp1 = voice->samples [brr_block_size + 2]; | ||
751 | voice->samples [0] = voice->samples [brr_block_size]; | ||
752 | |||
753 | /* output position */ | ||
754 | short* out = voice->samples + (1 + brr_block_size); | ||
755 | int offset = -brr_block_size << 2; | ||
756 | |||
757 | /* if next block has end flag set, | ||
758 | this block ends early (verified) */ | ||
759 | if ( (block_header & 3) != 3 && (*addr & 3) == 1 ) | ||
760 | { | ||
761 | /* arrange for last 9 samples to be skipped */ | ||
762 | int const skip = 9; | ||
763 | out += (skip & 1); | ||
764 | voice->samples [skip] = voice->samples [brr_block_size]; | ||
765 | voice->position += skip * 0x1000; | ||
766 | offset = (-brr_block_size + (skip & ~1)) << 2; | ||
767 | addr -= skip / 2; | ||
768 | /* force sample to end on next decode */ | ||
769 | voice->block_header = 1; | ||
770 | } | ||
771 | |||
772 | do /* decode and filter 16 samples */ | ||
773 | { | ||
774 | /* Get nybble, sign-extend, then scale | ||
775 | get byte, select which nybble, sign-extend, then shift | ||
776 | based on scaling. also handles invalid scaling values.*/ | ||
777 | int delta = (int) (int8_t) (addr [offset >> 3] << | ||
778 | (offset & 4)) >> right_shift << left_shift; | ||
779 | |||
780 | out [offset >> 2] = smp2; | ||
781 | |||
782 | if ( filter == 0 ) /* mode 0x08 (30-90% of the time) */ | ||
783 | { | ||
784 | delta -= smp2 >> 1; | ||
785 | delta += smp2 >> 5; | ||
786 | smp2 = smp1; | ||
787 | delta += smp1; | ||
788 | delta += (-smp1 - (smp1 >> 1)) >> 5; | ||
789 | } | ||
790 | else | ||
791 | { | ||
792 | if ( filter == -4 ) /* mode 0x04 */ | ||
793 | { | ||
794 | delta += smp1 >> 1; | ||
795 | delta += (-smp1) >> 5; | ||
796 | } | ||
797 | else if ( filter > -4 ) /* mode 0x0C */ | ||
798 | { | ||
799 | delta -= smp2 >> 1; | ||
800 | delta += (smp2 + (smp2 >> 1)) >> 4; | ||
801 | delta += smp1; | ||
802 | delta += (-smp1 * 13) >> 7; | ||
803 | } | ||
804 | smp2 = smp1; | ||
805 | } | ||
806 | |||
807 | CLAMP16( delta, delta ); | ||
808 | smp1 = (int16_t) (delta * 2); /* sign-extend */ | ||
809 | } | ||
810 | while ( (offset += 4) != 0 ); | ||
811 | |||
812 | out [0] = smp2; | ||
813 | out [1] = smp1; | ||
814 | |||
815 | skip_decode:; | ||
816 | } | ||
817 | #endif | ||
818 | |||
819 | /* Get rate (with possible modulation) */ | ||
820 | int rate = GET_LE16A( raw_voice->rate ) & 0x3FFF; | ||
821 | if ( this->r.g.pitch_mods & vbit ) | ||
822 | rate = (rate * (prev_outx + 32768)) >> 15; | ||
823 | |||
824 | #if !SPC_NOINTERP | ||
825 | /* Interleved gauss table (to improve cache coherency). */ | ||
826 | /* gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] */ | ||
827 | static short const gauss [512] = | ||
828 | { | ||
829 | 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, | ||
830 | 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, | ||
831 | 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, | ||
832 | 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, | ||
833 | 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, | ||
834 | 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, | ||
835 | 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, | ||
836 | 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, | ||
837 | 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, | ||
838 | 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, | ||
839 | 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, | ||
840 | 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, | ||
841 | 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, | ||
842 | 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, | ||
843 | 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, | ||
844 | 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, | ||
845 | 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, | ||
846 | 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, | ||
847 | 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, | ||
848 | 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, | ||
849 | 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, | ||
850 | 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, | ||
851 | 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, | ||
852 | 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, | ||
853 | 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, | ||
854 | 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, | ||
855 | 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, | ||
856 | 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, | ||
857 | 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, | ||
858 | 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, | ||
859 | 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, | ||
860 | 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, | ||
861 | }; | ||
862 | |||
863 | /* Gaussian interpolation using most recent 4 samples */ | ||
864 | long position = voice->position; | ||
865 | voice->position += rate; | ||
866 | short const* interp = voice->samples + (position >> 12); | ||
867 | int offset = position >> 4 & 0xFF; | ||
868 | |||
869 | /* Only left half of gaussian kernel is in table, so we must mirror | ||
870 | for right half */ | ||
871 | short const* fwd = gauss + offset * 2; | ||
872 | short const* rev = gauss + 510 - offset * 2; | ||
873 | |||
874 | /* Use faster gaussian interpolation when exact result isn't needed | ||
875 | by pitch modulator of next channel */ | ||
876 | int amp_0, amp_1; | ||
877 | if ( !(slow_gaussian & vbit) ) /* 99% of the time */ | ||
878 | { | ||
879 | /* Main optimization is lack of clamping. Not a problem since | ||
880 | output never goes more than +/- 16 outside 16-bit range and | ||
881 | things are clamped later anyway. Other optimization is to | ||
882 | preserve fractional accuracy, eliminating several masks. */ | ||
883 | int output = (((fwd [0] * interp [0] + | ||
884 | fwd [1] * interp [1] + | ||
885 | rev [1] * interp [2] + | ||
886 | rev [0] * interp [3] ) >> 11) * voice->envx) >> 11; | ||
887 | |||
888 | /* duplicated here to give compiler more to run in parallel */ | ||
889 | amp_0 = voice->volume [0] * output; | ||
890 | amp_1 = voice->volume [1] * output; | ||
891 | raw_voice->outx = output >> 8; | ||
892 | } | ||
893 | else | ||
894 | { | ||
895 | int output = *(int16_t*) &this->noise; | ||
896 | if ( !(this->r.g.noise_enables & vbit) ) | ||
897 | { | ||
898 | output = (fwd [0] * interp [0]) & ~0xFFF; | ||
899 | output = (output + fwd [1] * interp [1]) & ~0xFFF; | ||
900 | output = (output + rev [1] * interp [2]) >> 12; | ||
901 | output = (int16_t) (output * 2); | ||
902 | output += ((rev [0] * interp [3]) >> 12) * 2; | ||
903 | CLAMP16( output, output ); | ||
904 | } | ||
905 | output = (output * voice->envx) >> 11 & ~1; | ||
906 | |||
907 | /* duplicated here to give compiler more to run in parallel */ | ||
908 | amp_0 = voice->volume [0] * output; | ||
909 | amp_1 = voice->volume [1] * output; | ||
910 | prev_outx = output; | ||
911 | raw_voice->outx = (int8_t) (output >> 8); | ||
912 | } | ||
913 | #else | ||
914 | /* two-point linear interpolation */ | ||
915 | #ifdef CPU_COLDFIRE | ||
916 | int32_t output = (int16_t)this->noise; | ||
917 | |||
918 | if ( (this->r.g.noise_enables & vbit) == 0 ) | ||
919 | { | ||
920 | uint32_t f = voice->position; | ||
921 | int32_t y1; | ||
922 | asm ( | ||
923 | "move.l %[f], %[y0] \n" /* separate fraction */ | ||
924 | "and.l #0xfff, %[f] \n" /* and whole parts */ | ||
925 | "lsr.l %[sh], %[y0] \n" | ||
926 | "move.l 2(%[s], %[y0].l*2), %[y1] \n" /* load two samples */ | ||
927 | "move.l %[y1], %[y0] \n" /* separate samples */ | ||
928 | "ext.l %[y1] \n" /* y0=s[1], y1=s[2] */ | ||
929 | "swap %[y0] \n" | ||
930 | "ext.l %[y0] \n" | ||
931 | "sub.l %[y0], %[y1] \n" /* diff = y1 - y0 */ | ||
932 | "muls.l %[f], %[y1] \n" /* y0 += f*diff */ | ||
933 | "asr.l %[sh], %[y1] \n" | ||
934 | "add.l %[y1], %[y0] \n" | ||
935 | : [f]"+&d"(f), [y0]"=&d"(output), [y1]"=&d"(y1) | ||
936 | : [s]"a"(voice->samples), [sh]"r"(12) | ||
937 | ); | ||
938 | } | ||
939 | |||
940 | voice->position += rate; | ||
941 | #else | ||
942 | |||
943 | /* Try this one out on ARM and see - similar to above but the asm | ||
944 | on coldfire removes a redundant register load worth 1 or 2%; | ||
945 | switching to loading two samples at once may help too. That's | ||
946 | done above and while 6 to 7% faster on cf over two 16 bit loads | ||
947 | it makes it endian dependant. | ||
948 | |||
949 | measured small improvement (~1.5%) - hcs | ||
950 | */ | ||
951 | |||
952 | int output; | ||
953 | |||
954 | if ( (this->r.g.noise_enables & vbit) == 0 ) | ||
955 | { | ||
956 | int const fraction = voice->position & 0xfff; | ||
957 | short const* const pos = (voice->samples + (voice->position >> 12)) + 1; | ||
958 | output = pos[0] + ((fraction * (pos[1] - pos[0])) >> 12); | ||
959 | } else { | ||
960 | output = *(int16_t *)&this->noise; | ||
961 | } | ||
962 | |||
963 | voice->position += rate; | ||
964 | |||
965 | /* old version */ | ||
966 | #if 0 | ||
967 | int fraction = voice->position & 0xFFF; | ||
968 | short const* const pos = voice->samples + (voice->position >> 12); | ||
969 | voice->position += rate; | ||
970 | int output = | ||
971 | (pos [2] * fraction + pos [1] * (0x1000 - fraction)) >> 12; | ||
972 | /* no interpolation (hardly faster, and crappy sounding) */ | ||
973 | /*int output = pos [0];*/ | ||
974 | if ( this->r.g.noise_enables & vbit ) | ||
975 | output = *(int16_t*) &this->noise; | ||
976 | #endif | ||
977 | #endif /* CPU_COLDFIRE */ | ||
978 | |||
979 | output = (output * voice->envx) >> 11; | ||
980 | |||
981 | /* duplicated here to give compiler more to run in parallel */ | ||
982 | int amp_0 = voice->volume [0] * output; | ||
983 | int amp_1 = voice->volume [1] * output; | ||
984 | |||
985 | prev_outx = output; | ||
986 | raw_voice->outx = (int8_t) (output >> 8); | ||
987 | #endif | ||
988 | |||
989 | #if SPC_BRRCACHE | ||
990 | if ( voice->position >= voice->wave_end ) | ||
991 | { | ||
992 | long loop_len = voice->wave_loop << 12; | ||
993 | voice->position -= loop_len; | ||
994 | this->r.g.wave_ended |= vbit; | ||
995 | if ( !loop_len ) | ||
996 | { | ||
997 | this->keys_down ^= vbit; | ||
998 | raw_voice->envx = 0; | ||
999 | voice->envx = 0; | ||
1000 | } | ||
1001 | } | ||
1002 | #endif | ||
1003 | #if 0 | ||
1004 | EXIT_TIMER(dsp_gen); | ||
1005 | |||
1006 | ENTER_TIMER(dsp_mix); | ||
1007 | #endif | ||
1008 | chans_0 += amp_0; | ||
1009 | chans_1 += amp_1; | ||
1010 | #if !SPC_NOECHO | ||
1011 | if ( this->r.g.echo_ons & vbit ) | ||
1012 | { | ||
1013 | echo_0 += amp_0; | ||
1014 | echo_1 += amp_1; | ||
1015 | } | ||
1016 | #endif | ||
1017 | #if 0 | ||
1018 | EXIT_TIMER(dsp_mix); | ||
1019 | #endif | ||
1020 | } | ||
1021 | /* end of voice loop */ | ||
1022 | |||
1023 | #if !SPC_NOECHO | ||
1024 | /* Read feedback from echo buffer */ | ||
1025 | int echo_pos = this->echo_pos; | ||
1026 | uint8_t* const echo_ptr = RAM + | ||
1027 | ((this->r.g.echo_page * 0x100 + echo_pos) & 0xFFFF); | ||
1028 | echo_pos += 4; | ||
1029 | if ( echo_pos >= (this->r.g.echo_delay & 15) * 0x800 ) | ||
1030 | echo_pos = 0; | ||
1031 | this->echo_pos = echo_pos; | ||
1032 | int fb_0 = GET_LE16SA( echo_ptr ); | ||
1033 | int fb_1 = GET_LE16SA( echo_ptr + 2 ); | ||
1034 | |||
1035 | /* Keep last 8 samples */ | ||
1036 | int (* const fir_ptr) [2] = this->fir_buf + this->fir_pos; | ||
1037 | this->fir_pos = (this->fir_pos + 1) & (fir_buf_half - 1); | ||
1038 | fir_ptr [ 0] [0] = fb_0; | ||
1039 | fir_ptr [ 0] [1] = fb_1; | ||
1040 | /* duplicate at +8 eliminates wrap checking below */ | ||
1041 | fir_ptr [fir_buf_half] [0] = fb_0; | ||
1042 | fir_ptr [fir_buf_half] [1] = fb_1; | ||
1043 | |||
1044 | /* Apply FIR */ | ||
1045 | fb_0 *= this->fir_coeff [0]; | ||
1046 | fb_1 *= this->fir_coeff [0]; | ||
1047 | |||
1048 | #define DO_PT( i )\ | ||
1049 | fb_0 += fir_ptr [i] [0] * this->fir_coeff [i];\ | ||
1050 | fb_1 += fir_ptr [i] [1] * this->fir_coeff [i]; | ||
1051 | |||
1052 | DO_PT( 1 ) | ||
1053 | DO_PT( 2 ) | ||
1054 | DO_PT( 3 ) | ||
1055 | DO_PT( 4 ) | ||
1056 | DO_PT( 5 ) | ||
1057 | DO_PT( 6 ) | ||
1058 | DO_PT( 7 ) | ||
1059 | |||
1060 | /* Generate output */ | ||
1061 | int amp_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0) | ||
1062 | >> global_muting; | ||
1063 | int amp_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1) | ||
1064 | >> global_muting; | ||
1065 | CLAMP16( amp_0, amp_0 ); | ||
1066 | out_buf [0] = amp_0 * (1 << 8); | ||
1067 | CLAMP16( amp_1, amp_1 ); | ||
1068 | out_buf [WAV_CHUNK_SIZE] = amp_1 * (1 << 8); | ||
1069 | out_buf ++; | ||
1070 | |||
1071 | /* Feedback into echo buffer */ | ||
1072 | int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14); | ||
1073 | int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14); | ||
1074 | if ( !(this->r.g.flags & 0x20) ) | ||
1075 | { | ||
1076 | CLAMP16( e0, e0 ); | ||
1077 | SET_LE16A( echo_ptr , e0 ); | ||
1078 | CLAMP16( e1, e1 ); | ||
1079 | SET_LE16A( echo_ptr + 2, e1 ); | ||
1080 | } | ||
1081 | #else | ||
1082 | /* Generate output */ | ||
1083 | int amp_0 = (chans_0 * global_vol_0) >> global_muting; | ||
1084 | int amp_1 = (chans_1 * global_vol_1) >> global_muting; | ||
1085 | CLAMP16( amp_0, amp_0 ); | ||
1086 | out_buf [0] = amp_0 * (1 << 8); | ||
1087 | CLAMP16( amp_1, amp_1 ); | ||
1088 | out_buf [WAV_CHUNK_SIZE] = amp_1 * (1 << 8); | ||
1089 | out_buf ++; | ||
1090 | #endif | ||
1091 | } | ||
1092 | while ( --count ); | ||
1093 | #if 0 | ||
1094 | EXIT_TIMER(dsp); | ||
1095 | ENTER_TIMER(cpu); | ||
1096 | #endif | ||
1097 | } | ||
1098 | |||
1099 | static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) | ||
1100 | { | ||
1101 | /* Should we just fill the buffer with silence? Flags won't be cleared */ | ||
1102 | /* during this run so it seems it should keep resetting every sample. */ | ||
1103 | if ( this->r.g.flags & 0x80 ) | ||
1104 | DSP_reset( this ); | ||
1105 | |||
1106 | DSP_run_( this, count, out ); | ||
1107 | } | ||
diff --git a/apps/codecs/spc/spc_profiler.h b/apps/codecs/spc/spc_profiler.h new file mode 100644 index 0000000000..99d3fdf16b --- /dev/null +++ b/apps/codecs/spc/spc_profiler.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) | ||
10 | * | ||
11 | * All files in this archive are subject to the GNU General Public License. | ||
12 | * See the file COPYING in the source tree root for full license agreement. | ||
13 | * | ||
14 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
15 | * KIND, either express or implied. | ||
16 | * | ||
17 | ****************************************************************************/ | ||
18 | |||
19 | /* a fun simple elapsed time profiler */ | ||
20 | |||
21 | #if defined(SPC_PROFILE) && defined(USEC_TIMER) | ||
22 | |||
23 | #define CREATE_TIMER(name) static uint32_t spc_timer_##name##_start,\ | ||
24 | spc_timer_##name##_total | ||
25 | #define ENTER_TIMER(name) spc_timer_##name##_start=USEC_TIMER | ||
26 | #define EXIT_TIMER(name) spc_timer_##name##_total+=\ | ||
27 | (USEC_TIMER-spc_timer_##name##_start) | ||
28 | #define READ_TIMER(name) (spc_timer_##name##_total) | ||
29 | #define RESET_TIMER(name) spc_timer_##name##_total=0 | ||
30 | |||
31 | #define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf( \ | ||
32 | logfd,"%10ld ",READ_TIMER(bname));\ | ||
33 | ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\ | ||
34 | ((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname)) | ||
35 | |||
36 | CREATE_TIMER(total); | ||
37 | CREATE_TIMER(render); | ||
38 | #if 0 | ||
39 | CREATE_TIMER(cpu); | ||
40 | CREATE_TIMER(dsp); | ||
41 | CREATE_TIMER(dsp_pregen); | ||
42 | CREATE_TIMER(dsp_gen); | ||
43 | CREATE_TIMER(dsp_mix); | ||
44 | #endif | ||
45 | |||
46 | static void reset_profile_timers(void) { | ||
47 | RESET_TIMER(total); | ||
48 | RESET_TIMER(render); | ||
49 | #if 0 | ||
50 | RESET_TIMER(cpu); | ||
51 | RESET_TIMER(dsp); | ||
52 | RESET_TIMER(dsp_pregen); | ||
53 | RESET_TIMER(dsp_gen); | ||
54 | RESET_TIMER(dsp_mix); | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | static int logfd=-1; | ||
59 | |||
60 | static void print_timers(char * path) { | ||
61 | logfd = ci->open("/spclog.txt",O_WRONLY|O_CREAT|O_APPEND); | ||
62 | ci->fdprintf(logfd,"%s:\t",path); | ||
63 | ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total)); | ||
64 | PRINT_TIMER_PCT(render,total,"render"); | ||
65 | #if 0 | ||
66 | PRINT_TIMER_PCT(cpu,total,"CPU"); | ||
67 | PRINT_TIMER_PCT(dsp,total,"DSP"); | ||
68 | ci->fdprintf(logfd,"("); | ||
69 | PRINT_TIMER_PCT(dsp_pregen,dsp,"pregen"); | ||
70 | PRINT_TIMER_PCT(dsp_gen,dsp,"gen"); | ||
71 | PRINT_TIMER_PCT(dsp_mix,dsp,"mix"); | ||
72 | #endif | ||
73 | ci->fdprintf(logfd,"\n"); | ||
74 | |||
75 | ci->close(logfd); | ||
76 | logfd=-1; | ||
77 | } | ||
78 | |||
79 | #else | ||
80 | |||
81 | #define CREATE_TIMER(name) | ||
82 | #define ENTER_TIMER(name) | ||
83 | #define EXIT_TIMER(name) | ||
84 | #define READ_TIMER(name) | ||
85 | #define RESET_TIMER(name) | ||
86 | #define print_timers(path) | ||
87 | #define reset_profile_timers() | ||
88 | |||
89 | #endif | ||