diff options
author | Ben Firshman <ben@firshman.co.uk> | 2009-10-22 13:06:26 +0100 |
---|---|---|
committer | Ben Firshman <ben@firshman.co.uk> | 2009-10-22 13:06:26 +0100 |
commit | 0f4519e8dadbcda6829790de46f5c96d359fb81f (patch) | |
tree | dbd8cde9b712a6b362a4dc59d2e0dec3d45291cb | |
parent | 81d7dd2ed44c613df1446ef2372b663afd7aae6e (diff) | |
download | jsnes-0f4519e8dadbcda6829790de46f5c96d359fb81f.zip jsnes-0f4519e8dadbcda6829790de46f5c96d359fb81f.tar.gz jsnes-0f4519e8dadbcda6829790de46f5c96d359fb81f.tar.bz2 |
Various variable scope optimisations, split up PAPU code and added "enable sound" button.
-rw-r--r-- | channels.js | 685 | ||||
-rw-r--r-- | cpu.js | 2 | ||||
-rw-r--r-- | globals.js | 2 | ||||
-rw-r--r-- | index.html | 23 | ||||
-rw-r--r-- | jssound.swf | bin | 1416 -> 5307 bytes | |||
-rw-r--r-- | nes.js | 49 | ||||
-rw-r--r-- | papu.js | 841 | ||||
-rw-r--r-- | ppu.js | 169 |
8 files changed, 859 insertions, 912 deletions
diff --git a/channels.js b/channels.js new file mode 100644 index 0000000..afcb3d7 --- /dev/null +++ b/channels.js @@ -0,0 +1,685 @@ +function ChannelDM(papu) { + this.papu = papu; + + this.MODE_NORMAL = 0; + this.MODE_LOOP = 1; + this.MODE_IRQ = 2; + + this.isEnabled = null; + this.hasSample = null; + this.irqGenerated=false; + + this.playMode = null; + this.dmaFrequency = null; + this.dmaCounter = null; + this.deltaCounter = null; + this.playStartAddress = null; + this.playAddress = null; + this.playLength = null; + this.playLengthCounter = null; + this.shiftCounter = null; + this.reg4012 = null; + this.reg4013 = null; + this.sample = null; + this.dacLsb = null; + this.data = null; + + this.reset(); +} + +ChannelDM.prototype.clockDmc = function(){ + + // Only alter DAC value if the sample buffer has data: + if(this.hasSample){ + + if((this.data&1)===0){ + + // Decrement delta: + if(this.deltaCounter>0) { + this.deltaCounter--; + } + + }else{ + + // Increment delta: + if(this.deltaCounter<63) { + this.deltaCounter++; + } + + } + + // Update sample value: + this.sample = this.isEnabled ? (this.deltaCounter<<1)+this.dacLsb : 0; + + // Update shift register: + this.data>>=1; + + } + + this.dmaCounter--; + if(this.dmaCounter <= 0){ + + // No more sample bits. + this.hasSample = false; + this.endOfSample(); + this.dmaCounter = 8; + + } + + if(this.irqGenerated){ + this.papu.nes.cpu.requestIrq(this.papu.nes.cpu.IRQ_NORMAL); + } + +}; + +ChannelDM.prototype.endOfSample = function(){ + + + if(this.playLengthCounter===0 && this.playMode==this.MODE_LOOP){ + + // Start from beginning of sample: + this.playAddress = this.playStartAddress; + this.playLengthCounter = this.playLength; + + } + + if(this.playLengthCounter > 0){ + + // Fetch next sample: + this.nextSample(); + + if(this.playLengthCounter == 0){ + + // Last byte of sample fetched, generate IRQ: + if(this.playMode == this.MODE_IRQ){ + + // Generate IRQ: + this.irqGenerated = true; + + } + + } + + } + +}; + +ChannelDM.prototype.nextSample = function(){ + + // Fetch byte: + this.data = this.papu.nes.memMapper.load(this.playAddress); + this.papu.nes.cpu.haltCycles(4); + + this.playLengthCounter--; + this.playAddress++; + if(this.playAddress>0xFFFF){ + this.playAddress = 0x8000; + } + + this.hasSample = true; + +}; + +ChannelDM.prototype.writeReg = function(address, value){ + + if(address == 0x4010){ + + // Play mode, DMA Frequency + if((value>>6)==0){ + this.playMode = this.MODE_NORMAL; + }else if(((value>>6)&1)==1){ + this.playMode = this.MODE_LOOP; + }else if((value>>6)==2){ + this.playMode = this.MODE_IRQ; + } + + if((value&0x80)==0){ + this.irqGenerated = false; + } + + this.dmaFrequency = this.papu.getDmcFrequency(value&0xF); + + }else if(address == 0x4011){ + + // Delta counter load register: + this.deltaCounter = (value>>1)&63; + this.dacLsb = value&1; + this.sample = ((this.deltaCounter<<1)+this.dacLsb); // update sample value + + }else if(address == 0x4012){ + + // DMA address load register + this.playStartAddress = (value<<6)|0x0C000; + this.playAddress = this.playStartAddress; + this.reg4012 = value; + + }else if(address == 0x4013){ + + // Length of play code + this.playLength = (value<<4)+1; + this.playLengthCounter = this.playLength; + this.reg4013 = value; + + }else if(address == 0x4015){ + + // DMC/IRQ Status + if(((value>>4)&1)==0){ + // Disable: + this.playLengthCounter = 0; + }else{ + // Restart: + this.playAddress = this.playStartAddress; + this.playLengthCounter = this.playLength; + } + this.irqGenerated = false; + } + +}; + +ChannelDM.prototype.setEnabled = function(value){ + + if((!this.isEnabled) && value){ + this.playLengthCounter = this.playLength; + } + this.isEnabled = value; + +}; + +ChannelDM.prototype.getLengthStatus = function(){ + return ((this.playLengthCounter==0 || !this.isEnabled)?0:1); +}; + +ChannelDM.prototype.getIrqStatus = function(){ + return (this.irqGenerated?1:0); +}; + +ChannelDM.prototype.reset = function(){ + + this.isEnabled = false; + this.irqGenerated = false; + this.playMode = this.MODE_NORMAL; + this.dmaFrequency = 0; + this.dmaCounter = 0; + this.deltaCounter = 0; + this.playStartAddress = 0; + this.playAddress = 0; + this.playLength = 0; + this.playLengthCounter = 0; + this.sample = 0; + this.dacLsb = 0; + this.shiftCounter = 0; + this.reg4012 = 0; + this.reg4013 = 0; + this.data = 0; + +}; + + +function ChannelNoise(papu) { + this.papu = papu; + + this.isEnabled = null; + this.envDecayDisable = null; + this.envDecayLoopEnable = null; + this.lengthCounterEnable = null; + this.envReset = null; + this.shiftNow = null; + + this.lengthCounter = null; + this.progTimerCount = null; + this.progTimerMax = null; + this.envDecayRate = null; + this.envDecayCounter = null; + this.envVolume = null; + this.masterVolume = null; + this.shiftReg = 1<<14; + this.randomBit = null; + this.randomMode = null; + this.sampleValue = null; + this.accValue=0; + this.accCount=1; + this.tmp = null; + + this.reset(); +} + +ChannelNoise.prototype.reset = function(){ + this.progTimerCount = 0; + this.progTimerMax = 0; + this.isEnabled = false; + this.lengthCounter = 0; + this.lengthCounterEnable = false; + this.envDecayDisable = false; + this.envDecayLoopEnable = false; + this.shiftNow = false; + this.envDecayRate = 0; + this.envDecayCounter = 0; + this.envVolume = 0; + this.masterVolume = 0; + this.shiftReg = 1; + this.randomBit = 0; + this.randomMode = 0; + this.sampleValue = 0; + this.tmp = 0; +}; + +ChannelNoise.prototype.clockLengthCounter = function(){ + if(this.lengthCounterEnable && this.lengthCounter>0){ + this.lengthCounter--; + if(this.lengthCounter == 0) this.updateSampleValue(); + } +}; + +ChannelNoise.prototype.clockEnvDecay = function(){ + + if(this.envReset){ + + // Reset envelope: + this.envReset = false; + this.envDecayCounter = this.envDecayRate + 1; + this.envVolume = 0xF; + }else if(--this.envDecayCounter <= 0){ + + // Normal handling: + this.envDecayCounter = this.envDecayRate + 1; + if(this.envVolume>0){ + this.envVolume--; + }else{ + this.envVolume = this.envDecayLoopEnable ? 0xF : 0; + } + } + this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume; + this.updateSampleValue(); +}; + +ChannelNoise.prototype.updateSampleValue = function(){ + if(this.isEnabled && this.lengthCounter>0){ + this.sampleValue = this.randomBit * this.masterVolume; + } +}; + +ChannelNoise.prototype.writeReg = function(address, value){ + + if(address == 0x400C){ + + // Volume/Envelope decay: + this.envDecayDisable = ((value&0x10)!=0); + this.envDecayRate = value&0xF; + this.envDecayLoopEnable = ((value&0x20)!=0); + this.lengthCounterEnable = ((value&0x20)==0); + this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume; + + }else if(address == 0x400E){ + + // Programmable timer: + this.progTimerMax = this.papu.getNoiseWaveLength(value&0xF); + this.randomMode = value>>7; + + }else if(address == 0x400F){ + + // Length counter + this.lengthCounter = this.papu.getLengthMax(value&248); + this.envReset = true; + + } + + // Update: + //updateSampleValue(); + +}; + +ChannelNoise.prototype.setEnabled = function(value){ + this.isEnabled = value; + if(!value) this.lengthCounter = 0; + this.updateSampleValue(); +}; + +ChannelNoise.prototype.getLengthStatus = function(){ + return ((this.lengthCounter==0 || !this.isEnabled)?0:1); +}; + + +function ChannelSquare(papu, square1){ + this.papu = papu; + + this.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 + ]; + this.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 + ]; + + this.sqr1 = square1; + this.isEnabled = null; + this.lengthCounterEnable = null; + this.sweepActive = null; + this.envDecayDisable = null; + this.envDecayLoopEnable = null; + this.envReset = null; + this.sweepCarry = null; + this.updateSweepPeriod = null; + + this.progTimerCount = null; + this.progTimerMax = null; + this.lengthCounter = null; + this.squareCounter = null; + this.sweepCounter = null; + this.sweepCounterMax = null; + this.sweepMode = null; + this.sweepShiftAmount = null; + this.envDecayRate = null; + this.envDecayCounter = null; + this.envVolume = null; + this.masterVolume = null; + this.dutyMode = null; + this.sweepResult = null; + this.sampleValue = null; + this.vol = null; + + this.reset(); +} + + +ChannelSquare.prototype.reset = function() { + this.progTimerCount = 0; + this.progTimerMax = 0; + this.lengthCounter = 0; + this.squareCounter = 0; + this.sweepCounter = 0; + this.sweepCounterMax = 0; + this.sweepMode = 0; + this.sweepShiftAmount = 0; + this.envDecayRate = 0; + this.envDecayCounter = 0; + this.envVolume = 0; + this.masterVolume = 0; + this.dutyMode = 0; + this.vol = 0; + + this.isEnabled = false; + this.lengthCounterEnable = false; + this.sweepActive = false; + this.sweepCarry = false; + this.envDecayDisable = false; + this.envDecayLoopEnable = false; +}; + +ChannelSquare.prototype.clockLengthCounter = function(){ + + if(this.lengthCounterEnable && this.lengthCounter>0){ + this.lengthCounter--; + if(this.lengthCounter==0) this.updateSampleValue(); + } + +}; + +ChannelSquare.prototype.clockEnvDecay = function() { + + if(this.envReset){ + + // Reset envelope: + this.envReset = false; + this.envDecayCounter = this.envDecayRate + 1; + this.envVolume = 0xF; + + }else if((--this.envDecayCounter) <= 0){ + + // Normal handling: + this.envDecayCounter = this.envDecayRate + 1; + if(this.envVolume>0){ + this.envVolume--; + }else{ + this.envVolume = this.envDecayLoopEnable ? 0xF : 0; + } + + } + + this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume; + this.updateSampleValue(); + +}; + +ChannelSquare.prototype.clockSweep = function() { + + if(--this.sweepCounter<=0){ + + this.sweepCounter = this.sweepCounterMax + 1; + if(this.sweepActive && this.sweepShiftAmount>0 && this.progTimerMax>7){ + + // Calculate result from shifter: + this.sweepCarry = false; + if(this.sweepMode==0){ + this.progTimerMax += (this.progTimerMax>>this.sweepShiftAmount); + if(this.progTimerMax > 4095){ + this.progTimerMax = 4095; + this.sweepCarry = true; + } + }else{ + this.progTimerMax = this.progTimerMax - ((this.progTimerMax>>this.sweepShiftAmount)-(this.sqr1?1:0)); + } + + } + + } + + if(this.updateSweepPeriod){ + this.updateSweepPeriod = false; + this.sweepCounter = this.sweepCounterMax + 1; + } + +}; + +ChannelSquare.prototype.updateSampleValue = function() { + + if(this.isEnabled && this.lengthCounter>0 && this.progTimerMax>7){ + + if(this.sweepMode==0 && (this.progTimerMax + (this.progTimerMax>>this.sweepShiftAmount)) > 4095){ + //if(this.sweepCarry){ + this.sampleValue = 0; + }else{ + this.sampleValue = this.masterVolume*this.dutyLookup[(this.dutyMode<<3)+this.squareCounter]; + } + }else{ + this.sampleValue = 0; + } + +}; + +ChannelSquare.prototype.writeReg = function(address, value){ + + var addrAdd = (this.sqr1?0:4); + if(address == 0x4000+addrAdd){ + + // Volume/Envelope decay: + this.envDecayDisable = ((value&0x10)!=0); + this.envDecayRate = value & 0xF; + this.envDecayLoopEnable = ((value&0x20)!=0); + this.dutyMode = (value>>6)&0x3; + this.lengthCounterEnable = ((value&0x20)==0); + this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume; + this.updateSampleValue(); + + }else if(address == 0x4001+addrAdd){ + + // Sweep: + this.sweepActive = ((value&0x80)!=0); + this.sweepCounterMax = ((value>>4)&7); + this.sweepMode = (value>>3)&1; + this.sweepShiftAmount = value&7; + this.updateSweepPeriod = true; + + }else if(address == 0x4002+addrAdd){ + + // Programmable timer: + this.progTimerMax &= 0x700; + this.progTimerMax |= value; + + }else if(address == 0x4003+addrAdd){ + + // Programmable timer, length counter + this.progTimerMax &= 0xFF; + this.progTimerMax |= ((value&0x7)<<8); + + if(this.isEnabled){ + this.lengthCounter = this.papu.getLengthMax(value&0xF8); + } + + this.envReset = true; + + } + +}; + +ChannelSquare.prototype.setEnabled = function(value){ + this.isEnabled = value; + if(!value) this.lengthCounter = 0; + this.updateSampleValue(); +}; + +ChannelSquare.prototype.getLengthStatus = function() { + return ((this.lengthCounter==0 || !this.isEnabled)?0:1); +}; + + +function ChannelTriangle(papu) { + this.papu = papu; + + this.isEnabled = null; + this.sampleCondition = null; + this.lengthCounterEnable = null; + this.lcHalt = null; + this.lcControl = null; + + this.progTimerCount = null; + this.progTimerMax = null; + this.triangleCounter = null; + this.lengthCounter = null; + this.linearCounter = null; + this.lcLoadValue = null; + this.sampleValue = null; + this.tmp = null; + + this.reset(); +} + +ChannelTriangle.prototype.reset = function(){ + this.progTimerCount = 0; + this.progTimerMax = 0; + this.triangleCounter = 0; + this.isEnabled = false; + this.sampleCondition = false; + this.lengthCounter = 0; + this.lengthCounterEnable = false; + this.linearCounter = 0; + this.lcLoadValue = 0; + this.lcHalt = true; + this.lcControl = false; + this.tmp = 0; + this.sampleValue = 0xF; +}; + +ChannelTriangle.prototype.clockLengthCounter = function(){ + if(this.lengthCounterEnable && this.lengthCounter>0){ + this.lengthCounter--; + if(this.lengthCounter==0){ + this.updateSampleCondition(); + } + } +}; + +ChannelTriangle.prototype.clockLinearCounter = function(){ + if(this.lcHalt){ + // Load: + this.linearCounter = this.lcLoadValue; + this.updateSampleCondition(); + }else if(this.linearCounter > 0){ + // Decrement: + this.linearCounter--; + this.updateSampleCondition(); + } + if(!this.lcControl){ + // Clear halt flag: + this.lcHalt = false; + } +}; + +ChannelTriangle.prototype.getLengthStatus = function(){ + return ((this.lengthCounter === 0 || !this.isEnabled)?0:1); +}; + +ChannelTriangle.prototype.readReg = function(address){ + return 0; +}; + +ChannelTriangle.prototype.writeReg = function(address, value){ + + if(address == 0x4008){ + + // New values for linear counter: + this.lcControl = (value&0x80)!==0; + this.lcLoadValue = value&0x7F; + + // Length counter enable: + this.lengthCounterEnable = !this.lcControl; + + }else if(address == 0x400A){ + + // Programmable timer: + this.progTimerMax &= 0x700; + this.progTimerMax |= value; + + }else if(address == 0x400B){ + + // Programmable timer, length counter + this.progTimerMax &= 0xFF; + this.progTimerMax |= ((value&0x07)<<8); + this.lengthCounter = this.papu.getLengthMax(value&0xF8); + this.lcHalt = true; + + } + + this.updateSampleCondition(); + +}; + +ChannelTriangle.prototype.clockProgrammableTimer = function(nCycles){ + + if(this.progTimerMax>0){ + this.progTimerCount += nCycles; + while(this.progTimerMax > 0 && this.progTimerCount >= this.progTimerMax){ + this.progTimerCount-=this.progTimerMax; + if(this.isEnabled && this.lengthCounter>0 && this.linearCounter>0){ + this.clockTriangleGenerator(); + } + } + } + +}; + +ChannelTriangle.prototype.clockTriangleGenerator = function(){ + this.triangleCounter++; + this.triangleCounter &= 0x1F; +}; + +ChannelTriangle.prototype.setEnabled = function(value){ + this.isEnabled = value; + if(!value) this.lengthCounter = 0; + this.updateSampleCondition(); +}; + +ChannelTriangle.prototype.updateSampleCondition = function(){ + this.sampleCondition = + this.isEnabled && + this.progTimerMax>7 && + this.linearCounter>0 && + this.lengthCounter>0 + ; +}; + + @@ -100,7 +100,7 @@ function CPU(nes) { if(this.irqRequested){ temp = (this.F_CARRY)| - ((this.F_ZERO==0?1:0)<<1)| + ((this.F_ZERO===0?1:0)<<1)| (this.F_INTERRUPT<<2)| (this.F_DECIMAL<<3)| (this.F_BRK<<4)| @@ -8,7 +8,7 @@ var Globals = { nes: null, fpsInterval: 500, // Time between updating FPS in ms - emulateSound: true, + emulateSound: false, sampleRate: 44100, // Sound sample rate in hz } Globals.frameTime = 1000/Globals.preferredFrameRate; @@ -12,6 +12,7 @@ charset="utf-8"> <script src="jquery-1.2.6.min.js" type="text/javascript" charset="utf-8"></script> <script src="jquery.dimensions.min.js" type="text/javascript" charset="utf-8"></script> <script src="utils.js" type="text/javascript" charset="utf-8"></script> + <script src="channels.js" type="text/javascript" charset="utf-8"></script> <script src="cpu.js" type="text/javascript" charset="utf-8"></script> <script src="cpuinfo.js" type="text/javascript" charset="utf-8"></script> <script src="globals.js" type="text/javascript" charset="utf-8"></script> @@ -55,6 +56,7 @@ charset="utf-8"> <select id="roms"></select> <input type="button" value="pause" id="pause" disabled="disabled"> <input type="button" value="restart" id="restart" disabled="disabled"> + <input type="button" value="enable sound" id="enablesound" disabled="disabled"> </div> <p id="status">Loading...</p> <script type="text/javascript"> @@ -144,6 +146,13 @@ digg_url = 'http://digg.com/playable_web_games/JSNES_A_NES_emulator_written_enti function resetButtons() { $("#pause").attr("disabled", null).attr("value", "pause"); $("#restart").attr("disabled", null); + $("#enablesound").attr("disabled", null); + if (Globals.emulateSound) { + $("#enablesound").attr("value", "disable sound"); + } + else { + $("#enablesound").attr("value", "enable sound"); + } } $("#roms").change(function() { @@ -169,13 +178,25 @@ digg_url = 'http://digg.com/playable_web_games/JSNES_A_NES_emulator_written_enti } }); + $("#enablesound").click(function() { + b = $("#enablesound"); + if (b.attr("value") == "enable sound") { + Globals.emulateSound = true; + b.attr("value", "disable sound"); + } + else { + Globals.emulateSound = false; + b.attr("value", "enable sound"); + } + }); + $("#restart").click(function() { nes.reloadRom(); nes.start(); }); function readJSSoundBuffer() { - return nes.papu.readBuffer(); + return JSON.stringify(nes.papu.readBuffer()); } var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); diff --git a/jssound.swf b/jssound.swf Binary files differindex cde545f..f2c6a18 100644 --- a/jssound.swf +++ b/jssound.swf @@ -66,54 +66,57 @@ function NES() { this.ppu.startFrame(); var cycles = 0; var emulateSound = Globals.emulateSound; + var cpu = this.cpu; + var ppu = this.ppu; + var papu = this.papu; FRAMELOOP: for (;;) { - if (this.cpu.cyclesToHalt == 0) { + if (cpu.cyclesToHalt == 0) { // Execute a CPU instruction - cycles = this.cpu.emulate(); - if(emulateSound) { - this.papu.clockFrameCounter(cycles); + cycles = cpu.emulate(); + if (emulateSound) { + papu.clockFrameCounter(cycles); } cycles *= 3; } else { - if (this.cpu.cyclesToHalt > 8) { + if (cpu.cyclesToHalt > 8) { cycles = 24; if (emulateSound) { - this.papu.clockFrameCounter(8); + papu.clockFrameCounter(8); } - this.cpu.cyclesToHalt -= 8; + cpu.cyclesToHalt -= 8; } else { - cycles = this.cpu.cyclesToHalt * 3; + cycles = cpu.cyclesToHalt * 3; if (emulateSound) { - this.papu.clockFrameCounter(this.cpu.cyclesToHalt); + papu.clockFrameCounter(cpu.cyclesToHalt); } - this.cpu.cyclesToHalt = 0; + cpu.cyclesToHalt = 0; } } for(;cycles>0;cycles--){ - if(this.ppu.curX == this.ppu.spr0HitX - && this.ppu.f_spVisibility==1 - && this.ppu.scanline-21 == this.ppu.spr0HitY){ + if(ppu.curX == ppu.spr0HitX + && ppu.f_spVisibility==1 + && ppu.scanline-21 == ppu.spr0HitY){ // Set sprite 0 hit flag: - this.ppu.setStatusFlag(this.ppu.STATUS_SPRITE0HIT,true); + ppu.setStatusFlag(ppu.STATUS_SPRITE0HIT,true); } - if(this.ppu.requestEndFrame){ - this.ppu.nmiCounter--; - if(this.ppu.nmiCounter == 0){ - this.ppu.requestEndFrame = false; - this.ppu.startVBlank(); + if(ppu.requestEndFrame){ + ppu.nmiCounter--; + if(ppu.nmiCounter == 0){ + ppu.requestEndFrame = false; + ppu.startVBlank(); break FRAMELOOP; } } - this.ppu.curX++; - if(this.ppu.curX==341){ - this.ppu.curX = 0; - this.ppu.endScanline(); + ppu.curX++; + if(ppu.curX==341){ + ppu.curX = 0; + ppu.endScanline(); } } @@ -1,691 +1,6 @@ -function ChannelDM(papu) { - this.papu = papu; - - this.MODE_NORMAL = 0; - this.MODE_LOOP = 1; - this.MODE_IRQ = 2; - - this.isEnabled = null; - this.hasSample = null; - this.irqGenerated=false; - - this.playMode = null; - this.dmaFrequency = null; - this.dmaCounter = null; - this.deltaCounter = null; - this.playStartAddress = null; - this.playAddress = null; - this.playLength = null; - this.playLengthCounter = null; - this.shiftCounter = null; - this.reg4012,reg4013 = null; - this.status = null; - this.sample = null; - this.dacLsb = null; - this.data = null; - - this.reset(); -} - -ChannelDM.prototype.clockDmc = function(){ - - // Only alter DAC value if the sample buffer has data: - if(this.hasSample){ - - if((this.data&1)==0){ - - // Decrement delta: - if(this.deltaCounter>0) this.deltaCounter--; - - }else{ - - // Increment delta: - if(this.deltaCounter<63) this.deltaCounter++; - - } - - // Update sample value: - this.sample = this.isEnabled ? (this.deltaCounter<<1)+this.dacLsb : 0; - - // Update shift register: - this.data>>=1; - - } - - this.dmaCounter--; - if(this.dmaCounter <= 0){ - - // No more sample bits. - this.hasSample = false; - this.endOfSample(); - this.dmaCounter = 8; - - } - - if(this.irqGenerated){ - this.papu.nes.cpu.requestIrq(this.papu.nes.cpu.IRQ_NORMAL); - } - -} - -ChannelDM.prototype.endOfSample = function(){ - - - if(this.playLengthCounter==0 && this.playMode==this.MODE_LOOP){ - - // Start from beginning of sample: - this.playAddress = this.playStartAddress; - this.playLengthCounter = this.playLength; - - } - - if(this.playLengthCounter > 0){ - - // Fetch next sample: - this.nextSample(); - - if(this.playLengthCounter == 0){ - - // Last byte of sample fetched, generate IRQ: - if(this.playMode == this.MODE_IRQ){ - - // Generate IRQ: - this.irqGenerated = true; - - } - - } - - } - -} - -ChannelDM.prototype.nextSample = function(){ - - // Fetch byte: - this.data = this.papu.nes.memMapper.load(this.playAddress); - this.papu.nes.cpu.haltCycles(4); - - this.playLengthCounter--; - this.playAddress++; - if(this.playAddress>0xFFFF){ - this.playAddress = 0x8000; - } - - this.hasSample = true; - -} - -ChannelDM.prototype.writeReg = function(address, value){ - - if(address == 0x4010){ - - // Play mode, DMA Frequency - if((value>>6)==0){ - this.playMode = this.MODE_NORMAL; - }else if(((value>>6)&1)==1){ - this.playMode = this.MODE_LOOP; - }else if((value>>6)==2){ - this.playMode = this.MODE_IRQ; - } - - if((value&0x80)==0){ - this.irqGenerated = false; - } - - this.dmaFrequency = this.papu.getDmcFrequency(value&0xF); - - }else if(address == 0x4011){ - - // Delta counter load register: - this.deltaCounter = (value>>1)&63; - this.dacLsb = value&1; - this.sample = ((this.deltaCounter<<1)+this.dacLsb); // update sample value - - }else if(address == 0x4012){ - - // DMA address load register - this.playStartAddress = (value<<6)|0x0C000; - this.playAddress = this.playStartAddress; - this.reg4012 = value; - - }else if(address == 0x4013){ - - // Length of play code - this.playLength = (value<<4)+1; - this.playLengthCounter = this.playLength; - this.reg4013 = value; - - }else if(address == 0x4015){ - - // DMC/IRQ Status - if(((value>>4)&1)==0){ - // Disable: - this.playLengthCounter = 0; - }else{ - // Restart: - this.playAddress = this.playStartAddress; - this.playLengthCounter = this.playLength; - } - this.irqGenerated = false; - } - -} - -ChannelDM.prototype.setEnabled = function(value){ - - if((!this.isEnabled) && value){ - this.playLengthCounter = this.playLength; - } - this.isEnabled = value; - -} - -ChannelDM.prototype.getLengthStatus = function(){ - return ((this.playLengthCounter==0 || !this.isEnabled)?0:1); -} - -ChannelDM.prototype.getIrqStatus = function(){ - return (this.irqGenerated?1:0); -} - -ChannelDM.prototype.reset = function(){ - - this.isEnabled = false; - this.irqGenerated = false; - this.playMode = this.MODE_NORMAL; - this.dmaFrequency = 0; - this.dmaCounter = 0; - this.deltaCounter = 0; - this.playStartAddress = 0; - this.playAddress = 0; - this.playLength = 0; - this.playLengthCounter = 0; - this.status = 0; - this.sample = 0; - this.dacLsb = 0; - this.shiftCounter = 0; - this.reg4012 = 0; - this.reg4013 = 0; - this.data = 0; - -} - - -function ChannelNoise(papu) { - this.papu = papu; - - this.isEnabled = null; - this.envDecayDisable = null; - this.envDecayLoopEnable = null; - this.lengthCounterEnable = null; - this.envReset = null; - this.shiftNow = null; - - this.lengthCounter = null; - this.progTimerCount = null; - this.progTimerMax = null; - this.envDecayRate = null; - this.envDecayCounter = null; - this.envVolume = null; - this.masterVolume = null; - this.shiftReg = 1<<14; - this.randomBit = null; - this.randomMode = null; - this.sampleValue = null; - this.accValue=0; - this.accCount=1; - this.tmp = null; - - this.reset(); -} - -ChannelNoise.prototype.reset = function(){ - this.progTimerCount = 0; - this.progTimerMax = 0; - this.isEnabled = false; - this.lengthCounter = 0; - this.lengthCounterEnable = false; - this.envDecayDisable = false; - this.envDecayLoopEnable = false; - this.shiftNow = false; - this.envDecayRate = 0; - this.envDecayCounter = 0; - this.envVolume = 0; - this.masterVolume = 0; - this.shiftReg = 1; - this.randomBit = 0; - this.randomMode = 0; - this.sampleValue = 0; - this.tmp = 0; -} - -ChannelNoise.prototype.clockLengthCounter = function(){ - if(this.lengthCounterEnable && this.lengthCounter>0){ - this.lengthCounter--; - if(this.lengthCounter == 0) this.updateSampleValue(); - } -} - -ChannelNoise.prototype.clockEnvDecay = function(){ - - if(this.envReset){ - - // Reset envelope: - this.envReset = false; - this.envDecayCounter = this.envDecayRate + 1; - this.envVolume = 0xF; - }else if(--this.envDecayCounter <= 0){ - - // Normal handling: - this.envDecayCounter = this.envDecayRate + 1; - if(this.envVolume>0){ - this.envVolume--; - }else{ - this.envVolume = this.envDecayLoopEnable ? 0xF : 0; - } - } - this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume; - this.updateSampleValue(); -} - -ChannelNoise.prototype.updateSampleValue = function(){ - if(this.isEnabled && this.lengthCounter>0){ - this.sampleValue = this.randomBit * this.masterVolume; - } -} - -ChannelNoise.prototype.writeReg = function(address, value){ - - if(address == 0x400C){ - - // Volume/Envelope decay: - this.envDecayDisable = ((value&0x10)!=0); - this.envDecayRate = value&0xF; - this.envDecayLoopEnable = ((value&0x20)!=0); - this.lengthCounterEnable = ((value&0x20)==0); - this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume; - - }else if(address == 0x400E){ - - // Programmable timer: - this.progTimerMax = this.papu.getNoiseWaveLength(value&0xF); - this.randomMode = value>>7; - - }else if(address == 0x400F){ - - // Length counter - this.lengthCounter = this.papu.getLengthMax(value&248); - this.envReset = true; - - } - - // Update: - //updateSampleValue(); - -} - -ChannelNoise.prototype.setEnabled = function(value){ - this.isEnabled = value; - if(!value) this.lengthCounter = 0; - this.updateSampleValue(); -} - -ChannelNoise.prototype.getLengthStatus = function(){ - return ((this.lengthCounter==0 || !this.isEnabled)?0:1); -} - - -function ChannelSquare(papu, square1){ - this.papu = papu; - - this.dutyLookup = new Array( - 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 - ); - this.impLookup = new Array( - 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 - ); - - this.sqr1 = square1; - this.isEnabled = null; - this.lengthCounterEnable = null; - this.sweepActive = null; - this.envDecayDisable = null; - this.envDecayLoopEnable = null; - this.envReset = null; - this.sweepCarry = null; - this.updateSweepPeriod = null; - - this.progTimerCount = null; - this.progTimerMax = null; - this.lengthCounter = null; - this.squareCounter = null; - this.sweepCounter = null; - this.sweepCounterMax = null; - this.sweepMode = null; - this.sweepShiftAmount = null; - this.envDecayRate = null; - this.envDecayCounter = null; - this.envVolume = null; - this.masterVolume = null; - this.dutyMode = null; - this.sweepResult = null; - this.sampleValue = null; - this.vol = null; - - this.reset(); -} - - -ChannelSquare.prototype.reset = function() { - this.progTimerCount = 0; - this.progTimerMax = 0; - this.lengthCounter = 0; - this.squareCounter = 0; - this.sweepCounter = 0; - this.sweepCounterMax = 0; - this.sweepMode = 0; - this.sweepShiftAmount = 0; - this.envDecayRate = 0; - this.envDecayCounter = 0; - this.envVolume = 0; - this.masterVolume = 0; - this.dutyMode = 0; - this.vol = 0; - - this.isEnabled = false; - this.lengthCounterEnable = false; - this.sweepActive = false; - this.sweepCarry = false; - this.envDecayDisable = false; - this.envDecayLoopEnable = false; -} - -ChannelSquare.prototype.clockLengthCounter = function(){ - - if(this.lengthCounterEnable && this.lengthCounter>0){ - this.lengthCounter--; - if(this.lengthCounter==0) this.updateSampleValue(); - } - -} - -ChannelSquare.prototype.clockEnvDecay = function() { - - if(this.envReset){ - - // Reset envelope: - this.envReset = false; - this.envDecayCounter = this.envDecayRate + 1; - this.envVolume = 0xF; - - }else if((--this.envDecayCounter) <= 0){ - - // Normal handling: - this.envDecayCounter = this.envDecayRate + 1; - if(this.envVolume>0){ - this.envVolume--; - }else{ - this.envVolume = this.envDecayLoopEnable ? 0xF : 0; - } - - } - - this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume; - this.updateSampleValue(); - -} - -ChannelSquare.prototype.clockSweep = function() { - - if(--this.sweepCounter<=0){ - - this.sweepCounter = this.sweepCounterMax + 1; - if(this.sweepActive && this.sweepShiftAmount>0 && this.progTimerMax>7){ - - // Calculate result from shifter: - this.sweepCarry = false; - if(this.sweepMode==0){ - this.progTimerMax += (this.progTimerMax>>this.sweepShiftAmount); - if(this.progTimerMax > 4095){ - this.progTimerMax = 4095; - this.sweepCarry = true; - } - }else{ - this.progTimerMax = this.progTimerMax - ((this.progTimerMax>>this.sweepShiftAmount)-(this.sqr1?1:0)); - } - - } - - } - - if(this.updateSweepPeriod){ - this.updateSweepPeriod = false; - this.sweepCounter = this.sweepCounterMax + 1; - } - -} - -ChannelSquare.prototype.updateSampleValue = function() { - - if(this.isEnabled && this.lengthCounter>0 && this.progTimerMax>7){ - - if(this.sweepMode==0 && (this.progTimerMax + (this.progTimerMax>>this.sweepShiftAmount)) > 4095){ - //if(this.sweepCarry){ - this.sampleValue = 0; - }else{ - this.sampleValue = this.masterVolume*this.dutyLookup[(this.dutyMode<<3)+this.squareCounter]; - } - }else{ - this.sampleValue = 0; - } - -} - -ChannelSquare.prototype.writeReg = function(address, value){ - - var addrAdd = (this.sqr1?0:4); - if(address == 0x4000+addrAdd){ - - // Volume/Envelope decay: - this.envDecayDisable = ((value&0x10)!=0); - this.envDecayRate = value & 0xF; - this.envDecayLoopEnable = ((value&0x20)!=0); - this.dutyMode = (value>>6)&0x3; - this.lengthCounterEnable = ((value&0x20)==0); - this.masterVolume = this.envDecayDisable?this.envDecayRate:this.envVolume; - this.updateSampleValue(); - - }else if(address == 0x4001+addrAdd){ - - // Sweep: - this.sweepActive = ((value&0x80)!=0); - this.sweepCounterMax = ((value>>4)&7); - this.sweepMode = (value>>3)&1; - this.sweepShiftAmount = value&7; - this.updateSweepPeriod = true; - - }else if(address == 0x4002+addrAdd){ - - // Programmable timer: - this.progTimerMax &= 0x700; - this.progTimerMax |= value; - - }else if(address == 0x4003+addrAdd){ - - // Programmable timer, length counter - this.progTimerMax &= 0xFF; - this.progTimerMax |= ((value&0x7)<<8); - - if(this.isEnabled){ - this.lengthCounter = this.papu.getLengthMax(value&0xF8); - } - - this.envReset = true; - - } - -} - -ChannelSquare.prototype.setEnabled = function(value){ - this.isEnabled = value; - if(!value) this.lengthCounter = 0; - this.updateSampleValue(); -} - -ChannelSquare.prototype.getLengthStatus = function() { - return ((this.lengthCounter==0 || !this.isEnabled)?0:1); -} - - -function ChannelTriangle(papu) { - this.papu = papu; - - this.isEnabled = null; - this.sampleCondition = null; - this.lengthCounterEnable = null; - this.lcHalt = null; - this.lcControl = null; - - this.progTimerCount = null; - this.progTimerMax = null; - this.triangleCounter = null; - this.lengthCounter = null; - this.linearCounter = null; - this.lcLoadValue = null; - this.sampleValue = null; - this.tmp = null; - - this.reset(); -} - -ChannelTriangle.prototype.reset = function(){ - this.progTimerCount = 0; - this.progTimerMax = 0; - this.triangleCounter = 0; - this.isEnabled = false; - this.sampleCondition = false; - this.lengthCounter = 0; - this.lengthCounterEnable = false; - this.linearCounter = 0; - this.lcLoadValue = 0; - this.lcHalt = true; - this.lcControl = false; - this.tmp = 0; - this.sampleValue = 0xF; -} - -ChannelTriangle.prototype.clockLengthCounter = function(){ - if(this.lengthCounterEnable && this.lengthCounter>0){ - this.lengthCounter--; - if(this.lengthCounter==0){ - this.updateSampleCondition(); - } - } -} - -ChannelTriangle.prototype.clockLinearCounter = function(){ - if(this.lcHalt){ - // Load: - this.linearCounter = this.lcLoadValue; - this.updateSampleCondition(); - }else if(this.linearCounter > 0){ - // Decrement: - this.linearCounter--; - this.updateSampleCondition(); - } - if(!this.lcControl){ - // Clear halt flag: - this.lcHalt = false; - } -} - -ChannelTriangle.prototype.getLengthStatus = function(){ - return ((this.lengthCounter==0 || !this.isEnabled)?0:1); -} - -ChannelTriangle.prototype.readReg = function(address){ - return 0; -} - -ChannelTriangle.prototype.writeReg = function(address, value){ - - if(address == 0x4008){ - - // New values for linear counter: - this.lcControl = (value&0x80)!=0; - this.lcLoadValue = value&0x7F; - - // Length counter enable: - this.lengthCounterEnable = !this.lcControl; - - }else if(address == 0x400A){ - - // Programmable timer: - this.progTimerMax &= 0x700; - this.progTimerMax |= value; - - }else if(address == 0x400B){ - - // Programmable timer, length counter - this.progTimerMax &= 0xFF; - this.progTimerMax |= ((value&0x07)<<8); - this.lengthCounter = this.papu.getLengthMax(value&0xF8); - this.lcHalt = true; - - } - - this.updateSampleCondition(); - -} - -ChannelTriangle.prototype.clockProgrammableTimer = function(nCycles){ - - if(this.progTimerMax>0){ - this.progTimerCount += nCycles; - while(this.progTimerMax > 0 && this.progTimerCount >= this.progTimerMax){ - this.progTimerCount-=this.progTimerMax; - if(this.isEnabled && this.lengthCounter>0 && this.linearCounter>0){ - this.clockTriangleGenerator(); - } - } - } - -} - -ChannelTriangle.prototype.clockTriangleGenerator = function(){ - this.triangleCounter++; - this.triangleCounter &= 0x1F; -} - -ChannelTriangle.prototype.setEnabled = function(value){ - this.isEnabled = value; - if(!value) this.lengthCounter = 0; - this.updateSampleCondition(); -} - -ChannelTriangle.prototype.updateSampleCondition = function(){ - this.sampleCondition = - this.isEnabled && - this.progTimerMax>7 && - this.linearCounter>0 && - this.lengthCounter>0 - ; -} - - - function PAPU(nes) { this.nes = nes; - - this.buffer = nes; this.square1 = new ChannelSquare(this,true); this.square2 = new ChannelSquare(this,false); @@ -698,7 +13,7 @@ function PAPU(nes) { this.initCounter = 2048; this.channelEnableValue = null; - this.bufferSize = 4096; + this.bufferSize = 8192; this.bufferIndex = 0; this.sampleRate = 44100; @@ -707,7 +22,7 @@ function PAPU(nes) { this.noiseWavelengthLookup = null; this.square_table = null; this.tnd_table = null; - this.sampleBuffer = new Array(this.bufferSize*4); + this.sampleBuffer = new Array(this.bufferSize*2); this.frameIrqEnabled = false; this.frameIrqActive; @@ -723,7 +38,6 @@ function PAPU(nes) { this.frameTime = null; this.sampleTimerMax = null; this.sampleCount = null; - this.triValue = 0; this.smpSquare1 = null; this.smpSquare2 = null; @@ -758,7 +72,6 @@ function PAPU(nes) { this.stereoPosRDMC = null; this.extraCycles = null; - this.maxCycles = null; // Panning: this.panning = new Array( @@ -969,7 +282,7 @@ PAPU.prototype.clockFrameCounter = function(nCycles){ if(this.initCounter > 0){ if(this.initingHardware){ - this.initCounter-=nCycles; + this.initCounter -= nCycles; if(this.initCounter<=0) this.initingHardware = false; return; } @@ -977,10 +290,10 @@ PAPU.prototype.clockFrameCounter = function(nCycles){ // Don't process ticks beyond next sampling: nCycles += this.extraCycles; - this.maxCycles = this.sampleTimerMax-this.sampleTimer; - if((nCycles<<10) > this.maxCycles){ + var maxCycles = this.sampleTimerMax-this.sampleTimer; + if((nCycles<<10) > maxCycles){ - this.extraCycles = ((nCycles<<10) - this.maxCycles)>>10; + this.extraCycles = ((nCycles<<10) - maxCycles)>>10; nCycles -= this.extraCycles; }else{ @@ -989,38 +302,44 @@ PAPU.prototype.clockFrameCounter = function(nCycles){ } + var dmc = this.dmc; + var triangle = this.triangle; + var square1 = this.square1; + var square2 = this.square2; + var noise = this.noise; + // Clock DMC: - if(this.dmc.isEnabled){ + if(dmc.isEnabled){ - this.dmc.shiftCounter-=(nCycles<<3); - while(this.dmc.shiftCounter<=0 && this.dmc.dmaFrequency>0){ - this.dmc.shiftCounter += this.dmc.dmaFrequency; - this.dmc.clockDmc(); + dmc.shiftCounter-=(nCycles<<3); + while(dmc.shiftCounter<=0 && dmc.dmaFrequency>0){ + dmc.shiftCounter += dmc.dmaFrequency; + dmc.clockDmc(); } } // Clock Triangle channel Prog timer: - if(this.triangle.progTimerMax>0){ + if(triangle.progTimerMax>0){ - this.triangle.progTimerCount -= nCycles; - while(this.triangle.progTimerCount <= 0){ + triangle.progTimerCount -= nCycles; + while(triangle.progTimerCount <= 0){ - this.triangle.progTimerCount += this.triangle.progTimerMax+1; - if(this.triangle.linearCounter>0 && this.triangle.lengthCounter>0){ + triangle.progTimerCount += triangle.progTimerMax+1; + if(triangle.linearCounter>0 && triangle.lengthCounter>0){ - this.triangle.triangleCounter++; - this.triangle.triangleCounter &= 0x1F; + triangle.triangleCounter++; + triangle.triangleCounter &= 0x1F; - if(this.triangle.isEnabled){ - if(this.triangle.triangleCounter>=0x10){ + if(triangle.isEnabled){ + if(triangle.triangleCounter>=0x10){ // Normal value. - this.triangle.sampleValue = (this.triangle.triangleCounter&0xF); + triangle.sampleValue = (triangle.triangleCounter&0xF); }else{ // Inverted value. - this.triangle.sampleValue = (0xF - (this.triangle.triangleCounter&0xF)); + triangle.sampleValue = (0xF - (triangle.triangleCounter&0xF)); } - this.triangle.sampleValue <<= 4; + triangle.sampleValue <<= 4; } } @@ -1029,124 +348,117 @@ PAPU.prototype.clockFrameCounter = function(nCycles){ } // Clock Square channel 1 Prog timer: - this.square1.progTimerCount -= nCycles; - if(this.square1.progTimerCount <= 0){ + square1.progTimerCount -= nCycles; + if(square1.progTimerCount <= 0){ - this.square1.progTimerCount += (this.square1.progTimerMax+1)<<1; + square1.progTimerCount += (square1.progTimerMax+1)<<1; - this.square1.squareCounter++; - this.square1.squareCounter&=0x7; - this.square1.updateSampleValue(); + square1.squareCounter++; + square1.squareCounter&=0x7; + square1.updateSampleValue(); } // Clock Square channel 2 Prog timer: - this.square2.progTimerCount -= nCycles; - if(this.square2.progTimerCount <= 0){ + square2.progTimerCount -= nCycles; + if(square2.progTimerCount <= 0){ - this.square2.progTimerCount += (this.square2.progTimerMax+1)<<1; + square2.progTimerCount += (square2.progTimerMax+1)<<1; - this.square2.squareCounter++; - this.square2.squareCounter&=0x7; - this.square2.updateSampleValue(); + square2.squareCounter++; + square2.squareCounter&=0x7; + square2.updateSampleValue(); } // Clock noise channel Prog timer: var acc_c = nCycles; - if(this.noise.progTimerCount-acc_c > 0){ + if(noise.progTimerCount-acc_c > 0){ // Do all cycles at once: - this.noise.progTimerCount -= acc_c; - this.noise.accCount += acc_c; - this.noise.accValue += acc_c * this.noise.sampleValue; + noise.progTimerCount -= acc_c; + noise.accCount += acc_c; + noise.accValue += acc_c * noise.sampleValue; }else{ // Slow-step: while((acc_c--) > 0){ - if(--this.noise.progTimerCount <= 0 && this.noise.progTimerMax>0){ + if(--noise.progTimerCount <= 0 && noise.progTimerMax>0){ // Update noise shift register: - this.noise.shiftReg <<= 1; - this.noise.tmp = (((this.noise.shiftReg << (this.noise.randomMode==0?1:6)) ^ this.noise.shiftReg) & 0x8000 ); - if(this.noise.tmp!=0){ + noise.shiftReg <<= 1; + noise.tmp = (((noise.shiftReg << (noise.randomMode==0?1:6)) ^ noise.shiftReg) & 0x8000 ); + if(noise.tmp!=0){ // Sample value must be 0. - this.noise.shiftReg |= 0x01; - this.noise.randomBit = 0; - this.noise.sampleValue = 0; + noise.shiftReg |= 0x01; + noise.randomBit = 0; + noise.sampleValue = 0; }else{ // Find sample value: - this.noise.randomBit = 1; - if(this.noise.isEnabled && this.noise.lengthCounter>0){ - this.noise.sampleValue = this.noise.masterVolume; + noise.randomBit = 1; + if(noise.isEnabled && noise.lengthCounter>0){ + noise.sampleValue = noise.masterVolume; }else{ - this.noise.sampleValue = 0; + noise.sampleValue = 0; } } - this.noise.progTimerCount += this.noise.progTimerMax; + noise.progTimerCount += noise.progTimerMax; } - this.noise.accValue += this.noise.sampleValue; - this.noise.accCount++; + noise.accValue += noise.sampleValue; + noise.accCount++; } } // Frame IRQ handling: - if(this.frameIrqEnabled && this.frameIrqActive){ + if (this.frameIrqEnabled && this.frameIrqActive){ this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL); } // Clock frame counter at double CPU speed: - this.masterFrameCounter+=(nCycles<<1); - if(this.masterFrameCounter>=this.frameTime){ - + this.masterFrameCounter += (nCycles<<1); + if (this.masterFrameCounter >= this.frameTime) { // 240Hz tick: this.masterFrameCounter -= this.frameTime; this.frameCounterTick(); - - } - // Accumulate sample value: this.accSample(nCycles); - // Clock sample timer: this.sampleTimer += nCycles<<10; if(this.sampleTimer>=this.sampleTimerMax){ - // Sample channels: this.sample(); this.sampleTimer -= this.sampleTimerMax; - } - } PAPU.prototype.accSample = function(cycles){ + var triangle = this.triangle; // Special treatment for triangle channel - need to interpolate. - if(this.triangle.sampleCondition){ + if(triangle.sampleCondition){ - this.triValue = parseInt((this.triangle.progTimerCount<<4) / (this.triangle.progTimerMax+1)); - if(this.triValue>16) this.triValue = 16; - if(this.triangle.triangleCounter >= 16){ - this.triValue = 16-this.triValue; + var triValue = parseInt((triangle.progTimerCount<<4) / (triangle.progTimerMax+1)); + if(triValue>16) triValue = 16; + if(triangle.triangleCounter >= 16){ + triValue = 16-triValue; } // Add non-interpolated sample value: - this.triValue += this.triangle.sampleValue; + triValue += triangle.sampleValue; } @@ -1154,7 +466,7 @@ PAPU.prototype.accSample = function(cycles){ // Now sample normally: if(cycles == 2){ - this.smpTriangle += this.triValue << 1; + this.smpTriangle += triValue << 1; this.smpDmc += this.dmc.sample << 1; this.smpSquare1 += this.square1.sampleValue << 1; this.smpSquare2 += this.square2.sampleValue << 1; @@ -1162,7 +474,7 @@ PAPU.prototype.accSample = function(cycles){ }else if(cycles == 4){ - this.smpTriangle += this.triValue << 2; + this.smpTriangle += triValue << 2; this.smpDmc += this.dmc.sample << 2; this.smpSquare1 += this.square1.sampleValue << 2; this.smpSquare2 += this.square2.sampleValue << 2; @@ -1170,7 +482,7 @@ PAPU.prototype.accSample = function(cycles){ }else{ - this.smpTriangle += cycles * this.triValue; + this.smpTriangle += cycles * triValue; this.smpDmc += cycles * this.dmc.sample; this.smpSquare1 += cycles * this.square1.sampleValue; this.smpSquare2 += cycles * this.square2.sampleValue; @@ -1284,7 +596,7 @@ PAPU.prototype.sample = function(){ sampleValueR = this.smpAccumR; // Write: - if(this.bufferIndex+4 < this.sampleBuffer.length){ + if(this.bufferIndex+2 < this.sampleBuffer.length){ if (sampleValueL > this.maxSample) this.maxSample = sampleValueL; if (sampleValueL < this.minSample) this.minSample = sampleValueL; this.sampleBuffer[this.bufferIndex++] = (sampleValueL ); @@ -1301,12 +613,15 @@ PAPU.prototype.sample = function(){ PAPU.prototype.readBuffer = function() { //console.debug(this.bufferIndex); - if (this.bufferIndex+4 >= this.sampleBuffer.length) { + if (this.bufferIndex >= 2048) { var b = this.sampleBuffer; - this.sampleBuffer = new Array(this.bufferSize*4); + this.sampleBuffer = new Array(this.bufferSize*2); this.bufferIndex = 0; return b } + else { + console.debug("Insufficient buffer: "+this.bufferIndex); + } } PAPU.prototype.getLengthMax = function(value){ @@ -43,7 +43,6 @@ function PPU(nes) { this.vramMirrorTable = null; // Mirroring Lookup Table. - this.i = null; // SPR-RAM I/O: this.sramAddress = null; // 8-bit only. @@ -89,13 +88,12 @@ function PPU(nes) { // Name table data: this.ntable1 = new Array(4); this.nameTable = null; - this.currentMirroring=-1; + this.currentMirroring = -1; // Palette data: this.sprPalette = new Array(16); this.imgPalette = new Array(16); - // Misc: this.scanlineAlreadyRendered = null; this.requestEndFrame = null; @@ -104,14 +102,6 @@ function PPU(nes) { this.tmp = null; this.dummyCycleToggle = null; - // Vars used when updating regs/address: - this.address = null; - this.b1 = null; - this.b2 = null; - - - - // Variables used when rendering: this.attrib = new Array(32); this.buffer = new Array(256*240); @@ -120,38 +110,15 @@ function PPU(nes) { this.pixrendered = new Array(256*240); this.spr0dummybuffer = new Array(256*240); this.dummyPixPriTable = new Array(256*240); - this.oldFrame = new Array(256*240); - this.tpix = null; - this.scanlineChanged = Array(240); - this.requestRenderAll=false; this.validTileData = null; - this.att = null; this.scantile = new Array(32); - this.t = null; - - - // These are temporary variables used in rendering and sound procedures. // Their states outside of those procedures can be ignored. + // TODO: the use of this is a bit weird, investigate this.curNt = null; - this.destIndex = null; - this.x = null; - this.y = null; - this.sx = null; - this.si = null; - this.ei = null; - this.tile = null; - this.col = null; - this.baseTile = null; - this.tscanoffset = null; - this.srcy1 = null; - this.srcy2 = null; - this.bufferSize = null; - this.available = null; - this.scale = null; this.init = function() { // Get the memory: @@ -196,10 +163,6 @@ function PPU(nes) { this.lastRenderedScanline = -1; this.curX = 0; - // Initialize old frame buffer: - for(var i=0;i<this.oldFrame.length;i++){ - this.oldFrame[i]=-1; - } } @@ -813,59 +776,59 @@ function PPU(nes) { // Updates the scroll registers from a new VRAM address. this.regsFromAddress = function(){ - this.address = (this.vramTmpAddress>>8)&0xFF; - this.regFV = (this.address>>4)&7; - this.regV = (this.address>>3)&1; - this.regH = (this.address>>2)&1; - this.regVT = (this.regVT&7) | ((this.address&3)<<3); + var address = (this.vramTmpAddress>>8)&0xFF; + this.regFV = (address>>4)&7; + this.regV = (address>>3)&1; + this.regH = (address>>2)&1; + this.regVT = (this.regVT&7) | ((address&3)<<3); - this.address = this.vramTmpAddress&0xFF; - this.regVT = (this.regVT&24) | ((this.address>>5)&7); - this.regHT = this.address&31; + address = this.vramTmpAddress&0xFF; + this.regVT = (this.regVT&24) | ((address>>5)&7); + this.regHT = address&31; } // Updates the scroll registers from a new VRAM address. this.cntsFromAddress = function(){ - this.address = (this.vramAddress>>8)&0xFF; - this.cntFV = (this.address>>4)&3; - this.cntV = (this.address>>3)&1; - this.cntH = (this.address>>2)&1; - this.cntVT = (this.cntVT&7) | ((this.address&3)<<3); + var address = (this.vramAddress>>8)&0xFF; + this.cntFV = (address>>4)&3; + this.cntV = (address>>3)&1; + this.cntH = (address>>2)&1; + this.cntVT = (this.cntVT&7) | ((address&3)<<3); - this.address = this.vramAddress&0xFF; - this.cntVT = (this.cntVT&24) | ((this.address>>5)&7); - this.cntHT = this.address&31; + address = this.vramAddress&0xFF; + this.cntVT = (this.cntVT&24) | ((address>>5)&7); + this.cntHT = address&31; } this.regsToAddress = function(){ - this.b1 = (this.regFV&7)<<4; - this.b1 |= (this.regV&1)<<3; - this.b1 |= (this.regH&1)<<2; - this.b1 |= (this.regVT>>3)&3; + var b1 = (this.regFV&7)<<4; + b1 |= (this.regV&1)<<3; + b1 |= (this.regH&1)<<2; + b1 |= (this.regVT>>3)&3; - this.b2 = (this.regVT&7)<<5; - this.b2 |= this.regHT&31; + var b2 = (this.regVT&7)<<5; + b2 |= this.regHT&31; - this.vramTmpAddress = ((this.b1<<8) | this.b2)&0x7FFF; + this.vramTmpAddress = ((b1<<8) | b2)&0x7FFF; } this.cntsToAddress = function(){ - this.b1 = (this.cntFV&7)<<4; - this.b1 |= (this.cntV&1)<<3; - this.b1 |= (this.cntH&1)<<2; - this.b1 |= (this.cntVT>>3)&3; + var b1 = (this.cntFV&7)<<4; + b1 |= (this.cntV&1)<<3; + b1 |= (this.cntH&1)<<2; + b1 |= (this.cntVT>>3)&3; - this.b2 = (this.cntVT&7)<<5; - this.b2 |= this.cntHT&31; + var b2 = (this.cntVT&7)<<5; + b2 |= this.cntHT&31; - this.vramAddress = ((this.b1<<8) | this.b2)&0x7FFF; + this.vramAddress = ((b1<<8) | b2)&0x7FFF; } this.incTileCounter = function(count){ - for(this.i=count;this.i!=0;this.i--){ + for(var i=count; i!=0; i--){ this.cntHT++; if(this.cntHT==32){ this.cntHT=0; @@ -967,7 +930,6 @@ function PPU(nes) { var pixrendered = this.pixrendered; for(var destIndex=si;destIndex<ei;destIndex++){ if(pixrendered[destIndex]>0xFF){ - //console.log("Writing "+this.imgPalette[this.col+this.att].toString(16)+" to buffer at "+this.destIndex.toString(16)); buffer[destIndex] = bgbuffer[destIndex]; } } @@ -977,28 +939,6 @@ function PPU(nes) { this.renderSpritesPartially(startScan,scanCount,false); } - /*BufferView screen = nes.getGui().getScreenView(); - if(screen.scalingEnabled() && !screen.useHWScaling() && !requestRenderAll){ - - // Check which scanlines have changed, to try to - // speed up scaling: - int j,jmax; - if(startScan+scanCount>240)scanCount=240-startScan; - for(int i=startScan;i<startScan+scanCount;i++){ - scanlineChanged[i]=false; - si = i<<8; - jmax = si+256; - for(j=si;j<jmax;j++){ - if(buffer[j]!=oldFrame[j]){ - scanlineChanged[i]=true; - break; - } - oldFrame[j]=buffer[j]; - } - System.arraycopy(buffer,j,oldFrame,j,jmax-j); - } - - }*/ this.validTileData = false; @@ -1067,7 +1007,6 @@ function PPU(nes) { for(;sx<8;sx++){ col = tpix[tscanoffset+sx]; if(col != 0){ - //console.log("Writing "+this.imgPalette[this.col+this.att].toString(16)+" to buffer at "+this.destIndex.toString(16)); var pix = imgPalette[col+att]; targetBuffer[destIndex] = pix; pixrendered[destIndex] |= 256; @@ -1132,7 +1071,7 @@ function PPU(nes) { this.srcy2 = 8; if(this.sprY[i]<startscan){ - srcy1 = startscan - this.sprY[i]-1; + this.srcy1 = startscan - this.sprY[i]-1; } if(this.sprY[i]+8 > startscan+scancount){ @@ -1151,21 +1090,21 @@ function PPU(nes) { top = this.sprTile[i]-1+256; } - this.srcy1 = 0; - this.srcy2 = 8; + var srcy1 = 0; + var srcy2 = 8; if(this.sprY[i]<startscan){ - this.srcy1 = startscan - this.sprY[i]-1; + srcy1 = startscan - this.sprY[i]-1; } if(this.sprY[i]+8 > startscan+scancount){ - this.srcy2 = startscan+scancount-this.sprY[i]; + srcy2 = startscan+scancount-this.sprY[i]; } this.ptTile[top+(this.vertFlip[i]?1:0)].render(0, - this.srcy1, + srcy1, 8, - this.srcy2, + srcy2, this.sprX[i], this.sprY[i]+1, this.sprCol[i], @@ -1176,22 +1115,22 @@ function PPU(nes) { this.pixrendered ); - this.srcy1 = 0; - this.srcy2 = 8; + var srcy1 = 0; + var srcy2 = 8; if(this.sprY[i]+8<startscan){ - this.srcy1 = startscan - (this.sprY[i]+8+1); + srcy1 = startscan - (this.sprY[i]+8+1); } if(this.sprY[i]+16 > startscan+scancount){ - this.srcy2 = startscan+scancount-(this.sprY[i]+8); + srcy2 = startscan+scancount-(this.sprY[i]+8); } this.ptTile[top+(this.vertFlip[i]?0:1)].render( 0, - this.srcy1, + srcy1, 8, - this.srcy2, + srcy2, this.sprX[i], this.sprY[i]+1+8, this.sprCol[i], @@ -1443,16 +1382,6 @@ function PPU(nes) { leftOver-8, this.ppuMem[address-8], value); } } - - this.invalidateFrameCache = function(){ - - // Clear the no-update scanline buffer: - for(var i=0;i<240;i++) this.scanlineChanged[i]=true; - - for(var i=0;i<this.oldFrame.length;i++) this.oldFrame[i]=-1; - this.requestRenderAll = true; - - } // Updates the internal name table buffers // with this new byte. @@ -1533,8 +1462,6 @@ function PPU(nes) { this.validTileData = false; this.nmiCounter = 0; this.tmp = 0; - this.att = 0; - this.i = 0; // Control Flags Register 1: this.f_nmiOnVblank = 0; // NMI on VBlank. 0=disable, 1=enable @@ -1566,10 +1493,6 @@ function PPU(nes) { this.regFH = 0; this.regS = 0; - for (var i=0; i<this.scanlineChanged.length;i++) - this.scanlineChanged[i] = true; - for (var i=0; i<this.oldFrame.length;i++) - this.oldFrame[i] = -1; // Initialize stuff: this.init(); |