1
2// NES 2A03 APU sound chip emulator
3
4// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
5
6#ifndef NES_APU_H
7#define NES_APU_H
8
9typedef long cpu_time_t; // CPU clock cycle count
10typedef unsigned cpu_addr_t; // 16-bit memory address
11
12#include "Nes_Oscs.h"
13
14struct apu_snapshot_t;
15class Nonlinear_Buffer;
16
17class Nes_Apu {
18public:
19 Nes_Apu();
20 ~Nes_Apu();
21
22 // Set buffer to generate all sound into, or disable sound if NULL
23 void output( Blip_Buffer* );
24
25 // Set memory reader callback used by DMC oscillator to fetch samples.
26 // When callback is invoked, 'user_data' is passed unchanged as the
27 // first parameter.
28 void dmc_reader( int (*callback)( void* user_data, cpu_addr_t ), void* user_data = NULL );
29
30 // All time values are the number of CPU clock cycles relative to the
31 // beginning of the current time frame. Before resetting the CPU clock
32 // count, call end_frame( last_cpu_time ).
33
34 // Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
35 enum { start_addr = 0x4000 };
36 enum { end_addr = 0x4017 };
37 void write_register( cpu_time_t, cpu_addr_t, int data );
38
39 // Read from status register at 0x4015
40 enum { status_addr = 0x4015 };
41 int read_status( cpu_time_t );
42
43 // Run all oscillators up to specified time, end current time frame, then
44 // start a new time frame at time 0. Time frames have no effect on emulation
45 // and each can be whatever length is convenient.
46 void end_frame( cpu_time_t );
47
48// Additional optional features (can be ignored without any problem)
49
50 // Reset internal frame counter, registers, and all oscillators.
51 // Use PAL timing if pal_timing is true, otherwise use NTSC timing.
52 // Set the DMC oscillator's initial DAC value to initial_dmc_dac without
53 // any audible click.
54 void reset( bool pal_timing = false, int initial_dmc_dac = 0 );
55
56 // Save/load snapshot of exact emulation state
57 void save_snapshot( apu_snapshot_t* out ) const;
58 void load_snapshot( apu_snapshot_t const& );
59
60 // Set overall volume (default is 1.0)
61 void volume( double );
62
63 // Reset oscillator amplitudes. Must be called when clearing buffer while
64 // using non-linear sound.
65 void buffer_cleared();
66
67 // Set treble equalization (see notes.txt).
68 void treble_eq( const blip_eq_t& );
69
70 // Set sound output of specific oscillator to buffer. If buffer is NULL,
71 // the specified oscillator is muted and emulation accuracy is reduced.
72 // The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
73 // 2) Triangle, 3) Noise, 4) DMC.
74 enum { osc_count = 5 };
75 void osc_output( int index, Blip_Buffer* buffer );
76
77 // Set IRQ time callback that is invoked when the time of earliest IRQ
78 // may have changed, or NULL to disable. When callback is invoked,
79 // 'user_data' is passed unchanged as the first parameter.
80 void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
81
82 // Get time that APU-generated IRQ will occur if no further register reads
83 // or writes occur. If IRQ is already pending, returns irq_waiting. If no
84 // IRQ will occur, returns no_irq.
85 enum { no_irq = LONG_MAX / 2 + 1 };
86 enum { irq_waiting = 0 };
87 cpu_time_t earliest_irq() const;
88
89 // Count number of DMC reads that would occur if 'run_until( t )' were executed.
90 // If last_read is not NULL, set *last_read to the earliest time that
91 // 'count_dmc_reads( time )' would result in the same result.
92 int count_dmc_reads( cpu_time_t t, cpu_time_t* last_read = NULL ) const;
93
94 // Run APU until specified time, so that any DMC memory reads can be
95 // accounted for (i.e. inserting CPU wait states).
96 void run_until( cpu_time_t );
97
98// End of public interface.
99private:
100 friend class Nes_Nonlinearizer;
101 void enable_nonlinear( double volume );
102private:
103 // noncopyable
104 Nes_Apu( const Nes_Apu& );
105 Nes_Apu& operator = ( const Nes_Apu& );
106
107 Nes_Osc* oscs [osc_count];
108 Nes_Square square1;
109 Nes_Square square2;
110 Nes_Noise noise;
111 Nes_Triangle triangle;
112 Nes_Dmc dmc;
113
114 cpu_time_t last_time; // has been run until this time in current frame
115 cpu_time_t earliest_irq_;
116 cpu_time_t next_irq;
117 int frame_period;
118 int frame_delay; // cycles until frame counter runs next
119 int frame; // current frame (0-3)
120 int osc_enables;
121 int frame_mode;
122 bool irq_flag;
123 void (*irq_notifier_)( void* user_data );
124 void* irq_data;
125 Nes_Square::Synth square_synth; // shared by squares
126
127 void irq_changed();
128 void state_restored();
129
130 friend struct Nes_Dmc;
131};
132
133inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf )
134{
135 assert(( "Nes_Apu::osc_output(): Index out of range", 0 <= osc && osc < osc_count ));
136 oscs [osc]->output = buf;
137}
138
139inline cpu_time_t Nes_Apu::earliest_irq() const
140{
141 return earliest_irq_;
142}
143
144inline void Nes_Apu::dmc_reader( int (*func)( void*, cpu_addr_t ), void* user_data )
145{
146 dmc.rom_reader_data = user_data;
147 dmc.rom_reader = func;
148}
149
150inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data )
151{
152 irq_notifier_ = func;
153 irq_data = user_data;
154}
155
156inline int Nes_Apu::count_dmc_reads( cpu_time_t time, cpu_time_t* last_read ) const
157{
158 return dmc.count_reads( time, last_read );
159}
160
161#endif
162
163