Path: blob/main/misc/emulator/xnes/snes9x/apu/SNES_SPC.h
28798 views
// SNES SPC-700 APU emulator12// snes_spc 0.9.03#ifndef SNES_SPC_H4#define SNES_SPC_H56#include "SPC_DSP.h"7#include "blargg_endian.h"89#ifdef DEBUGGER10#include "snes9x.h"11#include "display.h"12#include "debug.h"13#endif1415struct SNES_SPC {16public:17typedef BOOST::uint8_t uint8_t;1819// Must be called once before using20blargg_err_t init();2122// Sample pairs generated per second23enum { sample_rate = 32000 };2425// Emulator use2627// Sets IPL ROM data. Library does not include ROM data. Most SPC music files28// don't need ROM, but a full emulator must provide this.29enum { rom_size = 0x40 };30void init_rom( uint8_t const rom [rom_size] );3132// Sets destination for output samples33typedef short sample_t;34void set_output( sample_t* out, int out_size );3536// Number of samples written to output since last set37int sample_count() const;3839// Resets SPC to power-on state. This resets your output buffer, so you must40// call set_output() after this.41void reset();4243// Emulates pressing reset switch on SNES. This resets your output buffer, so44// you must call set_output() after this.45void soft_reset();4647// 1024000 SPC clocks per second, sample pair every 32 clocks48typedef int time_t;49enum { clock_rate = 1024000 };50enum { clocks_per_sample = 32 };5152// Emulated port read/write at specified time53enum { port_count = 4 };54int read_port ( time_t, int port );55void write_port( time_t, int port, int data );5657// Runs SPC to end_time and starts a new time frame at 058void end_frame( time_t end_time );5960// Sound control6162// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).63// Reduces emulation accuracy.64enum { voice_count = 8 };65void mute_voices( int mask );6667// If true, prevents channels and global volumes from being phase-negated.68// Only supported by fast DSP.69void disable_surround( bool disable = true );7071// Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.72enum { tempo_unit = 0x100 };73void set_tempo( int );7475// SPC music files7677// Loads SPC data into emulator78enum { spc_min_file_size = 0x10180 };79enum { spc_file_size = 0x10200 };80blargg_err_t load_spc( void const* in, long size );8182// Clears echo region. Useful after loading an SPC as many have garbage in echo.83void clear_echo();8485// Plays for count samples and write samples to out. Discards samples if out86// is NULL. Count must be a multiple of 2 since output is stereo.87blargg_err_t play( int count, sample_t* out );8889// Skips count samples. Several times faster than play() when using fast DSP.90blargg_err_t skip( int count );9192// State save/load (only available with accurate DSP)9394#if !SPC_NO_COPY_STATE_FUNCS95// Saves/loads state96enum { state_size = 68 * 1024L }; // maximum space needed when saving97typedef SPC_DSP::copy_func_t copy_func_t;98void copy_state( unsigned char** io, copy_func_t );99100// Writes minimal header to spc_out101static void init_header( void* spc_out );102103// Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.104// Does not set up SPC header; use init_header() for that.105void save_spc( void* spc_out );106107// Returns true if new key-on events occurred since last check. Useful for108// trimming silence while saving an SPC.109bool check_kon();110#endif111112//// Snes9x Accessor113114void spc_allow_time_overflow( bool );115116void dsp_set_spc_snapshot_callback( void (*callback) (void) );117void dsp_dump_spc_snapshot( void );118void dsp_set_stereo_switch( int );119uint8_t dsp_reg_value( int, int );120int dsp_envx_value( int );121122//// Snes9x Debugger123124#ifdef DEBUGGER125void debug_toggle_trace( void );126bool debug_is_enabled( void );127void debug_do_trace( int, int, int, uint8_t const *, uint8_t *, int, int, int, int );128void debug_op_print( char *, int, int, int, uint8_t const *, uint8_t *, int, int, int, int );129void debug_io_print( char * );130#endif131132public:133BLARGG_DISABLE_NOTHROW134135typedef BOOST::uint16_t uint16_t;136137// Time relative to m_spc_time. Speeds up code a bit by eliminating need to138// constantly add m_spc_time to time from CPU. CPU uses time that ends at139// 0 to eliminate reloading end time every instruction. It pays off.140typedef int rel_time_t;141142struct Timer143{144rel_time_t next_time; // time of next event145int prescaler;146int period;147int divider;148int enabled;149int counter;150};151enum { reg_count = 0x10 };152enum { timer_count = 3 };153enum { extra_size = SPC_DSP::extra_size };154155enum { signature_size = 35 };156157private:158SPC_DSP dsp;159160#if SPC_LESS_ACCURATE161static signed char const reg_times_ [256];162signed char reg_times [256];163#endif164165struct state_t166{167Timer timers [timer_count];168169uint8_t smp_regs [2] [reg_count];170171struct172{173int pc;174int a;175int x;176int y;177int psw;178int sp;179} cpu_regs;180181rel_time_t dsp_time;182time_t spc_time;183bool echo_accessed;184185int tempo;186int skipped_kon;187int skipped_koff;188const char* cpu_error;189190int extra_clocks;191sample_t* buf_begin;192sample_t const* buf_end;193sample_t* extra_pos;194sample_t extra_buf [extra_size];195196int rom_enabled;197uint8_t rom [rom_size];198uint8_t hi_ram [rom_size];199200unsigned char cycle_table [256];201202struct203{204// padding to neutralize address overflow205union {206uint8_t padding1 [0x100];207uint16_t align; // makes compiler align data for 16-bit access208} padding1 [1];209uint8_t ram [0x10000];210uint8_t padding2 [0x100];211} ram;212};213state_t m;214215enum { rom_addr = 0xFFC0 };216217enum { skipping_time = 127 };218219// Value that padding should be filled with220enum { cpu_pad_fill = 0xFF };221222enum {223r_test = 0x0, r_control = 0x1,224r_dspaddr = 0x2, r_dspdata = 0x3,225r_cpuio0 = 0x4, r_cpuio1 = 0x5,226r_cpuio2 = 0x6, r_cpuio3 = 0x7,227r_f8 = 0x8, r_f9 = 0x9,228r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,229r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF230};231232void timers_loaded();233void enable_rom( int enable );234void reset_buf();235void save_extra();236void load_regs( uint8_t const in [reg_count] );237void ram_loaded();238void regs_loaded();239void reset_time_regs();240void reset_common( int timer_counter_init );241242Timer* run_timer_ ( Timer* t, rel_time_t );243Timer* run_timer ( Timer* t, rel_time_t );244int dsp_read ( rel_time_t );245void dsp_write ( int data, rel_time_t );246void cpu_write_smp_reg_( int data, rel_time_t, int addr );247void cpu_write_smp_reg ( int data, rel_time_t, int addr );248void cpu_write_high ( int data, int i, rel_time_t );249void cpu_write ( int data, int addr, rel_time_t );250int cpu_read_smp_reg ( int i, rel_time_t );251int cpu_read ( int addr, rel_time_t );252unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );253254bool check_echo_access ( int addr );255uint8_t* run_until_( time_t end_time );256257struct spc_file_t258{259char signature [signature_size];260uint8_t has_id666;261uint8_t version;262uint8_t pcl, pch;263uint8_t a;264uint8_t x;265uint8_t y;266uint8_t psw;267uint8_t sp;268char text [212];269uint8_t ram [0x10000];270uint8_t dsp [128];271uint8_t unused [0x40];272uint8_t ipl_rom [0x40];273};274275static char const signature [signature_size + 1];276277void save_regs( uint8_t out [reg_count] );278279// Snes9x timing hack280bool allow_time_overflow;281// Snes9x debugger282#ifdef DEBUGGER283FILE *apu_trace;284bool debug_trace;285#endif286};287288#include <assert.h>289290inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }291292inline int SNES_SPC::read_port( time_t t, int port )293{294assert( (unsigned) port < port_count );295return run_until_( t ) [port];296}297298inline void SNES_SPC::write_port( time_t t, int port, int data )299{300assert( (unsigned) port < port_count );301run_until_( t ) [0x10 + port] = data;302m.ram.ram [0xF4 + port] = data;303}304305inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); }306307inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); }308309#if !SPC_NO_COPY_STATE_FUNCS310inline bool SNES_SPC::check_kon() { return dsp.check_kon(); }311#endif312313inline void SNES_SPC::spc_allow_time_overflow( bool allow ) { allow_time_overflow = allow; }314315#endif316317318