JAVASCRIPT   174

apu.js

Guest on 22nd August 2021 06:39:48 PM

  1. //
  2. //  Audio Processing Unit
  3. //_________________________//
  4.  
  5. /**
  6.  * @namespace The audio processing unit for the nes.
  7.  */
  8.  
  9. nes.apu = {
  10.  
  11.         //
  12.         //  Properties
  13.         //______________//
  14.  
  15.         /**
  16.          * The dynamic audio object used to generate the audio.
  17.          * @type Object
  18.          */
  19.  
  20.         dynamicAudio:{
  21.  
  22.                 /**
  23.                  * @ignore
  24.                  */
  25.  
  26.                 writeInt:function(){}
  27.  
  28.         },
  29.  
  30.         /**
  31.          * Determines whether or not the apu is active.
  32.          * @type Boolean
  33.          */
  34.  
  35.         active:false,
  36.  
  37.         //Frame Interrupt Variables
  38.         frameIrqEnabled:null,
  39.         frameIrqActive:null,
  40.         frameIrqCounter:null,
  41.         frameIrqCounterMax:null,
  42.  
  43.         //Hardware Initiation Flag & Counter
  44.         initCounter:null,
  45.         initiatingHardware:null,
  46.  
  47.         //Sound Buffer
  48.         bufferIndex:null,
  49.         sampleBuffer:null,
  50.  
  51.         //Misc
  52.         masterFrameCounter:null,
  53.         derivedFrameCounter:null,
  54.         countSequence:null,
  55.         sampleTimer:null,
  56.         frameTime:null,
  57.         sampleTimerMax:null,
  58.         sampleCount:null,
  59.         triValue:null,
  60.  
  61.         //Channel Samples
  62.         smpSquare1:null,
  63.         smpSquare2:null,
  64.         smpTriangle:null,
  65.         smpDmc:null,
  66.         accCount:null,
  67.  
  68.         //DC Removal
  69.         prevSampleL:0,
  70.         prevSampleR:0,
  71.         smpAccumL:0,
  72.         smpAccumR:0,
  73.  
  74.         //DAC Range
  75.         dacRange:0,
  76.         dcValue:0,
  77.  
  78.         //Master Volume
  79.         masterVolume:null,
  80.  
  81.         //Stereo Positioning
  82.         stereoPosLSquare1:null,
  83.         stereoPosLSquare2:null,
  84.         stereoPosLTriangle:null,
  85.         stereoPosLNoise:null,
  86.         stereoPosLDMC:null,
  87.         stereoPosRSquare1:null,
  88.         stereoPosRSquare2:null,
  89.         stereoPosRTriangle:null,
  90.         stereoPosRNoise:null,
  91.         stereoPosRDMC:null,
  92.  
  93.         //Panning
  94.         panning:[80,170,100,150,128],
  95.  
  96.         //Sampling Extrema
  97.         maxSample:null,
  98.         minSample:null,
  99.  
  100.         //Lookup Tables
  101.         lengthLookup:[0x0A,0xFE,0x14,0x02,0x28,0x04,0x50,0x06,0xA0,0x08,0x3C,0x0A,0x0E,0x0C,0x1A,0x0E,0x0C,0x10,0x18,0x12,0x30,0x14,0x60,0x16,0xC0,0x18,0x48,0x1A,0x10,0x1C,0x20,0x1E],
  102.         dmcFreqLookup:[0xD60,0xBE0,0xAA0,0xA00,0x8F0,0x7F0,0x710,0x6B0,0x5F0,0x500,0x470,0x400,0x350,0x2A0,0x240,0x1B0],
  103.         noiseWavelengthLookup:[0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0,0x0CA,0x0FE,0x17C,0x1FC,0x2FA,0x3F8,0x7F2,0xFE4],
  104.  
  105. //Methods
  106.  
  107.         reset:function nes_apu_reset(){
  108.                 //Check if the dynamicAudioPath was sent.
  109.                 if(typeof nes.dynamicAudioPath === 'string' && nes.dynamicAudioPath !== ''){
  110.                         //Initiate the dynamic audio wrapper.
  111.                         this.dynamicAudio = new DynamicAudio({swf:nes.dynamicAudioPath});
  112.                         //Reset the channels.
  113.                         this.square1.reset();
  114.                         this.square2.reset();
  115.                         this.triangle.reset();
  116.                         this.noise.reset();
  117.                         this.dmc.reset();
  118.                         //Frame interrupt variables.
  119.                         this.frameIrqEnabled = false;
  120.                         this.frameIrqActive = null;
  121.                         this.frameIrqCounter = null;
  122.                         this.frameIrqCounterMax = 4;
  123.                         //Set the harware initiation flag and counter, how many cpu cycles must pass before the apu is "ready".
  124.                         this.initCounter = 2048;
  125.                         this.initiatingHardware = false;
  126.                         //???
  127.                         this.triValue = 0;
  128.                         //DAC range.
  129.                         this.dacRange = 0;
  130.                         this.dcValue = 0;
  131.                         //Master volume.
  132.                         this.masterVolume = 256;
  133.                         //???
  134.                         this.extraCycles = null;
  135.                         //Update the stereo panning/positioning.
  136.                         this.updateStereoPos();
  137.                         //Init some tables.
  138.                         this.initDACtables();
  139.                         //Init sound registers.
  140.                         for(var i=0x4000;i<0x4014;i++){
  141.                                 this.writeReg(i,0);
  142.                         }
  143.                         this.writeReg(0x4010,0x10);
  144.                         //???
  145.                         this.sampleTimerMax = 1832727040/44100;
  146.                         //???
  147.                         this.frameTime = parseInt((14915*nes.frameRate)/60,10);
  148.                         //???
  149.                         this.sampleTimer = 0;
  150.                         //Disable the channels.
  151.                         this.updateChannelEnable(0);
  152.                         //???
  153.                         this.masterFrameCounter = 0;
  154.                         this.derivedFrameCounter = 4;
  155.                         this.countSequence = 0;
  156.                         this.sampleCount = 0;
  157.                         //Reset the sample buffer and buffer index.
  158.                         this.bufferIndex = 0;
  159.                         this.sampleBuffer = new Array(16384);
  160.                         //???
  161.                         this.accCount = 0;
  162.                         //Reset the channel samples.
  163.                         this.smpSquare1 = 0;
  164.                         this.smpSquare2 = 0;
  165.                         this.smpTriangle = 0;
  166.                         this.smpDmc = 0;
  167.                         //DC removal variables.
  168.                         this.prevSampleL = 0;
  169.                         this.prevSampleR = 0;
  170.                         this.smpAccumL = 0;
  171.                         this.smpAccumR = 0;
  172.                         //Set the minimum and maximum sampling values.
  173.                         this.maxSample = -500000;
  174.                         this.minSample = 500000;
  175.                 }
  176.                 else{
  177.                         //Path to dynamic audio flash object not specified, deactivate the apu.
  178.                         this.active = false;
  179.                 }
  180.         },
  181.  
  182.         readReg:function nes_apu_readReg(){
  183.                 //Construct the value that would normally be held in 0x4015 from the apu flags.
  184.                 var temp = (this.dmc.getIrqStatus()<<7)|(((this.frameIrqActive && this.frameIrqEnabled)?1:0)<<6)|(this.getChannelLengthStatus(this.dmc)<<4)|(this.getChannelLengthStatus(this.noise)<<3)|(this.getChannelLengthStatus(this.triangle)<<2)|(this.getChannelLengthStatus(this.square2)<<1)|this.getChannelLengthStatus(this.square1);
  185.                 //???
  186.                 this.frameIrqActive = false;
  187.                 this.dmc.irqGenerated = false;
  188.                 //Return the previously constructed byte.
  189.                 return temp&0xFFFF;
  190.         },
  191.  
  192.         writeReg:function nes_apu_writeReg(address,value){
  193.                 //Square Wave 1 Control
  194.                 if(address >= 0x4000 && address < 0x4004){
  195.                         this.square1.writeReg(address,value);
  196.                 }
  197.                 //Square Wave 2 Control
  198.                 else if(address >= 0x4004 && address < 0x4008){
  199.                         this.square2.writeReg(address,value);
  200.                 }
  201.                 //Triangle Control
  202.                 else if(address >= 0x4008 && address < 0x400C){
  203.                         this.triangle.writeReg(address,value);
  204.                 }
  205.                 //Noise Control
  206.                 else if(address >= 0x400C && address <= 0x400F){
  207.                         this.noise.writeReg(address,value);
  208.                 }
  209.                 //DMC Play mode & DMA frequency
  210.                 else if(address === 0x4010){
  211.                         this.dmc.writeReg(address,value);
  212.                 }
  213.                 //DMC Delta Counter
  214.                 else if(address === 0x4011){
  215.                         this.dmc.writeReg(address,value);
  216.                 }
  217.                 //DMC Play code starting address
  218.                 else if(address === 0x4012){
  219.                         this.dmc.writeReg(address,value);
  220.                 }
  221.                 //DMC Play code length
  222.                 else if(address === 0x4013){
  223.                         this.dmc.writeReg(address,value);
  224.                 }
  225.                 //Channel enable.
  226.                 else if(address === 0x4015){
  227.                         //???
  228.                         this.updateChannelEnable(value);
  229.                         //???
  230.                         if(value !== 0 && this.initCounter > 0){
  231.                                 //Start hardware initialization.
  232.                                 this.initiatingHardware = true;
  233.                         }
  234.                         //DMC/IRQ Status
  235.                         this.dmc.writeReg(address,value);
  236.                 }
  237.                 //Frame counter control.
  238.                 else if(address === 0x4017){
  239.                         //???
  240.                         this.countSequence = (value>>7)&1;
  241.                         this.masterFrameCounter = 0;
  242.                         this.frameIrqActive = false;
  243.                         //???
  244.                         this.frameIrqEnabled = (((value>>6)&0x1) === 0);
  245.                         //???
  246.                         if(this.countSequence === 0){
  247.                                 //NTSC
  248.                                 this.frameIrqCounterMax = 4;
  249.                                 this.derivedFrameCounter = 4;
  250.                         }
  251.                         else{
  252.                                 //PAL
  253.                                 this.frameIrqCounterMax = 5;
  254.                                 this.derivedFrameCounter = 0;
  255.                                 this.frameCounterTick();
  256.                         }
  257.                 }
  258.  
  259.         },
  260.  
  261.         //Updates channel enable status.
  262.         //This is done on writes to the channel enable register, 0x4015.
  263.         updateChannelEnable:function nes_apu_updateChannelEnable(value){
  264.                 //Set the enabled status's for the channels.
  265.                 this.square1.setEnabled((value&1) !== 0);
  266.                 this.square2.setEnabled((value&2) !== 0);
  267.                 this.triangle.setEnabled((value&4) !== 0);
  268.                 this.noise.setEnabled((value&8) !== 0);
  269.                 this.dmc.setEnabled((value&16) !== 0);
  270.         },
  271.  
  272.         //Clocks the frame counter.
  273.         //It should be clocked at twice the cpu speed, so the cycles will be divided by 2 for those counters that are clocked at cpu speed.
  274.         clockFrameCounter:function nes_apu_clockFrameCounter(nCycles){
  275.                 //Check if active.
  276.                 if(this.active){
  277.                         //Do not clock the frame counter if still initiating hardware.
  278.                         if(this.initCounter > 0 && this.initiatingHardware){
  279.                                 this.initCounter -= nCycles;
  280.                                 if(this.initCounter <= 0){
  281.                                         this.initiatingHardware = false;
  282.                                 }
  283.                                 return;
  284.                         }
  285.                         //Don't process ticks beyond next sampling.
  286.                         nCycles += this.extraCycles;
  287.                         var maxCycles = this.sampleTimerMax-this.sampleTimer;
  288.                         if((nCycles<<10) > maxCycles){
  289.                                 this.extraCycles = ((nCycles<<10)-maxCycles)>>10;
  290.                                 nCycles -= this.extraCycles;
  291.                         }
  292.                         else{
  293.                                 this.extraCycles = 0;
  294.                         }
  295.                         //Clock the dm channel.
  296.                         if(this.dmc.isEnabled){
  297.                                 this.dmc.shiftCounter -= (nCycles<<3);
  298.                                 while(this.dmc.shiftCounter <= 0 && this.dmc.dmaFrequency > 0){
  299.                                         this.dmc.shiftCounter += this.dmc.dmaFrequency;
  300.                                 }
  301.                         }
  302.                         //Clock the triangle channel.
  303.                         if(this.triangle.progTimerMax > 0){
  304.                                 this.triangle.progTimerCount -= nCycles;
  305.                                 while(this.triangle.progTimerCount <= 0){
  306.                                         this.triangle.progTimerCount += this.triangle.progTimerMax+1;
  307.                                         if(this.triangle.linearCounter>0 && this.triangle.lengthCounter>0){
  308.                                                 this.triangle.triangleCounter++;
  309.                                                 this.triangle.triangleCounter &= 0x1F;
  310.                                                 if(this.triangle.isEnabled){
  311.                                                         if(this.triangle.triangleCounter>=0x10){
  312.                                                                 //Normal value.
  313.                                                                 this.triangle.sampleValue = (this.triangle.triangleCounter&0xF);
  314.                                                         }
  315.                                                         else{
  316.                                                                 //Inverted value.
  317.                                                                 this.triangle.sampleValue = (0xF - (this.triangle.triangleCounter&0xF));
  318.                                                         }
  319.                                                         this.triangle.sampleValue <<= 4;
  320.                                                 }
  321.                                         }
  322.                                 }
  323.                         }
  324.                         //Clock the square1 channel.
  325.                         this.square1.progTimerCount -= nCycles;
  326.                         if(this.square1.progTimerCount <= 0){
  327.                                 this.square1.progTimerCount += (this.square1.progTimerMax+1)<<1;
  328.                                 this.square1.squareCounter++;
  329.                                 this.square1.squareCounter &= 0x7;
  330.                                 this.square1.updateSampleValue();
  331.                         }
  332.  
  333.                         //Clock the square2 channel.
  334.                         this.square2.progTimerCount -= nCycles;
  335.                         if(this.square2.progTimerCount <= 0){
  336.                                 this.square2.progTimerCount += (this.square2.progTimerMax+1)<<1;
  337.                                 this.square2.squareCounter++;
  338.                                 this.square2.squareCounter &= 0x7;
  339.                                 this.square2.updateSampleValue();
  340.                         }
  341.                         //Clock the noise channel.
  342.                         var acc_c = nCycles;
  343.                         if(this.noise.progTimerCount-acc_c > 0){
  344.                                 //Do all cycles at once.
  345.                                 this.noise.progTimerCount -= acc_c;
  346.                                 this.noise.accCount += acc_c;
  347.                                 this.noise.accValue += acc_c*this.noise.sampleValue;
  348.                         }
  349.                         else{
  350.                                 //Slow-step.
  351.                                 while((acc_c--) > 0){
  352.                                         if(--this.noise.progTimerCount <= 0 && this.noise.progTimerMax > 0){
  353.                                                 //Update this.noise shift register.
  354.                                                 this.noise.shiftReg <<= 1;
  355.                                                 this.noise.tmp = (((this.noise.shiftReg<<(this.noise.randomMode === 0?1:6))^this.noise.shiftReg)&0x8000);
  356.                                                 if(this.noise.tmp !== 0){
  357.                                                         //Sample value must be 0.
  358.                                                         this.noise.shiftReg |= 0x01;
  359.                                                         this.noise.randomBit = 0;
  360.                                                         this.noise.sampleValue = 0;
  361.                                                 }
  362.                                                 else{
  363.                                                         //Find sample value.
  364.                                                         this.noise.randomBit = 1;
  365.                                                         if(this.noise.isEnabled && this.noise.lengthCounter>0){
  366.                                                                 this.noise.sampleValue = this.noise.masterVolume;
  367.                                                         }
  368.                                                         else{
  369.                                                                 this.noise.sampleValue = 0;
  370.                                                         }
  371.                                                 }
  372.                                                 this.noise.progTimerCount += this.noise.progTimerMax;
  373.                                         }
  374.                                         this.noise.accValue += this.noise.sampleValue;
  375.                                         this.noise.accCount++;
  376.                                 }
  377.                         }
  378.                         //Frame interrupt handling.
  379.                         if(this.frameIrqEnabled && this.frameIrqActive){
  380.                                 nes.cpu.requestIrq(0);
  381.                         }
  382.                         //Clock frame counter at double CPU speed.
  383.                         this.masterFrameCounter += (nCycles<<1);
  384.                         if(this.masterFrameCounter >= this.frameTime){
  385.                                 //240Hz tick.
  386.                                 this.masterFrameCounter -= this.frameTime;
  387.                                 this.frameCounterTick();
  388.                         }
  389.                         //Accumulate sample values.
  390.                         //Special treatment for triangle channel, need to interpolate.
  391.                         if(this.triangle.sampleCondition){
  392.                                 this.triValue = Math.min(parseInt((this.triangle.progTimerCount<<4)/(this.triangle.progTimerMax+1),10),16);
  393.                                 if(this.triangle.triangleCounter >= 16){
  394.                                         this.triValue = 16-this.triValue;
  395.                                 }
  396.                                 //Add non-interpolated sample value.
  397.                                 this.triValue += this.triangle.sampleValue;
  398.                         }
  399.                         //Now sample normally.
  400.                         if(nCycles === 2){
  401.                                 this.smpTriangle += this.triValue<<1;
  402.                                 this.smpDmc += this.dmc.sample<<1;
  403.                                 this.smpSquare1 += this.square1.sampleValue<<1;
  404.                                 this.smpSquare2 += this.square2.sampleValue<<1;
  405.                                 this.accCount += 2;
  406.                         }
  407.                         else if(nCycles === 4){
  408.                                 this.smpTriangle += this.triValue<<2;
  409.                                 this.smpDmc += this.dmc.sample<<2;
  410.                                 this.smpSquare1 += this.square1.sampleValue<<2;
  411.                                 this.smpSquare2 += this.square2.sampleValue<<2;
  412.                                 this.accCount += 4;
  413.                         }
  414.                         else{
  415.                                 this.smpTriangle += nCycles*this.triValue;
  416.                                 this.smpDmc += nCycles*this.dmc.sample;
  417.                                 this.smpSquare1 += nCycles*this.square1.sampleValue;
  418.                                 this.smpSquare2 += nCycles*this.square2.sampleValue;
  419.                                 this.accCount += nCycles;
  420.                         }
  421.                         //Clock sample timer.
  422.                         this.sampleTimer += nCycles<<10;
  423.                         if(this.sampleTimer >= this.sampleTimerMax){
  424.                                 //Sample channels.
  425.                                 this.sample();
  426.                                 this.sampleTimer -= this.sampleTimerMax;
  427.                         }
  428.                 }
  429.         },
  430.  
  431.         frameCounterTick:function nes_apu_frameCounterTick(){
  432.                 //???
  433.                 this.derivedFrameCounter++;
  434.                 if(this.derivedFrameCounter >= this.frameIrqCounterMax){
  435.                         this.derivedFrameCounter = 0;
  436.                 }
  437.                 //???
  438.                 if(this.derivedFrameCounter === 1 || this.derivedFrameCounter === 3){
  439.                         //Clock length & sweep.
  440.                         this.triangle.clockLengthCounter();
  441.                         this.square1.clockLengthCounter();
  442.                         this.square2.clockLengthCounter();
  443.                         this.noise.clockLengthCounter();
  444.                         this.square1.clockSweep();
  445.                         this.square2.clockSweep();
  446.                 }
  447.                 //???
  448.                 if(this.derivedFrameCounter >= 0 && this.derivedFrameCounter < 4){
  449.                         //Clock linear & decay.
  450.                         this.square1.clockEnvDecay();
  451.                         this.square2.clockEnvDecay();
  452.                         this.noise.clockEnvDecay();
  453.                         this.triangle.clockLinearCounter();
  454.                 }
  455.                 //???
  456.                 if(this.derivedFrameCounter === 3 && this.countSequence === 0){
  457.                         //Enable IRQ.
  458.                         this.frameIrqActive = true;
  459.                 }
  460.                 //End of 240Hz tick.
  461.         },
  462.  
  463.         //Samples the channels, mixes the output together, and writes the samples to the dynamic audio wrapper.
  464.         sample:function nes_apu_sample(){
  465.                 //???
  466.                 if(this.accCount > 0){
  467.                         //Square Wave 1
  468.                         this.smpSquare1 <<= 4;
  469.                         this.smpSquare1 = parseInt(this.smpSquare1/this.accCount,10);
  470.                         //Square Wave 2
  471.                         this.smpSquare2 <<= 4;
  472.                         this.smpSquare2 = parseInt(this.smpSquare2/this.accCount,10);
  473.                         //Triangle Wave
  474.                         this.smpTriangle = parseInt(this.smpTriangle/this.accCount,10);
  475.                         //DMC
  476.                         this.smpDmc <<= 4;
  477.                         this.smpDmc = parseInt(this.smpDmc / this.accCount,10);
  478.                         //???
  479.                         this.accCount = 0;
  480.                 }
  481.                 else{
  482.                         //Square Wave 1
  483.                         this.smpSquare1 = this.square1.sampleValue<<4;
  484.                         //Square Wave 2
  485.                         this.smpSquare2 = this.square2.sampleValue<<4;
  486.                         //Triangle Wave
  487.                         this.smpTriangle = this.triangle.sampleValue;
  488.                         //DMC
  489.                         this.smpDmc = this.dmc.sample<<4;
  490.                 }
  491.                 //???
  492.                 var smpNoise = parseInt((this.noise.accValue<<4)/this.noise.accCount,10);
  493.                 //???
  494.                 this.noise.accValue = smpNoise>>4;
  495.                 this.noise.accCount = 1;
  496.                 //Left sound channel.
  497.                 var sq_index  = (this.smpSquare1*this.stereoPosLSquare1+this.smpSquare2*this.stereoPosLSquare2)>>8;
  498.                 var tnd_index = (3*this.smpTriangle*this.stereoPosLTriangle+(smpNoise<<1)*this.stereoPosLNoise+this.smpDmc*this.stereoPosLDMC)>>8;
  499.                 if(sq_index >= this.square_table.length){
  500.                         sq_index  = this.square_table.length-1;
  501.                 }
  502.                 if(tnd_index >= this.tnd_table.length){
  503.                         tnd_index = this.tnd_table.length-1;
  504.                 }
  505.                 var sampleValueL = this.square_table[sq_index]+this.tnd_table[tnd_index]-this.dcValue;
  506.                 //Right sound channel.
  507.                 sq_index = (this.smpSquare1*this.stereoPosRSquare1+this.smpSquare2*this.stereoPosRSquare2)>>8;
  508.                 tnd_index = (3*this.smpTriangle*this.stereoPosRTriangle+(smpNoise<<1)*this.stereoPosRNoise+this.smpDmc*this.stereoPosRDMC)>>8;
  509.                 if(sq_index >= this.square_table.length){
  510.                         sq_index = this.square_table.length-1;
  511.                 }
  512.                 if(tnd_index >= this.tnd_table.length){
  513.                         tnd_index = this.tnd_table.length-1;
  514.                 }
  515.                 var sampleValueR = this.square_table[sq_index]+this.tnd_table[tnd_index]-this.dcValue;
  516.                 //Remove DC from left channel.
  517.                 var smpDiffL = sampleValueL-this.prevSampleL;
  518.                 this.prevSampleL += smpDiffL;
  519.                 this.smpAccumL += smpDiffL-(this.smpAccumL>>10);
  520.                 sampleValueL = this.smpAccumL;
  521.                 //Remove DC from right channel.
  522.                 var smpDiffR = sampleValueR-this.prevSampleR;
  523.                 this.prevSampleR += smpDiffR;
  524.                 this.smpAccumR += smpDiffR-(this.smpAccumR>>10);
  525.                 sampleValueR = this.smpAccumR;
  526.                 //Check that the sample values are not greater than or less than the maximum and minimum sample values.
  527.                 if(sampleValueL > this.maxSample){
  528.                         this.maxSample = sampleValueL;
  529.                 }
  530.                 if(sampleValueL < this.minSample){
  531.                         this.minSample = sampleValueL;
  532.                 }
  533.                 //Add the left and right sample values to the buffer, incrementing the index as well.
  534.                 this.sampleBuffer[this.bufferIndex++] = sampleValueL;
  535.                 this.sampleBuffer[this.bufferIndex++] = sampleValueR;
  536.                 //Send the buffer to the speakers if its full(the index is at the end).
  537.                 if(this.bufferIndex === this.sampleBuffer.length){
  538.                         //Write the samples to the audio wrapper.
  539.                         this.dynamicAudio.writeInt(this.sampleBuffer);
  540.                         //Reset the buffer and buffer index.
  541.                         this.sampleBuffer = new Array(16384);
  542.                         this.bufferIndex = 0;
  543.                 }
  544.                 //Reset sampled values.
  545.                 this.smpSquare1 = 0;
  546.                 this.smpSquare2 = 0;
  547.                 this.smpTriangle = 0;
  548.                 this.smpDmc = 0;
  549.         },
  550.  
  551.         getLengthMax:function nes_apu_getLengthMax(value){
  552.                 //???
  553.                 return this.lengthLookup[value>>3];
  554.         },
  555.  
  556.         getDmcFrequency:function nes_apu_getDmcFrequency(value){
  557.                 //???
  558.                 if(value >= 0 && value < 0x10){
  559.                         return this.dmcFreqLookup[value];
  560.                 }
  561.                 return 0;
  562.         },
  563.  
  564.         getNoiseWaveLength:function nes_apu_getNoiseWaveLength(value){
  565.                 //???
  566.                 if(value >= 0 && value < 0x10){
  567.                         return this.noiseWavelengthLookup[value];
  568.                 }
  569.                 return 0;
  570.         },
  571.  
  572.         setMasterVolume:function nes_apu_setMasterVolume(value){
  573.                 //Keep the master volume between 0 and 256.
  574.                 if(value < 0){
  575.                         value = 0;
  576.                 }
  577.                 if(value > 256){
  578.                         value = 256;
  579.                 }
  580.                 //Set the master volume.
  581.                 this.masterVolume = value;
  582.                 //Update the positioning.
  583.                 this.updateStereoPos();
  584.         },
  585.  
  586.         updateStereoPos:function nes_apu_updateSteroPos(){
  587.                 //Update left.
  588.                 this.stereoPosLSquare1 = (this.panning[0]*this.masterVolume)>>8;
  589.                 this.stereoPosLSquare2 = (this.panning[1]*this.masterVolume)>>8;
  590.                 this.stereoPosLTriangle = (this.panning[2]*this.masterVolume)>>8;
  591.                 this.stereoPosLNoise = (this.panning[3]*this.masterVolume)>>8;
  592.                 this.stereoPosLDMC = (this.panning[4]*this.masterVolume)>>8;
  593.                 //Update right.
  594.                 this.stereoPosRSquare1 = this.masterVolume-this.stereoPosLSquare1;
  595.                 this.stereoPosRSquare2 = this.masterVolume-this.stereoPosLSquare2;
  596.                 this.stereoPosRTriangle = this.masterVolume-this.stereoPosLTriangle;
  597.                 this.stereoPosRNoise = this.masterVolume-this.stereoPosLNoise;
  598.                 this.stereoPosRDMC = this.masterVolume-this.stereoPosLDMC;
  599.         },
  600.  
  601.         initDACtables:function nes_apu_initDACtables(){
  602.                 //???
  603.                 var max_sqr = 0;
  604.                 this.square_table = new Array(512);
  605.                 for(var i=0;i<512;i++){
  606.                         value = 95.52/(8128/(i/16)+100);
  607.                         value *= 0.98411;
  608.                         value *= 50000;
  609.                         var ival = parseInt(value,10);
  610.                         this.square_table[i] = ival;
  611.                         if(ival > max_sqr){
  612.                                 max_sqr = ival;
  613.                         }
  614.                 }
  615.                 //???
  616.                 var max_tnd = 0;
  617.                 this.tnd_table = new Array(3264);
  618.                 for(var i=0;i<3264;i++){
  619.                         var value = 163.67/(24329/(i/16)+100);
  620.                         value *= 0.98411;
  621.                         value *= 50000;
  622.                         ival = parseInt(value,10);
  623.                         this.tnd_table[i] = ival;
  624.                         if(ival > max_tnd){
  625.                                 max_tnd = ival;
  626.                         }
  627.                 }
  628.                 //???
  629.                 this.dacRange = max_sqr+max_tnd;
  630.                 this.dcValue = this.dacRange/2;
  631.         },
  632.  
  633.         getChannelLengthStatus:function nes_apu_getChannelLengthStatus(channel){
  634.                 //Return the length status.
  635.                 return ((channel.lengthCounter === 0 || !channel.isEnabled)?0:1);
  636.         },
  637.  
  638.         //
  639.         //  DM Channel
  640.         //______________//
  641.  
  642.         /**
  643.          * @namespace The dm channel on the apu.
  644.          */
  645.  
  646.         dmc:{
  647.  
  648.         //Properties
  649.  
  650.                 isEnabled:null,
  651.                 hasSample:null,
  652.                 irqGenerated:false,
  653.  
  654.                 playMode:null,
  655.                 dmaFrequency:null,
  656.                 dmaCounter:null,
  657.                 deltaCounter:null,
  658.                 playStartAddress:null,
  659.                 playAddress:null,
  660.                 playLength:null,
  661.                 playLengthCounter:null,
  662.                 shiftCounter:null,
  663.                 reg4012:null,
  664.                 reg4013:null,
  665.                 sample:null,
  666.                 dacLsb:null,
  667.                 data:null,
  668.  
  669.         //Methods
  670.  
  671.                 reset:function(){
  672.                         //Reset all the properties.
  673.                         this.isEnabled = false;
  674.                         this.irqGenerated = false;
  675.                         this.playMode = 0;
  676.                         this.dmaFrequency = 0;
  677.                         this.dmaCounter = 0;
  678.                         this.deltaCounter = 0;
  679.                         this.playStartAddress = 0;
  680.                         this.playAddress = 0;
  681.                         this.playLength = 0;
  682.                         this.playLengthCounter = 0;
  683.                         this.sample = 0;
  684.                         this.dacLsb = 0;
  685.                         this.shiftCounter = 0;
  686.                         this.reg4012 = 0;
  687.                         this.reg4013 = 0;
  688.                         this.data = 0;
  689.                 },
  690.  
  691.                 clockDmc:function(){
  692.                         //Only alter DAC value if the sample buffer has data.
  693.                         if(this.hasSample){
  694.                                 if((this.data&1) === 0){
  695.                                         // Decrement delta.
  696.                                         if(this.deltaCounter > 0){
  697.                                                 this.deltaCounter--;
  698.                                         }
  699.                                 }
  700.                                 else{
  701.                                         //Increment delta.
  702.                                         if(this.deltaCounter < 63){
  703.                                                 this.deltaCounter++;
  704.                                         }
  705.                                 }
  706.                                 //Update sample value.
  707.                                 this.sample = this.isEnabled?(this.deltaCounter<<1)+this.dacLsb:0;
  708.                                 //Update shift register.
  709.                                 this.data >>= 1;
  710.                         }
  711.                         //???
  712.                         this.dmaCounter--;
  713.                         if(this.dmaCounter <= 0){
  714.                                 //No more sample bits.
  715.                                 this.hasSample = false;
  716.                                 //End of sample.
  717.                                 if(this.playLengthCounter === 0 && this.playMode === 1){
  718.                                         //Start from beginning of sample.
  719.                                         this.playAddress = this.playStartAddress;
  720.                                         this.playLengthCounter = this.playLength;
  721.                                
  722.                                 }
  723.                                 //End of sample.
  724.                                 if(this.playLengthCounter > 0){
  725.                                         //Fetch next sample.
  726.                                         //Fetch byte.
  727.                                         this.data = nes.mmc.load(this.playAddress);
  728.                                         nes.cpu.haltCycles(4);
  729.                                         //???
  730.                                         this.playLengthCounter--;
  731.                                         this.playAddress++;
  732.                                         if(this.playAddress > 0xFFFF){
  733.                                                 this.playAddress = 0x8000;
  734.                                         }
  735.                                         //???
  736.                                         this.hasSample = true;
  737.                                         //???
  738.                                         if(this.playLengthCounter === 0){
  739.                                                 //Last byte of sample fetched, generate IRQ.
  740.                                                 if(this.playMode === 2){
  741.                                                         //Generate IRQ.
  742.                                                         this.irqGenerated = true;
  743.                                                 }
  744.                                         }
  745.                                 }
  746.                                 //???
  747.                                 this.dmaCounter = 8;
  748.                         }
  749.                         //Request a normal interrupt if generated.
  750.                         if(this.irqGenerated){
  751.                                 nes.cpu.requestIrq(0);
  752.                         }
  753.                 },
  754.  
  755.                 writeReg:function(address,value){
  756.                         //Check the register address.
  757.                         if(address === 0x4010){
  758.                                 //Play mode, DMA Frequency.
  759.                                 if((value>>6) === 0){
  760.                                         //Set the play mode to normal.
  761.                                         this.playMode = 0;
  762.                                 }
  763.                                 else if(((value>>6)&1) === 1){
  764.                                         //Set the play mode to loop.
  765.                                         this.playMode = 1;
  766.                                 }
  767.                                 else if((value>>6) === 2){
  768.                                         //Set the play mode to interrupt.
  769.                                         this.playMode = 2;
  770.                                         this.irqGenerated = false;
  771.                                 }
  772.                                 //???
  773.                                 this.dmaFrequency = nes.apu.getDmcFrequency(value&0xF);
  774.                         }
  775.                         else if(address === 0x4011){
  776.                                 //Delta counter load register.
  777.                                 this.deltaCounter = (value>>1)&63;
  778.                                 this.dacLsb = value&1;
  779.                                 //Update sample value.
  780.                                 this.sample = ((this.deltaCounter<<1)+this.dacLsb);
  781.                         }
  782.                         else if(address === 0x4012){
  783.                                 //DMA address load register.
  784.                                 this.playStartAddress = (value<<6)|0x0C000;
  785.                                 this.playAddress = this.playStartAddress;
  786.                                 this.reg4012 = value;
  787.                         }
  788.                         else if(address === 0x4013){
  789.                                 //Length of play code.
  790.                                 this.playLength = (value<<4)+1;
  791.                                 this.playLengthCounter = this.playLength;
  792.                                 this.reg4013 = value;
  793.                         }
  794.                         else if(address === 0x4015){
  795.                                 //DMC/IRQ Status
  796.                                 if(((value>>4)&1) === 0){
  797.                                         //Disable
  798.                                         this.playLengthCounter = 0;
  799.                                 }
  800.                                 else{
  801.                                         //Restart
  802.                                         this.playAddress = this.playStartAddress;
  803.                                         this.playLengthCounter = this.playLength;
  804.                                 }
  805.                                 this.irqGenerated = false;
  806.                         }
  807.                 },
  808.  
  809.                 setEnabled:function(value){
  810.                         //???
  811.                         if((!this.isEnabled) && value){
  812.                                 this.playLengthCounter = this.playLength;
  813.                         }
  814.                         //Set the enabled flag.
  815.                         this.isEnabled = value;
  816.                 },
  817.  
  818.                 getIrqStatus:function(){
  819.                         //???
  820.                         return (this.irqGenerated?1:0);
  821.                 },
  822.  
  823.         },
  824.  
  825.         //
  826.         //  Noise Channel
  827.         //_________________//
  828.  
  829.         /**
  830.          * @namespace The noise channel on the apu.
  831.          */
  832.  
  833.         noise:{
  834.  
  835.         //Properties
  836.  
  837.                 isEnabled:null,
  838.                 envDecayDisable:null,
  839.                 envDecayLoopEnable:null,
  840.                 lengthCounterEnable:null,
  841.                 envReset:null,
  842.  
  843.                 lengthCounter:null,
  844.                 progTimerCount:null,
  845.                 progTimerMax:null,
  846.                 envDecayRate:null,
  847.                 envDecayCounter:null,
  848.                 envVolume:null,
  849.                 masterVolume:null,
  850.                 shiftReg:16384,
  851.                 randomBit:null,
  852.                 randomMode:null,
  853.                 sampleValue:null,
  854.                 accValue:0,
  855.                 accCount:1,
  856.                 tmp:null,
  857.  
  858.         //Methods
  859.  
  860.                 reset:function(){
  861.                         //Reset all the properties.
  862.                         this.progTimerCount = 0;
  863.                         this.progTimerMax = 0;
  864.                         this.isEnabled = false;
  865.                         this.lengthCounter = 0;
  866.                         this.lengthCounterEnable = false;
  867.                         this.envDecayDisable = false;
  868.                         this.envDecayLoopEnable = false;
  869.                         this.envDecayRate = 0;
  870.                         this.envDecayCounter = 0;
  871.                         this.envVolume = 0;
  872.                         this.masterVolume = 0;
  873.                         this.shiftReg = 1;
  874.                         this.randomBit = 0;
  875.                         this.randomMode = 0;
  876.                         this.sampleValue = 0;
  877.                         this.tmp = 0;
  878.                 },
  879.  
  880.                 clockLengthCounter:function(){
  881.                         //???
  882.                         if(this.lengthCounterEnable && this.lengthCounter>0){
  883.                                 this.lengthCounter--;
  884.                                 if(this.lengthCounter === 0){
  885.                                         this.updateSampleValue();
  886.                                 }
  887.                         }
  888.                 },
  889.  
  890.                 clockEnvDecay:function(){
  891.                         if(this.envReset){
  892.                                 //Reset envelope.
  893.                                 this.envReset = false;
  894.                                 this.envDecayCounter = this.envDecayRate+1;
  895.                                 this.envVolume = 0xF;
  896.                         }
  897.                         else if(--this.envDecayCounter <= 0){
  898.                                 //Normal handling.
  899.                                 this.envDecayCounter = this.envDecayRate+1;
  900.                                 if(this.envVolume>0){
  901.                                         this.envVolume--;
  902.                                 }
  903.                                 else{
  904.                                         this.envVolume = this.envDecayLoopEnable?0xF:0;
  905.                                 }
  906.                         }
  907.                         //???
  908.                         this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume;
  909.                         //Update the sample value.
  910.                         this.updateSampleValue();
  911.                 },
  912.  
  913.                 updateSampleValue:function(){
  914.                         //???
  915.                         if(this.isEnabled && this.lengthCounter > 0){
  916.                                 this.sampleValue = this.randomBit*this.masterVolume;
  917.                         }
  918.                 },
  919.  
  920.                 writeReg:function(address,value){
  921.                         //Volume/Envelope decay
  922.                         if(address === 0x400C){
  923.                                 this.envDecayDisable = ((value&0x10) !== 0);
  924.                                 this.envDecayRate = value&0xF;
  925.                                 this.envDecayLoopEnable = ((value&0x20) !== 0);
  926.                                 this.lengthCounterEnable = ((value&0x20) === 0);
  927.                                 this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume;
  928.                         }
  929.                         //Programmable timer
  930.                         else if(address === 0x400E){
  931.                                 this.progTimerMax = nes.apu.getNoiseWaveLength(value&0xF);
  932.                                 this.randomMode = value>>7;
  933.                         }
  934.                         //Length counter
  935.                         else if(address === 0x400F){
  936.                                 this.lengthCounter = nes.apu.getLengthMax(value&248);
  937.                                 this.envReset = true;
  938.                         }
  939.                         //Update the sample value.
  940.                         this.updateSampleValue();
  941.                 },
  942.  
  943.                 setEnabled:function(value){
  944.                         //Set the enabled flag.
  945.                         this.isEnabled = value;
  946.                         //If not enabled, set the length counter to 0.
  947.                         if(!value){
  948.                                 this.lengthCounter = 0;
  949.                         }
  950.                         //Update the sample value.
  951.                         this.updateSampleValue();
  952.                 },
  953.  
  954.         },
  955.  
  956.         //
  957.         //  Triangle Channel
  958.         //____________________//
  959.  
  960.         /**
  961.          * @namespace The triangle channel on the apu.
  962.          */
  963.  
  964.         triangle:{
  965.  
  966.         //Properties
  967.  
  968.                 isEnabled:null,
  969.                 sampleCondition:null,
  970.                 lengthCounterEnable:null,
  971.                 lcHalt:null,
  972.                 lcControl:null,
  973.  
  974.                 progTimerCount:null,
  975.                 progTimerMax:null,
  976.                 triangleCounter:null,
  977.                 lengthCounter:null,
  978.                 linearCounter:null,
  979.                 lcLoadValue:null,
  980.                 sampleValue:null,
  981.  
  982.         //Methods
  983.  
  984.                 reset:function(){
  985.                         //Reset all the properties.
  986.                         this.progTimerCount = 0;
  987.                         this.progTimerMax = 0;
  988.                         this.triangleCounter = 0;
  989.                         this.isEnabled = false;
  990.                         this.sampleCondition = false;
  991.                         this.lengthCounter = 0;
  992.                         this.lengthCounterEnable = false;
  993.                         this.linearCounter = 0;
  994.                         this.lcLoadValue = 0;
  995.                         this.lcHalt = true;
  996.                         this.lcControl = false;
  997.                         this.sampleValue = 0xF;
  998.                 },
  999.  
  1000.                 clockLengthCounter:function(){
  1001.                         //???
  1002.                         if(this.lengthCounterEnable && this.lengthCounter>0){
  1003.                                 this.lengthCounter--;
  1004.                                 if(this.lengthCounter === 0){
  1005.                                         this.updateSampleCondition();
  1006.                                 }
  1007.                         }
  1008.                 },
  1009.  
  1010.                 clockLinearCounter:function(){
  1011.                         //???
  1012.                         if(this.lcHalt){
  1013.                                 //Load
  1014.                                 this.linearCounter = this.lcLoadValue;
  1015.                                 this.updateSampleCondition();
  1016.                         }
  1017.                         else if(this.linearCounter > 0){
  1018.                                 //Decrement
  1019.                                 this.linearCounter--;
  1020.                                 this.updateSampleCondition();
  1021.                         }
  1022.                         //???
  1023.                         if(!this.lcControl){
  1024.                                 //Clear halt flag.
  1025.                                 this.lcHalt = false;
  1026.                         }
  1027.                 },
  1028.  
  1029.                 writeReg:function(address,value){
  1030.                         //Check the register address.
  1031.                         if(address === 0x4008){
  1032.                                 //New values for linear counter.
  1033.                                 this.lcControl  = (value&0x80)!==0;
  1034.                                 this.lcLoadValue =  value&0x7F;
  1035.                                 //Length counter enable.
  1036.                                 this.lengthCounterEnable = !this.lcControl;
  1037.                         }
  1038.                         else if(address === 0x400A){
  1039.                                 //Programmable timer.
  1040.                                 this.progTimerMax &= 0x700;
  1041.                                 this.progTimerMax |= value;
  1042.                         }
  1043.                         else if(address === 0x400B){
  1044.                                 //Programmable timer, length counter.
  1045.                                 this.progTimerMax &= 0xFF;
  1046.                                 this.progTimerMax |= ((value&0x07)<<8);
  1047.                                 this.lengthCounter = nes.apu.getLengthMax(value&0xF8);
  1048.                                 this.lcHalt = true;
  1049.                         }
  1050.                         //???
  1051.                         this.updateSampleCondition();
  1052.                 },
  1053.  
  1054.                 clockProgrammableTimer:function(nCycles){
  1055.                         //???
  1056.                         if(this.progTimerMax > 0){
  1057.                                 this.progTimerCount += nCycles;
  1058.                                 while(this.progTimerMax > 0 && this.progTimerCount >= this.progTimerMax){
  1059.                                         this.progTimerCount -= this.progTimerMax;
  1060.                                         if(this.isEnabled && this.lengthCounter>0 && this.linearCounter > 0){
  1061.                                                 this.clockTriangleGenerator();
  1062.                                         }
  1063.                                 }
  1064.                         }
  1065.                 },
  1066.  
  1067.                 clockTriangleGenerator:function(){
  1068.                         //???
  1069.                         this.triangleCounter++;
  1070.                         this.triangleCounter &= 0x1F;
  1071.                 },
  1072.  
  1073.                 setEnabled:function(value){
  1074.                         //Set the enabled flag.
  1075.                         this.isEnabled = value;
  1076.                         //If not enabled, set the length counter to 0.
  1077.                         if(!value){
  1078.                                 this.lengthCounter = 0;
  1079.                         }
  1080.                 },
  1081.  
  1082.                 updateSampleCondition:function(){
  1083.                         //???
  1084.                         this.sampleCondition = this.isEnabled && this.progTimerMax > 7 && this.linearCounter > 0 && this.lengthCounter > 0;
  1085.                 },
  1086.  
  1087.         },
  1088.  
  1089.         //
  1090.         //  Square Channel 1
  1091.         //____________________//
  1092.  
  1093.         /**
  1094.          * @namespace The square channel 1 on the apu.
  1095.          */
  1096.  
  1097.         square1:{
  1098.  
  1099.         //Properties
  1100.  
  1101.                 dutyLookup:[0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,1,1,1,1,1],
  1102.  
  1103.                 impLookup:[1,-1,0,0,0,0,0,0,1,0,-1,0,0,0,0,0,1,0,0,0,-1,0,0,0,-1,0,1,0,0,0,0,0],
  1104.  
  1105.                 isEnabled:null,
  1106.                 lengthCounterEnable:null,
  1107.                 sweepActive:null,
  1108.                 envDecayDisable:null,
  1109.                 envDecayLoopEnable:null,
  1110.                 envReset:null,
  1111.                 updateSweepPeriod:null,
  1112.  
  1113.                 progTimerCount:null,
  1114.                 progTimerMax:null,
  1115.                 lengthCounter:null,
  1116.                 squareCounter:null,
  1117.                 sweepCounter:null,
  1118.                 sweepCounterMax:null,
  1119.                 sweepMode:null,
  1120.                 sweepShiftAmount:null,
  1121.                 envDecayRate:null,
  1122.                 envDecayCounter:null,
  1123.                 envVolume:null,
  1124.                 masterVolume:null,
  1125.                 dutyMode:null,
  1126.                 sampleValue:null,
  1127.  
  1128.         //Methods
  1129.  
  1130.                 reset:function(){
  1131.                         //Reset...numbers?
  1132.                         this.progTimerCount = 0;
  1133.                         this.progTimerMax = 0;
  1134.                         this.lengthCounter = 0;
  1135.                         this.squareCounter = 0;
  1136.                         this.sweepCounter = 0;
  1137.                         this.sweepCounterMax = 0;
  1138.                         this.sweepMode = 0;
  1139.                         this.sweepShiftAmount = 0;
  1140.                         this.envDecayRate = 0;
  1141.                         this.envDecayCounter = 0;
  1142.                         this.envVolume = 0;
  1143.                         this.masterVolume = 0;
  1144.                         this.dutyMode = 0;
  1145.                         //Reset...booleans?
  1146.                         this.isEnabled = false;
  1147.                         this.lengthCounterEnable = false;
  1148.                         this.sweepActive = false;
  1149.                         this.envDecayDisable = false;
  1150.                         this.envDecayLoopEnable = false;
  1151.                 },
  1152.  
  1153.                 clockLengthCounter:function(){
  1154.                         //???
  1155.                         if(this.lengthCounterEnable && this.lengthCounter > 0){
  1156.                                 this.lengthCounter--;
  1157.                                 if(this.lengthCounter === 0){
  1158.                                         this.updateSampleValue();
  1159.                                 }
  1160.                         }
  1161.                 },
  1162.  
  1163.                 clockEnvDecay:function(){
  1164.                         if(this.envReset){
  1165.                                 //Reset envelope.
  1166.                                 this.envReset = false;
  1167.                                 this.envDecayCounter = this.envDecayRate+1;
  1168.                                 this.envVolume = 0xF;
  1169.                         }
  1170.                         else if((--this.envDecayCounter) <= 0){
  1171.                                 //Normal handling.
  1172.                                 this.envDecayCounter = this.envDecayRate+1;
  1173.                                 if(this.envVolume>0){
  1174.                                         this.envVolume--;
  1175.                                 }
  1176.                                 else{
  1177.                                         this.envVolume = this.envDecayLoopEnable?0xF:0;
  1178.                                 }
  1179.                         }
  1180.                         //Set the master volume.
  1181.                         this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume;
  1182.                         //Update the sample value.
  1183.                         this.updateSampleValue();
  1184.                 },
  1185.  
  1186.                 clockSweep:function(){
  1187.                         //???
  1188.                         if(--this.sweepCounter <= 0){
  1189.                                 this.sweepCounter = this.sweepCounterMax + 1;
  1190.                                 if(this.sweepActive && this.sweepShiftAmount>0 && this.progTimerMax>7){
  1191.                                         //Calculate result from shifter.
  1192.                                         if(this.sweepMode === 0){
  1193.                                                 this.progTimerMax += (this.progTimerMax>>this.sweepShiftAmount);
  1194.                                                 if(this.progTimerMax > 4095){
  1195.                                                         this.progTimerMax = 4095;
  1196.                                                 }
  1197.                                         }
  1198.                                         else{
  1199.                                                 this.progTimerMax = this.progTimerMax-((this.progTimerMax>>this.sweepShiftAmount)-(this.sqr1?1:0));
  1200.                                         }
  1201.                                 }
  1202.                         }
  1203.                         //???
  1204.                         if(this.updateSweepPeriod){
  1205.                                 this.updateSweepPeriod = false;
  1206.                                 this.sweepCounter = this.sweepCounterMax+1;
  1207.                         }
  1208.                 },
  1209.  
  1210.                 updateSampleValue:function(){
  1211.                         //???
  1212.                         if((this.isEnabled && this.lengthCounter > 0 && this.progTimerMax > 7) && !(this.sweepMode === 0 && (this.progTimerMax+(this.progTimerMax>>this.sweepShiftAmount)) > 4095)){
  1213.                                 this.sampleValue = this.masterVolume*this.dutyLookup[(this.dutyMode<<3)+this.squareCounter];
  1214.                         }
  1215.                         else{
  1216.                                 this.sampleValue = 0;
  1217.                         }
  1218.                 },
  1219.  
  1220.                 writeReg:function(address,value){
  1221.                         //Switch between the registry addresses.
  1222.                         if(address === 0x4000){
  1223.                                 // Volume/Envelope decay:
  1224.                                 this.envDecayDisable = ((value&0x10) !== 0);
  1225.                                 this.envDecayRate = value&0xF;
  1226.                                 this.envDecayLoopEnable = ((value&0x20) !== 0);
  1227.                                 this.dutyMode = (value>>6)&0x3;
  1228.                                 this.lengthCounterEnable = ((value&0x20) === 0);
  1229.                                 this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume;
  1230.                                 this.updateSampleValue();
  1231.                         }
  1232.                         else if(address === 0x4001){
  1233.                                 //Sweep
  1234.                                 this.sweepActive = ((value&0x80) !== 0);
  1235.                                 this.sweepCounterMax = ((value>>4)&7);
  1236.                                 this.sweepMode = (value>>3)&1;
  1237.                                 this.sweepShiftAmount = value&7;
  1238.                                 this.updateSweepPeriod = true;
  1239.                         }
  1240.                         else if(address === 0x4002){
  1241.                                 //Programmable Timer
  1242.                                 this.progTimerMax &= 0x700;
  1243.                                 this.progTimerMax |= value;
  1244.                         }
  1245.                         else if(address === 0x4003){
  1246.                                 //Programmable Timer, Length Counter
  1247.                                 this.progTimerMax &= 0xFF;
  1248.                                 this.progTimerMax |= ((value&0x7)<<8);
  1249.                                 //???
  1250.                                 if(this.isEnabled){
  1251.                                         this.lengthCounter = nes.apu.getLengthMax(value&0xF8);
  1252.                                 }
  1253.                                 //???
  1254.                                 this.envReset = true;
  1255.                         }
  1256.                 },
  1257.  
  1258.                 setEnabled:function(value){
  1259.                         //Set the enabled flag.
  1260.                         this.isEnabled = value;
  1261.                         //If not enabled, set the length counter to 0.
  1262.                         if(!value){
  1263.                                 this.lengthCounter = 0;
  1264.                         }
  1265.                         //Update the sample value.
  1266.                         this.updateSampleValue();
  1267.                 },
  1268.  
  1269.         },
  1270.  
  1271.         //
  1272.         //  Square Channel 2
  1273.         //____________________//
  1274.  
  1275.         /**
  1276.          * @namespace The square channel 2 on the apu.
  1277.          */
  1278.  
  1279.         square2:{
  1280.  
  1281.         //Properties
  1282.  
  1283.                 dutyLookup:[0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,1,1,1,1,1],
  1284.  
  1285.                 impLookup:[1,-1,0,0,0,0,0,0,1,0,-1,0,0,0,0,0,1,0,0,0,-1,0,0,0,-1,0,1,0,0,0,0,0],
  1286.  
  1287.                 isEnabled:null,
  1288.                 lengthCounterEnable:null,
  1289.                 sweepActive:null,
  1290.                 envDecayDisable:null,
  1291.                 envDecayLoopEnable:null,
  1292.                 envReset:null,
  1293.                 updateSweepPeriod:null,
  1294.  
  1295.                 progTimerCount:null,
  1296.                 progTimerMax:null,
  1297.                 lengthCounter:null,
  1298.                 squareCounter:null,
  1299.                 sweepCounter:null,
  1300.                 sweepCounterMax:null,
  1301.                 sweepMode:null,
  1302.                 sweepShiftAmount:null,
  1303.                 envDecayRate:null,
  1304.                 envDecayCounter:null,
  1305.                 envVolume:null,
  1306.                 masterVolume:null,
  1307.                 dutyMode:null,
  1308.                 sampleValue:null,
  1309.  
  1310.         //Methods
  1311.  
  1312.                 reset:function(){
  1313.                         //Reset...numbers?
  1314.                         this.progTimerCount = 0;
  1315.                         this.progTimerMax = 0;
  1316.                         this.lengthCounter = 0;
  1317.                         this.squareCounter = 0;
  1318.                         this.sweepCounter = 0;
  1319.                         this.sweepCounterMax = 0;
  1320.                         this.sweepMode = 0;
  1321.                         this.sweepShiftAmount = 0;
  1322.                         this.envDecayRate = 0;
  1323.                         this.envDecayCounter = 0;
  1324.                         this.envVolume = 0;
  1325.                         this.masterVolume = 0;
  1326.                         this.dutyMode = 0;
  1327.                         //Reset...booleans?
  1328.                         this.isEnabled = false;
  1329.                         this.lengthCounterEnable = false;
  1330.                         this.sweepActive = false;
  1331.                         this.envDecayDisable = false;
  1332.                         this.envDecayLoopEnable = false;
  1333.                 },
  1334.  
  1335.                 clockLengthCounter:function(){
  1336.                         //???
  1337.                         if(this.lengthCounterEnable && this.lengthCounter > 0){
  1338.                                 this.lengthCounter--;
  1339.                                 if(this.lengthCounter === 0){
  1340.                                         this.updateSampleValue();
  1341.                                 }
  1342.                         }
  1343.                 },
  1344.  
  1345.                 clockEnvDecay:function(){
  1346.                         if(this.envReset){
  1347.                                 //Reset envelope.
  1348.                                 this.envReset = false;
  1349.                                 this.envDecayCounter = this.envDecayRate+1;
  1350.                                 this.envVolume = 0xF;
  1351.                         }
  1352.                         else if((--this.envDecayCounter) <= 0){
  1353.                                 //Normal handling.
  1354.                                 this.envDecayCounter = this.envDecayRate+1;
  1355.                                 if(this.envVolume>0){
  1356.                                         this.envVolume--;
  1357.                                 }
  1358.                                 else{
  1359.                                         this.envVolume = this.envDecayLoopEnable?0xF:0;
  1360.                                 }
  1361.                         }
  1362.                         //Set the master volume.
  1363.                         this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume;
  1364.                         //Update the sample value.
  1365.                         this.updateSampleValue();
  1366.                 },
  1367.  
  1368.                 clockSweep:function(){
  1369.                         //???
  1370.                         if(--this.sweepCounter <= 0){
  1371.                                 this.sweepCounter = this.sweepCounterMax + 1;
  1372.                                 if(this.sweepActive && this.sweepShiftAmount>0 && this.progTimerMax>7){
  1373.                                         //Calculate result from shifter.
  1374.                                         if(this.sweepMode === 0){
  1375.                                                 this.progTimerMax += (this.progTimerMax>>this.sweepShiftAmount);
  1376.                                                 if(this.progTimerMax > 4095){
  1377.                                                         this.progTimerMax = 4095;
  1378.                                                 }
  1379.                                         }
  1380.                                         else{
  1381.                                                 this.progTimerMax = this.progTimerMax-((this.progTimerMax>>this.sweepShiftAmount)-(this.sqr1?1:0));
  1382.                                         }
  1383.                                 }
  1384.                         }
  1385.                         //???
  1386.                         if(this.updateSweepPeriod){
  1387.                                 this.updateSweepPeriod = false;
  1388.                                 this.sweepCounter = this.sweepCounterMax+1;
  1389.                         }
  1390.                 },
  1391.  
  1392.                 updateSampleValue:function(){
  1393.                         //???
  1394.                         if((this.isEnabled && this.lengthCounter > 0 && this.progTimerMax > 7) && !(this.sweepMode === 0 && (this.progTimerMax+(this.progTimerMax>>this.sweepShiftAmount)) > 4095)){
  1395.                                 this.sampleValue = this.masterVolume*this.dutyLookup[(this.dutyMode<<3)+this.squareCounter];
  1396.                         }
  1397.                         else{
  1398.                                 this.sampleValue = 0;
  1399.                         }
  1400.                 },
  1401.  
  1402.                 writeReg:function(address,value){
  1403.                         //Switch between the registry addresses.
  1404.                         if(address === 0x4004){
  1405.                                 // Volume/Envelope decay:
  1406.                                 this.envDecayDisable = ((value&0x10) !== 0);
  1407.                                 this.envDecayRate = value&0xF;
  1408.                                 this.envDecayLoopEnable = ((value&0x20) !== 0);
  1409.                                 this.dutyMode = (value>>6)&0x3;
  1410.                                 this.lengthCounterEnable = ((value&0x20) === 0);
  1411.                                 this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume;
  1412.                                 this.updateSampleValue();
  1413.                         }
  1414.                         else if(address === 0x4005){
  1415.                                 //Sweep
  1416.                                 this.sweepActive = ((value&0x80) !== 0);
  1417.                                 this.sweepCounterMax = ((value>>4)&7);
  1418.                                 this.sweepMode = (value>>3)&1;
  1419.                                 this.sweepShiftAmount = value&7;
  1420.                                 this.updateSweepPeriod = true;
  1421.                         }
  1422.                         else if(address === 0x4006){
  1423.                                 //Programmable Timer
  1424.                                 this.progTimerMax &= 0x700;
  1425.                                 this.progTimerMax |= value;
  1426.                         }
  1427.                         else if(address === 0x4007){
  1428.                                 //Programmable Timer, Length Counter
  1429.                                 this.progTimerMax &= 0xFF;
  1430.                                 this.progTimerMax |= ((value&0x7)<<8);
  1431.                                 //???
  1432.                                 if(this.isEnabled){
  1433.                                         this.lengthCounter = nes.apu.getLengthMax(value&0xF8);
  1434.                                 }
  1435.                                 //???
  1436.                                 this.envReset = true;
  1437.                         }
  1438.                 },
  1439.  
  1440.                 setEnabled:function(value){
  1441.                         //Set the enabled flag.
  1442.                         this.isEnabled = value;
  1443.                         //If not enabled, set the length counter to 0.
  1444.                         if(!value){
  1445.                                 this.lengthCounter = 0;
  1446.                         }
  1447.                         //Update the sample value.
  1448.                         this.updateSampleValue();
  1449.                 }
  1450.  
  1451.         }
  1452.  
  1453. };

Raw Paste


Login or Register to edit or fork this paste. It's free.