1 module nes.apu;
2 
3 import std.conv;
4 import std.stdio;
5 
6 import nes.console;
7 import nes.cpu;
8 import nes.filter;
9 
10 enum frameCounterRate = CPUFrequency / 240.0;
11 
12 ubyte[] lengthTable = [
13     10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14,
14     12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
15 ];
16 
17 ubyte[][] dutyTable = [
18     [0, 1, 0, 0, 0, 0, 0, 0],
19     [0, 1, 1, 0, 0, 0, 0, 0],
20     [0, 1, 1, 1, 1, 0, 0, 0],
21     [1, 0, 0, 1, 1, 1, 1, 1]
22 ];
23 
24 ubyte[] triangleTable = [
25     15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
26     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
27 ];
28 
29 ushort[] noiseTable = [
30     4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
31 ];
32 
33 ubyte[] dmcTable = [
34     214, 190, 170, 160, 143, 127, 113, 107, 95, 80, 71, 64, 53, 42, 36, 27
35 ];
36 
37 float[31] pulseTable;
38 float[203] tndTable;
39 
40 static this() {
41     for (auto i = 0; i < 31; i++) {
42         pulseTable[i] = 95.52 / (8128.0 / cast(float)i + 100);
43     }
44     for (auto i = 0; i < 203; i++) {
45         tndTable[i] = 163.67 / (24329.0 / cast(float)i + 100);
46     }
47 }
48 
49 alias void delegate(float) ApuCallbackFuncType;
50 
51 class APU {
52     this(Console console) {
53         this.console = console;
54         this.noise.shiftRegister = 1;
55         this.pulse1.channel = 1;
56         this.pulse2.channel = 2;
57         this.dmc.cpu = console.cpu;
58     }
59 
60     void step() {
61         auto cycle1 = this.cycle;
62         this.cycle++;
63         auto cycle2 = this.cycle;
64         this.stepTimer();
65         auto f1 = cast(int)(cast(double)cycle1 / frameCounterRate);
66         auto f2 = cast(int)(cast(double)cycle2 / frameCounterRate);
67         if (f1 != f2) {
68             this.stepFrameCounter();
69         }
70         auto s1 = cast(int)(cast(double)cycle1 / this.sampleRate);
71         auto s2 = cast(int)(cast(double)cycle2 / this.sampleRate);
72         if (s1 != s2) {
73             this.sendSample();
74         }
75     }
76 
77     ubyte readRegister(ushort address) {
78         switch (address) {
79             case 0x4015:
80                 return this.readStatus();
81             default:
82                 break;
83             // default:
84             //     log.Fatalf("unhandled apu register read at address: 0x%04X", address)
85         }
86         return 0;
87     }
88 
89     void writeRegister(ushort address, ubyte value) {
90         switch (address) {
91             case 0x4000:
92                 this.pulse1.writeControl(value);
93                 break;
94             case 0x4001:
95                 this.pulse1.writeSweep(value);
96                 break;
97             case 0x4002:
98                 this.pulse1.writeTimerLow(value);
99                 break;
100             case 0x4003:
101                 this.pulse1.writeTimerHigh(value);
102                 break;
103             case 0x4004:
104                 this.pulse2.writeControl(value);
105                 break;
106             case 0x4005:
107                 this.pulse2.writeSweep(value);
108                 break;
109             case 0x4006:
110                 this.pulse2.writeTimerLow(value);
111                 break;
112             case 0x4007:
113                 this.pulse2.writeTimerHigh(value);
114                 break;
115             case 0x4008:
116                 this.triangle.writeControl(value);
117                 break;
118             case 0x4009:
119                 break;
120             case 0x4010:
121                 this.dmc.writeControl(value);
122                 break;
123             case 0x4011:
124                 this.dmc.writeValue(value);
125                 break;
126             case 0x4012:
127                 this.dmc.writeAddress(value);
128                 break;
129             case 0x4013:
130                 this.dmc.writeLength(value);
131                 break;
132             case 0x400A:
133                 this.triangle.writeTimerLow(value);
134                 break;
135             case 0x400B:
136                 this.triangle.writeTimerHigh(value);
137                 break;
138             case 0x400C:
139                 this.noise.writeControl(value);
140                 break;
141             case 0x400D:
142                 break;
143             case 0x400E:
144                 this.noise.writePeriod(value);
145                 break;
146             case 0x400F:
147                 this.noise.writeLength(value);
148                 break;
149             case 0x4015:
150                 this.writeControl(value);
151                 break;
152             case 0x4017:
153                 this.writeFrameCounter(value);
154                 break;
155             default:
156                 break;
157             // default:
158             //     log.Fatalf("unhandled apu register write at address: 0x%04X", address)
159         }
160     }
161 
162     void save(string[string] state) {
163         state["apu.cycle"] = to!string(this.cycle);
164         state["apu.framePeriod"] = to!string(this.framePeriod);
165         state["apu.frameValue"] = to!string(this.frameValue);
166         state["apu.frameIRQ"] = to!string(this.frameIRQ);
167 
168         this.pulse1.save(state, "1");
169         this.pulse2.save(state, "2");
170         this.triangle.save(state);
171         this.noise.save(state);
172         this.dmc.save(state);
173     }
174 
175     void load(string[string] state) {
176         this.cycle = to!ulong(state["apu.cycle"]);
177         this.framePeriod = to!ubyte(state["apu.framePeriod"]);
178         this.frameValue = to!ubyte(state["apu.frameValue"]);
179         this.frameIRQ = to!bool(state["apu.frameIRQ"]);
180 
181         this.pulse1.load(state, "1");
182         this.pulse2.load(state, "2");
183         this.triangle.load(state);
184         this.noise.load(state);
185         this.dmc.load(state);
186     }
187 
188     package:
189         double              sampleRate;
190         ApuCallbackFuncType callback;
191         FilterChain         filterChain;
192 
193         Console console;
194         Pulse pulse1;
195         Pulse pulse2;
196         Triangle triangle;
197         Noise noise;
198         DMC dmc;
199         ulong cycle;
200         ubyte framePeriod;
201         ubyte frameValue;
202         bool frameIRQ;
203 
204     private:
205         void sendSample() {
206             auto output = this.filterChain.step(this.output());
207             if (this.callback)
208                 this.callback(output);
209         }
210 
211         float output() {
212             auto p1 = this.pulse1.output();
213             auto p2 = this.pulse2.output();
214             auto t = this.triangle.output();
215             auto n = this.noise.output();
216             auto d = this.dmc.output();
217             auto pulseOut = pulseTable[p1 + p2];
218             auto tndOut = tndTable[3 * t + 2 * n + d];
219 
220             return pulseOut + tndOut;
221         }
222 
223         // mode 0:    mode 1:       function
224         // ---------  -----------  -----------------------------
225         //  - - - f    - - - - -    IRQ (if bit 6 is clear)
226         //  - l - l    l - l - -    Length counter and sweep
227         //  e e e e    e e e e -    Envelope and linear counter
228         void stepFrameCounter() {
229             switch (this.framePeriod) {
230                 case 4:
231                     this.frameValue = (this.frameValue + 1) % 4;
232                     switch (this.frameValue) {
233                         case 0, 2:
234                             this.stepEnvelope();
235                             break;
236                         case 1:
237                             this.stepEnvelope();
238                             this.stepSweep();
239                             this.stepLength();
240                             break;
241                         case 3:
242                             this.stepEnvelope();
243                             this.stepSweep();
244                             this.stepLength();
245                             this.fireIRQ();
246                             break;
247                         default:
248                             break;
249                     }
250 
251                     break;
252                 case 5:
253                     this.frameValue = (this.frameValue + 1) % 5;
254                     switch (this.frameValue) {
255                         case 1, 3:
256                             this.stepEnvelope();
257                             break;
258                         case 0, 2:
259                             this.stepEnvelope();
260                             this.stepSweep();
261                             this.stepLength();
262                             break;
263                         default:
264                             break;
265                     }
266 
267                     break;
268 
269                 default:
270                     break;
271             }
272         }
273 
274         void stepTimer() {
275             if (this.cycle % 2 == 0) {
276                 this.pulse1.stepTimer();
277                 this.pulse2.stepTimer();
278                 this.noise.stepTimer();
279                 this.dmc.stepTimer();
280             }
281             this.triangle.stepTimer();
282         }
283 
284         void stepEnvelope() {
285             this.pulse1.stepEnvelope();
286             this.pulse2.stepEnvelope();
287             this.triangle.stepCounter();
288             this.noise.stepEnvelope();
289         }
290 
291         void stepSweep() {
292             this.pulse1.stepSweep();
293             this.pulse2.stepSweep();
294         }
295 
296         void stepLength() {
297             this.pulse1.stepLength();
298             this.pulse2.stepLength();
299             this.triangle.stepLength();
300             this.noise.stepLength();
301         }
302 
303         void fireIRQ() {
304             if (this.frameIRQ) {
305                 this.console.cpu.triggerIRQ();
306             }
307         }
308 
309         ubyte readStatus() {
310             ubyte result;
311             if (this.pulse1.lengthValue > 0) {
312                 result |= 1;
313             }
314             if (this.pulse2.lengthValue > 0) {
315                 result |= 2;
316             }
317             if (this.triangle.lengthValue > 0) {
318                 result |= 4;
319             }
320             if (this.noise.lengthValue > 0) {
321                 result |= 8;
322             }
323             if (this.dmc.currentLength > 0) {
324                 result |= 16;
325             }
326             return result;
327         }
328 
329         void writeControl(ubyte value) {
330             this.pulse1.enabled = (value & 1) == 1;
331             this.pulse2.enabled = (value & 2) == 2;
332             this.triangle.enabled = (value & 4) == 4;
333             this.noise.enabled = (value & 8) == 8;
334             this.dmc.enabled = (value & 16) == 16;
335             if (!this.pulse1.enabled) {
336                 this.pulse1.lengthValue = 0;
337             }
338             if (!this.pulse2.enabled) {
339                 this.pulse2.lengthValue = 0;
340             }
341             if (!this.triangle.enabled) {
342                 this.triangle.lengthValue = 0;
343             }
344             if (!this.noise.enabled) {
345                 this.noise.lengthValue = 0;
346             }
347             if (!this.dmc.enabled) {
348                 this.dmc.currentLength = 0;
349             } else {
350                 if (this.dmc.currentLength == 0) {
351                     this.dmc.restart();
352                 }
353             }
354         }
355 
356         void writeFrameCounter(ubyte value) {
357             this.framePeriod = 4 + ((value >> 7) & 1);
358             this.frameIRQ = ((value >> 6) & 1) == 0;
359             // this.frameValue = 0;
360             if (this.framePeriod == 5) {
361                 this.stepEnvelope();
362                 this.stepSweep();
363                 this.stepLength();
364             }
365         }
366 }
367 
368 // Pulse
369 
370 struct Pulse {
371     bool   enabled;
372     ubyte  channel;
373     bool   lengthEnabled;
374     ubyte  lengthValue;
375     ushort timerPeriod;
376     ushort timerValue;
377     ubyte  dutyMode;
378     ubyte  dutyValue;
379     bool   sweepReload;
380     bool   sweepEnabled;
381     bool   sweepNegate;
382     ubyte  sweepShift;
383     ubyte  sweepPeriod;
384     ubyte  sweepValue;
385     bool   envelopeEnabled;
386     bool   envelopeLoop;
387     bool   envelopeStart;
388     ubyte  envelopePeriod;
389     ubyte  envelopeValue;
390     ubyte  envelopeVolume;
391     ubyte  constantVolume;
392 
393     void writeControl(ubyte value) {
394         this.dutyMode = (value >> 6) & 3;
395         this.lengthEnabled = ((value >> 5) & 1) == 0;
396         this.envelopeLoop = ((value >> 5) & 1) == 1;
397         this.envelopeEnabled = ((value >> 4) & 1) == 0;
398         this.envelopePeriod = value & 15;
399         this.constantVolume = value & 15;
400         this.envelopeStart = true;
401     }
402 
403     void writeSweep(ubyte value) {
404         this.sweepEnabled = ((value >> 7) & 1) == 1;
405         this.sweepPeriod = ((value >> 4) & 7) + 1;
406         this.sweepNegate = ((value >> 3) & 1) == 1;
407         this.sweepShift = value & 7;
408         this.sweepReload = true;
409     }
410 
411     void writeTimerLow(ubyte value) {
412         this.timerPeriod = (this.timerPeriod & 0xFF00) | cast(ushort)value;
413     }
414 
415     void writeTimerHigh(ubyte value) {
416         this.lengthValue = lengthTable[value >> 3];
417         this.timerPeriod = (this.timerPeriod & 0x00FF) | (cast(ushort)(value & 7) << 8);
418         this.envelopeStart = true;
419         this.dutyValue = 0;
420     }
421 
422     void stepTimer() {
423         if (this.timerValue == 0) {
424             this.timerValue = this.timerPeriod;
425             this.dutyValue = (this.dutyValue + 1) % 8;
426         } else {
427             this.timerValue--;
428         }
429     }
430 
431     void stepEnvelope() {
432         if (this.envelopeStart) {
433             this.envelopeVolume = 15;
434             this.envelopeValue = this.envelopePeriod;
435             this.envelopeStart = false;
436         } else if (this.envelopeValue > 0) {
437             this.envelopeValue--;
438         } else {
439             if (this.envelopeVolume > 0) {
440                 this.envelopeVolume--;
441             } else if (this.envelopeLoop) {
442                 this.envelopeVolume = 15;
443             }
444             this.envelopeValue = this.envelopePeriod;
445         }
446     }
447 
448     void stepSweep() {
449         if (this.sweepReload) {
450             if (this.sweepEnabled && this.sweepValue == 0) {
451                 this.sweep();
452             }
453             this.sweepValue = this.sweepPeriod;
454             this.sweepReload = false;
455         } else if (this.sweepValue > 0) {
456             this.sweepValue--;
457         } else {
458             if (this.sweepEnabled) {
459                 this.sweep();
460             }
461             this.sweepValue = this.sweepPeriod;
462         }
463     }
464 
465     void stepLength() {
466         if (this.lengthEnabled && this.lengthValue > 0) {
467             this.lengthValue--;
468         }
469     }
470 
471     void sweep() {
472         auto delta = this.timerPeriod >> this.sweepShift;
473         if (this.sweepNegate) {
474             this.timerPeriod -= delta;
475             if (this.channel == 1) {
476                 this.timerPeriod--;
477             }
478         } else {
479             this.timerPeriod += delta;
480         }
481     }
482 
483     ubyte output() {
484         if (!this.enabled) {
485             return 0;
486         }
487         if (this.lengthValue == 0) {
488             return 0;
489         }
490         if (dutyTable[this.dutyMode][this.dutyValue] == 0) {
491             return 0;
492         }
493         if (this.timerPeriod < 8 || this.timerPeriod > 0x7FF) {
494             return 0;
495         }
496         // if (!this.sweepNegate && this.timerPeriod + (this.timerPeriod >> this.sweepShift) > 0x7FF) {
497         //  return 0;
498         // }
499         if (this.envelopeEnabled) {
500             return this.envelopeVolume;
501         } else {
502             return this.constantVolume;
503         }
504     }
505 
506     void save(string[string] state, string id) {
507         id = "apu.pulse" ~ id;
508 
509         state[id ~ ".enabled"] = to!string(this.enabled);
510         state[id ~ ".channel"] = to!string(this.channel);
511         state[id ~ ".lengthEnabled"] = to!string(this.lengthEnabled);
512         state[id ~ ".lengthValue"] = to!string(this.lengthValue);
513         state[id ~ ".timerPeriod"] = to!string(this.timerPeriod);
514         state[id ~ ".timerValue"] = to!string(this.timerValue);
515         state[id ~ ".dutyMode"] = to!string(this.dutyMode);
516         state[id ~ ".dutyValue"] = to!string(this.dutyValue);
517         state[id ~ ".sweepReload"] = to!string(this.sweepReload);
518         state[id ~ ".sweepEnabled"] = to!string(this.sweepEnabled);
519         state[id ~ ".sweepNegate"] = to!string(this.sweepNegate);
520         state[id ~ ".sweepShift"] = to!string(this.sweepShift);
521         state[id ~ ".sweepPeriod"] = to!string(this.sweepPeriod);
522         state[id ~ ".sweepValue"] = to!string(this.sweepValue);
523         state[id ~ ".envelopeEnabled"] = to!string(this.envelopeEnabled);
524         state[id ~ ".envelopeLoop"] = to!string(this.envelopeLoop);
525         state[id ~ ".envelopeStart"] = to!string(this.envelopeStart);
526         state[id ~ ".envelopePeriod"] = to!string(this.envelopePeriod);
527         state[id ~ ".envelopeValue"] = to!string(this.envelopeValue);
528         state[id ~ ".envelopeVolume"] = to!string(this.envelopeVolume);
529         state[id ~ ".constantVolume"] = to!string(this.constantVolume);
530     }
531 
532     void load(string[string] state, string id) {
533         id = "apu.pulse" ~ id;
534 
535         this.enabled = to!bool(state[id ~ ".enabled"]);
536         this.channel = to!ubyte(state[id ~ ".channel"]);
537         this.lengthEnabled = to!bool(state[id ~ ".lengthEnabled"]);
538         this.lengthValue = to!ubyte(state[id ~ ".lengthValue"]);
539         this.timerPeriod = to!ushort(state[id ~ ".timerPeriod"]);
540         this.timerValue = to!ushort(state[id ~ ".timerValue"]);
541         this.dutyMode = to!ubyte(state[id ~ ".dutyMode"]);
542         this.dutyValue = to!ubyte(state[id ~ ".dutyValue"]);
543         this.sweepReload = to!bool(state[id ~ ".sweepReload"]);
544         this.sweepEnabled = to!bool(state[id ~ ".sweepEnabled"]);
545         this.sweepNegate = to!bool(state[id ~ ".sweepNegate"]);
546         this.sweepShift = to!ubyte(state[id ~ ".sweepShift"]);
547         this.sweepPeriod = to!ubyte(state[id ~ ".sweepPeriod"]);
548         this.sweepValue = to!ubyte(state[id ~ ".sweepValue"]);
549         this.envelopeEnabled = to!bool(state[id ~ ".envelopeEnabled"]);
550         this.envelopeLoop = to!bool(state[id ~ ".envelopeLoop"]);
551         this.envelopeStart = to!bool(state[id ~ ".envelopeStart"]);
552         this.envelopePeriod = to!ubyte(state[id ~ ".envelopePeriod"]);
553         this.envelopeValue = to!ubyte(state[id ~ ".envelopeValue"]);
554         this.envelopeVolume = to!ubyte(state[id ~ ".envelopeVolume"]);
555         this.constantVolume = to!ubyte(state[id ~ ".constantVolume"]);
556     }
557 }
558 
559 // Triangle
560 
561 struct Triangle {
562     bool   enabled;
563     bool   lengthEnabled;
564     ubyte  lengthValue;
565     ushort timerPeriod;
566     ushort timerValue;
567     ubyte  dutyValue;
568     ubyte  counterPeriod;
569     ubyte  counterValue;
570     bool   counterReload;
571 
572     void writeControl(ubyte value) {
573         this.lengthEnabled = ((value >> 7) & 1) == 0;
574         this.counterPeriod = value & 0x7F;
575     }
576 
577     void writeTimerLow(ubyte value) {
578         this.timerPeriod = (this.timerPeriod & 0xFF00) | cast(ushort)value;
579     }
580 
581     void writeTimerHigh(ubyte value) {
582         this.lengthValue = lengthTable[value >> 3];
583         this.timerPeriod = (this.timerPeriod & 0x00FF) | (cast(ushort)(value & 7) << 8);
584         this.timerValue = this.timerPeriod;
585         this.counterReload = true;
586     }
587 
588     void stepTimer() {
589         if (this.timerValue == 0) {
590             this.timerValue = this.timerPeriod;
591             if (this.lengthValue > 0 && this.counterValue > 0) {
592                 this.dutyValue = (this.dutyValue + 1) % 32;
593             }
594         } else {
595             this.timerValue--;
596         }
597     }
598 
599     void stepLength() {
600         if (this.lengthEnabled && this.lengthValue > 0) {
601             this.lengthValue--;
602         }
603     }
604 
605     void stepCounter() {
606         if (this.counterReload) {
607             this.counterValue = this.counterPeriod;
608         } else if (this.counterValue > 0) {
609             this.counterValue--;
610         }
611         if (this.lengthEnabled) {
612             this.counterReload = false;
613         }
614     }
615 
616     ubyte output() {
617         if (!this.enabled) {
618             return 0;
619         }
620         if (this.lengthValue == 0) {
621             return 0;
622         }
623         if (this.counterValue == 0) {
624             return 0;
625         }
626         return triangleTable[this.dutyValue];
627     }
628 
629     void save(string[string] state) {
630         state["apu.triangle.enabled"] = to!string(this.enabled);
631         state["apu.triangle.lengthEnabled"] = to!string(this.lengthEnabled);
632         state["apu.triangle.lengthValue"] = to!string(this.lengthValue);
633         state["apu.triangle.timerPeriod"] = to!string(this.timerPeriod);
634         state["apu.triangle.timerValue"] = to!string(this.timerValue);
635         state["apu.triangle.dutyValue"] = to!string(this.dutyValue);
636         state["apu.triangle.counterPeriod"] = to!string(this.counterPeriod);
637         state["apu.triangle.counterValue"] = to!string(this.counterValue);
638         state["apu.triangle.counterReload"] = to!string(this.counterReload);
639     }
640 
641     void load(string[string] state) {
642         this.enabled = to!bool(state["apu.triangle.enabled"]);
643         this.lengthEnabled = to!bool(state["apu.triangle.lengthEnabled"]);
644         this.lengthValue = to!ubyte(state["apu.triangle.lengthValue"]);
645         this.timerPeriod = to!ushort(state["apu.triangle.timerPeriod"]);
646         this.timerValue = to!ushort(state["apu.triangle.timerValue"]);
647         this.dutyValue = to!ubyte(state["apu.triangle.dutyValue"]);
648         this.counterPeriod = to!ubyte(state["apu.triangle.counterPeriod"]);
649         this.counterValue = to!ubyte(state["apu.triangle.counterValue"]);
650         this.counterReload = to!bool(state["apu.triangle.counterReload"]);
651     }
652 }
653 
654 // Noise
655 
656 struct Noise {
657     bool   enabled;
658     bool   mode;
659     ushort shiftRegister;
660     bool   lengthEnabled;
661     ubyte  lengthValue;
662     ushort timerPeriod;
663     ushort timerValue;
664     bool   envelopeEnabled;
665     bool   envelopeLoop;
666     bool   envelopeStart;
667     ubyte  envelopePeriod;
668     ubyte  envelopeValue;
669     ubyte  envelopeVolume;
670     ubyte  constantVolume;
671 
672     void writeControl(ubyte value) {
673         this.lengthEnabled = ((value >> 5) & 1) == 0;
674         this.envelopeLoop = ((value >> 5) & 1) == 1;
675         this.envelopeEnabled = ((value >> 4) & 1) == 0;
676         this.envelopePeriod = value & 15;
677         this.constantVolume = value & 15;
678         this.envelopeStart = true;
679     }
680 
681     void writePeriod(ubyte value) {
682         this.mode = (value & 0x80) == 0x80;
683         this.timerPeriod = noiseTable[value & 0x0F];
684     }
685 
686     void writeLength(ubyte value) {
687         this.lengthValue = lengthTable[value >> 3];
688         this.envelopeStart = true;
689     }
690 
691     void stepTimer() {
692         if (this.timerValue == 0) {
693             this.timerValue = this.timerPeriod;
694             ubyte shift;
695             if (this.mode) {
696                 shift = 6;
697             } else {
698                 shift = 1;
699             }
700             auto b1 = this.shiftRegister & 1;
701             auto b2 = (this.shiftRegister >> shift) & 1;
702             this.shiftRegister >>= 1;
703             this.shiftRegister |= (b1 ^ b2) << 14;
704         } else {
705             this.timerValue--;
706         }
707     }
708 
709     void stepEnvelope() {
710         if (this.envelopeStart) {
711             this.envelopeVolume = 15;
712             this.envelopeValue = this.envelopePeriod;
713             this.envelopeStart = false;
714         } else if (this.envelopeValue > 0) {
715             this.envelopeValue--;
716         } else {
717             if (this.envelopeVolume > 0) {
718                 this.envelopeVolume--;
719             } else if (this.envelopeLoop) {
720                 this.envelopeVolume = 15;
721             }
722             this.envelopeValue = this.envelopePeriod;
723         }
724     }
725 
726     void stepLength() {
727         if (this.lengthEnabled && this.lengthValue > 0) {
728             this.lengthValue--;
729         }
730     }
731 
732     ubyte output() {
733         if (!this.enabled) {
734             return 0;
735         }
736         if (this.lengthValue == 0) {
737             return 0;
738         }
739         if ((this.shiftRegister & 1) == 1) {
740             return 0;
741         }
742         if (this.envelopeEnabled) {
743             return this.envelopeVolume;
744         } else {
745             return this.constantVolume;
746         }
747     }
748 
749     void save(string[string] state) {
750         state["apu.noise.enabled"] = to!string(this.enabled);
751         state["apu.noise.mode"] = to!string(this.mode);
752         state["apu.noise.shiftRegister"] = to!string(this.shiftRegister);
753         state["apu.noise.lengthEnabled"] = to!string(this.lengthEnabled);
754         state["apu.noise.lengthValue"] = to!string(this.lengthValue);
755         state["apu.noise.timerPeriod"] = to!string(this.timerPeriod);
756         state["apu.noise.timerValue"] = to!string(this.timerValue);
757         state["apu.noise.envelopeEnabled"] = to!string(this.envelopeEnabled);
758         state["apu.noise.envelopeLoop"] = to!string(this.envelopeLoop);
759         state["apu.noise.envelopeStart"] = to!string(this.envelopeStart);
760         state["apu.noise.envelopePeriod"] = to!string(this.envelopePeriod);
761         state["apu.noise.envelopeValue"] = to!string(this.envelopeValue);
762         state["apu.noise.envelopeVolume"] = to!string(this.envelopeVolume);
763         state["apu.noise.constantVolume"] = to!string(this.constantVolume);
764     }
765 
766     void load(string[string] state) {
767         this.enabled = to!bool(state["apu.noise.enabled"]);
768         this.mode = to!bool(state["apu.noise.mode"]);
769         this.shiftRegister = to!ushort(state["apu.noise.shiftRegister"]);
770         this.lengthEnabled = to!bool(state["apu.noise.lengthEnabled"]);
771         this.lengthValue = to!ubyte(state["apu.noise.lengthValue"]);
772         this.timerPeriod = to!ushort(state["apu.noise.timerPeriod"]);
773         this.timerValue = to!ushort(state["apu.noise.timerValue"]);
774         this.envelopeEnabled = to!bool(state["apu.noise.envelopeEnabled"]);
775         this.envelopeLoop = to!bool(state["apu.noise.envelopeLoop"]);
776         this.envelopeStart = to!bool(state["apu.noise.envelopeStart"]);
777         this.envelopePeriod = to!ubyte(state["apu.noise.envelopePeriod"]);
778         this.envelopeValue = to!ubyte(state["apu.noise.envelopeValue"]);
779         this.envelopeVolume = to!ubyte(state["apu.noise.envelopeVolume"]);
780         this.constantVolume = to!ubyte(state["apu.noise.constantVolume"]);
781     }
782 }
783 
784 // DMC
785 
786 struct DMC {
787     CPU    cpu;
788     bool   enabled;
789     ubyte  value;
790     ushort sampleAddress;
791     ushort sampleLength;
792     ushort currentAddress;
793     ushort currentLength;
794     ubyte  shiftRegister;
795     ubyte  bitCount;
796     ubyte  tickPeriod;
797     ubyte  tickValue;
798     bool   loop;
799     bool   irq;
800 
801     void writeControl(ubyte value) {
802         this.irq = (value & 0x80) == 0x80;
803         this.loop = (value & 0x40) == 0x40;
804         this.tickPeriod = dmcTable[value & 0x0F];
805     }
806 
807     void writeValue(ubyte value) {
808         this.value = value & 0x7F;
809     }
810 
811     void writeAddress(ubyte value) {
812         // Sample address = %11AAAAAA.AA000000
813         this.sampleAddress = 0xC000 | (cast(ushort)value << 6);
814     }
815 
816     void writeLength(ubyte value) {
817         // Sample length = %0000LLLL.LLLL0001
818         this.sampleLength = (cast(ushort)value << 4) | 1;
819     }
820 
821     void restart() {
822         this.currentAddress = this.sampleAddress;
823         this.currentLength = this.sampleLength;
824     }
825 
826     void stepTimer() {
827         if (!this.enabled) {
828             return;
829         }
830         this.stepReader();
831         if (this.tickValue == 0) {
832             this.tickValue = this.tickPeriod;
833             this.stepShifter();
834         } else {
835             this.tickValue--;
836         }
837     }
838 
839     void stepReader() {
840         if (this.currentLength > 0 && this.bitCount == 0) {
841             this.cpu.stall += 4;
842             this.shiftRegister = this.cpu.read(this.currentAddress);
843             this.bitCount = 8;
844             this.currentAddress++;
845             if (this.currentAddress == 0) {
846                 this.currentAddress = 0x8000;
847             }
848             this.currentLength--;
849             if (this.currentLength == 0 && this.loop) {
850                 this.restart();
851             }
852         }
853     }
854 
855     void stepShifter() {
856         if (this.bitCount == 0) {
857             return;
858         }
859         if ((this.shiftRegister & 1) == 1) {
860             if (this.value <= 125) {
861                 this.value += 2;
862             }
863         } else {
864             if (this.value >= 2) {
865                 this.value -= 2;
866             }
867         }
868         this.shiftRegister >>= 1;
869         this.bitCount--;
870     }
871 
872     ubyte output() {
873         return this.value;
874     }
875 
876     void save(string[string] state) {
877         state["apu.dmc.enabled"] = to!string(this.enabled);
878         state["apu.dmc.value"] = to!string(this.value);
879         state["apu.dmc.sampleAddress"] = to!string(this.sampleAddress);
880         state["apu.dmc.sampleLength"] = to!string(this.sampleLength);
881         state["apu.dmc.currentAddress"] = to!string(this.currentAddress);
882         state["apu.dmc.currentLength"] = to!string(this.currentLength);
883         state["apu.dmc.shiftRegister"] = to!string(this.shiftRegister);
884         state["apu.dmc.bitCount"] = to!string(this.bitCount);
885         state["apu.dmc.tickPeriod"] = to!string(this.tickPeriod);
886         state["apu.dmc.tickValue"] = to!string(this.tickValue);
887         state["apu.dmc.loop"] = to!string(this.loop);
888         state["apu.dmc.irq"] = to!string(this.irq);
889     }
890 
891     void load(string[string] state) {
892         this.enabled = to!bool(state["apu.dmc.enabled"]);
893         this.value = to!ubyte(state["apu.dmc.value"]);
894         this.sampleAddress = to!ushort(state["apu.dmc.sampleAddress"]);
895         this.sampleLength = to!ushort(state["apu.dmc.sampleLength"]);
896         this.currentAddress = to!ushort(state["apu.dmc.currentAddress"]);
897         this.currentLength = to!ushort(state["apu.dmc.currentLength"]);
898         this.shiftRegister = to!ubyte(state["apu.dmc.shiftRegister"]);
899         this.bitCount = to!ubyte(state["apu.dmc.bitCount"]);
900         this.tickPeriod = to!ubyte(state["apu.dmc.tickPeriod"]);
901         this.tickValue = to!ubyte(state["apu.dmc.tickValue"]);
902         this.loop = to!bool(state["apu.dmc.loop"]);
903         this.irq = to!bool(state["apu.dmc.irq"]);
904     }
905 }