1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006-2007 Adam Gashlin (hcs)
11 * Copyright (C) 2004-2007 Shay Green (blargg)
12 * Copyright (C) 2002 Brad Martin
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 #include "spc_codec.h"
25 #include "spc_profiler.h"
27 /* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
28 /* DSP Based on Brad Martin's OpenSPC DSP emulator */
29 /* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
31 struct cpu_ram_t ram CACHEALIGN_ATTR
;
33 /**************** Timers ****************/
35 void Timer_run_( struct Timer
* t
, long time
)
37 /* when disabled, next_tick should always be in the future */
40 int elapsed
= ((time
- t
->next_tick
) >> t
->shift
) + 1;
41 t
->next_tick
+= elapsed
<< t
->shift
;
44 if ( elapsed
>= t
->period
) /* avoid unnecessary division */
46 int n
= elapsed
/ t
->period
;
47 elapsed
-= n
* t
->period
;
48 t
->counter
= (t
->counter
+ n
) & 15;
53 /**************** SPC emulator ****************/
54 /* 1.024 MHz clock / 32000 samples per second */
56 static void SPC_enable_rom( THIS
, int enable
)
58 if ( this->rom_enabled
!= enable
)
60 this->rom_enabled
= enable
;
61 ci
->memcpy( RAM
+ ROM_ADDR
, (enable
? this->boot_rom
: this->extra_ram
), ROM_SIZE
);
62 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
68 this->timer
[0].shift
= 4 + 3; /* 8 kHz */
69 this->timer
[1].shift
= 4 + 3; /* 8 kHz */
70 this->timer
[2].shift
= 4; /* 8 kHz */
72 /* Put STOP instruction around memory to catch PC underflow/overflow. */
73 ci
->memset( ram
.padding1
, 0xFF, sizeof ram
.padding1
);
74 ci
->memset( ram
.padding2
, 0xFF, sizeof ram
.padding2
);
76 /* A few tracks read from the last four bytes of IPL ROM */
77 this->boot_rom
[sizeof this->boot_rom
- 2] = 0xC0;
78 this->boot_rom
[sizeof this->boot_rom
- 1] = 0xFF;
79 ci
->memset( this->boot_rom
, 0, sizeof this->boot_rom
- 2 );
81 /* Have DSP in a defined state in case EMU is run and hasn't loaded
83 DSP_reset(&this->dsp
);
86 static void SPC_load_state( THIS
, struct cpu_regs_t
const* cpu_state
,
87 const void* new_ram
, const void* dsp_state
)
89 ci
->memcpy(&(this->r
),cpu_state
,sizeof this->r
);
92 ci
->memcpy( RAM
, new_ram
, sizeof RAM
);
93 ci
->memcpy( this->extra_ram
, RAM
+ ROM_ADDR
, sizeof this->extra_ram
);
95 /* boot rom (have to force enable_rom() to update it) */
96 this->rom_enabled
= !(RAM
[0xF1] & 0x80);
97 SPC_enable_rom( this, !this->rom_enabled
);
100 /* some SPCs rely on DSP immediately generating one sample */
101 this->extra_cycles
= 32;
102 DSP_reset( &this->dsp
);
104 for ( i
= 0; i
< REGISTER_COUNT
; i
++ )
105 DSP_write( &this->dsp
, i
, ((uint8_t const*) dsp_state
) [i
] );
108 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
110 struct Timer
* t
= &this->timer
[i
];
112 t
->next_tick
= -EXTRA_CLOCKS
;
113 t
->enabled
= (RAM
[0xF1] >> i
) & 1;
115 t
->next_tick
= TIMER_DISABLED_TIME
;
117 t
->counter
= RAM
[0xFD + i
] & 15;
119 int p
= RAM
[0xFA + i
];
125 /* Handle registers which already give 0 when read by
126 setting RAM and not changing it.
127 Put STOP instruction in registers which can be read,
128 to catch attempted execution. */
140 static void clear_echo( THIS
)
142 if ( !(DSP_read( &this->dsp
, 0x6C ) & 0x20) )
144 unsigned addr
= 0x100 * DSP_read( &this->dsp
, 0x6D );
145 size_t size
= 0x800 * DSP_read( &this->dsp
, 0x7D );
146 size_t max_size
= sizeof RAM
- addr
;
147 if ( size
> max_size
)
148 size
= sizeof RAM
- addr
;
149 ci
->memset( RAM
+ addr
, 0xFF, size
);
153 int SPC_load_spc( THIS
, const void* data
, long size
)
155 struct spc_file_t
const* spc
= (struct spc_file_t
const*) data
;
156 struct cpu_regs_t regs
;
158 if ( size
< SPC_FILE_SIZE
)
161 if ( ci
->memcmp( spc
->signature
, "SNES-SPC700 Sound File Data", 27 ) != 0 )
164 regs
.pc
= spc
->pc
[1] * 0x100 + spc
->pc
[0];
168 regs
.status
= spc
->status
;
171 if ( (unsigned long) size
>= sizeof *spc
)
172 ci
->memcpy( this->boot_rom
, spc
->ipl_rom
, sizeof this->boot_rom
);
174 SPC_load_state( this, ®s
, spc
->ram
, spc
->dsp
);
181 /**************** DSP interaction ****************/
182 void SPC_run_dsp_( THIS
, long time
)
184 /* divide by CLOCKS_PER_SAMPLE */
185 int count
= ((time
- this->next_dsp
) >> 5) + 1;
186 int32_t* buf
= this->sample_buf
;
187 this->sample_buf
= buf
+ count
;
188 this->next_dsp
+= count
* CLOCKS_PER_SAMPLE
;
189 DSP_run( &this->dsp
, count
, buf
);
192 int SPC_read( THIS
, unsigned addr
, long const time
)
194 int result
= RAM
[addr
];
196 if ( ((unsigned) (addr
- 0xF0)) < 0x10 )
198 assert( 0xF0 <= addr
&& addr
<= 0xFF );
204 struct Timer
* t
= &this->timer
[i
];
205 Timer_run( t
, time
);
210 else if ( addr
== 0xF3 )
212 SPC_run_dsp( this, time
);
213 result
= DSP_read( &this->dsp
, RAM
[0xF2] & 0x7F );
219 void SPC_write( THIS
, unsigned addr
, int data
, long const time
)
221 /* first page is very common */
224 RAM
[addr
] = (uint8_t) data
;
230 if ( addr
< ROM_ADDR
)
232 RAM
[addr
] = (uint8_t) data
;
236 this->extra_ram
[addr
- ROM_ADDR
] = (uint8_t) data
;
237 if ( !this->rom_enabled
)
238 RAM
[addr
] = (uint8_t) data
;
243 /*case 0xF2:*/ /* mapped to RAM */
245 SPC_run_dsp( this, time
);
246 int reg
= RAM
[0xF2];
247 if ( reg
< REGISTER_COUNT
) {
248 DSP_write( &this->dsp
, reg
, data
);
251 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
256 case 0xF0: /* Test register */
257 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
265 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
267 struct Timer
* t
= this->timer
+i
;
268 if ( !(data
& (1 << i
)) )
271 t
->next_tick
= TIMER_DISABLED_TIME
;
273 else if ( !t
->enabled
)
295 SPC_enable_rom( this, (data
& 0x80) != 0 );
304 /* to do: handle output ports */
307 /* verified on SNES that these are read/write (RAM) */
316 struct Timer
* t
= &this->timer
[i
];
317 if ( (t
->period
& 0xFF) != data
)
319 Timer_run( t
, time
);
320 this->timer
[i
].period
= data
? data
: 0x100;
325 /* Counters (cleared on write) */
329 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
330 this->timer
[addr
- 0xFD].counter
= 0;
335 /**************** Sample generation ****************/
336 int SPC_play( THIS
, long count
, int32_t* out
)
339 assert( count
% 2 == 0 ); /* output is always in pairs of samples */
341 long start_time
= -(count
>> 1) * CLOCKS_PER_SAMPLE
- EXTRA_CLOCKS
;
343 /* DSP output is made on-the-fly when DSP registers are read or written */
344 this->sample_buf
= out
;
345 this->next_dsp
= start_time
+ CLOCKS_PER_SAMPLE
;
347 /* Localize timer next_tick times and run them to the present to prevent
348 a running but ignored timer's next_tick from getting too far behind
350 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
352 struct Timer
* t
= &this->timer
[i
];
355 t
->next_tick
+= start_time
+ EXTRA_CLOCKS
;
356 Timer_run( t
, start_time
);
360 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
361 this->extra_cycles
= CPU_run( this, start_time
+ this->extra_cycles
) +
363 if ( this->extra_cycles
< 0 )
365 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
366 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
371 /* Catch DSP up to present */
375 SPC_run_dsp( this, -EXTRA_CLOCKS
);
379 assert( this->next_dsp
== CLOCKS_PER_SAMPLE
- EXTRA_CLOCKS
);
380 assert( this->sample_buf
- out
== count
);