JAVASCRIPT   230

nes.js

Guest on 22nd August 2021 06:43:38 PM

  1. //
  2. //  Nintendo Entertainment System
  3. //_________________________________//
  4.  
  5. //Todo List
  6.  
  7.         //Optimizations:
  8.                 //Clean up the ppu.
  9.                 //Clean up the apu?
  10.  
  11.         //Move to typed arrays eventually, very browser specific though.
  12.  
  13.         //Abstract 64 sprite objects out of the currently used arrays?
  14.                 //Might make it easier to move more towards native canvas rendering.
  15.  
  16.         //Move more towards using native canvas rendering methods, less javascript.
  17.                 //Move from rendering to the buffer to rendering directly onto the canvas.
  18.                 //Instead of redrawing the sprites pixel by pixel, keep them in 64 buffers and write the buffers to the canvas.
  19.                         //What to do about mirroring?
  20.  
  21.         //More comments, remove all instances of "//???".
  22.  
  23.         //Fix saving the battery ram.
  24.                 //Look into Ben's method.
  25.  
  26.         //Fix Mapper 1
  27.                 //Null tiles occur on some games, Final Fantasy, Kid Icaurus
  28.  
  29.         //Fix errors showing from the test rom?
  30.  
  31.         //Fix the Nintendo Zapper implementation?
  32.                 //Used in place of the controller.
  33.                 //It sets a certain number when the color white is clicked on.
  34.  
  35.         //Sound:
  36.                 //Fix skipping/lagging?
  37.                 //Research the error with the flash object not having the method "write".
  38.                         //Just not loaded in time?
  39.                 //Upgrade to Ben's newer dynamic audio wrapper?
  40.  
  41. //Notes
  42.  
  43.         //Status flags:
  44.                 //Uninitialized.
  45.                         //Error initializing emulator, division with id "jsnes" not found on the web page.
  46.                         //Error initializing emulator, your browser does not adequately support the canvas element needed for emulation.
  47.                 //Reset, ready to load a rom.
  48.                         //Error loading rom(src), error retrieving rom file.
  49.                         //Error loading rom(src), rom file does not appear to be a valid NES rom.
  50.                         //Error loading rom(src), rom uses a mapper currently unsupported by jsnes.
  51.                 //Running at x FPS.
  52.                 //Stopped.
  53.  
  54.         //PPU Mirroring Types
  55.                 //VERTICAL_MIRRORING:0,
  56.                 //HORIZONTAL_MIRRORING:1,
  57.                 //FOURSCREEN_MIRRORING:2,
  58.                 //SINGLESCREEN_MIRRORING:3,
  59.                 //SINGLESCREEN_MIRRORING2:4,
  60.                 //SINGLESCREEN_MIRRORING3:5,
  61.                 //SINGLESCREEN_MIRRORING4:6,
  62.                 //CHRROM_MIRRORING:7,
  63.  
  64.         //Common names for the memory mappers.
  65.         //mapperNames = ["Direct Access","Nintendo MMC1","UNROM","CNROM","Nintendo MMC3","Nintendo MMC5","FFE F4xxx","AOROM","FFE F3xxx","Nintendo MMC2","Nintendo MMC4","Color Dreams Chip","FFE F6xxx","Unknown Mapper","Unknown Mapper","100-in-1 switch","Bandai chip","FFE F8xxx","Jaleco SS8806 chip","Namcot 106 chip","Famicom Disk System","Konami VRC4a","Konami VRC2a","Konami VRC2a","Konami VRC6","Konami VRC4b","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Irem G-101 chip","Taito TC0190/TC0350","32kB ROM switch","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Tengen RAMBO-1 chip","Irem H-3001 chip","GNROM switch","SunSoft3 chip","SunSoft4 chip","SunSoft5 FME-7 chip","Unknown Mapper","Camerica chip","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Irem 74HC161/32-based","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Unknown Mapper","Pirate HK-SF3 chip"];
  66.  
  67. /**
  68.  * @namespace The global nes object.
  69.  */
  70.  
  71. nes = {
  72.  
  73. //Properties
  74.  
  75.         /**
  76.          * A flag indicating whether the nes is running or not.
  77.          * @type Boolean
  78.          */
  79.  
  80.         running:false,
  81.  
  82.         /**
  83.          * The desired framerate for the nes to run at.
  84.          * @type Number
  85.          * @default 60
  86.          */
  87.  
  88.         frameRate:60,
  89.  
  90.         /**
  91.          * A placeholder for the interval to run the nes at.
  92.          * @type Interval
  93.          */
  94.  
  95.         frameInterval:null,
  96.  
  97.         /**
  98.          * The actual framerate that the nes is running at.
  99.          * @type Number
  100.          */
  101.  
  102.         fps:0,
  103.  
  104.         /**
  105.          * The current status of the nes.
  106.          * @type String
  107.          * @default "Uninitialized."
  108.          */
  109.  
  110.         status:'Uninitialized.',
  111.  
  112.         /**
  113.          * A placeholder for the memory mapper needed by the loaded rom file.
  114.          * @type Object
  115.          */
  116.  
  117.         mmc:null,
  118.  
  119.         /**
  120.          * The rom data.
  121.          * @type Object
  122.          */
  123.  
  124.         rom:null,
  125.  
  126.         /**
  127.          * The path to the dynamic audio flash object(lib/dynamicaudio.swf).
  128.          * @type String
  129.          */
  130.  
  131.         dynamicAudioPath:'',
  132.  
  133. //Methods
  134.  
  135.         /**
  136.          * Returns whether or not the browser supports the canvas element.
  137.          * @returns {Boolean}
  138.          */
  139.  
  140.         doesBrowserSupportCanvas:function nes_doesBrowserSupportCanvas(){
  141.                 //Create the screen.
  142.                 var canvas = document.createElement('canvas');
  143.                 //Check the canvas.
  144.                 if(typeof canvas.getContext === 'function'){
  145.                         //Get the canvas context.
  146.                         var context = canvas.getContext('2d');
  147.                         //Check the context.
  148.                         if(typeof context === 'object'){
  149.                                 //Return true.
  150.                                 return true;
  151.                         }
  152.                 }
  153.                 //Return false.
  154.                 return false;
  155.         },
  156.  
  157.         /**
  158.          * Initiates the nes, returning whether it could be initiated or not.
  159.          * @returns {Boolean}
  160.          */
  161.  
  162.         init:function nes_init(dynamicAudioPath){
  163.                 //Get the jsnes screen division.
  164.                 var screenDiv = document.getElementById('jsnes');
  165.                 //Check the division.
  166.                 if(screenDiv !== null){
  167.                         //Check if canvas is supported.
  168.                         if(this.doesBrowserSupportCanvas()){
  169.                                 //Initiate the screen.
  170.                                 this.screen.init(screenDiv);
  171.                                 //Set the dynamic audio flash object path.
  172.                                 this.dynamicAudioPath = dynamicAudioPath;
  173.                                 //Initiate the controllers.
  174.                                 this.controllers.init();
  175.                                 //Reset the system.
  176.                                 this.reset();
  177.                                 return true;
  178.                         }
  179.                         //Browser does not support canvas, set the status.
  180.                         this.status = 'Error initializing emulator, your browser does not adequately support the canvas element needed for emulation.';
  181.                         return false;
  182.                 }
  183.                 //Required division not found on web page, set the status.
  184.                 this.status = 'Error initializing emulator, division with id "jsnes" not found on the web page.';
  185.                 return false;
  186.         },
  187.  
  188.         /**
  189.          * Resets the nes, clearing the current rom.
  190.          */
  191.  
  192.         reset:function nes_reset(){
  193.                 //Stop the nes if running.
  194.                 this.stop();
  195.                 //Reset the cpu.
  196.                 this.cpu.reset();
  197.                 //Reset the ppu.
  198.                 this.ppu.reset();
  199.                 //Reset the apu.
  200.                 this.apu.reset();
  201.                 //Reset(remove) the mmc.
  202.                 this.mmc = null;
  203.                 //Reset(remove) the rom.
  204.                 this.rom = null;
  205.                 //Set the status.
  206.                 this.status = 'Reset, ready to load a rom.';
  207.         },
  208.  
  209.         /**
  210.          * Starts the nes if a valid rom is loaded.
  211.          */
  212.  
  213.         start:function nes_start(){
  214.                 //Check if a valid rom is loaded.
  215.                 if(this.hasRom()){
  216.                         //Set the running flag.
  217.                         this.running = true;
  218.                         //Start the frame interval.
  219.                         this.frameInterval = setInterval(function frameInterval(){nes.frame();},1000/nes.frameRate);
  220.                 }
  221.         },
  222.  
  223.         /**
  224.          * Stops the nes if running.
  225.          */
  226.  
  227.         stop:function nes_stop(){
  228.                 //Set the running flag.
  229.                 this.running = false;
  230.                 //Set the status.
  231.                 this.status = 'Stopped.';
  232.                 //Clear the frame interval.
  233.                 clearInterval(this.frameInterval);
  234.         },
  235.  
  236.         /**
  237.          * Stops then starts the nes.
  238.          */
  239.  
  240.         restart:function nes_restart(){
  241.                 //Stop the nes.
  242.                 this.stop();
  243.                 //Save the rom source.
  244.                 var romSource = this.rom.source;
  245.                 //Reset the nes.
  246.                 this.reset();
  247.                 //Load the rom.
  248.                 this.loadRom(romSource);
  249.                 //Start the nes.
  250.                 this.start();
  251.         },
  252.  
  253.         /**
  254.          * Emulates one frame for the nes.
  255.          */
  256.  
  257.         frame:function nes_frame(){
  258.                 //Check if running.
  259.                 if(this.running){
  260.                         //Clear the ppu buffer.
  261.                         this.ppu.startFrame();
  262.                         //Reset the cycle count to 0.
  263.                         var cycles = 0;
  264.                         //Set the inFrameLoop flag.
  265.                         var inFrameLoop = true;
  266.                         //Start the frame loop.
  267.                         while(inFrameLoop){
  268.                                 //Check if no cycles are to be halted.
  269.                                 if(this.cpu.cyclesToHalt === 0){
  270.                                         //Execute a CPU instruction.
  271.                                         cycles = this.cpu.emulate();
  272.                                         //Set the cycles to the apu.
  273.                                         this.apu.clockFrameCounter(cycles);
  274.                                         //???
  275.                                         cycles *= 3;
  276.                                 }
  277.                                 //Else check if the number of cycles to halt is less than or equal to 8.
  278.                                 else if(this.cpu.cyclesToHalt < 9){
  279.                                         //???
  280.                                         cycles = this.cpu.cyclesToHalt*3;
  281.                                         //Set the cycles to halt to the apu.
  282.                                         this.apu.clockFrameCounter(this.cpu.cyclesToHalt);
  283.                                         //Set the cycles to halt to 0.
  284.                                         this.cpu.cyclesToHalt = 0;
  285.                                 }
  286.                                 //Else the number of cycles to halt is greater than 8.
  287.                                 else{
  288.                                         //Set the cycles to 24.
  289.                                         cycles = 24;
  290.                                         //Set the cycles to halt to the apu.
  291.                                         this.apu.clockFrameCounter(8);
  292.                                         //Remove 8 from the cycles to halt counter.
  293.                                         this.cpu.cyclesToHalt -= 8;
  294.                                 }
  295.                                 //Loop for every cycle executed by the cpu.
  296.                                 for(;cycles>0;cycles--){
  297.                                         //Check for a sprite 0 hit.
  298.                                         if(this.ppu.curX === this.ppu.spr0HitX && this.ppu.f_spVisibility === 1 && this.ppu.scanline-21 === this.ppu.spr0HitY){
  299.                                                 //Set the sprite 0 hit flag.
  300.                                                 nes.cpu.mem[0x2002] |= 64;
  301.                                         }
  302.                                         //Check if the ppu is done rendering.
  303.                                         if(this.ppu.requestEndFrame){
  304.                                                 //Decrement the non-maskable interrupt counter.
  305.                                                 this.ppu.nmiCounter--;
  306.                                                 //???
  307.                                                 if(this.ppu.nmiCounter === 0){
  308.                                                         //Reset the end of frame flag.
  309.                                                         this.ppu.requestEndFrame = false;
  310.                                                         //Draw the frame and start the vBlank period.
  311.                                                         this.ppu.endFrame();
  312.                                                         //Break the frame loop.
  313.                                                         inFrameLoop = false;
  314.                                                 }
  315.                                         }
  316.                                         //Increment the horizontal pixel counter for every cycle.
  317.                                         this.ppu.curX++;
  318.                                         //Check if 341 pixels have been accounted for, even though the scanlines are only 256 pixels wide.
  319.                                         if(this.ppu.curX === 341){
  320.                                                 //Reset the horizontal pixel counter.
  321.                                                 this.ppu.curX = 0;
  322.                                                 //Have the ppu end the scanline.
  323.                                                 this.ppu.endScanline();
  324.                                         }
  325.                                 }
  326.                         }
  327.                         //Calculate the fps.
  328.                         var now = new Date().getTime();
  329.                         var frameDifference = this.lastFrameTime-now;
  330.                         this.fps = -1000/frameDifference;
  331.                         this.lastFrameTime = now;
  332.                         //Set the status.
  333.                         this.status = 'Running at '+this.fps.toFixed(2)+' FPS.';
  334.                 }
  335.         },
  336.  
  337.         /**
  338.          * Returns whether a valid rom is currently loaded.
  339.          * @returns {Boolean}
  340.          */
  341.  
  342.         hasRom:function nes_hasRom(){
  343.                 //Return whether the current rom and mmc are not null.
  344.                 return this.rom !== null && this.mmc !== null;
  345.         },
  346.  
  347.         /**
  348.          * Loads the specified rom file, returns true if succesfully loaded.
  349.          * @returns {Boolean}
  350.          * @param {String} src The source of the rom file.
  351.          */
  352.  
  353.         loadRom:function nes_loadRom(src){
  354.                 //Stop the emulator if running.
  355.                 this.stop();
  356.                 //Create a new HTTP request to get the rom file.
  357.                 var request = new XMLHttpRequest();
  358.                 request.open('GET',src,false);
  359.                 //Set the mime type to binary.
  360.                 request.overrideMimeType('text/plain; charset=x-user-defined');
  361.                 //Try to send the request.
  362.                 try{
  363.                         request.send(null);
  364.                 }
  365.                 catch(error){
  366.                         //Error retrieving file, set the status.
  367.                         this.status = 'Error loading rom('+src+'), error retrieving rom file.';
  368.                         //Return false.
  369.                         return false;
  370.                 }
  371.                 //Cache the rom data.
  372.                 var data = request.responseText;
  373.                 //Check that the rom file is valid.
  374.                 if(data.indexOf('NES\x1a') === 0){
  375.                         //Create a blank object for the rom data.
  376.                         var rom = {};
  377.                         //Create an array holding information about the rom.
  378.                         rom.header = new Array(16);
  379.                         //Load the header.
  380.                         for(var i=0;i<16;i++){
  381.                                 rom.header[i] = data.charCodeAt(i)&0xFF;
  382.                         }
  383.                         //Get the rom count.
  384.                         rom.romCount = rom.header[4];
  385.                         //Get the number of 4kb vrom banks, not 8kb.
  386.                         rom.vromCount = rom.header[5]*2;
  387.                         //Get the mirroring type.
  388.                         rom.mirroring = (rom.header[6]&1);
  389.                         //Check whether the rom has battery ram.
  390.                         rom.hasBatteryRam = (rom.header[6]&2) !== 0;
  391.                         //Get the trainer flag.
  392.                         rom.trainer = (rom.header[6]&4) !== 0;
  393.                         //Get the four screen flag.
  394.                         rom.fourScreen = (rom.header[6]&8) !== 0;
  395.                         //Get the mapper needed.
  396.                         rom.mapperType = (rom.header[6]>>4)|(rom.header[7]&0xF0);
  397.                         //Check whether any byte 8-15 is not a zero.
  398.                         for(var i=8;i<16;i++){
  399.                                 if(rom.header[i] !== 0){
  400.                                         //If so ignore byte 7.
  401.                                         rom.mapperType &= 0xF;
  402.                                 }
  403.                         }
  404.                         //Load PRG-ROM banks.
  405.                         rom.rom = new Array(rom.romCount);
  406.                         var offset = 16;
  407.                         for(var i=0;i<rom.romCount;i++){
  408.                                 rom.rom[i] = new Array(16384);
  409.                                 for(var j=0;j<16384;j++){
  410.                                         if(offset+j >= data.length){
  411.                                                 break;
  412.                                         }
  413.                                         rom.rom[i][j] = data.charCodeAt(offset+j)&0xFF;
  414.                                 }
  415.                                 offset += 16384;
  416.                         }
  417.                         //Reset the vrom and vrom tiles.
  418.                         rom.vrom = new Array(rom.vromCount);
  419.                         rom.vromTile = new Array(rom.vromCount);
  420.                         //Loop through the vrom banks.
  421.                         for(var i=0;i<rom.vromCount;i++){
  422.                                 //Create the tiles.
  423.                                 rom.vromTile[i] = new Array(256);
  424.                                 for(var j=0;j<256;j++){
  425.                                         rom.vromTile[i][j] = new Tile();
  426.                                 }
  427.                                 //Load the bank.
  428.                                 rom.vrom[i] = new Array(4096);
  429.                                 for(var j=0;j<4096;j++){
  430.                                         if(offset+j >= data.length){
  431.                                                 break;
  432.                                         }
  433.                                         rom.vrom[i][j] = data.charCodeAt(offset+j)&0xFF;
  434.                                 }
  435.                                 offset += 4096;
  436.                                 //Convert the bank to the tile.
  437.                                 for(var j=0;j<4096;j++){
  438.                                         if((j%16)<8){
  439.                                                 rom.vromTile[i][j>>4].setScanline(j%16,rom.vrom[i][j],rom.vrom[i][j+8]);
  440.                                         }
  441.                                         else{
  442.                                                 rom.vromTile[i][j>>4].setScanline((j%16)-8,rom.vrom[i][j-8],rom.vrom[i][j]);
  443.                                         }
  444.                                 }
  445.                         }
  446.                         //Reset the nes.
  447.                         this.reset();
  448.                         //Check the mapper needed.
  449.                         if(typeof this.mappers['mmc'+rom.mapperType] === 'object'){
  450.                                 //Save the rom.
  451.                                 this.rom = rom;
  452.                                 //Save the rom's source.
  453.                                 rom.source = src;
  454.                                 //Save the rom's filename, not including the extension.
  455.                                 rom.name = src.substring(src.lastIndexOf('/')+1);
  456.                                 rom.name = rom.name.substring(0,rom.name.lastIndexOf('.'));
  457.                                 //Get the mapper.
  458.                                 this.mmc = this.mappers['mmc'+rom.mapperType];
  459.                                 //Load the rom data.
  460.                                 this.mmc.loadROM();
  461.                                 //Set the ppu mirroring from the rom.
  462.                                 if(rom.fourScreen){
  463.                                         //Set fourscreen mirroring.
  464.                                         this.ppu.setMirroring(2);
  465.                                 }
  466.                                 else if(rom.mirroring === 0){
  467.                                         //Set horizontal mirroring.
  468.                                         this.ppu.setMirroring(1);
  469.                                 }
  470.                                 else{
  471.                                         //Set vertical mirroring.
  472.                                         this.ppu.setMirroring(0);
  473.                                 }
  474.                                 //Start the emulator.
  475.                                 this.start();
  476.                                 //Return true.
  477.                                 return true;
  478.                         }
  479.                         //Rom requires an unknown mapper.
  480.                         //Set the status.
  481.                         this.status = 'Error loading rom('+src+'), rom uses a mapper currently unsupported by jsnes.';
  482.                         //Return false.
  483.                         return false;
  484.                 }
  485.                 //Rom is not valid.
  486.                 //Set the status.
  487.                 this.status = 'Error loading rom('+src+'), rom file does not appear to be a valid NES rom.';
  488.                 //Return false.
  489.                 return false;
  490.         },
  491.  
  492.         /**
  493.          * Loads the save file for the current rom if it exists.
  494.          */
  495.  
  496.         loadBatteryRam:function nes_loadBatteryRam(){
  497.                 //Check if the rom has batteryRAM.
  498.                 if(nes.rom.hasBatteryRam){
  499.                         //Check if the save file exists.
  500.                         if(this.hasSave(this.rom.name)){
  501.                                 //Get the save data.
  502.                                 var data = this.getSave(this.rom.name);
  503.                                 //Check the length of the data is valid.
  504.                                 if(data.length === 8192){
  505.                                         //Loop through the characters in the data.
  506.                                         for(var i=0;i++;i<8192){
  507.                                                 //Set the corresponding address in memory as the character code minus 100.
  508.                                                 nes.cpu.mem[0x6000+i] = data.charCodeAt(i)-100;
  509.                                         }
  510.                                 }
  511.                         }
  512.                 }
  513.         },
  514.  
  515.         /**
  516.          * Saves the save file for the current rom.
  517.          */
  518.  
  519.         saveBatteryRam:function nes_saveBatteryRam(){
  520.                 //Check if the rom has battery ram.
  521.                 if(nes.rom.hasBatteryRam){
  522.                         //Create a string for storing the battery ram in the window.name property.
  523.                         var batteryRam = '';
  524.                         //Loop through the battery ram data.
  525.                         for(var i=0x6000;i<0x8000;i++){
  526.                                 //Add the battery ram data into the string by using characters to encode the numbers.
  527.                                 //The extra value is added to keep odd characters from being used, as well as the semicolon which would skewer the data.
  528.                                 batteryRam += String.fromCharCode(nes.cpu.mem[i]+100);
  529.                         }
  530.                         //Set the save data.
  531.                         this.setSave(this.rom.name,batteryRam);
  532.                 }
  533.         },
  534.  
  535.         /**
  536.          * Returns whether or not save data exists for the specified name.
  537.          * @type Boolean
  538.          * @param {String} name
  539.          */
  540.  
  541.         hasSave:function nes_hasSave(name){
  542.                 //Return whether the name is in the divided window.name data.
  543.                 return (window.name.split(';').indexOf(name) !== -1);
  544.         },
  545.  
  546.         /**
  547.          * Sets the sent data to the name in the browser's window.name property, overwriting the old save data if it exists.
  548.          * @param {String} name
  549.          * @param {String} value
  550.          */
  551.  
  552.         setSave:function nes_setSave(name,value){
  553.                 //Check if the name exists in the save data.
  554.                 if(this.hasSave(name)){
  555.                         //Get the save data.
  556.                         var data = window.name.split(';');
  557.                         //Remove the extra element added by split().
  558.                         data.pop();
  559.                         //Clear the window.name property.
  560.                         window.name = '';
  561.                         //Replace the old data associated with the name.
  562.                         data[data.indexOf(name)+1] = value;
  563.                         //Loop through the data elements and add them back into window.name property.
  564.                         for(var i=0;i<data.length;i++){
  565.                                 window.name += data[i]+';';
  566.                         }
  567.                 }
  568.                 else{
  569.                         //Else add the save data to the window.name property.
  570.                         window.name += name+';'+value+';';
  571.                 }
  572.         },
  573.  
  574.         /**
  575.          * Returns the save data associated with the specified name
  576.          * @returns {String}
  577.          * @param {String} name
  578.          */
  579.  
  580.         getSave:function nes_getSave(name){
  581.                 //Check if the name exists in the save data.
  582.                 if(this.hasSave(name)){
  583.                         //Get the save data.
  584.                         var data = window.name.split(';');
  585.                         //Return the element right after the name, the data.
  586.                         return data[data.indexOf(name)+1];
  587.                 }
  588.                 //Name not in the save data, return null.
  589.                 return null;
  590.         },
  591.  
  592.         /**
  593.          * Clears the save data associated with the specified name.
  594.          * @param {String} name
  595.          */
  596.  
  597.         clearSave:function nes_clearSave(name){
  598.                 //Get the save data.
  599.                 var data = window.name.split(';');
  600.                 //Clear the window.name property.
  601.                 window.name = '';
  602.                 //Check if the name exists in the save data.
  603.                 if(data.indexOf(name)){
  604.                         //Remove the name and following element from the array.
  605.                         data.splice(data.indexOf(name),2);
  606.                 }
  607.                 //Loop through the data elements and add them back into window.name property.
  608.                 for(var i=0;i++;i<data.length){
  609.                         window.name += data[i]+';';
  610.                 }
  611.         },
  612.  
  613.         /**
  614.          * Clears the browser's window.name property, deleting all the save data.
  615.          */
  616.  
  617.         clearSaves:function nes_clearSaves(){
  618.                 //Clear the window.name property.
  619.                 window.name = '';
  620.         },
  621.  
  622.         /**
  623.          * Copies a variable number of elements from one array into another.
  624.          * @param {Array} srcArray The array to copy the elements from.
  625.          * @param {Number} srcPos The position in the source array to start copying elements from.
  626.          * @param {Array} destArray The array to copy the elements into.
  627.          * @param {Number} destPos The position in the destination array to start copying elements into.
  628.          * @param {Number} length The number of elements to copy.
  629.          */
  630.  
  631.         copyArrayElements:function nes_copyArrayElements(srcArray,srcPos,destArray,destPos,length){
  632.                 //Loop through the length of elements to copy.
  633.                 for(var i=0;i<length;i++){
  634.                         //Copy the element.
  635.                         destArray[destPos+i] = srcArray[srcPos+i];
  636.                 }
  637.         },
  638.  
  639.         /**
  640.          * Returns a shallow copy of the specified object, used with mapper inheritance.
  641.          * @returns {Object}
  642.          * @param {Object} object
  643.          */
  644.  
  645.         copyObject:function nes_copyObject(object){
  646.                 //Create a new object.
  647.                 var obj = {};
  648.                 //Loop through the items in the object.
  649.                 for(var item in object){
  650.                         //Copy the item.
  651.                         obj[item] = object[item];
  652.                 }
  653.                 //Return the new object.
  654.                 return obj;
  655.         },
  656.  
  657.         /**
  658.          * Copies the members of the first object into the second one, used with mapper inheritance.
  659.          * @param {Object} destObject
  660.          * @param {Object} srcObject
  661.          */
  662.  
  663.         applyObject:function nes_applyObject(destObject,srcObject){
  664.                 //Loop through the items in the source object.
  665.                 for(var item in srcObject){
  666.                         //Set it into the destination object.
  667.                         destObject[item] = srcObject[item];
  668.                 }
  669.         },
  670.  
  671.         //
  672.         //  Screen
  673.         //__________//
  674.  
  675.         /**
  676.          * @namespace An abstraction of the HTML5 Canvas element to use as the screen.
  677.          */
  678.  
  679.         screen:{
  680.  
  681.         //Properties
  682.  
  683.                 /**
  684.                  * The canvas element.
  685.                  * @type Object
  686.                  */
  687.  
  688.                 canvas:null,
  689.  
  690.                 /**
  691.                  * The canvas' 2d context interface.
  692.                  * @type Object
  693.                  */
  694.  
  695.                 context:null,
  696.  
  697.                 /**
  698.                  * A copy of the canvas' image data for manipulation.
  699.                  * @type Object
  700.                  */
  701.  
  702.                 imageData:null,
  703.  
  704.                 /**
  705.                  * A copy of the canvas' pixel data for manipulation.
  706.                  * @type Array
  707.                  */
  708.  
  709.                 pixelData:null,
  710.  
  711.         //Methods
  712.  
  713.                 /**
  714.                  * Initiates the nes' screen, called by nes.init().
  715.                  * @param {Object} container The element to place the Nes's screen into.
  716.                  */
  717.  
  718.                 init:function nes_screen_init(container){
  719.                         //Create the screen.
  720.                         this.canvas = document.createElement('canvas');
  721.                         //Add the screen into the container.
  722.                         container.appendChild(this.canvas);
  723.                         //Get the 2d context.
  724.                         this.context = this.canvas.getContext('2d');
  725.                         //Set the width and height.
  726.                         this.canvas.width = 256;
  727.                         this.canvas.height = 240;
  728.                         //Set the border.
  729.                         this.canvas.style.border = '1px solid black';
  730.                 }
  731.  
  732.         },
  733.  
  734.         //
  735.         //  Controllers
  736.         //_______________//
  737.  
  738.         /**
  739.          * @namespace Holds interface's for the controllers.
  740.          */
  741.  
  742.         controllers:{
  743.  
  744.         //Properties
  745.  
  746.                 /**
  747.                  * The state(as if in memory) of the first controller.
  748.                  * @type Array
  749.                  */
  750.  
  751.                 state1:[64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
  752.  
  753.                 /**
  754.                  * The state(as if in memory) of the second controller.
  755.                  * @type Array
  756.                  */
  757.  
  758.                 state2:[64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
  759.  
  760.                 /**
  761.                  * The key bindings associated with the first controller's buttons.
  762.                  * @type Array
  763.                  */
  764.  
  765.                 keys1:[
  766.                         88,      //A             X
  767.                         90,      //B             Z
  768.                         17,      //Select       Right Ctrl
  769.                         13,      //Start         Enter
  770.                         38,      //Up           Up
  771.                         40,      //Down   Down
  772.                         37,      //Left   Left
  773.                         39,      //Right         Right
  774.                 ],
  775.  
  776.                 /**
  777.                  * The key bindings associated with the second controller's buttons.
  778.                  * @type Array
  779.                  */
  780.  
  781.                 keys2:[
  782.                         103,    //A              Numpad-7
  783.                         105,    //B              Numpad-9
  784.                         99,      //Select       Numpad-3
  785.                         97,      //Start         Numpad-1
  786.                         104,    //Up            Numpad-8
  787.                         98,      //Down   Numpad-2
  788.                         100,    //Left    Numpad-4
  789.                         102,    //Right  Numpad-6
  790.                 ],
  791.  
  792.         //Methods
  793.  
  794.                 /**
  795.                  * Defines the document event handlers, called by nes.init().
  796.                  */
  797.  
  798.                 init:function nes_controller(){
  799.  
  800.                         /**
  801.                          * @ignore
  802.                          */
  803.  
  804.                         //Define the key down event handler.
  805.                         document.onkeydown = function document_onkeydown(event){
  806.                                 //Loop through the key codes.
  807.                                 for(var i=0;i<8;i++){
  808.                                         //Check the controller 1 code.
  809.                                         if(event.keyCode === nes.controllers.keys1[i]){
  810.                                                 //Set the controller state at that key.
  811.                                                 nes.controllers.state1[i] = 65;
  812.                                                 //Return false to interrupt the default key behavior.
  813.                                                 return false;
  814.                                         }
  815.                                         //Else check the controller 2 code.
  816.                                         if(event.keyCode === nes.controllers.keys2[i]){
  817.                                                 //Set the controller state at that key.
  818.                                                 nes.controllers.state2[i] = 65;
  819.                                                 //Return false to interrupt the default key behavior.
  820.                                                 return false;
  821.                                         }
  822.                                 }
  823.                         }
  824.  
  825.                         /**
  826.                          * @ignore
  827.                          */
  828.  
  829.                         //Define the key up event handler.
  830.                         document.onkeyup = function document_onkeyup(event){
  831.                                 //Loop through the key codes.
  832.                                 for(var i=0;i<8;i++){
  833.                                         //Check the controller 1 code.
  834.                                         if(event.keyCode === nes.controllers.keys1[i]){
  835.                                                 //Set the controller state at that key.
  836.                                                 nes.controllers.state1[i] = 64;
  837.                                                 //Return false to interrupt the default key behavior.
  838.                                                 return false;
  839.                                         }
  840.                                         //Else check the controller 2 code.
  841.                                         if(event.keyCode === nes.controllers.keys2[i]){
  842.                                                 //Set the controller state at that key.
  843.                                                 nes.controllers.state2[i] = 64;
  844.                                                 //Return false to interrupt the default key behavior.
  845.                                                 return false;
  846.                                         }
  847.                                 }
  848.                         }
  849.  
  850.                 }
  851.  
  852.         }
  853.  
  854. };

Raw Paste


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