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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "spc_codec.h"
24 #include "spc_profiler.h"
26 /* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
27 /* DSP Based on Brad Martin's OpenSPC DSP emulator */
28 /* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */
30 struct cpu_ram_t ram CACHEALIGN_ATTR
;
32 /**************** Timers ****************/
34 void Timer_run_( struct Timer
* t
, long time
)
36 /* when disabled, next_tick should always be in the future */
39 int elapsed
= ((time
- t
->next_tick
) >> t
->shift
) + 1;
40 t
->next_tick
+= elapsed
<< t
->shift
;
43 if ( elapsed
>= t
->period
) /* avoid unnecessary division */
45 int n
= elapsed
/ t
->period
;
46 elapsed
-= n
* t
->period
;
47 t
->counter
= (t
->counter
+ n
) & 15;
52 /**************** SPC emulator ****************/
53 /* 1.024 MHz clock / 32000 samples per second */
55 static void SPC_enable_rom( THIS
, int enable
)
57 if ( this->rom_enabled
!= enable
)
59 this->rom_enabled
= enable
;
60 ci
->memcpy( RAM
+ ROM_ADDR
, (enable
? this->boot_rom
: this->extra_ram
), ROM_SIZE
);
61 /* TODO: ROM can still get overwritten when DSP writes to echo buffer */
67 this->timer
[0].shift
= 4 + 3; /* 8 kHz */
68 this->timer
[1].shift
= 4 + 3; /* 8 kHz */
69 this->timer
[2].shift
= 4; /* 8 kHz */
71 /* Put STOP instruction around memory to catch PC underflow/overflow. */
72 ci
->memset( ram
.padding1
, 0xFF, sizeof ram
.padding1
);
73 ci
->memset( ram
.padding2
, 0xFF, sizeof ram
.padding2
);
75 /* A few tracks read from the last four bytes of IPL ROM */
76 this->boot_rom
[sizeof this->boot_rom
- 2] = 0xC0;
77 this->boot_rom
[sizeof this->boot_rom
- 1] = 0xFF;
78 ci
->memset( this->boot_rom
, 0, sizeof this->boot_rom
- 2 );
80 /* Have DSP in a defined state in case EMU is run and hasn't loaded
82 DSP_reset(&this->dsp
);
85 static void SPC_load_state( THIS
, struct cpu_regs_t
const* cpu_state
,
86 const void* new_ram
, const void* dsp_state
)
88 ci
->memcpy(&(this->r
),cpu_state
,sizeof this->r
);
91 ci
->memcpy( RAM
, new_ram
, sizeof RAM
);
92 ci
->memcpy( this->extra_ram
, RAM
+ ROM_ADDR
, sizeof this->extra_ram
);
94 /* boot rom (have to force enable_rom() to update it) */
95 this->rom_enabled
= !(RAM
[0xF1] & 0x80);
96 SPC_enable_rom( this, !this->rom_enabled
);
99 /* some SPCs rely on DSP immediately generating one sample */
100 this->extra_cycles
= 32;
101 DSP_reset( &this->dsp
);
103 for ( i
= 0; i
< REGISTER_COUNT
; i
++ )
104 DSP_write( &this->dsp
, i
, ((uint8_t const*) dsp_state
) [i
] );
107 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
109 struct Timer
* t
= &this->timer
[i
];
111 t
->next_tick
= -EXTRA_CLOCKS
;
112 t
->enabled
= (RAM
[0xF1] >> i
) & 1;
114 t
->next_tick
= TIMER_DISABLED_TIME
;
116 t
->counter
= RAM
[0xFD + i
] & 15;
118 int p
= RAM
[0xFA + i
];
124 /* Handle registers which already give 0 when read by
125 setting RAM and not changing it.
126 Put STOP instruction in registers which can be read,
127 to catch attempted execution. */
139 static void clear_echo( THIS
)
141 if ( !(DSP_read( &this->dsp
, 0x6C ) & 0x20) )
143 unsigned addr
= 0x100 * DSP_read( &this->dsp
, 0x6D );
144 size_t size
= 0x800 * DSP_read( &this->dsp
, 0x7D );
145 size_t max_size
= sizeof RAM
- addr
;
146 if ( size
> max_size
)
147 size
= sizeof RAM
- addr
;
148 ci
->memset( RAM
+ addr
, 0xFF, size
);
152 int SPC_load_spc( THIS
, const void* data
, long size
)
154 struct spc_file_t
const* spc
= (struct spc_file_t
const*) data
;
155 struct cpu_regs_t regs
;
157 if ( size
< SPC_FILE_SIZE
)
160 if ( ci
->memcmp( spc
->signature
, "SNES-SPC700 Sound File Data", 27 ) != 0 )
163 regs
.pc
= spc
->pc
[1] * 0x100 + spc
->pc
[0];
167 regs
.status
= spc
->status
;
170 if ( (unsigned long) size
>= sizeof *spc
)
171 ci
->memcpy( this->boot_rom
, spc
->ipl_rom
, sizeof this->boot_rom
);
173 SPC_load_state( this, ®s
, spc
->ram
, spc
->dsp
);
180 /**************** DSP interaction ****************/
181 void SPC_run_dsp_( THIS
, long time
)
183 /* divide by CLOCKS_PER_SAMPLE */
184 int count
= ((time
- this->next_dsp
) >> 5) + 1;
185 int32_t* buf
= this->sample_buf
;
186 this->sample_buf
= buf
+ count
;
187 this->next_dsp
+= count
* CLOCKS_PER_SAMPLE
;
188 DSP_run( &this->dsp
, count
, buf
);
191 int SPC_read( THIS
, unsigned addr
, long const time
)
193 int result
= RAM
[addr
];
195 if ( ((unsigned) (addr
- 0xF0)) < 0x10 )
197 assert( 0xF0 <= addr
&& addr
<= 0xFF );
203 struct Timer
* t
= &this->timer
[i
];
204 Timer_run( t
, time
);
209 else if ( addr
== 0xF3 )
211 SPC_run_dsp( this, time
);
212 result
= DSP_read( &this->dsp
, RAM
[0xF2] & 0x7F );
218 void SPC_write( THIS
, unsigned addr
, int data
, long const time
)
220 /* first page is very common */
223 RAM
[addr
] = (uint8_t) data
;
229 if ( addr
< ROM_ADDR
)
231 RAM
[addr
] = (uint8_t) data
;
235 this->extra_ram
[addr
- ROM_ADDR
] = (uint8_t) data
;
236 if ( !this->rom_enabled
)
237 RAM
[addr
] = (uint8_t) data
;
242 /*case 0xF2:*/ /* mapped to RAM */
244 SPC_run_dsp( this, time
);
245 int reg
= RAM
[0xF2];
246 if ( reg
< REGISTER_COUNT
) {
247 DSP_write( &this->dsp
, reg
, data
);
250 /*dprintf( "DSP write to $%02X\n", (int) reg ); */
255 case 0xF0: /* Test register */
256 /*dprintf( "Wrote $%02X to $F0\n", (int) data ); */
264 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
266 struct Timer
* t
= this->timer
+i
;
267 if ( !(data
& (1 << i
)) )
270 t
->next_tick
= TIMER_DISABLED_TIME
;
272 else if ( !t
->enabled
)
294 SPC_enable_rom( this, (data
& 0x80) != 0 );
303 /* to do: handle output ports */
306 /* verified on SNES that these are read/write (RAM) */
315 struct Timer
* t
= &this->timer
[i
];
316 if ( (t
->period
& 0xFF) != data
)
318 Timer_run( t
, time
);
319 this->timer
[i
].period
= data
? data
: 0x100;
324 /* Counters (cleared on write) */
328 /*dprintf( "Wrote to counter $%02X\n", (int) addr ); */
329 this->timer
[addr
- 0xFD].counter
= 0;
334 /**************** Sample generation ****************/
335 int SPC_play( THIS
, long count
, int32_t* out
)
338 assert( count
% 2 == 0 ); /* output is always in pairs of samples */
340 long start_time
= -(count
>> 1) * CLOCKS_PER_SAMPLE
- EXTRA_CLOCKS
;
342 /* DSP output is made on-the-fly when DSP registers are read or written */
343 this->sample_buf
= out
;
344 this->next_dsp
= start_time
+ CLOCKS_PER_SAMPLE
;
346 /* Localize timer next_tick times and run them to the present to prevent
347 a running but ignored timer's next_tick from getting too far behind
349 for ( i
= 0; i
< TIMER_COUNT
; i
++ )
351 struct Timer
* t
= &this->timer
[i
];
354 t
->next_tick
+= start_time
+ EXTRA_CLOCKS
;
355 Timer_run( t
, start_time
);
359 /* Run from start_time to 0, pre-advancing by extra cycles from last run */
360 this->extra_cycles
= CPU_run( this, start_time
+ this->extra_cycles
) +
362 if ( this->extra_cycles
< 0 )
364 /*dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
365 (int) CPU_read( r.pc ), (unsigned) r.pc ); */
370 /* Catch DSP up to present */
374 SPC_run_dsp( this, -EXTRA_CLOCKS
);
378 assert( this->next_dsp
== CLOCKS_PER_SAMPLE
- EXTRA_CLOCKS
);
379 assert( this->sample_buf
- out
== count
);