#include namespace Famicom { #include "envelope.cpp" #include "sweep.cpp" #include "pulse.cpp" #include "triangle.cpp" #include "noise.cpp" #include "dmc.cpp" #include "serialization.cpp" APU apu; APU::APU() { for(uint amp : range(32)) { if(amp == 0) { pulseDAC[amp] = 0; } else { pulseDAC[amp] = 16384.0 * 95.88 / (8128.0 / amp + 100.0); } } for(uint dmc_amp : range(128)) { for(uint triangle_amp : range(16)) { for(uint noise_amp : range(16)) { if(dmc_amp == 0 && triangle_amp == 0 && noise_amp == 0) { dmcTriangleNoiseDAC[dmc_amp][triangle_amp][noise_amp] = 0; } else { dmcTriangleNoiseDAC[dmc_amp][triangle_amp][noise_amp] = 16384.0 * 159.79 / (100.0 + 1.0 / (triangle_amp / 8227.0 + noise_amp / 12241.0 + dmc_amp / 22638.0)); } } } } } auto APU::Enter() -> void { while(true) scheduler.synchronize(), apu.main(); } auto APU::main() -> void { uint pulse_output, triangle_output, noise_output, dmc_output; pulse_output = pulse[0].clock(); pulse_output += pulse[1].clock(); triangle_output = triangle.clock(); noise_output = noise.clock(); dmc_output = dmc.clock(); clockFrameCounterDivider(); int output = 0; output += pulseDAC[pulse_output]; output += dmcTriangleNoiseDAC[dmc_output][triangle_output][noise_output]; output += cartridgeSample; stream->sample(sclamp<16>(output) / 32768.0); tick(); } auto APU::tick() -> void { Thread::step(rate()); synchronize(cpu); } auto APU::setIRQ() -> void { cpu.apuLine(frame.irqPending || dmc.irqPending); } auto APU::setSample(int16 sample) -> void { cartridgeSample = sample; } auto APU::power() -> void { create(APU::Enter, system.frequency()); stream = Emulator::audio.createStream(1, frequency() / rate()); stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 90.0); stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 440.0); stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 14000.0); stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); pulse[0].power(); pulse[1].power(); triangle.power(); noise.power(); dmc.power(); frame.irqPending = 0; frame.mode = 0; frame.counter = 0; frame.divider = 1; enabledChannels = 0; cartridgeSample = 0; setIRQ(); } auto APU::readIO(uint16 addr) -> uint8 { switch(addr) { case 0x4015: { uint8 result = 0x00; result |= pulse[0].lengthCounter ? 0x01 : 0; result |= pulse[1].lengthCounter ? 0x02 : 0; result |= triangle.lengthCounter ? 0x04 : 0; result |= noise.lengthCounter ? 0x08 : 0; result |= dmc.lengthCounter ? 0x10 : 0; result |= frame.irqPending ? 0x40 : 0; result |= dmc.irqPending ? 0x80 : 0; frame.irqPending = false; setIRQ(); return result; } } return cpu.mdr(); } auto APU::writeIO(uint16 addr, uint8 data) -> void { const uint n = (addr >> 2) & 1; //pulse# switch(addr) { case 0x4000: case 0x4004: { pulse[n].duty = data >> 6; pulse[n].envelope.loopMode = data & 0x20; pulse[n].envelope.useSpeedAsVolume = data & 0x10; pulse[n].envelope.speed = data & 0x0f; return; } case 0x4001: case 0x4005: { pulse[n].sweep.enable = data & 0x80; pulse[n].sweep.period = (data & 0x70) >> 4; pulse[n].sweep.decrement = data & 0x08; pulse[n].sweep.shift = data & 0x07; pulse[n].sweep.reload = true; return; } case 0x4002: case 0x4006: { pulse[n].period = (pulse[n].period & 0x0700) | (data << 0); pulse[n].sweep.pulsePeriod = (pulse[n].sweep.pulsePeriod & 0x0700) | (data << 0); return; } case 0x4003: case 0x4007: { pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8); pulse[n].sweep.pulsePeriod = (pulse[n].sweep.pulsePeriod & 0x00ff) | (data << 8); pulse[n].dutyCounter = 0; pulse[n].envelope.reloadDecay = true; if(enabledChannels & (1 << n)) { pulse[n].lengthCounter = lengthCounterTable[(data >> 3) & 0x1f]; } return; } case 0x4008: { triangle.haltLengthCounter = data & 0x80; triangle.linearLength = data & 0x7f; return; } case 0x400a: { triangle.period = (triangle.period & 0x0700) | (data << 0); return; } case 0x400b: { triangle.period = (triangle.period & 0x00ff) | (data << 8); triangle.reloadLinear = true; if(enabledChannels & (1 << 2)) { triangle.lengthCounter = lengthCounterTable[(data >> 3) & 0x1f]; } return; } case 0x400c: { noise.envelope.loopMode = data & 0x20; noise.envelope.useSpeedAsVolume = data & 0x10; noise.envelope.speed = data & 0x0f; return; } case 0x400e: { noise.shortMode = data & 0x80; noise.period = data & 0x0f; return; } case 0x400f: { noise.envelope.reloadDecay = true; if(enabledChannels & (1 << 3)) { noise.lengthCounter = lengthCounterTable[(data >> 3) & 0x1f]; } return; } case 0x4010: { dmc.irqEnable = data & 0x80; dmc.loopMode = data & 0x40; dmc.period = data & 0x0f; dmc.irqPending = dmc.irqPending && dmc.irqEnable && !dmc.loopMode; setIRQ(); return; } case 0x4011: { dmc.dacLatch = data & 0x7f; return; } case 0x4012: { dmc.addrLatch = data; return; } case 0x4013: { dmc.lengthLatch = data; return; } case 0x4015: { if((data & 0x01) == 0) pulse[0].lengthCounter = 0; if((data & 0x02) == 0) pulse[1].lengthCounter = 0; if((data & 0x04) == 0) triangle.lengthCounter = 0; if((data & 0x08) == 0) noise.lengthCounter = 0; (data & 0x10) ? dmc.start() : dmc.stop(); dmc.irqPending = false; setIRQ(); enabledChannels = data & 0x1f; return; } case 0x4017: { frame.mode = data >> 6; frame.counter = 0; if(frame.mode & 2) clockFrameCounter(); if(frame.mode & 1) { frame.irqPending = false; setIRQ(); } frame.divider = FrameCounter::NtscPeriod; return; } } } auto APU::clockFrameCounter() -> void { frame.counter++; if(frame.counter & 1) { pulse[0].clockLength(); pulse[0].sweep.clock(0); pulse[1].clockLength(); pulse[1].sweep.clock(1); triangle.clockLength(); noise.clockLength(); } pulse[0].envelope.clock(); pulse[1].envelope.clock(); triangle.clockLinearLength(); noise.envelope.clock(); if(frame.counter == 0) { if(frame.mode & 2) frame.divider += FrameCounter::NtscPeriod; if(frame.mode == 0) { frame.irqPending = true; setIRQ(); } } } auto APU::clockFrameCounterDivider() -> void { frame.divider -= 2; if(frame.divider <= 0) { clockFrameCounter(); frame.divider += FrameCounter::NtscPeriod; } } const uint8 APU::lengthCounterTable[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, }; const uint16 APU::noisePeriodTableNTSC[16] = { 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068, }; const uint16 APU::noisePeriodTablePAL[16] = { 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778, }; const uint16 APU::dmcPeriodTableNTSC[16] = { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54, }; const uint16 APU::dmcPeriodTablePAL[16] = { 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50, }; }