UNPKG

broken-neees

Version:

A really broken NEEES emulator that introduces glitches and random bugs on purpose!

268 lines (267 loc) 8.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _InMemoryRegister = _interopRequireDefault(require("../lib/InMemoryRegister")); var _noteLengths = _interopRequireDefault(require("../lib/apu/noteLengths")); var _byte = _interopRequireDefault(require("../lib/byte")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } class PulseControl extends _InMemoryRegister.default.APU { onLoad() { this.addField("volumeOrEnvelopePeriod", 0, 4).addField("constantVolume", 4).addField("envelopeLoopOrLengthCounterHalt", 5).addField("dutyCycleId", 6, 2); } onWrite(value) { this.setValue(value); } } class PulseSweep extends _InMemoryRegister.default.APU { onLoad() { this.addField("shiftCount", 0, 3).addField("negateFlag", 3).addField("dividerPeriodMinusOne", 4, 3).addField("enabledFlag", 7); } onWrite(value) { this.setValue(value); const channel = this.apu.channels.pulses[this.id]; channel.frequencySweep.startFlag = true; } } class PulseTimerLow extends _InMemoryRegister.default.APU { onWrite(value) { this.setValue(value); const channel = this.apu.channels.pulses[this.id]; channel.updateTimer(); } } class PulseTimerHighLCL extends _InMemoryRegister.default.APU { onLoad() { this.addField("timerHigh", 0, 3).addField("lengthCounterLoad", 3, 5); } onWrite(value) { this.setValue(value); const channel = this.apu.channels.pulses[this.id]; channel.updateTimer(); channel.lengthCounter.counter = _noteLengths.default[this.lengthCounterLoad]; channel.volumeEnvelope.startFlag = true; } } class TriangleLengthControl extends _InMemoryRegister.default.APU { onLoad() { this.addField("linearCounterReload", 0, 7).addField("halt", 7, 1); } onWrite(value) { this.setValue(value); this.apu.channels.triangle.linearLengthCounter.reload = this.linearCounterReload; } } class TriangleTimerLow extends _InMemoryRegister.default.APU { onWrite(value) { this.setValue(value); } } class TriangleTimerHighLCL extends _InMemoryRegister.default.APU { onLoad() { this.addField("timerHigh", 0, 3).addField("lengthCounterLoad", 3, 5); } onWrite(value) { this.setValue(value); const triangle = this.apu.channels.triangle; triangle.lengthCounter.counter = _noteLengths.default[this.lengthCounterLoad]; triangle.linearLengthCounter.reloadFlag = true; } } class NoiseControl extends _InMemoryRegister.default.APU { onLoad() { this.addField("volumeOrEnvelopePeriod", 0, 4).addField("constantVolume", 4).addField("envelopeLoopOrLengthCounterHalt", 5); } onWrite(value) { this.setValue(value); } } class NoiseForm extends _InMemoryRegister.default.APU { onLoad() { this.addField("periodId", 0, 4).addField("mode", 7); } onWrite(value) { this.setValue(value); } } class NoiseLCL extends _InMemoryRegister.default.APU { onLoad() { this.addField("lengthCounterLoad", 3, 5); } onWrite(value) { this.setValue(value); const channel = this.apu.channels.noise; channel.lengthCounter.counter = _noteLengths.default[this.lengthCounterLoad]; channel.volumeEnvelope.startFlag = true; } } class DMCControl extends _InMemoryRegister.default.APU { onLoad() { this.addField("dpcmPeriodId", 0, 4).addField("loop", 6); } onWrite(value) { this.setValue(value); } } class DMCLoad extends _InMemoryRegister.default.APU { onLoad() { this.addField("directLoad", 0, 7); } onWrite(value) { this.setValue(value); const { apu } = this; apu.channels.dmc.outputSample = this.directLoad; } } class DMCSampleAddress extends _InMemoryRegister.default.APU { onWrite(value) { this.setValue(value); } } class DMCSampleLength extends _InMemoryRegister.default.APU { onWrite(value) { this.setValue(value); } } class APUStatus extends _InMemoryRegister.default.APU { onRead() { const { apu } = this; const channels = apu.channels; return _byte.default.bitfield(+(channels.pulses[0].lengthCounter.counter > 0), +(channels.pulses[1].lengthCounter.counter > 0), +(channels.triangle.lengthCounter.counter > 0), +(channels.noise.lengthCounter.counter > 0), channels.dmc.dpcm.remainingBytes() > 0, 0, 0, 0); } } class APUControl extends _InMemoryRegister.default.APU { onLoad() { this.addField("enablePulse1", 0).addField("enablePulse2", 1).addField("enableTriangle", 2).addField("enableNoise", 3).addField("enableDMC", 4); } onWrite(value) { const { channels } = this.apu; this.setValue(value); const { enablePulse1, enablePulse2, enableTriangle, enableNoise, enableDMC } = this; if (!enablePulse1) channels.pulses[0].lengthCounter.reset(); if (!enablePulse2) channels.pulses[1].lengthCounter.reset(); if (!enableTriangle) { channels.triangle.lengthCounter.reset(); channels.triangle.linearLengthCounter.fullReset(); } if (!enableNoise) channels.noise.lengthCounter.reset(); if (!enableDMC) channels.dmc.dpcm.stop();else if (channels.dmc.dpcm.remainingBytes() === 0) channels.dmc.dpcm.start(); } } class APUFrameCounter extends _InMemoryRegister.default.APU { onLoad() { this.addField("use5StepSequencer", 7); } onWrite(value) { this.setValue(value); this.apu.frameSequencer.reset(); this.apu.onQuarterFrameClock(); this.apu.onHalfFrameClock(); } } class AudioRegisters { constructor(apu) { this.pulses = [0, 1].map(id => ({ control: new PulseControl(apu), // $4000/$4004 sweep: new PulseSweep(apu, id), // $4001/$4005 timerLow: new PulseTimerLow(apu, id), // $4002/$4006 timerHighLCL: new PulseTimerHighLCL(apu, id) // $4003/$4007 })); this.triangle = { lengthControl: new TriangleLengthControl(apu), // $4008 timerLow: new TriangleTimerLow(apu), // $400A timerHighLCL: new TriangleTimerHighLCL(apu) // $400B }; this.noise = { control: new NoiseControl(apu), // $400C form: new NoiseForm(apu), // $400E lcl: new NoiseLCL(apu) // $400F }; this.dmc = { control: new DMCControl(apu), // $4010 load: new DMCLoad(apu), // $4011 sampleAddress: new DMCSampleAddress(apu), // $4012 sampleLength: new DMCSampleLength(apu) // $4013 }; this.apuStatus = new APUStatus(apu); // $4015 (read) this.apuControl = new APUControl(apu); // $4015 (write) this.apuFrameCounter = new APUFrameCounter(apu); // $4017 } read(address) { var _this$_getRegister; if (address === 0x4015) return this.apuStatus.onRead(); return (_this$_getRegister = this._getRegister(address)) === null || _this$_getRegister === void 0 ? void 0 : _this$_getRegister.onRead(); } write(address, value) { var _this$_getRegister2; if (address === 0x4015) return this.apuControl.onWrite(value); (_this$_getRegister2 = this._getRegister(address)) === null || _this$_getRegister2 === void 0 || _this$_getRegister2.onWrite(value); } _getRegister(address) { switch (address) { case 0x4000: return this.pulses[0].control; case 0x4001: return this.pulses[0].sweep; case 0x4002: return this.pulses[0].timerLow; case 0x4003: return this.pulses[0].timerHighLCL; case 0x4004: return this.pulses[1].control; case 0x4005: return this.pulses[1].sweep; case 0x4006: return this.pulses[1].timerLow; case 0x4007: return this.pulses[1].timerHighLCL; case 0x4008: return this.triangle.lengthControl; case 0x400a: return this.triangle.timerLow; case 0x400b: return this.triangle.timerHighLCL; case 0x400c: return this.noise.control; case 0x400e: return this.noise.form; case 0x400f: return this.noise.lcl; case 0x4010: return this.dmc.control; case 0x4011: return this.dmc.load; case 0x4012: return this.dmc.sampleAddress; case 0x4013: return this.dmc.sampleLength; case 0x4017: return this.apuFrameCounter; default: } } } exports.default = AudioRegisters;