📄 nes_apu.c
字号:
noise->regs[1] = value;
noise->freq = APU_TO_FIXED(noise_freq[value & 0x0F]);
#ifdef REALTIME_NOISE
noise->xor_tap = (value & 0x80) ? 0x40: 0x02;
#else /* !REALTIME_NOISE */
/* detect transition from long->short sample */
if ((value & 0x80) && FALSE == noise->short_sample)
{
/* recalculate short noise buffer */
shift_register15(noise_short_lut, APU_NOISE_93);
noise->cur_pos = 0;
}
noise->short_sample = (value & 0x80) ? TRUE : FALSE;
#endif /* !REALTIME_NOISE */
}
#endif /* if !0 */
break;
case APU_WRD3:
#if 0
apu->apus.noise.regs[2] = value;
apu->apus.noise.vbl_length = vbl_lut[value >> 3];
apu->apus.noise.env_vol = 0; /* reset envelope */
if ( apu->enable_reg & 0x08) apu->apus.noise.enabled = TRUE;
#else
{
noise_t * noise = &(apu->apus.noise);
noise->regs[2] = value;
noise->vbl_length = vbl_lut[value >> 3];
noise->env_vol = 0; /* reset envelope */
if (apu->enable_reg & 0x08)
noise->enabled = TRUE;
}
#endif
break;
/* DMC */
case APU_WRE0:
#if 0
apu->apus.dmc.regs[0] = value;
apu->apus.dmc.freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]);
apu->apus.dmc.looping = (value & 0x40) ? TRUE : FALSE;
if (value & 0x80)
apu->apus.dmc.irq_gen = TRUE;
else
{
apu->apus.dmc.irq_gen = FALSE;
apu->apus.dmc.irq_occurred = FALSE;
}
#else
{
dmc_t * dmc = &(apu->apus.dmc);
dmc->regs[0] = value;
dmc->freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]);
dmc->looping = (value & 0x40) ? TRUE : FALSE;
if (value & 0x80)
dmc->irq_gen = TRUE;
else
{
dmc->irq_gen = FALSE;
dmc->irq_occurred = FALSE;
}
}
#endif
break;
case APU_WRE1: /* 7-bit DAC */
#if 0
/* add the _delta_ between written value and
** current output level of the volume reg
*/
value &= 0x7F; /* bit 7 ignored */
#ifndef APU_YANO
apu->apus.dmc.output_vol += ((value - apu->apus.dmc.regs[1]) << 8);
#endif /* !APU_YANO */
apu->apus.dmc.regs[1] = value;
#else
{
dmc_t * dmc = &(apu->apus.dmc);
value &= 0x7F; /* bit 7 ignored */
#ifndef APU_YANO
dmc->output_vol += ((value - dmc->regs[1]) << 8);
#endif
dmc->regs[1] = value;
}
break;
#endif
break;
case APU_WRE2:
#if 0
apu->apus.dmc.regs[2] = value;
apu->apus.dmc.cached_addr = 0xC000 + (uint16) (value << 6);
#else
{
dmc_t * dmc = &(apu->apus.dmc);
dmc->regs[2] = value;
dmc->cached_addr = 0xC000 + (uint16) (value << 6);
}
#endif
break;
case APU_WRE3:
#if 0
apu->apus.dmc.regs[3] = value;
apu->apus.dmc.cached_dmalength = ((value << 4) + 1) << 3;
#else
{
dmc_t * dmc = &(apu->apus.dmc);
dmc->regs[3] = value;
dmc->cached_dmalength = ((value << 4) + 1) << 3;
}
#endif
break;
case APU_SMASK:
/* bodge for timestamp queue */
#if 0
apu->apus.dmc.enabled = (value & 0x10) ? TRUE : FALSE;
apu->enable_reg = value;
for (chan = 0; chan < 2; chan++)
{
if (0 == (value & (1 << chan)) )
{
apu->apus.rectangle[chan].enabled = FALSE;
apu->apus.rectangle[chan].vbl_length = 0;
}
}
if (0 == (value & 0x04))
{
apu->apus.triangle.enabled = FALSE;
apu->apus.triangle.vbl_length = 0;
apu->apus.triangle.linear_length = 0;
apu->apus.triangle.counter_started = FALSE;
apu->apus.triangle.write_latency = 0;
}
if (0 == (value & 0x08))
{
apu->apus.noise.enabled = FALSE;
apu->apus.noise.vbl_length = 0;
}
if (value & 0x10)
{
if (0 == apu->apus.dmc.dma_length)
apu_dmcreload(&apu->apus.dmc);
}
else
{
apu->apus.dmc.dma_length = 0;
apu->apus.dmc.irq_occurred = FALSE;
}
#else
{
//int chan;
dmc_t * dmc = &(apu->apus.dmc);
apu->enable_reg = value;
if (0 == (value & 1))
{
rectangle_t * rect = &apu->apus.rectangle[0];
rect->enabled = FALSE;
rect->vbl_length = 0;
}
if (0 == (value & 2))
{
rectangle_t * rect = &apu->apus.rectangle[1];
rect->enabled = FALSE;
rect->vbl_length = 0;
}
if (0 == (value & 0x04))
{
triangle_t * tri = &(apu->apus.triangle);
tri->enabled = FALSE;
tri->vbl_length = 0;
tri->linear_length = 0;
tri->counter_started = FALSE;
tri->write_latency = 0;
}
if (0 == (value & 0x08))
{
noise_t * noise = &(apu->apus.noise);
noise->enabled = FALSE;
noise->vbl_length = 0;
}
if (value & 0x10)
{
dmc->enabled = TRUE;
if (0 == dmc->dma_length)
apu_dmcreload(dmc);
}
else
{
dmc->enabled = FALSE;
dmc->dma_length = 0;
dmc->irq_occurred = FALSE;
}
}
#endif
break;
/* unused, but they get hit in some mem-clear loops */
case 0x4009:
case 0x400D:
break;
case 0x4017:
if (value & 0x80)
apu_cnt_rate = 4;
else
apu_cnt_rate = 5;
break;
default:
break;
}
}
/* Read from $4000-$4017 */
uint8 apu_read(uint32 address)
{
uint8 value;
ASSERT(apu);
switch (address)
{
case APU_SMASK:
value = 0;
/* Return 1 in 0-5 bit pos if a channel is playing */
if (apu->apus.rectangle[0].enabled_cur && apu->apus.rectangle[0].vbl_length_cur>0)
value |= 0x01;
if (apu->apus.rectangle[1].enabled_cur && apu->apus.rectangle[1].vbl_length_cur>0)
value |= 0x02;
if (apu->apus.triangle.enabled_cur && apu->apus.triangle.vbl_length_cur>0)
value |= 0x04;
if (apu->apus.noise.enabled_cur && apu->apus.noise.vbl_length_cur>0)
value |= 0x08;
/* bodge for timestamp queue */
if (apu->apus.dmc.enabled_cur)
value |= 0x10;
if (apu->apus.dmc.irq_occurred_cur)
value |= 0x80;
break;
default:
value = (address >> 8); /* heavy capacitance on data bus */
break;
}
return value;
}
uint8 ex_read(uint32 address)
{
if(apu->ex_chip & 4)
{
return FDSSoundRead (address);
}
else if(apu->ex_chip & 16)
{
//return N106SoundReadData (address);
apudata_t d;
d.timestamp = nes6502_getcycles(FALSE);
d.address = address + 0x10000;
//ex_enqueue(&d);
apu_enqueue(&apu->ex_queue, &d);
return 0x00;
}
else
{
return 0x00;
}
}
void apu_write(uint32 address, uint8 value)
{
apudata_t d;
switch (address)
{
case 0x4015:
/* bodge for timestamp queue */
apu->apus.dmc.enabled = (value & 0x10) ? TRUE : FALSE;
case 0x4000: case 0x4001: case 0x4002: case 0x4003:
case 0x4004: case 0x4005: case 0x4006: case 0x4007:
case 0x4008: case 0x4009: case 0x400A: case 0x400B:
case 0x400C: case 0x400D: case 0x400E: case 0x400F:
case 0x4010: case 0x4011: case 0x4012: case 0x4013:
case 0x4017:
d.timestamp = nes6502_getcycles(FALSE);
d.address = address;
d.value = value;
//apu_enqueue(&d);
apu_enqueue(&apu->queue, &d);
break;
default:
break;
}
}
void apu_write_cur(uint32 address, uint8 value)
{
/* for sync read $4015 */
int chan;
switch (address)
{
case APU_WRA0:
case APU_WRB0:
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].holdnote_cur = (value & 0x20) ? TRUE : FALSE;
break;
case APU_WRA3:
case APU_WRB3:
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].vbl_length_cur = vbl_length[value >> 3] * 5;
if (apu->enable_reg_cur & (1<<chan))
apu->apus.rectangle[chan].enabled_cur = TRUE;
break;
case APU_WRC0:
apu->apus.triangle.holdnote_cur = (value & 0x80) ? TRUE : FALSE;
break;
case APU_WRC3:
apu->apus.triangle.vbl_length_cur = vbl_length[value >> 3] * 5;
if (apu->enable_reg_cur & 0x04)
apu->apus.triangle.enabled_cur = TRUE;
apu->apus.triangle.counter_started_cur = TRUE;
break;
case APU_WRD0:
apu->apus.noise.holdnote_cur = (value & 0x20) ? TRUE : FALSE;
break;
case APU_WRD3:
apu->apus.noise.vbl_length_cur = vbl_length[value >> 3] * 5;
if (apu->enable_reg_cur & 0x08)
apu->apus.noise.enabled_cur = TRUE;
break;
case APU_WRE0:
apu->apus.dmc.freq_cur = dmc_clocks[value & 0x0F];
apu->apus.dmc.phaseacc_cur = 0;
apu->apus.dmc.looping_cur = (value & 0x40) ? TRUE : FALSE;
if (value & 0x80)
apu->apus.dmc.irq_gen_cur = TRUE;
else
{
apu->apus.dmc.irq_gen_cur = FALSE;
apu->apus.dmc.irq_occurred_cur = FALSE;
}
break;
case APU_WRE3:
apu->apus.dmc.cached_dmalength_cur = (value << 4) + 1;
break;
case APU_SMASK:
apu->enable_reg_cur = value;
for (chan = 0; chan < 2; chan++)
{
if (0 == (value & (1 << chan)))
{
apu->apus.rectangle[chan].enabled_cur = FALSE;
apu->apus.rectangle[chan].vbl_length_cur = 0;
}
}
if (0 == (value & 0x04))
{
apu->apus.triangle.enabled_cur = FALSE;
apu->apus.triangle.vbl_length_cur = 0;
apu->apus.triangle.counter_started_cur = FALSE;
}
if (0 == (value & 0x08))
{
apu->apus.noise.enabled_cur = FALSE;
apu->apus.noise.vbl_length_cur = 0;
}
if (value & 0x10)
{
if(0 == apu->apus.dmc.dma_length_cur)
{
apu->apus.dmc.dma_length_cur = apu->apus.dmc.cached_dmalength_cur;
}
apu->apus.dmc.enabled_cur = TRUE;
}
else
{
apu->apus.dmc.dma_length_cur = 0;
apu->apus.dmc.enabled_cur = FALSE;
apu->apus.dmc.irq_occurred_cur = FALSE;
}
break;
}
}
void ex_write(uint32 address, uint8 value)
{
apudata_t d;
d.timestamp = nes6502_getcycles(FALSE);
d.address = address;
d.value = value;
//ex_enqueue(&d);
apu_enqueue(&apu->ex_queue, &d);
if(apu->ex_chip & 4)
{
FDSSoundWriteCurrent(address, value);
}
}
void apu_getpcmdata(void **data, int *num_samples, int *sample_bits)
{
ASSERT(apu);
*data = apu->buffer;
*num_samples = apu->num_samples;
*sample_bits = apu->sample_bits;
}
void apu_process(void *buffer, int num_samples)
{
apudata_t *d;
uint32 elapsed_cycles;
static int32 prev_sample = 0;
int32 next_sample, accum;
ASSERT(apu);
/* grab it, keep it local for speed */
elapsed_cycles = (uint32) apu->elapsed_cycles;
if (NULL == buffer)
{
/* just go through the motions... */
while (num_samples--)
{
apuqueue_t *q = &apu->queue;
apuqueue_t *ex_q = &apu->ex_queue;
/*while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles))
{
d = apu_dequeue();
apu_regwrite(d->address, d->value);
}*/
while ((FALSE == APU_QEMPTY(q)) && (q->queue[q->q_tail].timestamp <= elapsed_cycles))
{
d = &q->queue[q->q_tail];
q->q_tail = (q->q_tail + 1) & APUQUEUE_MASK;
apu_regwrite(d->address, d->value);
}
//while((FALSE == EX_QEMPTY()) && apu->ex_queue[apu->ex_q_tail].timestamp <= elapsed_cycles)
while((FALSE == APU_QEMPTY(ex_q)) && ex_q->queue[ex_q->q_tail].timestamp <= elapsed_cycles)
{
//d = ex_dequeue();
d = &ex_q->queue[ex_q->q_tail];
ex_q->q_tail = (ex_q->q_tail + 1) & APUQUEUE_MASK;
if(apu->ex_chip & 1)
{
VRC6SoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 2)
{
OPLLSoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 4)
{
FDSSoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 8)
{
MMC5SoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 16)
{
if(d->address & 0x10000)
{
uint8 dummy = N106SoundRead(d->address & 0xFFFF);
}
else
{
N106SoundWrite(d->address, d->value);
}
}
else if(apu->ex_chip & 32)
{
PSGSoundWrite(d->address, d->value);
}
}
elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate);
}
}
else
{
/* bleh */
apu->buffer = buffer;
while (num_samples--)
{
apuqueue_t *q = &apu->queue;
apuqueue_t *ex_q = &apu->ex_queue;
/*
while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles))
{
d = apu_dequeue();
apu_regwrite(d->address, d->value);
}
*/
while ((FALSE == APU_QEMPTY(q)) && (q->queue[q->q_tail].timestamp <= elapsed_cycles))
{
//d = apu_dequeue(q);
d = &q->queue[q->q_tail];
q->q_tail = (q->q_tail + 1) & APUQUEUE_MASK;
apu_regwrite(d->address, d->value);
}
//while((FALSE == EX_QEMPTY()) && apu->ex_queue[apu->ex_q_tail].timestamp <= elapsed_cycles)
while((FALSE == APU_QEMPTY(ex_q)) && ex_q->queue[ex_q->q_tail].timestamp <= elapsed_cycles)
{
//d = ex_dequeue();
d = &ex_q->queue[ex_q->q_tail];
ex_q->q_tail = (ex_q->q_tail + 1) & APUQUEUE_MASK;
if(apu->ex_chip & 1)
{
VRC6SoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 2)
{
OPLLSoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 4)
{
FDSSoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 8)
{
MMC5SoundWrite(d->address, d->value);
}
else if(apu->ex_chip & 16)
{
if(d->address & 0x10000)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -