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 }