1 module nes.apu;
2 
3 import std.conv;
4 import std.math;
5 import std.stdio;
6 
7 import nes.console;
8 import nes.cpu;
9 
10 import blip_buf;
11 
12 enum MAX_SAMPLE_RATE = 96000;
13 
14 immutable ulong[6][2] stepCycles = [[7457, 14913, 22371, 29828, 29829, 29830],
15                                     [7457, 14913, 22371, 29829, 37281, 37282]];
16 
17 immutable ubyte[] lengthTable = [
18     10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14,
19     12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
20 ];
21 
22 immutable ubyte[][] dutyTable = [
23     [0, 1, 0, 0, 0, 0, 0, 0],
24     [0, 1, 1, 0, 0, 0, 0, 0],
25     [0, 1, 1, 1, 1, 0, 0, 0],
26     [1, 0, 0, 1, 1, 1, 1, 1]
27 ];
28 
29 immutable ubyte[] triangleTable = [
30     15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
31     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
32 ];
33 
34 immutable ushort[] noiseTable = [
35     4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
36 ];
37 
38 immutable ubyte[] dmcTable = [
39     214, 190, 170, 160, 143, 127, 113, 107, 95, 80, 71, 64, 53, 42, 36, 27
40 ];
41 
42 immutable float[31] pulseTable =
43     () {
44         float[31] result;
45         foreach (i; 0 .. 31)
46             result[i] = 95.52 / (8128.0 / cast(float)i + 100);
47         return result;
48     }();
49 
50 immutable float[203] tndTable =
51     () {
52         float[203] result;
53         foreach (i; 0 .. 203)
54             result[i] = 163.67 / (24329.0 / cast(float)i + 100);
55         return result;
56     }();
57 
58 alias void delegate(short) ApuCallbackFuncType;
59 
60 class APU {
61     this(Console console) {
62         this.console = console;
63         this.pulse1.channel = 1;
64         this.pulse2.channel = 2;
65         this.dmc.cpu = console.cpu;
66 
67         this.blipBuf = blip_new(MAX_SAMPLE_RATE);
68 
69         this.reset(true);
70     }
71 
72     ~this() {
73         blip_delete(this.blipBuf);
74     }
75 
76     void step() {
77         auto cycle1 = this.cycle;
78         this.cycle++;
79         auto cycle2 = this.cycle;
80 
81         this.counter++;
82 
83         if (this.counter >= stepCycles[this.stepMode][this.currentStep]) {
84             this.stepFrameCounter();
85         }
86 
87         if (this.frameCounterValue >= 0 && this.frameCounterDelay > 0)
88             tryDelayedFrameCounterWrite();
89 
90         if (this.blockFrameCounterTick > 0) {
91             this.blockFrameCounterTick--;
92         }
93 
94         this.stepTimer();
95 
96         short currentOutput = floatSampleToShort(this.output());
97 
98         short delta = cast(short)(currentOutput - this.blipPrevOutput);
99 
100         if (delta != 0) blip_add_delta(this.blipBuf, this.outputTick, delta);
101 
102         this.blipPrevOutput = currentOutput;
103 
104         this.outputTick++;
105 
106         auto s1 = cast(int)(cast(double)cycle1 / this.ticksPerSample);
107         auto s2 = cast(int)(cast(double)cycle2 / this.ticksPerSample);
108         if (s1 != s2) {
109            this.sendSample();
110            this.outputTick = 0;
111         }
112     }
113 
114     void reset(bool powerUp = false) {
115         this.cycle = 0;
116         this.counter = 0;
117         this.currentStep = 0;
118 
119         this.inhibitIRQ = false;
120         this.frameIRQ = false;
121 
122         this.frameCounterDelay = -1;
123         this.frameCounterValue = -1;
124         this.blockFrameCounterTick = 0;
125 
126         this.writeControl(0);
127 
128         this.pulse1.reset();
129         this.pulse2.reset();
130         this.triangle.reset(powerUp);
131         this.noise.reset();
132         this.dmc.reset();
133 
134         this.outputTick = 0;
135 
136         blip_clear(this.blipBuf);
137 
138         foreach (_; 0 .. 8)
139             this.step();
140     }
141 
142     ubyte readRegister(ushort address) {
143         switch (address) {
144             case 0x4015:
145                 return this.readStatus();
146             default:
147                 break;
148             // default:
149             //     log.Fatalf("unhandled apu register read at address: 0x%04X", address)
150         }
151         return 0;
152     }
153 
154     void writeRegister(ushort address, ubyte value) {
155         switch (address) {
156             case 0x4000:
157                 this.pulse1.writeControl(value);
158                 break;
159             case 0x4001:
160                 this.pulse1.writeSweep(value);
161                 break;
162             case 0x4002:
163                 this.pulse1.writeTimerLow(value);
164                 break;
165             case 0x4003:
166                 this.pulse1.writeTimerHigh(value);
167                 break;
168             case 0x4004:
169                 this.pulse2.writeControl(value);
170                 break;
171             case 0x4005:
172                 this.pulse2.writeSweep(value);
173                 break;
174             case 0x4006:
175                 this.pulse2.writeTimerLow(value);
176                 break;
177             case 0x4007:
178                 this.pulse2.writeTimerHigh(value);
179                 break;
180             case 0x4008:
181                 this.triangle.writeControl(value);
182                 break;
183             case 0x4009:
184                 break;
185             case 0x4010:
186                 this.dmc.writeControl(value);
187                 break;
188             case 0x4011:
189                 this.dmc.writeValue(value);
190                 break;
191             case 0x4012:
192                 this.dmc.writeAddress(value);
193                 break;
194             case 0x4013:
195                 this.dmc.writeLength(value);
196                 break;
197             case 0x400A:
198                 this.triangle.writeTimerLow(value);
199                 break;
200             case 0x400B:
201                 this.triangle.writeTimerHigh(value);
202                 break;
203             case 0x400C:
204                 this.noise.writeControl(value);
205                 break;
206             case 0x400D:
207                 break;
208             case 0x400E:
209                 this.noise.writePeriod(value);
210                 break;
211             case 0x400F:
212                 this.noise.writeLength(value);
213                 break;
214             case 0x4015:
215                 this.writeControl(value);
216                 break;
217             case 0x4017:
218                 this.writeFrameCounter(value);
219                 break;
220             default:
221                 break;
222             // default:
223             //     log.Fatalf("unhandled apu register write at address: 0x%04X", address)
224         }
225     }
226 
227     void setAudioSampleRate(double sampleRate) {
228         if (sampleRate != 0) {
229             if (sampleRate > MAX_SAMPLE_RATE) sampleRate = MAX_SAMPLE_RATE;
230 
231             // Convert samples per second to cpu steps per sample
232             this.ticksPerSample = CPUFrequency / sampleRate;
233 
234             blip_set_rates(this.blipBuf, CPUFrequency, sampleRate);
235         }
236     }
237 
238     void save(string[string] state) {
239         state["apu.cycle"] = to!string(this.cycle);
240         state["apu.frameIRQ"] = to!string(this.frameIRQ);
241         state["apu.inhibitIRQ"] = to!string(this.inhibitIRQ);
242 
243         state["apu.counter"] = to!string(this.counter);
244         state["apu.stepMode"] = to!string(this.stepMode);
245         state["apu.currentStep"] = to!string(this.currentStep);
246         state["apu.frameCounterValue"] = to!string(this.frameCounterValue);
247         state["apu.frameCounterDelay"] = to!string(this.frameCounterDelay);
248         state["apu.blockFrameCounterTick"] = to!string(this.blockFrameCounterTick);
249 
250         this.pulse1.save(state);
251         this.pulse2.save(state);
252         this.triangle.save(state);
253         this.noise.save(state);
254         this.dmc.save(state);
255     }
256 
257     void load(string[string] state) {
258         this.cycle = to!ulong(state["apu.cycle"]);
259         this.frameIRQ = to!bool(state["apu.frameIRQ"]);
260         this.inhibitIRQ = to!bool(state["apu.inhibitIRQ"]);
261 
262         this.counter = to!ulong(state["apu.counter"]);
263         this.stepMode = to!uint(state["apu.stepMode"]);
264         this.currentStep = to!uint(state["apu.currentStep"]);
265         this.frameCounterValue = to!short(state["apu.frameCounterValue"]);
266         this.frameCounterDelay = to!byte(state["apu.frameCounterDelay"]);
267         this.blockFrameCounterTick = to!ubyte(state["apu.blockFrameCounterTick"]);
268 
269         this.pulse1.load(state);
270         this.pulse2.load(state);
271         this.triangle.load(state);
272         this.noise.load(state);
273         this.dmc.load(state);
274     }
275 
276     package:
277         ApuCallbackFuncType callback;
278 
279         Console  console;
280         Pulse    pulse1;
281         Pulse    pulse2;
282         Triangle triangle;
283         Noise    noise;
284         DMC      dmc;
285         ulong    cycle, counter;
286         bool     inhibitIRQ;
287         bool     frameIRQ;
288 
289     private:
290         uint    stepMode, currentStep;
291         short   frameCounterValue;
292         byte    frameCounterDelay;
293         ubyte   blockFrameCounterTick;
294         blip_t* blipBuf;
295         double  ticksPerSample;
296         uint    outputTick;
297         short   blipOutput, blipPrevOutput;
298         short[MAX_SAMPLE_RATE] outBuf;
299 
300         void sendSample() {
301             if (this.callback == null) return;
302 
303             blip_end_frame(this.blipBuf, this.outputTick);
304 
305             auto sampleCount = blip_read_samples(this.blipBuf, outBuf.ptr,
306                 MAX_SAMPLE_RATE, 0);
307 
308             for (uint i = 0; i < sampleCount; i++) {
309                 this.callback(outBuf[i]);
310             }
311         }
312 
313         short floatSampleToShort(float f) {
314             if (f > 1.0) f = 1.0;
315             if (f < -1.0) f = -1.0;
316             return cast(short)(f * 0x7fff);
317         }
318 
319         float output() {
320             auto p1 = this.pulse1.output();
321             auto p2 = this.pulse2.output();
322             auto t = this.triangle.output();
323             auto n = this.noise.output();
324             auto d = this.dmc.output();
325 
326             float pulseOut, tndOut;
327 
328             pulseOut = pulseTable[p1 + p2];
329             tndOut = tndTable[3 * t + 2 * n + d];
330 
331             return pulseOut + tndOut;
332         }
333 
334         // mode 0:    mode 1:       function
335         // ---------  -----------  -----------------------------
336         //  - - - f    - - - - -    IRQ (if bit 6 is clear)
337         //  - l - l    l - l - -    Length counter and sweep
338         //  e e e e    e e e e -    Envelope and linear counter
339         void stepFrameCounter() {
340             if (this.currentStep == 0 || this.currentStep == 2) {
341                 if (!this.blockFrameCounterTick) {
342                     this.stepEnvelope();
343 
344                     this.blockFrameCounterTick = 2;
345                 }
346             }
347             else if (this.currentStep == 1 || this.currentStep == 4) {
348                 if (!this.blockFrameCounterTick) {
349                     this.stepEnvelope();
350 
351                     this.stepSweep();
352                     this.stepLength();
353 
354                     this.blockFrameCounterTick = 2;
355                 }
356 
357                 if (this.currentStep == 4 && this.stepMode == 0 && !this.inhibitIRQ) {
358                     this.frameIRQ = true;
359                 }
360             }
361             else if (this.currentStep == 3 || this.currentStep == 5) {
362                 if (this.stepMode == 0) {
363                     if (!this.inhibitIRQ) {
364                         this.frameIRQ = true;
365 
366                         if (this.currentStep == 3) {
367                             this.console.cpu.addIrqSource(IrqSource.FrameCounter);
368                         }
369                     }
370                 }
371             }
372 
373             this.currentStep++;
374             if (this.currentStep == 6) {
375                 this.currentStep = 0;
376                 this.counter = 0;
377             }
378         }
379 
380         void stepTimer() {
381             if (this.cycle % 2 == 0) {
382                 this.pulse1.stepTimer();
383                 this.pulse2.stepTimer();
384                 this.noise.stepTimer();
385                 this.dmc.stepTimer();
386             }
387             this.triangle.stepTimer();
388         }
389 
390         void stepEnvelope() {
391             this.pulse1.stepEnvelope();
392             this.pulse2.stepEnvelope();
393             this.triangle.stepCounter();
394             this.noise.stepEnvelope();
395         }
396 
397         void stepSweep() {
398             this.pulse1.stepSweep();
399             this.pulse2.stepSweep();
400         }
401 
402         void stepLength() {
403             this.pulse1.stepLength();
404             this.pulse2.stepLength();
405             this.triangle.stepLength();
406             this.noise.stepLength();
407         }
408 
409         ubyte readStatus() {
410             ubyte result;
411 
412             if (this.pulse1.lengthValue > 0) {
413                 result |= 1;
414             }
415             if (this.pulse2.lengthValue > 0) {
416                 result |= 2;
417             }
418             if (this.triangle.lengthValue > 0) {
419                 result |= 4;
420             }
421             if (this.noise.lengthValue > 0) {
422                 result |= 8;
423             }
424             if (this.dmc.currentLength > 0) {
425                 result |= 16;
426             }
427             if (this.frameIRQ) {
428                 result |= 64;
429             }
430             if (this.console.cpu.hasIrqSource(IrqSource.DMC)) {
431                 result |= 128;
432             }
433 
434             this.frameIRQ = false;
435             this.console.cpu.clearIrqSource(IrqSource.FrameCounter);
436 
437             return result;
438         }
439 
440         void writeControl(ubyte value) {
441             this.console.cpu.clearIrqSource(IrqSource.DMC);
442 
443             this.pulse1.enabled = (value & 1) == 1;
444             this.pulse2.enabled = (value & 2) == 2;
445             this.triangle.enabled = (value & 4) == 4;
446             this.noise.enabled = (value & 8) == 8;
447             this.dmc.enabled = (value & 16) == 16;
448             if (!this.pulse1.enabled) {
449                 this.pulse1.lengthValue = 0;
450             }
451             if (!this.pulse2.enabled) {
452                 this.pulse2.lengthValue = 0;
453             }
454             if (!this.triangle.enabled) {
455                 this.triangle.lengthValue = 0;
456             }
457             if (!this.noise.enabled) {
458                 this.noise.lengthValue = 0;
459             }
460             if (!this.dmc.enabled) {
461                 this.dmc.currentLength = 0;
462             } else {
463                 if (this.dmc.currentLength == 0) {
464                     this.dmc.restart();
465                 }
466             }
467         }
468 
469         void writeFrameCounter(ubyte value) {
470             this.frameCounterValue = value;
471 
472             /**
473              * If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle,
474              *  and if the write occurs between APU cycles, the effects occurs 4 CPU cycles after the write cycle.
475              * First CPU cycle we see is odd so even cycles are APU cycles.
476              */
477             this.frameCounterDelay = (this.console.cpu.cycles & 0x01) == 1 ? 4 : 3;
478 
479             this.inhibitIRQ = ((value >> 6) & 1) == 1;
480 
481             if (this.inhibitIRQ) {
482                 this.frameIRQ = false;
483                 this.console.cpu.clearIrqSource(IrqSource.FrameCounter);
484             }
485         }
486 
487         void tryDelayedFrameCounterWrite() {
488             this.frameCounterDelay--;
489 
490             if (this.frameCounterDelay == 0) {
491                 ubyte value = cast(ubyte)this.frameCounterValue;
492 
493                 this.stepMode = ((value >> 7) & 1) ? 1 : 0;
494 
495                 if (this.stepMode == 1 && !this.blockFrameCounterTick) {
496                     this.stepEnvelope();
497                     this.stepSweep();
498                     this.stepLength();
499 
500                     this.blockFrameCounterTick = 2;
501                 }
502 
503                 this.currentStep = 0;
504                 this.counter = 0;
505                 this.frameCounterDelay = -1;
506                 this.frameCounterValue = -1;
507             }
508         }
509 }
510 
511 // Pulse
512 
513 struct Pulse {
514     bool   enabled;
515     ubyte  channel;
516     bool   lengthEnabled;
517     bool   lengthEnabledNewValue;
518     ubyte  lengthValue;
519     ubyte  lengthValueNew;
520     ubyte  lengthValuePrev;
521     ushort timerPeriod;
522     ushort timerValue;
523     ubyte  dutyMode;
524     ubyte  dutyValue;
525     bool   sweepReload;
526     bool   sweepEnabled;
527     bool   sweepNegate;
528     ubyte  sweepShift;
529     ubyte  sweepPeriod;
530     uint   sweepTargetPeriod;
531     ubyte  sweepValue;
532     bool   envelopeEnabled;
533     bool   envelopeLoop;
534     bool   envelopeStart;
535     byte   envelopeDivider;
536     ubyte  envelopeCounter;
537     ubyte  envelopeConstantVolume;
538 
539     void reset() {
540         this.dutyMode = 0;
541         this.dutyValue = 0;
542 
543         this.timerPeriod = 0;
544 
545         this.sweepEnabled = false;
546         this.sweepPeriod = 1;
547         this.sweepNegate = false;
548         this.sweepShift = 0;
549         this.sweepReload = true;
550         this.sweepValue = 0;
551         this.sweepTargetPeriod = 0;
552         this.updateSweepTargetPeriod();
553 
554         this.lengthEnabled = true;
555         this.lengthEnabledNewValue = true;
556         this.lengthValue = 0;
557         this.lengthValueNew = 0;
558         this.lengthValuePrev = 0;
559 
560         this.envelopeEnabled = false;
561         this.envelopeLoop = false;
562         this.envelopeConstantVolume = 0;
563         this.envelopeCounter = 0;
564         this.envelopeStart = false;
565         this.envelopeDivider = 0;
566     }
567 
568     // 0x4000 & 0x4004
569     void writeControl(ubyte value) {
570         this.dutyMode = (value >> 6) & 3;
571         this.lengthEnabledNewValue = ((value >> 5) & 1) == 0;
572         this.envelopeLoop = ((value >> 5) & 1) == 1;
573         this.envelopeEnabled = ((value >> 4) & 1) == 0;
574         this.envelopeConstantVolume = value & 15;
575     }
576 
577     // 0x4001 & 0x4005
578     void writeSweep(ubyte value) {
579         this.sweepEnabled = ((value >> 7) & 1) == 1;
580         this.sweepPeriod = ((value >> 4) & 7) + 1;
581         this.sweepNegate = ((value >> 3) & 1) == 1;
582         this.sweepShift = value & 7;
583         this.sweepReload = true;
584         this.updateSweepTargetPeriod();
585     }
586 
587     // 0x4002 & 0x4006
588     void writeTimerLow(ubyte value) {
589         this.timerPeriod = (this.timerPeriod & 0xFF00) | cast(ushort)value;
590         this.updateSweepTargetPeriod();
591     }
592 
593     // 0x4003 & 0x4007
594     void writeTimerHigh(ubyte value) {
595         if (this.enabled) {
596             this.lengthValueNew = lengthTable[value >> 3];
597             this.lengthValuePrev = this.lengthValue;
598         }
599 
600         this.timerPeriod = (this.timerPeriod & 0x00FF) | (cast(ushort)(value & 7) << 8);
601         this.updateSweepTargetPeriod();
602         this.envelopeStart = true;
603         this.dutyValue = 0;
604     }
605 
606     void stepTimer() {
607         if (this.timerValue == 0) {
608             this.timerValue = this.timerPeriod;
609             this.dutyValue = (this.dutyValue + 1) % 8;
610         } else {
611             this.timerValue--;
612         }
613     }
614 
615     void stepEnvelope() {
616         if (this.envelopeStart) {
617             this.envelopeStart = false;
618             this.envelopeCounter = 15;
619             this.envelopeDivider = this.envelopeConstantVolume;
620         }  else {
621             this.envelopeDivider--;
622 
623             if (this.envelopeDivider < 0) {
624                 this.envelopeDivider = this.envelopeConstantVolume;
625                 if (this.envelopeCounter > 0) {
626                     this.envelopeCounter--;
627                 }
628                 else if (this.envelopeLoop)
629                     this.envelopeCounter = 15;
630             }
631         }
632     }
633 
634     void stepSweep() {
635         this.sweepValue--;
636         if (this.sweepValue == 0) {
637             if (this.sweepShift > 0 && this.sweepEnabled && this.timerPeriod >= 8 && this.sweepTargetPeriod <= 0x7ff) {
638                 this.timerPeriod = cast(ushort)this.sweepTargetPeriod;
639                 this.updateSweepTargetPeriod();
640             }
641             this.sweepValue = this.sweepPeriod;
642         }
643 
644         if (this.sweepReload) {
645             this.sweepValue = this.sweepPeriod;
646             this.sweepReload = false;
647         }
648     }
649 
650     void stepLength() {
651         if (this.lengthEnabled && this.lengthValue > 0) {
652             this.lengthValue--;
653         }
654     }
655 
656     void updateSweepTargetPeriod() {
657         auto delta = this.timerPeriod >> this.sweepShift;
658         if (this.sweepNegate) {
659             this.sweepTargetPeriod = this.timerPeriod - delta;
660             if (this.channel == 1) {
661                 this.sweepTargetPeriod--;
662             }
663         } else {
664             this.sweepTargetPeriod = this.timerPeriod + delta;
665         }
666     }
667 
668     ubyte output() {
669         // Emulate 1 cycle delay
670         this.lengthEnabled = this.lengthEnabledNewValue;
671 
672         // Emulate 1 cycle delay
673         if (this.lengthValueNew) {
674             if (this.lengthValue == this.lengthValuePrev) {
675                 this.lengthValue = this.lengthValueNew;
676             }
677 
678             this.lengthValueNew = 0;
679         }
680 
681         if (!this.enabled) {
682             return 0;
683         }
684         if (this.lengthValue == 0) {
685             return 0;
686         }
687         if (this.timerPeriod < 8 || (!this.sweepNegate && this.sweepTargetPeriod > 0x7FF)) {
688             return 0;
689         }
690         if (this.envelopeEnabled) {
691             return cast(ubyte)(dutyTable[this.dutyMode][this.dutyValue] * this.envelopeCounter);
692         } else {
693             return cast(ubyte)(dutyTable[this.dutyMode][this.dutyValue] * this.envelopeConstantVolume);
694         }
695     }
696 
697     void save(string[string] state) {
698         auto id = "apu.pulse" ~ to!string(this.channel);
699 
700         state[id ~ ".enabled"] = to!string(this.enabled);
701         state[id ~ ".channel"] = to!string(this.channel);
702         state[id ~ ".lengthEnabled"] = to!string(this.lengthEnabled);
703         state[id ~ ".lengthEnabledNewValue"] = to!string(this.lengthEnabledNewValue);
704         state[id ~ ".lengthValue"] = to!string(this.lengthValue);
705         state[id ~ ".lengthValueNew"] = to!string(this.lengthValueNew);
706         state[id ~ ".lengthValuePrev"] = to!string(this.lengthValuePrev);
707         state[id ~ ".timerPeriod"] = to!string(this.timerPeriod);
708         state[id ~ ".timerValue"] = to!string(this.timerValue);
709         state[id ~ ".dutyMode"] = to!string(this.dutyMode);
710         state[id ~ ".dutyValue"] = to!string(this.dutyValue);
711         state[id ~ ".sweepReload"] = to!string(this.sweepReload);
712         state[id ~ ".sweepEnabled"] = to!string(this.sweepEnabled);
713         state[id ~ ".sweepNegate"] = to!string(this.sweepNegate);
714         state[id ~ ".sweepShift"] = to!string(this.sweepShift);
715         state[id ~ ".sweepPeriod"] = to!string(this.sweepPeriod);
716         state[id ~ ".sweepTargetPeriod"] = to!string(this.sweepTargetPeriod);
717         state[id ~ ".sweepValue"] = to!string(this.sweepValue);
718         state[id ~ ".envelopeEnabled"] = to!string(this.envelopeEnabled);
719         state[id ~ ".envelopeLoop"] = to!string(this.envelopeLoop);
720         state[id ~ ".envelopeStart"] = to!string(this.envelopeStart);
721         state[id ~ ".envelopeDivider"] = to!string(this.envelopeDivider);
722         state[id ~ ".envelopeCounter"] = to!string(this.envelopeCounter);
723         state[id ~ ".envelopeConstantVolume"] = to!string(this.envelopeConstantVolume);
724     }
725 
726     void load(string[string] state) {
727         auto id = "apu.pulse" ~ to!string(this.channel);
728 
729         this.enabled = to!bool(state[id ~ ".enabled"]);
730         this.channel = to!ubyte(state[id ~ ".channel"]);
731         this.lengthEnabled = to!bool(state[id ~ ".lengthEnabled"]);
732         this.lengthEnabledNewValue = to!bool(state[id ~ ".lengthEnabledNewValue"]);
733         this.lengthValue = to!ubyte(state[id ~ ".lengthValue"]);
734         this.lengthValueNew = to!ubyte(state[id ~ ".lengthValueNew"]);
735         this.lengthValuePrev = to!ubyte(state[id ~ ".lengthValuePrev"]);
736         this.timerPeriod = to!ushort(state[id ~ ".timerPeriod"]);
737         this.timerValue = to!ushort(state[id ~ ".timerValue"]);
738         this.dutyMode = to!ubyte(state[id ~ ".dutyMode"]);
739         this.dutyValue = to!ubyte(state[id ~ ".dutyValue"]);
740         this.sweepReload = to!bool(state[id ~ ".sweepReload"]);
741         this.sweepEnabled = to!bool(state[id ~ ".sweepEnabled"]);
742         this.sweepNegate = to!bool(state[id ~ ".sweepNegate"]);
743         this.sweepShift = to!ubyte(state[id ~ ".sweepShift"]);
744         this.sweepPeriod = to!ubyte(state[id ~ ".sweepPeriod"]);
745         this.sweepTargetPeriod = to!uint(state[id ~ ".sweepTargetPeriod"]);
746         this.sweepValue = to!ubyte(state[id ~ ".sweepValue"]);
747         this.envelopeEnabled = to!bool(state[id ~ ".envelopeEnabled"]);
748         this.envelopeLoop = to!bool(state[id ~ ".envelopeLoop"]);
749         this.envelopeStart = to!bool(state[id ~ ".envelopeStart"]);
750         this.envelopeDivider = to!ubyte(state[id ~ ".envelopeDivider"]);
751         this.envelopeCounter = to!ubyte(state[id ~ ".envelopeCounter"]);
752         this.envelopeConstantVolume = to!ubyte(state[id ~ ".envelopeConstantVolume"]);
753     }
754 }
755 
756 // Triangle
757 
758 struct Triangle {
759     CPU cpu;
760     bool   enabled;
761     bool   lengthEnabled;
762     bool   lengthEnabledNewValue;
763     ubyte  lengthValue;
764     ubyte  lengthValueNew;
765     ubyte  lengthValuePrev;
766     ushort timerPeriod;
767     ushort timerValue;
768     ubyte  dutyValue;
769     ubyte  counterPeriod;
770     ubyte  counterValue;
771     bool   counterReload;
772 
773     void reset(bool powerUp) {
774         this.enabled = false;
775 
776         this.timerPeriod = 0;
777         this.timerValue = 0;
778 
779         // apu_reset: len_ctrs_enabled
780         // "At reset, length counters should be enabled, triangle unaffected"
781         if (powerUp) {
782             this.lengthEnabled = true;
783             this.lengthEnabledNewValue = true;
784             this.lengthValue = 0;
785             this.lengthValueNew = 0;
786             this.lengthValuePrev = 0;
787         }
788 
789         this.counterValue = 0;
790         this.counterPeriod = 0;
791         this.counterReload = false;
792 
793         this.dutyValue = 0; // hack to "fix" apu_mixer test
794     }
795 
796     // 0x4008
797     void writeControl(ubyte value) {
798         this.lengthEnabledNewValue = ((value >> 7) & 1) == 0;
799         this.counterPeriod = value & 0x7F;
800     }
801 
802     // 0x400A
803     void writeTimerLow(ubyte value) {
804         this.timerPeriod = (this.timerPeriod & 0xFF00) | cast(ushort)value;
805     }
806 
807     // 0x400B
808     void writeTimerHigh(ubyte value) {
809         if (this.enabled) {
810             this.lengthValueNew = lengthTable[value >> 3];
811             this.lengthValuePrev = this.lengthValue;
812         }
813 
814         this.timerPeriod = (this.timerPeriod & 0x00FF) | (cast(ushort)(value & 7) << 8);
815 
816         this.counterReload = true;
817     }
818 
819     void stepTimer() {
820         if (this.timerValue == 0) {
821             this.timerValue = this.timerPeriod;
822             if (this.lengthValue > 0 && this.counterValue > 0) {
823                 this.dutyValue = (this.dutyValue + 1) % 32;
824             }
825         } else {
826             this.timerValue--;
827         }
828     }
829 
830     void stepLength() {
831         if (this.lengthEnabled && this.lengthValue > 0) {
832             this.lengthValue--;
833         }
834     }
835 
836     void stepCounter() {
837         if (this.counterReload) {
838             this.counterValue = this.counterPeriod;
839         } else if (this.counterValue > 0) {
840             this.counterValue--;
841         }
842         if (this.lengthEnabled) {
843             this.counterReload = false;
844         }
845     }
846 
847     ubyte output() {
848         // Emulate 1 cycle delay
849         this.lengthEnabled = this.lengthEnabledNewValue;
850 
851         // Emulate 1 cycle delay
852         if (this.lengthValueNew) {
853             if (this.lengthValue == this.lengthValuePrev) {
854                 this.lengthValue = this.lengthValueNew;
855             }
856 
857             this.lengthValueNew = 0;
858         }
859 
860         if (!this.enabled) {
861             return 0;
862         }
863 
864         return triangleTable[this.dutyValue];
865     }
866 
867     void save(string[string] state) {
868         state["apu.triangle.enabled"] = to!string(this.enabled);
869         state["apu.triangle.lengthEnabled"] = to!string(this.lengthEnabled);
870         state["apu.triangle.lengthEnabledNewValue"] = to!string(this.lengthEnabledNewValue);
871         state["apu.triangle.lengthValue"] = to!string(this.lengthValue);
872         state["apu.triangle.lengthValueNew"] = to!string(this.lengthValueNew);
873         state["apu.triangle.lengthValuePrev"] = to!string(this.lengthValuePrev);
874         state["apu.triangle.timerPeriod"] = to!string(this.timerPeriod);
875         state["apu.triangle.timerValue"] = to!string(this.timerValue);
876         state["apu.triangle.dutyValue"] = to!string(this.dutyValue);
877         state["apu.triangle.counterPeriod"] = to!string(this.counterPeriod);
878         state["apu.triangle.counterValue"] = to!string(this.counterValue);
879         state["apu.triangle.counterReload"] = to!string(this.counterReload);
880     }
881 
882     void load(string[string] state) {
883         this.enabled = to!bool(state["apu.triangle.enabled"]);
884         this.lengthEnabled = to!bool(state["apu.triangle.lengthEnabled"]);
885         this.lengthEnabledNewValue = to!bool(state["apu.triangle.lengthEnabledNewValue"]);
886         this.lengthValue = to!ubyte(state["apu.triangle.lengthValue"]);
887         this.lengthValueNew = to!ubyte(state["apu.triangle.lengthValueNew"]);
888         this.lengthValuePrev = to!ubyte(state["apu.triangle.lengthValuePrev"]);
889         this.timerPeriod = to!ushort(state["apu.triangle.timerPeriod"]);
890         this.timerValue = to!ushort(state["apu.triangle.timerValue"]);
891         this.dutyValue = to!ubyte(state["apu.triangle.dutyValue"]);
892         this.counterPeriod = to!ubyte(state["apu.triangle.counterPeriod"]);
893         this.counterValue = to!ubyte(state["apu.triangle.counterValue"]);
894         this.counterReload = to!bool(state["apu.triangle.counterReload"]);
895     }
896 }
897 
898 // Noise
899 
900 struct Noise {
901     bool   enabled;
902     bool   mode;
903     ushort shiftRegister;
904     bool   lengthEnabled;
905     bool   lengthEnabledNewValue;
906     ubyte  lengthValue;
907     ubyte  lengthValueNew;
908     ubyte  lengthValuePrev;
909     ushort timerPeriod;
910     ushort timerValue;
911     bool   envelopeEnabled;
912     bool   envelopeLoop;
913     bool   envelopeStart;
914     byte   envelopeDivider;
915     ubyte  envelopeCounter;
916     ubyte  envelopeConstantVolume;
917 
918     void reset() {
919         this.timerPeriod = cast(ushort)(noiseTable[0] - 1);
920         this.shiftRegister = 1;
921         this.mode = false;
922 
923         this.lengthEnabled = true;
924         this.lengthEnabledNewValue = true;
925         this.lengthValue = 0;
926         this.lengthValueNew = 0;
927         this.lengthValuePrev = 0;
928 
929         this.envelopeEnabled = false;
930         this.envelopeLoop = false;
931         this.envelopeConstantVolume = 0;
932         this.envelopeCounter = 0;
933         this.envelopeStart = false;
934         this.envelopeDivider = 0;
935     }
936 
937     // 0x400C
938     void writeControl(ubyte value) {
939         this.lengthEnabledNewValue = ((value >> 5) & 1) == 0;
940         this.envelopeLoop = ((value >> 5) & 1) == 1;
941         this.envelopeEnabled = ((value >> 4) & 1) == 0;
942         this.envelopeConstantVolume = value & 15;
943     }
944 
945     // 0x400E
946     void writePeriod(ubyte value) {
947         this.mode = (value & 0x80) == 0x80;
948         this.timerPeriod = cast(ushort)(noiseTable[value & 0x0F] - 1);
949     }
950 
951     // 0x400F
952     void writeLength(ubyte value) {
953         if (this.enabled) {
954             this.lengthValueNew = lengthTable[value >> 3];
955             this.lengthValuePrev = this.lengthValue;
956         }
957 
958         this.envelopeStart = true;
959     }
960 
961     void stepTimer() {
962         if (this.timerValue == 0) {
963             this.timerValue = this.timerPeriod;
964 
965             ushort feedback = (this.shiftRegister & 0x01) ^ ((this.shiftRegister >> (this.mode ? 6 : 1)) & 0x01);
966             this.shiftRegister >>= 1;
967             this.shiftRegister |= (feedback << 14);
968         } else {
969             this.timerValue--;
970         }
971     }
972 
973     void stepEnvelope() {
974         if (this.envelopeStart) {
975             this.envelopeStart = false;
976             this.envelopeCounter = 15;
977             this.envelopeDivider = this.envelopeConstantVolume;
978         }  else {
979             this.envelopeDivider--;
980 
981             if (this.envelopeDivider < 0) {
982                 this.envelopeDivider = this.envelopeConstantVolume;
983                 if (this.envelopeCounter > 0) {
984                     this.envelopeCounter--;
985                 }
986                 else if (this.envelopeLoop)
987                     this.envelopeCounter = 15;
988             }
989         }
990     }
991 
992     void stepLength() {
993         if (this.lengthEnabled && this.lengthValue > 0) {
994             this.lengthValue--;
995         }
996     }
997 
998     ubyte output() {
999         // Emulate 1 cycle delay
1000         this.lengthEnabled = this.lengthEnabledNewValue;
1001 
1002         // Emulate 1 cycle delay
1003         if (this.lengthValueNew) {
1004             if (this.lengthValue == this.lengthValuePrev) {
1005                 this.lengthValue = this.lengthValueNew;
1006             }
1007 
1008             this.lengthValueNew = 0;
1009         }
1010 
1011         if (!this.enabled) {
1012             return 0;
1013         }
1014         if (this.lengthValue == 0) {
1015             return 0;
1016         }
1017         if ((this.shiftRegister & 1) == 1) {
1018             return 0;
1019         }
1020         if (this.envelopeEnabled) {
1021             return this.envelopeCounter;
1022         } else {
1023             return this.envelopeConstantVolume;
1024         }
1025     }
1026 
1027     void save(string[string] state) {
1028         state["apu.noise.enabled"] = to!string(this.enabled);
1029         state["apu.noise.mode"] = to!string(this.mode);
1030         state["apu.noise.shiftRegister"] = to!string(this.shiftRegister);
1031         state["apu.noise.lengthEnabled"] = to!string(this.lengthEnabled);
1032         state["apu.noise.lengthEnabledNewValue"] = to!string(this.lengthEnabledNewValue);
1033         state["apu.noise.lengthValue"] = to!string(this.lengthValue);
1034         state["apu.noise.lengthValueNew"] = to!string(this.lengthValueNew);
1035         state["apu.noise.lengthValuePrev"] = to!string(this.lengthValuePrev);
1036         state["apu.noise.timerPeriod"] = to!string(this.timerPeriod);
1037         state["apu.noise.timerValue"] = to!string(this.timerValue);
1038         state["apu.noise.envelopeEnabled"] = to!string(this.envelopeEnabled);
1039         state["apu.noise.envelopeLoop"] = to!string(this.envelopeLoop);
1040         state["apu.noise.envelopeStart"] = to!string(this.envelopeStart);
1041         state["apu.noise.envelopeDivider"] = to!string(this.envelopeDivider);
1042         state["apu.noise.envelopeCounter"] = to!string(this.envelopeCounter);
1043         state["apu.noise.envelopeConstantVolume"] = to!string(this.envelopeConstantVolume);
1044     }
1045 
1046     void load(string[string] state) {
1047         this.enabled = to!bool(state["apu.noise.enabled"]);
1048         this.mode = to!bool(state["apu.noise.mode"]);
1049         this.shiftRegister = to!ushort(state["apu.noise.shiftRegister"]);
1050         this.lengthEnabled = to!bool(state["apu.noise.lengthEnabled"]);
1051         this.lengthEnabledNewValue = to!bool(state["apu.noise.lengthEnabledNewValue"]);
1052         this.lengthValue = to!ubyte(state["apu.noise.lengthValue"]);
1053         this.lengthValueNew = to!ubyte(state["apu.noise.lengthValueNew"]);
1054         this.lengthValuePrev = to!ubyte(state["apu.noise.lengthValuePrev"]);
1055         this.timerPeriod = to!ushort(state["apu.noise.timerPeriod"]);
1056         this.timerValue = to!ushort(state["apu.noise.timerValue"]);
1057         this.envelopeEnabled = to!bool(state["apu.noise.envelopeEnabled"]);
1058         this.envelopeLoop = to!bool(state["apu.noise.envelopeLoop"]);
1059         this.envelopeStart = to!bool(state["apu.noise.envelopeStart"]);
1060         this.envelopeDivider = to!ubyte(state["apu.noise.envelopeDivider"]);
1061         this.envelopeCounter = to!ubyte(state["apu.noise.envelopeCounter"]);
1062         this.envelopeConstantVolume = to!ubyte(state["apu.noise.envelopeConstantVolume"]);
1063     }
1064 }
1065 
1066 // DMC
1067 
1068 struct DMC {
1069     CPU    cpu;
1070     bool   enabled;
1071     ubyte  value;
1072     ushort sampleAddress;
1073     ushort sampleLength;
1074     ushort currentAddress;
1075     ushort currentLength;
1076     ubyte  shiftRegister;
1077     ubyte  bitCount;
1078     ubyte  tickPeriod;
1079     ubyte  tickValue;
1080     bool   loop;
1081     bool   irq;
1082 
1083     void reset() {
1084         this.tickPeriod = cast(ubyte)(dmcTable[0] - 1);
1085         this.bitCount = 8;
1086 
1087         this.value = 0;
1088         this.sampleAddress = 0;
1089         this.sampleLength = 0;
1090         this.currentAddress = 0;
1091         this.currentLength = 0;
1092         this.shiftRegister = 0;
1093         this.loop = false;
1094         this.irq = false;
1095     }
1096 
1097     // 0x4010
1098     void writeControl(ubyte value) {
1099         this.irq = (value & 0x80) == 0x80;
1100         this.loop = (value & 0x40) == 0x40;
1101         this.tickPeriod = cast(ubyte)(dmcTable[value & 0x0F] - 1);
1102 
1103         if (!this.irq) this.cpu.clearIrqSource(IrqSource.DMC);
1104     }
1105 
1106     // 0x4011
1107     void writeValue(ubyte value) {
1108         this.value = value & 0x7F;
1109     }
1110 
1111     // 0x4012
1112     void writeAddress(ubyte value) {
1113         // Sample address = %11AAAAAA.AA000000
1114         this.sampleAddress = 0xC000 | (cast(ushort)value << 6);
1115     }
1116 
1117     // 0x4013
1118     void writeLength(ubyte value) {
1119         // Sample length = %0000LLLL.LLLL0001
1120         this.sampleLength = (cast(ushort)value << 4) | 1;
1121     }
1122 
1123     void restart() {
1124         this.currentAddress = this.sampleAddress;
1125         this.currentLength = this.sampleLength;
1126     }
1127 
1128     void stepTimer() {
1129         if (!this.enabled) {
1130             return;
1131         }
1132         this.stepReader();
1133         if (this.tickValue == 0) {
1134             this.tickValue = this.tickPeriod;
1135             this.stepShifter();
1136         } else {
1137             this.tickValue--;
1138         }
1139     }
1140 
1141     void stepReader() {
1142         if (this.currentLength > 0 && this.bitCount == 0) {
1143             this.cpu.stall += 4;
1144             this.shiftRegister = this.cpu.read(this.currentAddress);
1145             this.bitCount = 8;
1146             this.currentAddress++;
1147             if (this.currentAddress == 0) {
1148                 this.currentAddress = 0x8000;
1149             }
1150             this.currentLength--;
1151             if (this.currentLength == 0) {
1152                 if (this.loop) {
1153                     this.restart();
1154                 }
1155                 else if (this.irq) {
1156                     this.cpu.addIrqSource(IrqSource.DMC);
1157                 }
1158             }
1159         }
1160     }
1161 
1162     void stepShifter() {
1163         if (this.bitCount == 0) {
1164             return;
1165         }
1166         if ((this.shiftRegister & 1) == 1) {
1167             if (this.value <= 125) {
1168                 this.value += 2;
1169             }
1170         } else {
1171             if (this.value >= 2) {
1172                 this.value -= 2;
1173             }
1174         }
1175         this.shiftRegister >>= 1;
1176         this.bitCount--;
1177     }
1178 
1179     ubyte output() {
1180         return this.value;
1181     }
1182 
1183     void save(string[string] state) {
1184         state["apu.dmc.enabled"] = to!string(this.enabled);
1185         state["apu.dmc.value"] = to!string(this.value);
1186         state["apu.dmc.sampleAddress"] = to!string(this.sampleAddress);
1187         state["apu.dmc.sampleLength"] = to!string(this.sampleLength);
1188         state["apu.dmc.currentAddress"] = to!string(this.currentAddress);
1189         state["apu.dmc.currentLength"] = to!string(this.currentLength);
1190         state["apu.dmc.shiftRegister"] = to!string(this.shiftRegister);
1191         state["apu.dmc.bitCount"] = to!string(this.bitCount);
1192         state["apu.dmc.tickPeriod"] = to!string(this.tickPeriod);
1193         state["apu.dmc.tickValue"] = to!string(this.tickValue);
1194         state["apu.dmc.loop"] = to!string(this.loop);
1195         state["apu.dmc.irq"] = to!string(this.irq);
1196     }
1197 
1198     void load(string[string] state) {
1199         this.enabled = to!bool(state["apu.dmc.enabled"]);
1200         this.value = to!ubyte(state["apu.dmc.value"]);
1201         this.sampleAddress = to!ushort(state["apu.dmc.sampleAddress"]);
1202         this.sampleLength = to!ushort(state["apu.dmc.sampleLength"]);
1203         this.currentAddress = to!ushort(state["apu.dmc.currentAddress"]);
1204         this.currentLength = to!ushort(state["apu.dmc.currentLength"]);
1205         this.shiftRegister = to!ubyte(state["apu.dmc.shiftRegister"]);
1206         this.bitCount = to!ubyte(state["apu.dmc.bitCount"]);
1207         this.tickPeriod = to!ubyte(state["apu.dmc.tickPeriod"]);
1208         this.tickValue = to!ubyte(state["apu.dmc.tickValue"]);
1209         this.loop = to!bool(state["apu.dmc.loop"]);
1210         this.irq = to!bool(state["apu.dmc.irq"]);
1211     }
1212 }