1// NES 2A03 APU sound chip emulator23// Nes_Snd_Emu 0.1.745#ifndef NES_APU_H6#define NES_APU_H78typedef long nes_time_t; // CPU clock cycle count9typedef unsigned nes_addr_t; // 16-bit memory address1011#include "Nes_Oscs.h"1213struct apu_state_t;14class Nes_Buffer;1516class Nes_Apu {17public:18Nes_Apu();19~Nes_Apu();2021// Set buffer to generate all sound into, or disable sound if NULL22void output( Blip_Buffer* );2324// Set memory reader callback used by DMC oscillator to fetch samples.25// When callback is invoked, 'user_data' is passed unchanged as the26// first parameter.27void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL );2829// All time values are the number of CPU clock cycles relative to the30// beginning of the current time frame. Before resetting the CPU clock31// count, call end_frame( last_cpu_time ).3233// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)34enum { start_addr = 0x4000 };35enum { end_addr = 0x4017 };36void write_register( nes_time_t, nes_addr_t, int data );3738// Read from status register at 0x401539enum { status_addr = 0x4015 };40int read_status( nes_time_t );4142// Run all oscillators up to specified time, end current time frame, then43// start a new time frame at time 0. Time frames have no effect on emulation44// and each can be whatever length is convenient.45void end_frame( nes_time_t );4647// Additional optional features (can be ignored without any problem)4849// Reset internal frame counter, registers, and all oscillators.50// Use PAL timing if pal_timing is true, otherwise use NTSC timing.51// Set the DMC oscillator's initial DAC value to initial_dmc_dac without52// any audible click.53void reset( bool pal_timing = false, int initial_dmc_dac = 0 );5455// Save/load exact emulation state56void save_state( apu_state_t* out ) const;57void load_state( apu_state_t const& );5859// Set overall volume (default is 1.0)60void volume( double );6162// Set treble equalization (see notes.txt)63void treble_eq( const blip_eq_t& );6465// Set sound output of specific oscillator to buffer. If buffer is NULL,66// the specified oscillator is muted and emulation accuracy is reduced.67// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,68// 2) Triangle, 3) Noise, 4) DMC.69enum { osc_count = 5 };70void osc_output( int index, Blip_Buffer* buffer );7172// Set IRQ time callback that is invoked when the time of earliest IRQ73// may have changed, or NULL to disable. When callback is invoked,74// 'user_data' is passed unchanged as the first parameter.75void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );7677// Get time that APU-generated IRQ will occur if no further register reads78// or writes occur. If IRQ is already pending, returns irq_waiting. If no79// IRQ will occur, returns no_irq.80enum { no_irq = LONG_MAX / 2 + 1 };81enum { irq_waiting = 0 };82nes_time_t earliest_irq( nes_time_t ) const;8384// Count number of DMC reads that would occur if 'run_until( t )' were executed.85// If last_read is not NULL, set *last_read to the earliest time that86// 'count_dmc_reads( time )' would result in the same result.87int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;8889// Time when next DMC memory read will occur90nes_time_t next_dmc_read_time() const;9192// Run DMC until specified time, so that any DMC memory reads can be93// accounted for (i.e. inserting CPU wait states).94void run_until( nes_time_t );9596// End of public interface.97private:98friend class Nes_Nonlinearizer;99void enable_nonlinear( double volume );100static double nonlinear_tnd_gain() { return 0.75; }101private:102friend struct Nes_Dmc;103104// noncopyable105Nes_Apu( const Nes_Apu& );106Nes_Apu& operator = ( const Nes_Apu& );107108Nes_Osc* oscs [osc_count];109Nes_Square square1;110Nes_Square square2;111Nes_Noise noise;112Nes_Triangle triangle;113Nes_Dmc dmc;114115nes_time_t last_time; // has been run until this time in current frame116nes_time_t last_dmc_time;117nes_time_t earliest_irq_;118nes_time_t next_irq;119int frame_period;120int frame_delay; // cycles until frame counter runs next121int frame; // current frame (0-3)122int osc_enables;123int frame_mode;124bool irq_flag;125void (*irq_notifier_)( void* user_data );126void* irq_data;127Nes_Square::Synth square_synth; // shared by squares128129void irq_changed();130void state_restored();131void run_until_( nes_time_t );132133// TODO: remove134friend class Nes_Core;135};136137inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf )138{139assert( (unsigned) osc < osc_count );140oscs [osc]->output = buf;141}142143inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const144{145return earliest_irq_;146}147148inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data )149{150dmc.prg_reader_data = user_data;151dmc.prg_reader = func;152}153154inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data )155{156irq_notifier_ = func;157irq_data = user_data;158}159160inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const161{162return dmc.count_reads( time, last_read );163}164165inline nes_time_t Nes_Dmc::next_read_time() const166{167if ( length_counter == 0 )168return Nes_Apu::no_irq; // not reading169170return apu->last_dmc_time + delay + long (bits_remain - 1) * period;171}172173inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); }174175#endif176177178179