Skip to content

Commit 3a17e2a

Browse files
Rewrite nes apu core
- start using structs for each channel - revised duty cycle / step counter in square duty logic - use lookup table for square duty cycle and triangle wave - use more accurate frame counter timing (pass some tests, improves accuracy but causes moderate loss of performance) - cleanups and others
1 parent 32d8fd3 commit 3a17e2a

File tree

3 files changed

+1014
-810
lines changed

3 files changed

+1014
-810
lines changed

src/apu.h

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#ifndef _FCEU_APU_H
2+
#define _FCEU_APU_H
3+
4+
enum FrameSeqMode {
5+
FrameFourStepMode = 0,
6+
FrameFiveStepMode = 1
7+
};
8+
9+
enum FrameType {
10+
FrameNone = 0,
11+
FrameHalf = 1,
12+
FrameQuarter = 2
13+
};
14+
15+
enum WavePositionShift {
16+
SQ_SHIFT = 24,
17+
TRINPCM_SHIFT = 16
18+
};
19+
20+
typedef struct Timer {
21+
uint16 period; /* Frequency period for square wave (controls pitch) */
22+
23+
/* Internal */
24+
int32 counter; /* Counter tracking the square wave period (timing of cycles) */
25+
int32 count2; /* Shifted period timer for low-quality mode */
26+
} Timer;
27+
28+
typedef struct LengthCount {
29+
/* Registers */
30+
uint8 enabled; /* set by channel enable flag in 4015 write */
31+
uint8 halt; /* Halt flag: if set, disables further counting */
32+
33+
uint8 counter; /* Current count value for length counter (counts down) */
34+
35+
uint8 delayHalt;
36+
uint8 delayCounter;
37+
uint8 nextHalt;
38+
uint8 nextCounter;
39+
} LengthCount;
40+
41+
typedef struct Envelope {
42+
/* Register */
43+
uint8 loop; /* Loop mode flag: if set, envelope restarts after reaching a certain state */
44+
uint8 constant; /* Constant mode flag: if set, volume remains constant */
45+
uint8 speed; /* Speed of volume decay, affecting the rate of decrease */
46+
47+
/* internal */
48+
uint8 decay_volume; /* Current volume level during the decay phase */
49+
uint8 counter; /* Counter tracking the current state of decay process */
50+
uint8 reload; /* Flag to reload decay counter (restarts volume decrease) */
51+
} Envelope;
52+
53+
typedef struct Sweep {
54+
/* Register */
55+
uint8 enabled; /* Enable flag for sweep operation (if set, sweep occurs) */
56+
uint8 period; /* Sweep period: determines the rate at which frequency is adjusted */
57+
uint8 negate; /* Negate flag: if set, subtracts from the frequency during sweep */
58+
uint8 shift; /* Number of bits to shift for frequency adjustment in the sweep */
59+
60+
uint16 pulsePeriod; /* Period of the pulse being swept (frequency of waveform) */
61+
62+
/* Internal */
63+
uint8 id;
64+
uint8 counter; /* Counter for sweep timing (decrements until reload) */
65+
uint8 reload; /* Reload flag: when set, resets sweep counter to initial value */
66+
} Sweep;
67+
68+
typedef struct SquareUnit {
69+
/* Registers*/
70+
uint8 duty; /* Duty cycle defines the waveform's high-to-low ratio */
71+
72+
/* Internal */
73+
uint8 step; /* Counter for tracking the current phase of the duty cycle */
74+
75+
LengthCount length; /* Length counter to manage note duration for square wave */
76+
Envelope envelope; /* Envelope to control volume for square wave */
77+
Sweep sweep; /* Sweep to modify frequency of square wave */
78+
Timer timer; /* Cycle counter and period to reload */
79+
} SquareUnit;
80+
81+
typedef struct TriangleUnit {
82+
uint8 linearPeriod; /* Linear length control value for the triangle wave */
83+
84+
/* Internal */
85+
uint8 linearCounter; /* Counter to track linear length during envelope decay */
86+
uint8 linearReload; /* Flag to reload the linear length counter */
87+
uint8 stepCounter; /* Counter for tracking the steps in the triangle wave's length */
88+
89+
LengthCount length; /* Length counter for triangle wave channel (note duration) */
90+
Timer timer; /* Cycle counter and period to reload */
91+
} TriangleUnit;
92+
93+
typedef struct NoiseUnit {
94+
/* Register */
95+
uint8 shortMode; /* Short mode flag for noise wave: alters frequency generation behavior */
96+
uint8 periodIndex; /* The period determines how many CPU cycles happen between shift register clocks. */
97+
98+
/* Internal */
99+
uint16 shiftRegister; /* Shift register used to generate noise waveform */
100+
101+
LengthCount length; /* Length counter for noise wave channel (note duration) */
102+
Envelope envelope; /* Envelope to control volume for noise wave */
103+
Timer timer; /* Cycle counter and period to reload */
104+
} NoiseUnit;
105+
106+
typedef struct DMCUnit {
107+
uint8 bitCounter; /* Bit counter for reading sample data bits (tracks position in current sample) */
108+
109+
uint8 addressLatch; /* Address latch for DMC sample address (mapped to $4012) */
110+
uint8 lengthLatch; /* Length latch for DMC sample length (mapped to $4013) */
111+
uint8 periodIndex; /* The rate determines for how many CPU cycles happen between changes in the output level during automatic delta-encoded sample playback */
112+
uint8 loop; /* Loop flag for DMC sample (1 = loop, 0 = no loop) */
113+
uint8 irqEnabled; /* IRQ enabled flag: if set, triggers IRQ when sample finishes loading */
114+
uint8 irqPending; /* Flag indicating if an IRQ is pending */
115+
116+
uint16 readAddress; /* Address to read data from in memory for DMC sample */
117+
uint16 lengthCounter; /* Counter for the length of the sample data to play */
118+
uint8 sampleShiftReg; /* Holds the current sample data during DMA shift processing */
119+
120+
uint8 dmaBufferValid; /* Flag indicating whether the DMA buffer contains valid data */
121+
uint8 dmaBuffer; /* DMA buffer for transferring sample data to audio output */
122+
uint8 sampleValid; /* Sample validity flag: indicates if sample data is properly loaded */
123+
uint8 rawDataLatch; /* Raw data latch for DMC control (mapped to $4011 0xxxxxxx) */
124+
125+
Timer timer;
126+
} DMCUnit;
127+
128+
typedef struct FrameCounter {
129+
uint8 mode; /* Mode of the frame counter operation, 1=5-step, 0=4-step */
130+
uint8 irqInhibit; /* Flag to inhibit IRQ generation */
131+
uint8 irqPending; /* Flag indicating if an IRQ is pending */
132+
uint8 step; /* Current step in frame counter operation */
133+
134+
/* Timers */
135+
int32 counter; /* frame cycle counter */
136+
137+
uint8 delay; /* Delay after 4017 write */
138+
uint8 newMode;
139+
} FrameCounter;
140+
141+
#endif /* _FCEU_APU_H */

0 commit comments

Comments
 (0)