annotate snes_spc/SNES_SPC.h @ 0:e38dacceb958

initial import
author Robert McIntyre <rlm@mit.edu>
date Fri, 21 Oct 2011 05:53:11 -0700
parents
children
rev   line source
rlm@0 1 // SNES SPC-700 APU emulator
rlm@0 2
rlm@0 3 // snes_spc 0.9.0
rlm@0 4 #ifndef SNES_SPC_H
rlm@0 5 #define SNES_SPC_H
rlm@0 6
rlm@0 7 #include "SPC_DSP.h"
rlm@0 8 #include "blargg_endian.h"
rlm@0 9
rlm@0 10 struct SNES_SPC {
rlm@0 11 public:
rlm@0 12 typedef BOOST::uint8_t uint8_t;
rlm@0 13
rlm@0 14 // Must be called once before using
rlm@0 15 blargg_err_t init();
rlm@0 16
rlm@0 17 // Sample pairs generated per second
rlm@0 18 enum { sample_rate = 32000 };
rlm@0 19
rlm@0 20 // Emulator use
rlm@0 21
rlm@0 22 // Sets IPL ROM data. Library does not include ROM data. Most SPC music files
rlm@0 23 // don't need ROM, but a full emulator must provide this.
rlm@0 24 enum { rom_size = 0x40 };
rlm@0 25 void init_rom( uint8_t const rom [rom_size] );
rlm@0 26
rlm@0 27 // Sets destination for output samples
rlm@0 28 typedef short sample_t;
rlm@0 29 void set_output( sample_t* out, int out_size );
rlm@0 30
rlm@0 31 // Number of samples written to output since last set
rlm@0 32 int sample_count() const;
rlm@0 33
rlm@0 34 // Resets SPC to power-on state. This resets your output buffer, so you must
rlm@0 35 // call set_output() after this.
rlm@0 36 void reset();
rlm@0 37
rlm@0 38 // Emulates pressing reset switch on SNES. This resets your output buffer, so
rlm@0 39 // you must call set_output() after this.
rlm@0 40 void soft_reset();
rlm@0 41
rlm@0 42 // 1024000 SPC clocks per second, sample pair every 32 clocks
rlm@0 43 typedef int time_t;
rlm@0 44 enum { clock_rate = 1024000 };
rlm@0 45 enum { clocks_per_sample = 32 };
rlm@0 46
rlm@0 47 // Emulated port read/write at specified time
rlm@0 48 enum { port_count = 4 };
rlm@0 49 int read_port ( time_t, int port );
rlm@0 50 void write_port( time_t, int port, int data );
rlm@0 51
rlm@0 52 // Runs SPC to end_time and starts a new time frame at 0
rlm@0 53 void end_frame( time_t end_time );
rlm@0 54
rlm@0 55 // Sound control
rlm@0 56
rlm@0 57 // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
rlm@0 58 // Reduces emulation accuracy.
rlm@0 59 enum { voice_count = 8 };
rlm@0 60 void mute_voices( int mask );
rlm@0 61
rlm@0 62 // If true, prevents channels and global volumes from being phase-negated.
rlm@0 63 // Only supported by fast DSP.
rlm@0 64 void disable_surround( bool disable = true );
rlm@0 65
rlm@0 66 // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
rlm@0 67 enum { tempo_unit = 0x100 };
rlm@0 68 void set_tempo( int );
rlm@0 69
rlm@0 70 // SPC music files
rlm@0 71
rlm@0 72 // Loads SPC data into emulator
rlm@0 73 enum { spc_min_file_size = 0x10180 };
rlm@0 74 enum { spc_file_size = 0x10200 };
rlm@0 75 blargg_err_t load_spc( void const* in, long size );
rlm@0 76
rlm@0 77 // Clears echo region. Useful after loading an SPC as many have garbage in echo.
rlm@0 78 void clear_echo();
rlm@0 79
rlm@0 80 // Plays for count samples and write samples to out. Discards samples if out
rlm@0 81 // is NULL. Count must be a multiple of 2 since output is stereo.
rlm@0 82 blargg_err_t play( int count, sample_t* out );
rlm@0 83
rlm@0 84 // Skips count samples. Several times faster than play() when using fast DSP.
rlm@0 85 blargg_err_t skip( int count );
rlm@0 86
rlm@0 87 // State save/load (only available with accurate DSP)
rlm@0 88
rlm@0 89 #if !SPC_NO_COPY_STATE_FUNCS
rlm@0 90 // Saves/loads state
rlm@0 91 enum { state_size = 67 * 1024L }; // maximum space needed when saving
rlm@0 92 typedef SPC_DSP::copy_func_t copy_func_t;
rlm@0 93 void copy_state( unsigned char** io, copy_func_t );
rlm@0 94
rlm@0 95 // Writes minimal header to spc_out
rlm@0 96 static void init_header( void* spc_out );
rlm@0 97
rlm@0 98 // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
rlm@0 99 // Does not set up SPC header; use init_header() for that.
rlm@0 100 void save_spc( void* spc_out );
rlm@0 101
rlm@0 102 // Returns true if new key-on events occurred since last check. Useful for
rlm@0 103 // trimming silence while saving an SPC.
rlm@0 104 bool check_kon();
rlm@0 105 #endif
rlm@0 106
rlm@0 107 public:
rlm@0 108 BLARGG_DISABLE_NOTHROW
rlm@0 109
rlm@0 110 typedef BOOST::uint16_t uint16_t;
rlm@0 111
rlm@0 112 // Time relative to m_spc_time. Speeds up code a bit by eliminating need to
rlm@0 113 // constantly add m_spc_time to time from CPU. CPU uses time that ends at
rlm@0 114 // 0 to eliminate reloading end time every instruction. It pays off.
rlm@0 115 typedef int rel_time_t;
rlm@0 116
rlm@0 117 struct Timer
rlm@0 118 {
rlm@0 119 rel_time_t next_time; // time of next event
rlm@0 120 int prescaler;
rlm@0 121 int period;
rlm@0 122 int divider;
rlm@0 123 int enabled;
rlm@0 124 int counter;
rlm@0 125 };
rlm@0 126 enum { reg_count = 0x10 };
rlm@0 127 enum { timer_count = 3 };
rlm@0 128 enum { extra_size = SPC_DSP::extra_size };
rlm@0 129
rlm@0 130 enum { signature_size = 35 };
rlm@0 131
rlm@0 132 private:
rlm@0 133 SPC_DSP dsp;
rlm@0 134
rlm@0 135 #if SPC_LESS_ACCURATE
rlm@0 136 static signed char const reg_times_ [256];
rlm@0 137 signed char reg_times [256];
rlm@0 138 #endif
rlm@0 139
rlm@0 140 struct state_t
rlm@0 141 {
rlm@0 142 Timer timers [timer_count];
rlm@0 143
rlm@0 144 uint8_t smp_regs [2] [reg_count];
rlm@0 145
rlm@0 146 struct
rlm@0 147 {
rlm@0 148 int pc;
rlm@0 149 int a;
rlm@0 150 int x;
rlm@0 151 int y;
rlm@0 152 int psw;
rlm@0 153 int sp;
rlm@0 154 } cpu_regs;
rlm@0 155
rlm@0 156 rel_time_t dsp_time;
rlm@0 157 time_t spc_time;
rlm@0 158 bool echo_accessed;
rlm@0 159
rlm@0 160 int tempo;
rlm@0 161 int skipped_kon;
rlm@0 162 int skipped_koff;
rlm@0 163 const char* cpu_error;
rlm@0 164
rlm@0 165 int extra_clocks;
rlm@0 166 sample_t* buf_begin;
rlm@0 167 sample_t const* buf_end;
rlm@0 168 sample_t* extra_pos;
rlm@0 169 sample_t extra_buf [extra_size];
rlm@0 170
rlm@0 171 int rom_enabled;
rlm@0 172 uint8_t rom [rom_size];
rlm@0 173 uint8_t hi_ram [rom_size];
rlm@0 174
rlm@0 175 unsigned char cycle_table [256];
rlm@0 176
rlm@0 177 struct
rlm@0 178 {
rlm@0 179 // padding to neutralize address overflow
rlm@0 180 union {
rlm@0 181 uint8_t padding1 [0x100];
rlm@0 182 uint16_t align; // makes compiler align data for 16-bit access
rlm@0 183 } padding1 [1];
rlm@0 184 uint8_t ram [0x10000];
rlm@0 185 uint8_t padding2 [0x100];
rlm@0 186 } ram;
rlm@0 187 };
rlm@0 188 state_t m;
rlm@0 189
rlm@0 190 enum { rom_addr = 0xFFC0 };
rlm@0 191
rlm@0 192 enum { skipping_time = 127 };
rlm@0 193
rlm@0 194 // Value that padding should be filled with
rlm@0 195 enum { cpu_pad_fill = 0xFF };
rlm@0 196
rlm@0 197 enum {
rlm@0 198 r_test = 0x0, r_control = 0x1,
rlm@0 199 r_dspaddr = 0x2, r_dspdata = 0x3,
rlm@0 200 r_cpuio0 = 0x4, r_cpuio1 = 0x5,
rlm@0 201 r_cpuio2 = 0x6, r_cpuio3 = 0x7,
rlm@0 202 r_f8 = 0x8, r_f9 = 0x9,
rlm@0 203 r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
rlm@0 204 r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
rlm@0 205 };
rlm@0 206
rlm@0 207 void timers_loaded();
rlm@0 208 void enable_rom( int enable );
rlm@0 209 void reset_buf();
rlm@0 210 void save_extra();
rlm@0 211 void load_regs( uint8_t const in [reg_count] );
rlm@0 212 void ram_loaded();
rlm@0 213 void regs_loaded();
rlm@0 214 void reset_time_regs();
rlm@0 215 void reset_common( int timer_counter_init );
rlm@0 216
rlm@0 217 Timer* run_timer_ ( Timer* t, rel_time_t );
rlm@0 218 Timer* run_timer ( Timer* t, rel_time_t );
rlm@0 219 int dsp_read ( rel_time_t );
rlm@0 220 void dsp_write ( int data, rel_time_t );
rlm@0 221 void cpu_write_smp_reg_( int data, rel_time_t, int addr );
rlm@0 222 void cpu_write_smp_reg ( int data, rel_time_t, int addr );
rlm@0 223 void cpu_write_high ( int data, int i, rel_time_t );
rlm@0 224 void cpu_write ( int data, int addr, rel_time_t );
rlm@0 225 int cpu_read_smp_reg ( int i, rel_time_t );
rlm@0 226 int cpu_read ( int addr, rel_time_t );
rlm@0 227 unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );
rlm@0 228
rlm@0 229 bool check_echo_access ( int addr );
rlm@0 230 uint8_t* run_until_( time_t end_time );
rlm@0 231
rlm@0 232 struct spc_file_t
rlm@0 233 {
rlm@0 234 char signature [signature_size];
rlm@0 235 uint8_t has_id666;
rlm@0 236 uint8_t version;
rlm@0 237 uint8_t pcl, pch;
rlm@0 238 uint8_t a;
rlm@0 239 uint8_t x;
rlm@0 240 uint8_t y;
rlm@0 241 uint8_t psw;
rlm@0 242 uint8_t sp;
rlm@0 243 char text [212];
rlm@0 244 uint8_t ram [0x10000];
rlm@0 245 uint8_t dsp [128];
rlm@0 246 uint8_t unused [0x40];
rlm@0 247 uint8_t ipl_rom [0x40];
rlm@0 248 };
rlm@0 249
rlm@0 250 static char const signature [signature_size + 1];
rlm@0 251
rlm@0 252 void save_regs( uint8_t out [reg_count] );
rlm@0 253 };
rlm@0 254
rlm@0 255 #include <assert.h>
rlm@0 256
rlm@0 257 inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
rlm@0 258
rlm@0 259 inline int SNES_SPC::read_port( time_t t, int port )
rlm@0 260 {
rlm@0 261 assert( (unsigned) port < port_count );
rlm@0 262 return run_until_( t ) [port];
rlm@0 263 }
rlm@0 264
rlm@0 265 inline void SNES_SPC::write_port( time_t t, int port, int data )
rlm@0 266 {
rlm@0 267 assert( (unsigned) port < port_count );
rlm@0 268 run_until_( t ) [0x10 + port] = data;
rlm@0 269 }
rlm@0 270
rlm@0 271 inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }
rlm@0 272
rlm@0 273 inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
rlm@0 274
rlm@0 275 #if !SPC_NO_COPY_STATE_FUNCS
rlm@0 276 inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }
rlm@0 277 #endif
rlm@0 278
rlm@0 279 #endif