diff options
Diffstat (limited to 'src/devices/sound/nes_apu.cpp')
-rw-r--r-- | src/devices/sound/nes_apu.cpp | 527 |
1 files changed, 266 insertions, 261 deletions
diff --git a/src/devices/sound/nes_apu.cpp b/src/devices/sound/nes_apu.cpp index 27ef8977f72..3f8755398bc 100644 --- a/src/devices/sound/nes_apu.cpp +++ b/src/devices/sound/nes_apu.cpp @@ -4,16 +4,12 @@ MAME/MESS NES APU CORE - Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by + Based on the Nofrendo/Nosefart NES RP2A03 sound emulation core written by Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by Who Wants to Know? (wwtk@mail.com) This core is written with the advise and consent of Matthew Conte and is - released under the GNU Public License. This core is freely available for - use in any freeware project, subject to the following terms: - - Any modifications to this code must be duly noted in the source and - approved by Matthew Conte and myself prior to public submission. + released under the GNU Public License. timing notes: master = 21477270 @@ -22,7 +18,7 @@ ***************************************************************************** - NES_APU.C + NES_APU.CPP Actual NES APU interface. @@ -33,10 +29,6 @@ point should be just about 100% accurate, though I cannot tell for certain as yet. - A queue interface is also available for additional speed. However, - the implementation is not yet 100% (DPCM sounds are inaccurate), - so it is disabled by default. - ***************************************************************************** BUGFIXES: @@ -49,114 +41,67 @@ #include "emu.h" #include "nes_apu.h" -/* INTERNAL FUNCTIONS */ +DEFINE_DEVICE_TYPE(NES_APU, nesapu_device, "nesapu", "RP2A0X APU") +DEFINE_DEVICE_TYPE(APU_2A03, apu2a03_device, "apu2a03", "RP2A03 APU") -/* INITIALIZE WAVE TIMES RELATIVE TO SAMPLE RATE */ -static void create_vbltimes(u32 * table,const u8 *vbl,unsigned int rate) +nesapu_device::nesapu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_is_pal(0) + , m_samps_per_sync(0) + , m_stream(nullptr) + , m_irq_handler(*this) + , m_mem_read_cb(*this, 0x00) + , m_frame_timer(nullptr) { - int i; - - for (i = 0; i < 0x20; i++) - table[i] = vbl[i] * rate; } -/* INITIALIZE SAMPLE TIMES IN TERMS OF VSYNCS */ -void nesapu_device::create_syncs(unsigned long sps) +nesapu_device::nesapu_device(const machine_config& mconfig, const char* tag, device_t* owner, u32 clock) + : nesapu_device(mconfig, NES_APU, tag, owner, clock) { - int i; - unsigned long val = sps; - - for (i = 0; i < SYNCS_MAX1; i++) - { - m_sync_times1[i] = val; - val += sps; - } - - val = 0; - for (i = 0; i < SYNCS_MAX2; i++) - { - m_sync_times2[i] = val; - m_sync_times2[i] >>= 2; - val += sps; - } } -/* INITIALIZE NOISE LOOKUP TABLE */ -static void create_noise(u8 *buf, const int bits, int size) +apu2a03_device::apu2a03_device(const machine_config& mconfig, const char* tag, device_t* owner, u32 clock) + : nesapu_device(mconfig, APU_2A03, tag, owner, clock) { - int m = 0x0011; - int xor_val, i; - - for (i = 0; i < size; i++) - { - xor_val = m & 1; - m >>= 1; - xor_val ^= (m & 1); - m |= xor_val << (bits - 1); - - buf[i] = m; - } } -DEFINE_DEVICE_TYPE(NES_APU, nesapu_device, "nesapu", "N2A03 APU") - -nesapu_device::nesapu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, NES_APU, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , m_samps_per_sync(0) - , m_buffer_size(0) - , m_stream(nullptr) - , m_irq_handler(*this) - , m_mem_read_cb(*this) -{ - for (auto & elem : m_noise_lut) - { - elem = 0; - } - - for (auto & elem : m_vbl_times) - { - elem = 0; - } - - for (auto & elem : m_sync_times1) - { - elem = 0; - } - - for (auto & elem : m_sync_times2) - { - elem = 0; - } -} void nesapu_device::device_reset() { write(0x15, 0x00); + m_APU.tri.adder = 0; + + if (m_APU.frame_irq_enabled) + m_frame_timer->adjust(m_frame_period, 0, m_frame_period); } void nesapu_device::device_clock_changed() { calculate_rates(); + m_is_pal = m_clock == PAL_APU_CLOCK; } void nesapu_device::calculate_rates() { - int rate = clock() / 4; + m_samps_per_sync = m_frame_clocks / 4; // FIXME: tables are 4-step mode ONLY - m_samps_per_sync = 89490 / 12; // Is there a different PAL value? - m_buffer_size = m_samps_per_sync; + // initialize sample times in terms of vsyncs + for (int i = 0; i < SYNCS_MAX1; i++) + { + m_vbl_times[i] = vbl_length[i] * m_samps_per_sync / 2; + m_sync_times1[i] = m_samps_per_sync * (i + 1); + } - create_vbltimes(m_vbl_times,vbl_length,m_samps_per_sync); - create_syncs(m_samps_per_sync); + for (int i = 0; i < SYNCS_MAX2; i++) + m_sync_times2[i] = (m_samps_per_sync * i) >> 2; - /* Adjust buffer size if 16 bits */ - m_buffer_size+=m_samps_per_sync; + int rate = clock() / 4; if (m_stream != nullptr) m_stream->set_sample_rate(rate); else - m_stream = machine().sound().stream_alloc(*this, 0, 1, rate); + m_stream = stream_alloc(0, 1, rate); } //------------------------------------------------- @@ -165,14 +110,56 @@ void nesapu_device::calculate_rates() void nesapu_device::device_start() { - // resolve callbacks - m_irq_handler.resolve_safe(); - m_mem_read_cb.resolve_safe(0x00); - - create_noise(m_noise_lut, 13, apu_t::NOISE_LONG); + m_frame_timer = timer_alloc(FUNC(nesapu_device::frame_timer_cb), this); + m_frame_clocks = m_is_pal ? 33254 : 29830; + m_frame_period = clocks_to_attotime(m_frame_clocks); + m_APU.step_mode = 4; + m_APU.frame_irq_enabled = true; calculate_rates(); + // calculate mixer output + /* + pulse channel output: + + 95.88 + ----------------------- + 8128 + ----------------- + 100 + pulse 1 + pulse 2 + + */ + for (int i = 0; i < 31; i++) + { + sound_stream::sample_t pulse_out = (i == 0) ? 0.0 : 95.88 / ((8128.0 / i) + 100.0); + m_square_lut[i] = pulse_out; + } + + /* + triangle, noise, DMC channel output: + + 159.79 + ------------------------------- + 1 + ------------------------- + 100 + triangle noise dmc + -------- + ----- + ----- + 8227 12241 22638 + + */ + for (int t = 0; t < 16; t++) + { + for (int n = 0; n < 16; n++) + { + for (int d = 0; d < 128; d++) + { + sound_stream::sample_t tnd_out = (t / 8227.0) + (n / 12241.0) + (d / 22638.0); + tnd_out = (tnd_out == 0.0) ? 0.0 : 159.79 / ((1.0 / tnd_out) + 100.0); + m_tnd_lut[t][n][d] = tnd_out; + } + } + } + /* register for save */ for (int i = 0; i < 2; i++) { @@ -180,64 +167,70 @@ void nesapu_device::device_start() save_item(NAME(m_APU.squ[i].vbl_length), i); save_item(NAME(m_APU.squ[i].freq), i); save_item(NAME(m_APU.squ[i].phaseacc), i); - save_item(NAME(m_APU.squ[i].output_vol), i); save_item(NAME(m_APU.squ[i].env_phase), i); save_item(NAME(m_APU.squ[i].sweep_phase), i); save_item(NAME(m_APU.squ[i].adder), i); save_item(NAME(m_APU.squ[i].env_vol), i); save_item(NAME(m_APU.squ[i].enabled), i); + save_item(NAME(m_APU.squ[i].output), i); } save_item(NAME(m_APU.tri.regs)); save_item(NAME(m_APU.tri.linear_length)); + save_item(NAME(m_APU.tri.linear_reload)); save_item(NAME(m_APU.tri.vbl_length)); save_item(NAME(m_APU.tri.write_latency)); save_item(NAME(m_APU.tri.phaseacc)); - save_item(NAME(m_APU.tri.output_vol)); save_item(NAME(m_APU.tri.adder)); save_item(NAME(m_APU.tri.counter_started)); save_item(NAME(m_APU.tri.enabled)); + save_item(NAME(m_APU.tri.output)); save_item(NAME(m_APU.noi.regs)); - save_item(NAME(m_APU.noi.cur_pos)); + save_item(NAME(m_APU.noi.lfsr)); save_item(NAME(m_APU.noi.vbl_length)); save_item(NAME(m_APU.noi.phaseacc)); - save_item(NAME(m_APU.noi.output_vol)); save_item(NAME(m_APU.noi.env_phase)); save_item(NAME(m_APU.noi.env_vol)); save_item(NAME(m_APU.noi.enabled)); + save_item(NAME(m_APU.noi.output)); save_item(NAME(m_APU.dpcm.regs)); save_item(NAME(m_APU.dpcm.address)); save_item(NAME(m_APU.dpcm.length)); save_item(NAME(m_APU.dpcm.bits_left)); save_item(NAME(m_APU.dpcm.phaseacc)); - save_item(NAME(m_APU.dpcm.output_vol)); save_item(NAME(m_APU.dpcm.cur_byte)); save_item(NAME(m_APU.dpcm.enabled)); save_item(NAME(m_APU.dpcm.irq_occurred)); save_item(NAME(m_APU.dpcm.vol)); + save_item(NAME(m_APU.dpcm.output)); - save_item(NAME(m_APU.regs)); - - #ifdef USE_QUEUE - save_item(NAME(m_APU.queue)); - save_item(NAME(m_APU.head)); - save_item(NAME(m_APU.tail)); - #else - save_item(NAME(m_APU.buf_pos)); save_item(NAME(m_APU.step_mode)); - #endif + save_item(NAME(m_APU.frame_irq_enabled)); + save_item(NAME(m_APU.frame_irq_occurred)); } + +TIMER_CALLBACK_MEMBER(nesapu_device::frame_timer_cb) +{ + m_stream->update(); + + if (m_APU.step_mode == 4 && m_APU.frame_irq_enabled) + { + m_APU.frame_irq_occurred = true; + m_irq_handler(true); + } +} + + /* TODO: sound channels should *ALL* have DC volume decay */ -/* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM -16 to +15) */ -s8 nesapu_device::apu_square(apu_t::square_t *chan) +/* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM 0 to +15) */ +void nesapu_device::apu_square(apu_t::square_t *chan) { int env_delay; int sweep_delay; - s8 output; /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on @@ -245,11 +238,14 @@ s8 nesapu_device::apu_square(apu_t::square_t *chan) ** reg3: 0-2=high freq, 7-4=vbl length counter */ - if (false == chan->enabled) - return 0; + if (!chan->enabled) + { + chan->output = 0; + return; + } /* enveloping */ - env_delay = m_sync_times1[chan->regs[0] & 0x0F]; + env_delay = m_sync_times1[chan->regs[0] & 0x0f]; /* decay is at a rate of (env_regs + 1) / 240 secs */ chan->env_phase -= 4; @@ -263,11 +259,14 @@ s8 nesapu_device::apu_square(apu_t::square_t *chan) } /* vbl length counter */ - if (chan->vbl_length > 0 && 0 == (chan->regs [0] & 0x20)) + if (chan->vbl_length > 0 && !(chan->regs[0] & 0x20)) chan->vbl_length--; - if (0 == chan->vbl_length) - return 0; + if (!chan->vbl_length) + { + chan->output = 0; + return; + } /* freqsweeps */ if ((chan->regs[1] & 0x80) && (chan->regs[1] & 7)) @@ -284,104 +283,103 @@ s8 nesapu_device::apu_square(apu_t::square_t *chan) } } - if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[chan->regs[1] & 7]) + if ((!(chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[chan->regs[1] & 7]) || (chan->freq >> 16) < 4) - return 0; + { + chan->output = 0; + return; + } chan->phaseacc -= 4; while (chan->phaseacc < 0) { chan->phaseacc += (chan->freq >> 16); - chan->adder = (chan->adder + 1) & 0x0F; + chan->adder = (chan->adder + 1) & 0x0f; } if (chan->regs[0] & 0x10) /* fixed volume */ - output = chan->regs[0] & 0x0F; + chan->output = chan->regs[0] & 0x0f; else - output = 0x0F - chan->env_vol; + chan->output = 0x0f - chan->env_vol; - if (chan->adder < (duty_lut[chan->regs[0] >> 6])) - output = -output; - - return (s8) output; + chan->output *= BIT(duty_lut[chan->regs[0] >> 6], 7 - BIT(chan->adder, 1, 3)); } -/* OUTPUT TRIANGLE WAVE SAMPLE (VALUES FROM -16 to +15) */ -s8 nesapu_device::apu_triangle(apu_t::triangle_t *chan) +/* OUTPUT TRIANGLE WAVE SAMPLE (VALUES FROM 0 to +15) */ +void nesapu_device::apu_triangle(apu_t::triangle_t *chan) { - int freq; - s8 output; /* reg0: 7=holdnote, 6-0=linear length counter ** reg2: low 8 bits of frequency ** reg3: 7-3=length counter, 2-0=high 3 bits of frequency */ - if (false == chan->enabled) - return 0; + if (!chan->enabled) + return; + + bool not_held = !BIT(chan->regs[0], 7); - if (false == chan->counter_started && 0 == (chan->regs[0] & 0x80)) + if (!chan->counter_started && not_held) { if (chan->write_latency) chan->write_latency--; - if (0 == chan->write_latency) + if (!chan->write_latency) chan->counter_started = true; } if (chan->counter_started) { - if (chan->linear_length > 0) + if (chan->linear_reload) + chan->linear_length = m_sync_times2[chan->regs[0] & 0x7f]; + else if (chan->linear_length > 0) chan->linear_length--; - if (chan->vbl_length && 0 == (chan->regs[0] & 0x80)) - chan->vbl_length--; - if (0 == chan->vbl_length) - return 0; + if (not_held) + chan->linear_reload = false; + + if (chan->vbl_length && not_held) + chan->vbl_length--; } - if (0 == chan->linear_length) - return 0; + if (!(chan->linear_length && chan->vbl_length)) + return; - freq = (((chan->regs[3] & 7) << 8) + chan->regs[2]) + 1; + int freq = ((chan->regs[3] & 7) << 8) + chan->regs[2] + 1; - if (freq < 4) /* inaudible */ - return 0; +// FIXME: This halts ultrasonic frequencies. On hardware there should be some popping noise? Crash Man's stage in Mega Man 2 is an example. This can probably be removed if hardware filters are implemented (they vary by machine, NES, FC, VS, etc). + if (freq < 2) + return; chan->phaseacc -= 4; while (chan->phaseacc < 0) { chan->phaseacc += freq; - chan->adder = (chan->adder + 1) & 0x1F; + chan->adder++; - output = (chan->adder & 7) << 1; - if (chan->adder & 8) - output = 0x10 - output; - if (chan->adder & 0x10) - output = -output; - - chan->output_vol = output; + chan->output = chan->adder & 0xf; + if (!BIT(chan->adder, 4)) + chan->output ^= 0xf; } - - return (s8) chan->output_vol; } -/* OUTPUT NOISE WAVE SAMPLE (VALUES FROM -16 to +15) */ -s8 nesapu_device::apu_noise(apu_t::noise_t *chan) +/* OUTPUT NOISE WAVE SAMPLE (VALUES FROM 0 to +15) */ +void nesapu_device::apu_noise(apu_t::noise_t *chan) { int freq, env_delay; - u8 outvol; - u8 output; /* reg0: 0-3=volume, 4=envelope, 5=hold ** reg2: 7=small(93 byte) sample,3-0=freq lookup ** reg3: 7-4=vbl length counter */ - if (false == chan->enabled) - return 0; + if (!chan->enabled) + { + chan->output = 0; + return; + } /* enveloping */ - env_delay = m_sync_times1[chan->regs[0] & 0x0F]; + env_delay = m_sync_times1[chan->regs[0] & 0x0f]; /* decay is at a rate of (env_regs + 1) / 240 secs */ chan->env_phase -= 4; @@ -395,57 +393,62 @@ s8 nesapu_device::apu_noise(apu_t::noise_t *chan) } /* length counter */ - if (0 == (chan->regs[0] & 0x20)) + if (!(chan->regs[0] & 0x20)) { if (chan->vbl_length > 0) chan->vbl_length--; } - if (0 == chan->vbl_length) - return 0; + if (!chan->vbl_length) + { + chan->output = 0; + return; + } - freq = noise_freq[chan->regs[2] & 0x0F]; + freq = noise_freq[m_is_pal][chan->regs[2] & 0x0f]; chan->phaseacc -= 4; while (chan->phaseacc < 0) { chan->phaseacc += freq; + update_lfsr(*chan); + } - chan->cur_pos++; - if (apu_t::NOISE_SHORT == chan->cur_pos && (chan->regs[2] & 0x80)) - chan->cur_pos = 0; - else if (apu_t::NOISE_LONG == chan->cur_pos) - chan->cur_pos = 0; + if (BIT(chan->lfsr, 0)) /* silence channel */ + { + chan->output = 0; + return; } if (chan->regs[0] & 0x10) /* fixed volume */ - outvol = chan->regs[0] & 0x0F; + chan->output = chan->regs[0] & 0x0f; else - outvol = 0x0F - chan->env_vol; - - output = m_noise_lut[chan->cur_pos]; - if (output > outvol) - output = outvol; + chan->output = 0x0f - chan->env_vol; +} - if (m_noise_lut[chan->cur_pos] & 0x80) /* make it negative */ - output = -output; +void nesapu_device::update_lfsr(apu_t::noise_t &chan) +{ + chan.lfsr |= (BIT(chan.lfsr, 0) ^ BIT(chan.lfsr, (chan.regs[2] & 0x80) ? 6 : 1)) << 15; + chan.lfsr >>= 1; +} - return (s8) output; +void apu2a03_device::update_lfsr(apu_t::noise_t &chan) +{ + chan.lfsr |= (BIT(chan.lfsr, 0) ^ BIT(chan.lfsr, 1)) << 15; + chan.lfsr >>= 1; } /* RESET DPCM PARAMETERS */ static inline void apu_dpcmreset(apu_t::dpcm_t *chan) { - chan->address = 0xC000 + u16(chan->regs[2] << 6); + chan->address = 0xc000 + u16(chan->regs[2] << 6); chan->length = u16(chan->regs[3] << 4) + 1; chan->bits_left = chan->length << 3; - chan->irq_occurred = false; chan->enabled = true; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ - chan->vol = 0; /* Fixed * DPCM DAC resets itself when restarted */ } -/* OUTPUT DPCM WAVE SAMPLE (VALUES FROM -64 to +63) */ +/* OUTPUT DPCM WAVE SAMPLE (VALUES FROM 0 to +127) */ /* TODO: centerline naughtiness */ -s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan) +void nesapu_device::apu_dpcm(apu_t::dpcm_t *chan) { int freq, bit_pos; @@ -457,17 +460,16 @@ s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan) if (chan->enabled) { - freq = dpcm_clocks[chan->regs[0] & 0x0F]; + freq = dpcm_clocks[m_is_pal][chan->regs[0] & 0x0f]; chan->phaseacc -= 4; while (chan->phaseacc < 0) { chan->phaseacc += freq; - if (0 == chan->length) + if (!chan->length) { chan->enabled = false; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ - chan->vol=0; /* Fixed * DPCM DAC resets itself when restarted */ if (chan->regs[0] & 0x40) apu_dpcmreset(chan); else @@ -481,7 +483,6 @@ s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan) } } - chan->bits_left--; bit_pos = 7 - (chan->bits_left & 7); if (7 == bit_pos) @@ -491,29 +492,26 @@ s8 nesapu_device::apu_dpcm(apu_t::dpcm_t *chan) chan->length--; } - if (chan->cur_byte & (1 << bit_pos)) -// chan->regs[1]++; - chan->vol+=2; /* FIXED * DPCM channel only uses the upper 6 bits of the DAC */ - else -// chan->regs[1]--; - chan->vol-=2; + if ((chan->cur_byte & (1 << bit_pos)) && (chan->vol <= 125)) +// chan->regs[1] += 2; + chan->vol += 2; /* FIXED * DPCM channel only uses the upper 6 bits of the DAC */ + else if (chan->vol >= 2) +// chan->regs[1] -= 2; + chan->vol -= 2; } } - if (chan->vol > 63) - chan->vol = 63; - else if (chan->vol < -64) - chan->vol = -64; - - return (s8) (chan->vol); + chan->output = (u8)(chan->vol); } /* WRITE REGISTER VALUE */ -inline void nesapu_device::apu_regwrite(int address, u8 value) +void nesapu_device::write(offs_t offset, u8 value) { - int chan = (address & 4) ? 1 : 0; + m_stream->update(); + + int chan = BIT(offset, 2); - switch (address) + switch (offset) { /* squares */ case apu_t::WRA0: @@ -552,8 +550,8 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) if (m_APU.tri.enabled) { /* ??? */ - if (false == m_APU.tri.counter_started) - m_APU.tri.linear_length = m_sync_times2[value & 0x7F]; + if (!m_APU.tri.counter_started) + m_APU.tri.linear_length = m_sync_times2[value & 0x7f]; } break; @@ -592,7 +590,8 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) { m_APU.tri.counter_started = false; m_APU.tri.vbl_length = m_vbl_times[value >> 3]; - m_APU.tri.linear_length = m_sync_times2[m_APU.tri.regs[0] & 0x7F]; + m_APU.tri.linear_length = m_sync_times2[m_APU.tri.regs[0] & 0x7f]; + m_APU.tri.linear_reload = true; } break; @@ -624,16 +623,17 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) /* DMC */ case apu_t::WRE0: m_APU.dpcm.regs[0] = value; - if (0 == (value & 0x80)) { - m_irq_handler(false); + if (!(value & 0x80)) + { m_APU.dpcm.irq_occurred = false; + if (!m_APU.frame_irq_occurred) + m_irq_handler(false); } break; case apu_t::WRE1: /* 7-bit DAC */ - //m_APU.dpcm.regs[1] = value - 0x40; - m_APU.dpcm.regs[1] = value & 0x7F; - m_APU.dpcm.vol = (m_APU.dpcm.regs[1]-64); + m_APU.dpcm.regs[1] = value & 0x7f; + m_APU.dpcm.vol = m_APU.dpcm.regs[1]; break; case apu_t::WRE2: @@ -646,10 +646,24 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) break; case apu_t::IRQCTRL: - if(value & 0x80) + if (value & 0x80) m_APU.step_mode = 5; else m_APU.step_mode = 4; + + m_APU.frame_irq_enabled = !BIT(value, 6); + if (m_APU.frame_irq_enabled) + { + m_frame_timer->adjust(m_frame_period, 0, m_frame_period); + } + else + { + m_APU.frame_irq_occurred = false; + if (!m_APU.dpcm.irq_occurred) + m_irq_handler(false); + m_frame_timer->reset(); + } + break; case apu_t::SMASK: @@ -691,7 +705,7 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) if (value & 0x10) { /* only reset dpcm values if DMA is finished */ - if (false == m_APU.dpcm.enabled) + if (!m_APU.dpcm.enabled) { m_APU.dpcm.enabled = true; apu_dpcmreset(&m_APU.dpcm); @@ -700,56 +714,51 @@ inline void nesapu_device::apu_regwrite(int address, u8 value) else m_APU.dpcm.enabled = false; - //m_irq_handler(false); m_APU.dpcm.irq_occurred = false; + if (!m_APU.frame_irq_occurred) + m_irq_handler(false); break; default: #ifdef MAME_DEBUG -logerror("invalid apu write: $%02X at $%04X\n", value, address); +logerror("invalid apu write: $%02X at $%04X\n", value, offset); #endif break; } } +// Read status register at $4015 +u8 nesapu_device::status_r() +{ + m_stream->update(); + u8 readval = 0; + if (m_APU.squ[0].vbl_length > 0) + readval |= 0x01; -/* READ VALUES FROM REGISTERS */ -u8 nesapu_device::read(offs_t address) -{ - if (address == 0x15) /*FIXED* Address $4015 has different behaviour*/ - { - int readval = 0; - if (m_APU.squ[0].vbl_length > 0) - readval |= 0x01; + if (m_APU.squ[1].vbl_length > 0) + readval |= 0x02; - if (m_APU.squ[1].vbl_length > 0) - readval |= 0x02; + if (m_APU.tri.vbl_length > 0) + readval |= 0x04; - if (m_APU.tri.vbl_length > 0) - readval |= 0x04; + if (m_APU.noi.vbl_length > 0) + readval |= 0x08; - if (m_APU.noi.vbl_length > 0) - readval |= 0x08; + if (m_APU.dpcm.enabled) + readval |= 0x10; - if (m_APU.dpcm.enabled == true) - readval |= 0x10; + if (m_APU.frame_irq_occurred) + readval |= 0x40; - if (m_APU.dpcm.irq_occurred == true) - readval |= 0x80; + if (m_APU.dpcm.irq_occurred) + readval |= 0x80; - return readval; - } - else - return m_APU.regs[address]; -} + m_APU.frame_irq_occurred = false; + if (!m_APU.dpcm.irq_occurred) + m_irq_handler(false); -/* WRITE VALUE TO TEMP REGISTRY AND QUEUE EVENT */ -void nesapu_device::write(offs_t address, u8 value) -{ - m_APU.regs[address]=value; - m_stream->update(); - apu_regwrite(address,value); + return readval; } @@ -757,25 +766,21 @@ void nesapu_device::write(offs_t address, u8 value) // sound_stream_update - handle a stream update //------------------------------------------------- -void nesapu_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void nesapu_device::sound_stream_update(sound_stream &stream) { - int accum; - memset( outputs[0], 0, samples*sizeof(*outputs[0]) ); + sound_stream::sample_t accum = 0.0; - while (samples--) + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { - accum = apu_square(&m_APU.squ[0]); - accum += apu_square(&m_APU.squ[1]); - accum += apu_triangle(&m_APU.tri); - accum += apu_noise(&m_APU.noi); - accum += apu_dpcm(&m_APU.dpcm); - - /* 8-bit clamps */ - if (accum > 127) - accum = 127; - else if (accum < -128) - accum = -128; - - *(outputs[0]++)=accum<<8; + apu_square(&m_APU.squ[0]); + apu_square(&m_APU.squ[1]); + apu_triangle(&m_APU.tri); + apu_noise(&m_APU.noi); + apu_dpcm(&m_APU.dpcm); + + accum = m_square_lut[m_APU.squ[0].output + m_APU.squ[1].output]; + accum += m_tnd_lut[m_APU.tri.output][m_APU.noi.output][m_APU.dpcm.output]; + + stream.put(0, sampindex, accum); } } |