|
|
|
@@ -12,16 +12,20 @@ |
|
|
|
#define apu_gain_dmc (240U) |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
static const uint8_t length_lut [32] = { |
|
|
|
|
|
|
|
static const uint8_t apu_length_lut[32] = { |
|
|
|
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, |
|
|
|
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, |
|
|
|
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, |
|
|
|
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E, |
|
|
|
}; |
|
|
|
*/ |
|
|
|
|
|
|
|
static const uint16_t dmc_period_lut [16] = { |
|
|
|
static const uint16_t noise_period_lut[16] = { |
|
|
|
4, 8, 16, 32, 64, 96, 128, 160, |
|
|
|
202, 254, 380, 508, 762, 1016, 2034, 4068, |
|
|
|
}; |
|
|
|
|
|
|
|
static const uint16_t dmc_period_lut[16] = { |
|
|
|
428, 380, 340, 320, 286, 254, 226, 214, |
|
|
|
190, 160, 142, 128, 106, 84, 72, 54, |
|
|
|
}; |
|
|
|
@@ -38,9 +42,12 @@ static const void nes_apu_dmc_fetch(nes_apu* apu, |
|
|
|
channel->mask = 1; |
|
|
|
channel->addr = (channel->addr + 1) & 0x7FFFU; |
|
|
|
channel->length--; |
|
|
|
if ( channel->length <= 0 && |
|
|
|
(channel->reg[0] & apu_DMC_Loop)) { |
|
|
|
dmc_restart(channel); |
|
|
|
if (channel->length <= 0) { |
|
|
|
if (channel->reg[0] & apu_DMC_Loop) { |
|
|
|
dmc_restart(channel); |
|
|
|
} else if (channel->reg[0] & apu_DMC_IRQ_Enable) { |
|
|
|
channel->interrupt = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@@ -65,7 +72,14 @@ static void nes_apu_write_triangle(nes_apu_Channel* channel, |
|
|
|
static void nes_apu_write_noise(nes_apu_Channel* channel, |
|
|
|
int reg, uint8_t val) { |
|
|
|
APU_LOG("APU: Noise %d < %02x\n", reg, val); |
|
|
|
// TODO |
|
|
|
|
|
|
|
if (reg == 0) { |
|
|
|
channel->envelope = (val & apu_Envelope_Volume); |
|
|
|
|
|
|
|
} else if (reg == 2) { |
|
|
|
channel->period = noise_period_lut[val & apu_Noise_Period] * |
|
|
|
nes_clock_cpu_div; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void nes_apu_write_dmc(nes_apu_Channel* channel, |
|
|
|
@@ -73,7 +87,7 @@ static void nes_apu_write_dmc(nes_apu_Channel* channel, |
|
|
|
APU_LOG("APU: DMC %d < %02x\n", reg, val); |
|
|
|
|
|
|
|
if (reg == 0) { |
|
|
|
channel->period = dmc_period_lut[val & 0xFU] * |
|
|
|
channel->period = dmc_period_lut[val & apu_DMC_Period] * |
|
|
|
nes_clock_cpu_div; |
|
|
|
if (!(val & apu_DMC_IRQ_Enable)) { |
|
|
|
channel->interrupt = 0; |
|
|
|
@@ -116,7 +130,8 @@ int nes_apu_init(nes_apu* apu, int clock, int frequency, |
|
|
|
apu->channels[apu_Channel_Noise].gain = apu_gain_noise; |
|
|
|
apu->channels[apu_Channel_DMC].gain = apu_gain_dmc; |
|
|
|
|
|
|
|
apu->frame_lfsr = 0x7FFFU; |
|
|
|
apu->channels[apu_Channel_Noise].lfsr = 0x7FFFU; |
|
|
|
|
|
|
|
apu->frame_period = clock / 240; |
|
|
|
} |
|
|
|
|
|
|
|
@@ -171,6 +186,15 @@ void nes_apu_write(nes_apu* apu, uint16_t addr, uint8_t val) { |
|
|
|
int reg = (addr - nes_apu_reg_base) % nes_apu_chan_size; |
|
|
|
|
|
|
|
nes_apu_Channel* channel = &apu->channels[chan]; |
|
|
|
if (3 == reg) { |
|
|
|
if ( chan != apu_Channel_DMC && |
|
|
|
apu->status & (1 << chan)) { |
|
|
|
channel->length = apu_length_lut[val >> 3]; |
|
|
|
channel->envelope = 15; |
|
|
|
channel->env_period = (channel->reg[0] & apu_Envelope_Volume); |
|
|
|
channel->env_delay = channel->env_period; |
|
|
|
} |
|
|
|
} |
|
|
|
channel->write(channel, reg, val); |
|
|
|
channel->reg[reg] = val; |
|
|
|
|
|
|
|
@@ -220,7 +244,7 @@ static int channel_update(nes_apu_Channel* channel) { |
|
|
|
static void nes_apu_run_dmc(nes_apu* apu, |
|
|
|
nes_apu_Channel* channel, |
|
|
|
int cycles) { |
|
|
|
if (channel->length <= 0) return; |
|
|
|
if (channel->length <= 0 || channel->period <= 0) return; |
|
|
|
|
|
|
|
int time = apu->time; |
|
|
|
int delta = channel_update(channel); |
|
|
|
@@ -260,6 +284,72 @@ static void nes_apu_run_dmc(nes_apu* apu, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline int apu_envelope_volume(nes_apu_Channel* channel) { |
|
|
|
return ( (channel->reg[0] & apu_Envelope_Constant) ? |
|
|
|
(channel->reg[0] & apu_Envelope_Volume) : |
|
|
|
(channel->envelope) ); |
|
|
|
} |
|
|
|
|
|
|
|
static void nes_apu_run_noise(nes_apu* apu, |
|
|
|
nes_apu_Channel* channel, |
|
|
|
int cycles) { |
|
|
|
if (channel->length <= 0 || channel->period <= 0) return; |
|
|
|
|
|
|
|
int time = apu->time; |
|
|
|
|
|
|
|
while (cycles > 0) { |
|
|
|
int run = cycles; |
|
|
|
if (run > channel->delay) { |
|
|
|
run = channel->delay; |
|
|
|
} |
|
|
|
|
|
|
|
channel->delay -= run; |
|
|
|
time += run; |
|
|
|
|
|
|
|
if (channel->delay <= 0) { |
|
|
|
channel->delay = channel->period; |
|
|
|
|
|
|
|
uint16_t feedback = (1 & (channel->lfsr ^ ( |
|
|
|
channel->lfsr >> ( |
|
|
|
(channel->reg[2] & apu_Noise_Mode) ? |
|
|
|
6 : 1 |
|
|
|
) |
|
|
|
) |
|
|
|
)); |
|
|
|
channel->lfsr = (feedback << 14) | |
|
|
|
(channel->lfsr >> 1); |
|
|
|
channel->sample = (channel->lfsr & 1) ? |
|
|
|
apu_envelope_volume(channel) : 0; |
|
|
|
int delta = channel_update(channel); |
|
|
|
if (delta) { |
|
|
|
blip_add_delta(apu->blip, time, delta); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
cycles -= run; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline void nes_apu_clock_length(nes_apu_Channel* channel, |
|
|
|
uint8_t halt_mask) { |
|
|
|
if ( channel->length > 0 && |
|
|
|
!(channel->reg[0] & halt_mask)) { |
|
|
|
channel->length--; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline void nes_apu_clock_envelope(nes_apu_Channel* channel) { |
|
|
|
if (channel->env_delay <= 0) { |
|
|
|
channel->env_delay = channel->env_period; |
|
|
|
if ( channel->envelope > 0 || |
|
|
|
(channel->reg[0] & apu_Envelope_Halt)) { |
|
|
|
channel->envelope = ((channel->envelope - 1) & 0xFU); |
|
|
|
} |
|
|
|
} else { |
|
|
|
channel->env_delay--; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { |
|
|
|
nes_apu_run_dmc(apu, &apu->channels[apu_Channel_DMC], cycles); |
|
|
|
|
|
|
|
@@ -275,9 +365,10 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { |
|
|
|
apu->blip, run); |
|
|
|
nes_apu_run_triangle(&apu->channels[apu_Channel_Triangle], |
|
|
|
apu->blip, run); |
|
|
|
nes_apu_run_noise(&apu->channels[apu_Channel_Noise], |
|
|
|
apu->blip, run); |
|
|
|
*/ |
|
|
|
nes_apu_run_noise(apu, &apu->channels[apu_Channel_Noise], |
|
|
|
run); |
|
|
|
|
|
|
|
apu->frame_delay -= run; |
|
|
|
|
|
|
|
if (apu->frame_delay <= 0) { |
|
|
|
@@ -307,11 +398,12 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { |
|
|
|
|
|
|
|
if (half_clock) { |
|
|
|
/* |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Square_0], 32); |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Square_1], 32); |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Triangle], 128); |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Noise], 32); |
|
|
|
|
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Square_0], apu_Envelope_Halt); |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Square_1], apu_Envelope_Halt); |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Triangle], apu_Triangle_Halt); |
|
|
|
*/ |
|
|
|
nes_apu_clock_length(&apu->channels[apu_Channel_Noise], apu_Envelope_Halt); |
|
|
|
/* |
|
|
|
nes_apu_clock_sweep(&apu->channels[apu_Channel_Square_0], -1); |
|
|
|
nes_apu_clock_sweep(&apu->channels[apu_Channel_Square_1], 0); |
|
|
|
*/ |
|
|
|
@@ -321,8 +413,10 @@ nes_apu_Result nes_apu_run(nes_apu* apu, int cycles) { |
|
|
|
/* |
|
|
|
nes_apu_clock_envelope(&apu->channels[apu_Channel_Square_0]); |
|
|
|
nes_apu_clock_envelope(&apu->channels[apu_Channel_Square_1]); |
|
|
|
nes_apu_clock_linear(&apu->channels[apu_Channel_Triangle]); |
|
|
|
*/ |
|
|
|
nes_apu_clock_envelope(&apu->channels[apu_Channel_Noise]); |
|
|
|
/* |
|
|
|
nes_apu_clock_linear(&apu->channels[apu_Channel_Triangle]); |
|
|
|
*/ |
|
|
|
} |
|
|
|
|
|
|
|
|