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 }