JAVASCRIPT   233

ppu.js

Guest on 22nd August 2021 06:44:22 PM

  1. //
  2. //  Picture Processing Unit
  3. //___________________________//
  4.  
  5. /**
  6.  * @namespace The picture processing unit for the nes.
  7.  */
  8.  
  9. nes.ppu = {
  10.  
  11. //Properties
  12.  
  13.         //Memory
  14.  
  15.         /**
  16.          * The video ram memory.
  17.          * @type Array
  18.          */
  19.  
  20.         vramMem:null,
  21.  
  22.         /**
  23.          * The sprite ram memory.
  24.          * @type Array
  25.          */
  26.  
  27.         spriteMem:null,
  28.  
  29.         //Input/Output
  30.  
  31.         /**
  32.          * A flag indicating whether this is the first or second vram write.
  33.          * @type Boolean
  34.          */
  35.  
  36.         firstWrite:null,
  37.  
  38.         /**
  39.          * The currently indexed video ram element.
  40.          * @type Number
  41.          */
  42.  
  43.         vramAddress:null,
  44.  
  45.         /**
  46.          * Unknown
  47.          * @type Number
  48.          */
  49.  
  50.         vramTmpAddress:null,
  51.  
  52.         /**
  53.          * Unknown
  54.          * @type Number
  55.          */
  56.  
  57.         vramBufferedReadValue:null,
  58.  
  59.         /**
  60.          * The currently indexed sprite memory element.
  61.          * @type Number
  62.          */
  63.  
  64.         sramAddress:null,
  65.  
  66.         //Control Register
  67.  
  68.         /**
  69.          * Non-Maskable Interrupt on vertical blank flag.
  70.          * 0 - Disabled
  71.          * 1 - Enabled
  72.          * @type Number
  73.          */
  74.  
  75.         f_nmiOnVblank:null,
  76.  
  77.         /**
  78.          * Sprite size flag.
  79.          * <br>
  80.          * 0 - 8x8
  81.          * <br>
  82.          * 1 - 8x16
  83.          * @type Number
  84.          */
  85.  
  86.         f_spriteSize:null,
  87.  
  88.         /**
  89.          * Background pattern table address.
  90.          * <br>
  91.          * 0 - 0x0000
  92.          * <br>
  93.          * 1 - 0x1000
  94.          * @type Number
  95.          */
  96.  
  97.         f_bgPatternTable:null,
  98.  
  99.         /**
  100.          * Sprite pattern table address.
  101.          * <br>
  102.          * 0 - 0x0000
  103.          * <br>
  104.          * 1 - 0x1000
  105.          * @type Number
  106.          */
  107.  
  108.         f_spPatternTable:null,
  109.  
  110.         /**
  111.          * PPU address increment flag.
  112.          * <br>
  113.          * 0 - 1
  114.          * <br>
  115.          * 1 - 32
  116.          * @type Number
  117.          */
  118.  
  119.         f_addrInc:null,
  120.  
  121.         /**
  122.          * Name table address flag.
  123.          * <br>
  124.          * 0 - 0x2000
  125.          * <br>
  126.          * 1 - 0x2400
  127.          * <br>
  128.          * 2 - 0x2800
  129.          * <br>
  130.          * 3 - 0x2C00
  131.          * @type Number
  132.          */
  133.  
  134.         f_nTblAddress:null,
  135.  
  136.         //Masking Register
  137.  
  138.         /**
  139.          * Color emphasis flag, also determines bg color in monochrome mode.
  140.          * <br>
  141.          * 0 - black
  142.          * <br>
  143.          * 1 - blue
  144.          * <br>
  145.          * 2 - green
  146.          * <br>
  147.          * 4 - red
  148.          * @type Number
  149.          */
  150.  
  151.         f_color:null,
  152.  
  153.         /**
  154.          * Sprite visibility flag.
  155.          * <br>
  156.          * 0 - Sprites are hidden
  157.          * <br>
  158.          * 1 - Sprites are visible
  159.          * @type Number
  160.          */
  161.  
  162.         f_spVisibility:null,
  163.  
  164.         /**
  165.          * Background visibility flag.
  166.          * <br>
  167.          * 0 - Background is hidden
  168.          * <br>
  169.          * 1 - Background is visible
  170.          * @type Number
  171.          */
  172.  
  173.         f_bgVisibility:null,
  174.  
  175.         /**
  176.          * Sprite clipping flag.
  177.          * <br>
  178.          * 0 - Sprites are clipped from left 8 pixels
  179.          * <br>
  180.          * 1 - No Clipping
  181.          * <br>
  182.          * Poorly implemented, clips background as well.
  183.          * @type Number
  184.          */
  185.  
  186.         f_spClipping:null,
  187.  
  188.         /**
  189.          * Background clipping flag.
  190.          * <br>
  191.          * 0 - Background is clipped from left 8 pixels
  192.          * <br>
  193.          * 1 - No Clipping
  194.          * <br>
  195.          * Poorly implemented, clips sprites as well.
  196.          * @type Number
  197.          */
  198.  
  199.         f_bgClipping:null,
  200.  
  201.         /**
  202.          * Display type flag.
  203.          * <br>
  204.          * 0 - Color
  205.          * <br>
  206.          * 1 - Monochrome
  207.          * @type Number
  208.          */
  209.  
  210.         f_dispType:null,
  211.  
  212.         //Counters
  213.  
  214.         /**
  215.          * Unknown counter.
  216.          * Somehow tied into screen scrolling and the control/masking register.
  217.          * @type Number
  218.          */
  219.  
  220.         cntFV:null,
  221.  
  222.         /**
  223.          * Unknown counter.
  224.          * Somehow tied into screen scrolling and the control/masking register.
  225.          * @type Number
  226.          */
  227.  
  228.         cntVT:null,
  229.  
  230.         /**
  231.          * Unknown counter.
  232.          * Somehow tied into screen scrolling and the control/masking register.
  233.          * @type Number
  234.          */
  235.  
  236.         cntHT:null,
  237.  
  238.         /**
  239.          * Unknown counter.
  240.          * Somehow tied into screen scrolling and the control/masking register.
  241.          * @type Number
  242.          */
  243.  
  244.         cntV:null,
  245.  
  246.         /**
  247.          * Unknown counter.
  248.          * Somehow tied into screen scrolling and the control/masking register.
  249.          * @type Number
  250.          */
  251.  
  252.         cntH:null,
  253.  
  254.         //Registers
  255.  
  256.         /**
  257.          * Unknown register.
  258.          * Somehow tied into screen scrolling and the control/masking register.
  259.          * @type Number
  260.          */
  261.  
  262.         regFV:null,
  263.  
  264.         /**
  265.          * Unknown register.
  266.          * Somehow tied into screen scrolling and the control/masking register.
  267.          * @type Number
  268.          */
  269.  
  270.         regV:null,
  271.  
  272.         /**
  273.          * Unknown register.
  274.          * Somehow tied into screen scrolling and the control/masking register.
  275.          * @type Number
  276.          */
  277.  
  278.         regH:null,
  279.  
  280.         /**
  281.          * Unknown register.
  282.          * Somehow tied into screen scrolling and the control/masking register.
  283.          * @type Number
  284.          */
  285.  
  286.         regVT:null,
  287.  
  288.         /**
  289.          * Unknown register.
  290.          * Somehow tied into screen scrolling and the control/masking register.
  291.          * @type Number
  292.          */
  293.  
  294.         regHT:null,
  295.  
  296.         /**
  297.          * Unknown register.
  298.          * Somehow tied into screen scrolling and the control/masking register.
  299.          * @type Number
  300.          */
  301.  
  302.         regFH:null,
  303.  
  304.         /**
  305.          * Unknown register.
  306.          * Somehow tied into screen scrolling and the control/masking register.
  307.          * @type Number
  308.          */
  309.  
  310.         regS:null,
  311.  
  312.         //Rendering Variables
  313.  
  314.         attrib:null,
  315.         buffer:null,
  316.         bgbuffer:null,
  317.         pixRendered:null,
  318.         scantile:null,
  319.  
  320.         //Misc Rendering Variables
  321.  
  322.         /**
  323.          * The scanline counter, goes from 0 to 261.
  324.          * <br>
  325.          * Scanlines 0-20 and 241-261 are not visible.
  326.          * <br>
  327.          * Scanline 19 is the "dummy" scanline.
  328.          * @type Number
  329.          */
  330.  
  331.         scanline:null,
  332.  
  333.         /**
  334.          * The last rendered scanline.
  335.          * @type Number
  336.          */
  337.  
  338.         lastRenderedScanline:null,
  339.  
  340.         /**
  341.          * Horizontal pixel counter used when rendering scanlines.
  342.          * <br>
  343.          * Goes from 0 to 341 before ending the current scanline, even though scanlines are only 256 pixels wide.
  344.          * @type Number
  345.          */
  346.  
  347.         curX:null,
  348.  
  349.         /**
  350.          * Indicates when the ppu has drawn the last scanline.
  351.          * @type Boolean
  352.          */
  353.  
  354.         requestEndFrame:null,
  355.  
  356.         /**
  357.          * Non-Maskable Interrupt counter.
  358.          * @type Number
  359.          */
  360.  
  361.         nmiCounter:null,
  362.  
  363.         /**
  364.          * Indicates when internally buffered tile data is valid.
  365.          * @type Boolean
  366.          */
  367.  
  368.         validTileData:null,
  369.  
  370.         //Sprite Data
  371.  
  372.         /**
  373.          * Sprite x values.
  374.          * @type Array
  375.          */
  376.  
  377.         sprX:null,
  378.  
  379.         /**
  380.          * Sprite y values.
  381.          * @type Array
  382.          */
  383.  
  384.         sprY:null,
  385.  
  386.         /**
  387.          * Sprite tile indexes, indexes the pattern table.
  388.          * @type Array
  389.          */
  390.  
  391.         sprTile:null,
  392.  
  393.         /**
  394.          * Upper two bites of sprite color.
  395.          * @type Array
  396.          */
  397.  
  398.         sprCol:null,
  399.  
  400.         /**
  401.          * Sprite vertically flipped flags.
  402.          * @type Array
  403.          */
  404.  
  405.         vertFlip:null,
  406.  
  407.         /**
  408.          * Sprite horizontally flipped flags.
  409.          * @type Array
  410.          */
  411.  
  412.         horiFlip:null,
  413.  
  414.         /**
  415.          * Sprite background priority.
  416.          * @type Array
  417.          */
  418.  
  419.         bgPriority:null,
  420.  
  421.         //Sprite 0 Hit Flags
  422.  
  423.         /**
  424.          * True or false depending on whether there has been a sprite 0 hit.
  425.          * @type Boolean
  426.          */
  427.  
  428.         hitSpr0:null,
  429.  
  430.         /**
  431.          * The x position of the sprite 0 hit.
  432.          * @type Number
  433.          */
  434.  
  435.         spr0HitX:null,
  436.  
  437.         /**
  438.          * The y position of the sprite 0 hit.
  439.          * @type Number
  440.          */
  441.  
  442.         spr0HitY:null,
  443.  
  444.         //Buffered Color Palettes
  445.  
  446.         /**
  447.          * Buffered background color palette.
  448.          * <br>
  449.          * Holds 16 color values that come from color indexes at vram 0x3f00 to 0x3f10.
  450.          * <br>
  451.          * The first entry is used as the background color in color mode.
  452.          * <br>
  453.          * If in monochrome mode these colors are saturated.
  454.          * @type Array
  455.          */
  456.  
  457.         imgPalette:null,
  458.  
  459.         /**
  460.          * Buffered sprite color palette.
  461.          * <br>
  462.          * Holds 16 color values that come from color indexes at vram 0x3f10 to 0x3f20.
  463.          * <br>
  464.          * If in monochrome mode these colors are saturated.
  465.          * @type Array
  466.          */
  467.  
  468.         sprPalette:null,
  469.  
  470.         //Pattern Table Tile Buffers
  471.  
  472.         /**
  473.          * Pattern table tile buffer.
  474.          * @type Array
  475.          */
  476.  
  477.         ptTile:null,
  478.  
  479.         //Nametable Buffers
  480.  
  481.         /**
  482.          * First nametable?
  483.          * @type Array
  484.          */
  485.  
  486.         ntable1:null,
  487.  
  488.         /**
  489.          * Nametable?
  490.          * @type Array
  491.          */
  492.  
  493.         nameTable:null,
  494.  
  495.         //VRAM Mirror Table
  496.  
  497.         /**
  498.          * Vram mirror table.
  499.          * @type Array
  500.          */
  501.  
  502.         vramMirrorTable:null,
  503.  
  504.         //Rendering Options
  505.  
  506.         /**
  507.          * Determines whether or not to clip the image to the tv size.
  508.          * @type Boolean
  509.          */
  510.  
  511.         clipToTvSize:true,
  512.  
  513.         //
  514.         //  Methods
  515.         //___________//
  516.  
  517.                 //
  518.                 //  Initialization
  519.                 //__________________//
  520.  
  521.         /**
  522.          * Resets the ppu.
  523.          */
  524.  
  525.         reset:function ppu_reset(){
  526.                 //Reset the video ram.
  527.                 this.vramMem = new Array(0x8000);
  528.                 for(var i=0;i<this.vramMem.length;i++){
  529.                         this.vramMem[i] = 0;
  530.                 }
  531.                 //Reset the sprite memory.
  532.                 this.spriteMem = new Array(0x100);
  533.                 for(var i=0;i<this.spriteMem.length;i++){
  534.                         this.spriteMem[i] = 0;
  535.                 }
  536.                 //VRAM I/O
  537.                 this.vramAddress = null;
  538.                 this.vramTmpAddress = null;
  539.                 this.vramBufferedReadValue = 0;
  540.                 //VRAM/Scroll High/Low Byte latch
  541.                 this.firstWrite = true;
  542.                 //SPR-RAM I/O
  543.                 this.sramAddress = 0;
  544.                 //Control Register
  545.                 this.f_nmiOnVblank = 0;
  546.                 this.f_spriteSize = 0;
  547.                 this.f_bgPatternTable = 0;
  548.                 this.f_spPatternTable = 0;
  549.                 this.f_addrInc = 0;
  550.                 this.f_nTblAddress = 0;
  551.                 //Masking Register
  552.                 this.f_color = 0;
  553.                 this.f_spVisibility = 0;
  554.                 this.f_bgVisibility = 0;
  555.                 this.f_spClipping = 0;
  556.                 this.f_bgClipping = 0;
  557.                 this.f_dispType = 0;
  558.                 //Counters
  559.                 this.cntFV = 0;
  560.                 this.cntVT = 0;
  561.                 this.cntHT = 0;
  562.                 this.cntV = 0;
  563.                 this.cntH = 0;
  564.                 //Registers
  565.                 this.regFV = 0;
  566.                 this.regV = 0;
  567.                 this.regH = 0;
  568.                 this.regVT = 0;
  569.                 this.regHT = 0;
  570.                 this.regFH = 0;
  571.                 this.regS = 0;
  572.                 //Variables used when rendering.
  573.                 this.attrib = new Array(32);
  574.                 this.bgbuffer = new Array(61440);
  575.                 this.pixRendered = new Array(61440);
  576.                 //???
  577.                 this.requestEndFrame = false;
  578.                 this.validTileData = false;
  579.                 this.nmiCounter = 0;
  580.                 this.scantile = new Array(32);
  581.                 //Initialize misc vars.
  582.                 this.scanline = 0;
  583.                 this.lastRenderedScanline = 19;
  584.                 this.curX = 0;
  585.                 //Sprite data.
  586.                 this.sprX = new Array(64);
  587.                 this.sprY = new Array(64);
  588.                 this.sprTile = new Array(64);
  589.                 this.sprCol = new Array(64);
  590.                 this.vertFlip = new Array(64);
  591.                 this.horiFlip = new Array(64);
  592.                 this.bgPriority = new Array(64);
  593.                 //Sprite 0 hit flags.
  594.                 this.hitSpr0 = false;
  595.                 this.spr0HitX = 0;
  596.                 this.spr0HitY = 0;
  597.                 //Buffered color palettes.
  598.                 this.sprPalette = new Array(16);
  599.                 this.imgPalette = new Array(16);
  600.                 //Create pattern table tile buffers.
  601.                 this.ptTile = new Array(512);
  602.                 for(var i=0;i<512;i++){
  603.                         this.ptTile[i] = new Tile();
  604.                 }
  605.                 //Create nametable buffers.
  606.                 //Name table data.
  607.                 this.ntable1 = new Array(4);
  608.                 this.nameTable = new Array(4);
  609.                 this.nameTable[0] = new this.NameTable(32,32);
  610.                 this.nameTable[1] = new this.NameTable(32,32);
  611.                 this.nameTable[2] = new this.NameTable(32,32);
  612.                 this.nameTable[3] = new this.NameTable(32,32);
  613.                 //Initialize vram mirroring lookup table.
  614.                 this.vramMirrorTable = new Array(0x8000);
  615.                 for(var i=0;i<0x8000;i++){
  616.                         this.vramMirrorTable[i] = i;
  617.                 }
  618.                 //Set the color palette.
  619.                 this.colorPalette.loadNTSCPalette();
  620.                 //Intermediate Canvas Buffer
  621.                 this.frameCanvas = document.createElement('canvas');
  622.                 this.frameCanvas.width = 256;
  623.                 this.frameCanvas.height = 240;
  624.                 this.frameCanvasContext = this.frameCanvas.getContext('2d');
  625.                 //Create a fresh buffer to draw onto for the first frame.
  626.                 this.frameBuffer = this.frameCanvasContext.createImageData(256,240);
  627.         },
  628.  
  629.         /**
  630.          * Sets the specified vram mirroring.
  631.          * @param {Number} mirroring
  632.          */
  633.  
  634.         setMirroring:function ppu_setMirroring(mirroring){
  635.                 //???
  636.                 this.triggerRendering();
  637.                 //Remove mirroring
  638.                 this.vramMirrorTable = new Array(0x8000);
  639.                 for(var i=0;i<0x8000;i++){
  640.                         this.vramMirrorTable[i] = i;
  641.                 }
  642.                 //Palette Mirroring
  643.                 this.defineMirrorRegion(0x3f20,0x3f00,0x20);
  644.                 this.defineMirrorRegion(0x3f40,0x3f00,0x20);
  645.                 this.defineMirrorRegion(0x3f80,0x3f00,0x20);
  646.                 this.defineMirrorRegion(0x3fc0,0x3f00,0x20);
  647.                 //Additional Mirroring
  648.                 this.defineMirrorRegion(0x3000,0x2000,0xf00);
  649.                 this.defineMirrorRegion(0x4000,0x0000,0x4000);
  650.                 //Horizontal Mirroring
  651.                 if(mirroring === 1){
  652.                         this.ntable1[0] = this.ntable1[1] = 0;
  653.                         this.ntable1[2] = this.ntable1[3] = 1;
  654.                         this.defineMirrorRegion(0x2400,0x2000,0x400);
  655.                         this.defineMirrorRegion(0x2c00,0x2800,0x400);
  656.                 }
  657.                 //Vertical Mirroring
  658.                 else if(mirroring === 0){
  659.                         this.ntable1[0] = this.ntable1[2] = 0;
  660.                         this.ntable1[1] = this.ntable1[3] = 1;
  661.                         this.defineMirrorRegion(0x2800,0x2000,0x400);
  662.                         this.defineMirrorRegion(0x2c00,0x2400,0x400);
  663.                 }
  664.                 //Single Screen Mirroring
  665.                 else if(mirroring === 3){
  666.                         this.ntable1[0] = this.ntable1[1] = this.ntable1[2] = this.ntable1[3] = 0;
  667.                         this.defineMirrorRegion(0x2400,0x2000,0x400);
  668.                         this.defineMirrorRegion(0x2800,0x2000,0x400);
  669.                         this.defineMirrorRegion(0x2c00,0x2000,0x400);
  670.                 }
  671.                 //Single Screen Mirroring 2
  672.                 else if(mirroring === 4){
  673.                         this.ntable1[0] = this.ntable1[1] = this.ntable1[2] = this.ntable1[3] = 1;
  674.                         this.defineMirrorRegion(0x2400,0x2400,0x400);
  675.                         this.defineMirrorRegion(0x2800,0x2400,0x400);
  676.                         this.defineMirrorRegion(0x2c00,0x2400,0x400);
  677.                 }
  678.                 //Assume Four-screen mMirroring
  679.                 else{
  680.                         this.ntable1[0] = 0;
  681.                         this.ntable1[1] = 1;
  682.                         this.ntable1[2] = 2;
  683.                         this.ntable1[3] = 3;
  684.                 }
  685.         },
  686.  
  687.         /**
  688.          * Defines a mirrored area in the vram address lookup table.
  689.          * @param {Number} from The area of memory to redirect.
  690.          * @param {Number} to The physical memory.
  691.          * @param {Number} size The size of the mirrored section.
  692.          */
  693.  
  694.         defineMirrorRegion:function ppu_defineMirrorRegion(from,to,size){
  695.                 for(var i=0;i<size;i++){
  696.                         this.vramMirrorTable[from+i] = to+i;
  697.                 }
  698.         },
  699.  
  700.         //
  701.         //  Frame Rendering
  702.         //___________________//
  703.  
  704.         /**
  705.          * Clears the screen buffer.
  706.          */
  707.  
  708.         startFrame:function ppu_startFrame(){
  709.                 //Get the new background color.
  710.                 var newBgColor = this.getBackgroundColor();
  711.                 //Check if different.
  712.                 if(this.bgColor !== newBgColor){
  713.                         //Set the new background color.
  714.                         this.bgColor = newBgColor;
  715.                 }
  716.                 //Clear the pixels rendered array.
  717.                 this.resetPixRenderedArray();
  718.         },
  719.  
  720.         resetPixRenderedArray:function(){
  721.                 //Loop through each pixel.
  722.                         //FIXME, big lag from this.
  723.                 for(var i=0;i<61440;i++){
  724.                         //???
  725.                         this.pixRendered[i] = 65;
  726.                 }
  727.         },
  728.  
  729.         getBackgroundColor:function nes_ppu_getBackgroundColor(){
  730.                 //Check if monochrome.
  731.                 if(this.f_dispType === 0){
  732.                         //No, use first entry of image palette as bg color.
  733.                         return this.getRGBStyleFrom24BitColor(this.imgPalette[0]);
  734.                 }
  735.                 //Yes, color emphasis determines the bg color.
  736.                 switch(this.f_color){
  737.                         //Black
  738.                         case 0:
  739.                                 return 'black';
  740.                         //Green
  741.                         case 1:
  742.                                 return 'green';
  743.                         //Blue
  744.                         case 2:
  745.                                 return 'blue';
  746.                         //Red
  747.                         case 4:
  748.                                 return 'red';
  749.                 }
  750.                 //Some error ocurred, return pink for a heads up.
  751.                 return 'pink';
  752.         },
  753.  
  754.         /**
  755.          * Starts the non-maskable interrupt, ensures that every scanline is rendered, and draws the pixel buffer to the screen.
  756.          */
  757.  
  758.         endFrame:function ppu_endFrame(){
  759.  
  760.                 //Do the non-maskable interrupt.
  761.                 nes.cpu.requestInterrupt(1);
  762.  
  763.                 //Make sure everything is rendered.
  764.                 if(this.lastRenderedScanline < 259){
  765.                         this.renderFramePartially(260-this.lastRenderedScanline);
  766.                 }
  767.  
  768.                 //Draw spr0 hit coordinates.
  769.                 //if(this.showSpr0Hit){
  770.                 //   //Spr 0 position
  771.                 //   if(this.sprX[0] >= 0 && this.sprX[0] < 256 && this.sprY[0] >= 0 && this.sprY[0] < 240){
  772.                 //         for(var i=0;i<256;i++){
  773.                 //                 buffer[(this.sprY[0]<<8)+i] = 0xFF5555;
  774.                 //         }
  775.                 //         for(var i=0;i<240;i++){
  776.                 //                 buffer[(i<<8)+this.sprX[0]] = 0xFF5555;
  777.                 //         }
  778.                 //   }
  779.                 //   //Hit position
  780.                 //   if(this.spr0HitX >= 0 && this.spr0HitX < 256 && this.spr0HitY >= 0 && this.spr0HitY < 240){
  781.                 //         for(var i=0;i<256;i++){
  782.                 //                 buffer[(this.spr0HitY<<8)+i] = 0x55FF55;
  783.                 //         }
  784.                 //         for(var i=0;i<240;i++){
  785.                 //                 buffer[(i<<8)+this.spr0HitX] = 0x55FF55;
  786.                 //         }
  787.                 //   }
  788.                 //}
  789.  
  790.                 //Reset the last rendered scanline counter.
  791.                 this.lastRenderedScanline = 19;
  792.  
  793.                 //Draw the background color onto the screen.
  794.                 nes.screen.context.fillStyle = this.bgColor;
  795.                 nes.screen.context.fillRect(0,0,256,240);
  796.  
  797.                 //Place the frame buffer onto the frame canvas.
  798.                 this.frameCanvasContext.putImageData(this.frameBuffer,0,0);
  799.  
  800.                 //Create a fresh buffer to draw onto for the next frame.
  801.                 this.frameBuffer = this.frameCanvasContext.createImageData(256,240);
  802.  
  803.                 //Draw the frame canvas onto the screen.
  804.                         //drawImage() is used here because it abides by the alpha values when blitting pixels.
  805.                 nes.screen.context.drawImage(this.frameCanvas,0,0);
  806.  
  807.                 //Check to clip the screen to the tv size.
  808.                 if(this.clipToTvSize){
  809.                         //Set the fillStyle to black.
  810.                         nes.screen.context.fillStyle = 'black';
  811.                         //Clip the left 8 pixels.
  812.                         nes.screen.context.fillRect(0,0,8,240);
  813.                         //Clip the right 8 pixels.
  814.                         nes.screen.context.fillRect(248,0,8,240);
  815.                         //Clip the top 8 pixels.
  816.                         nes.screen.context.fillRect(0,0,256,8);
  817.                         //Clip the bottom 8 pixels.
  818.                         nes.screen.context.fillRect(0,232,256,8);
  819.                 }
  820.  
  821.                 //Else check to clip the sprites or background.
  822.                         //FIXME, does not selectively clip the sprites or background.
  823.                 else if(this.f_bgClipping === 0 || this.f_spClipping === 0){
  824.                         //Set the fillStyle to black.
  825.                         nes.screen.context.fillStyle = 'black';
  826.                         //Clip the left 8 pixels.
  827.                         nes.screen.context.fillRect(0,0,8,240);
  828.                 }
  829.  
  830.         },
  831.  
  832.         setPixelInBuffer:function(index,color){
  833.                 //Set the red color component.
  834.                 this.frameBuffer.data[index*4] = color&0xFF;
  835.                 //Set the green color component.
  836.                 this.frameBuffer.data[index*4+1] = (color>>8)&0xFF;
  837.                 //Set the blue color component.
  838.                 this.frameBuffer.data[index*4+2] = (color>>16)&0xFF;
  839.                 //Set the pixel as visible.
  840.                 this.frameBuffer.data[index*4+3] = 255;
  841.         },
  842.  
  843.         /**
  844.          * Ends the current scaline.
  845.          * <br>
  846.          * First scanline(20): Clear the vertical blank flag, clear sprite 0 hit flags.
  847.          * <br>
  848.          * Normal scanline(20-260): Render, update scrolling, check for sprite 0 hit.
  849.          * <br>
  850.          * Last Scanline(261): Set vertical blank flag, set requestEndFrame flag, reset scanline counter.
  851.          */
  852.  
  853.         endScanline:function ppu_endScanline(){
  854.                 //Check if its a normal scanline.
  855.                 if(this.scanline > 20 && this.scanline < 261){
  856.                         //Check if the bg is visible.
  857.                         if(this.f_bgVisibility === 1){
  858.                                 //Update scroll.
  859.                                 this.cntHT = this.regHT;
  860.                                 this.cntH = this.regH;
  861.                                 this.renderBgScanlineOntoBgBuffer(this.scanline);
  862.                                 //Check for sprite 0 hit on next scanline.
  863.                                 if(!this.hitSpr0 && this.f_spVisibility === 1){
  864.                                         if(this.checkSprite0()){
  865.                                                 this.hitSpr0 = true;
  866.                                         }
  867.                                 }
  868.                                 //Clock mapper IRQ counter.
  869.                                 nes.mmc.clockIrqCounter();
  870.                         }
  871.                         //Else check if the sprites are visible.
  872.                         else if(this.f_spVisibility === 1){
  873.                                 //Clock mapper IRQ counter.
  874.                                 nes.mmc.clockIrqCounter();
  875.                         }
  876.                 }
  877.                 //Else check if its the first scanline.
  878.                 else if(this.scanline === 20){
  879.                         //Clear the vertical blank flag.
  880.                         nes.cpu.mem[0x2002] &= 127;
  881.                         //Clear the sprite 0 hit flags.
  882.                         nes.cpu.mem[0x2002] &= 191;
  883.                         this.hitSpr0 = false;
  884.                         this.spr0HitX = -1;
  885.                         this.spr0HitY = -1;
  886.                         //Check if either the bg or sprites are visible.
  887.                         if(this.f_bgVisibility === 1 || this.f_spVisibility === 1){
  888.                                 //Update counters from registers.
  889.                                 this.cntFV = this.regFV;
  890.                                 this.cntV = this.regV;
  891.                                 this.cntH = this.regH;
  892.                                 this.cntVT = this.regVT;
  893.                                 this.cntHT = this.regHT;
  894.                                 //Check if the bg is visible.
  895.                                 if(this.f_bgVisibility === 1){
  896.                                         //Render dummy scanline.
  897.                                         this.renderBgScanlineOntoBuffer(20);
  898.                                 }
  899.                                 //Check sprite 0 hit on the first visible scanline.
  900.                                 this.checkSprite0();
  901.                                 //Clock mapper IRQ Counter.
  902.                                 nes.mmc.clockIrqCounter();
  903.                         }
  904.                 }
  905.                 //Else check if its the last(dead) scanline.
  906.                 else if(this.scanline === 261){
  907.                         //Set the vBlank flag.
  908.                         nes.cpu.mem[0x2002] |= 128;
  909.                         //Set the end frame flag.
  910.                         this.requestEndFrame = true;
  911.                         //???
  912.                         this.nmiCounter = 9;
  913.                         //Reset the scanline counter to -1, will be incremented to 0.
  914.                         this.scanline = -1;
  915.                 }
  916.                 //Increment the scanline.
  917.                 this.scanline++;
  918.                 //???
  919.                 this.regsToAddress();
  920.                 this.cntsToAddress();
  921.         },
  922.  
  923.                 //
  924.                 //  Registers / Counters
  925.                 //________________________//
  926.  
  927.         /**
  928.          * Converts the scroll registers to a temporary vram address.
  929.          */
  930.  
  931.         regsToAddress:function(){
  932.                 this.vramTmpAddress = (((((this.regFV&7)<<4)|((this.regV&1)<<3)|((this.regH&1)<<2)|((this.regVT>>3)&3))<<8)|(((this.regVT&7)<<5)|(this.regHT&31)))&0x7FFF;
  933.         },
  934.  
  935.         /**
  936.          * Converts the scroll counters to the vram address.
  937.          */
  938.  
  939.         cntsToAddress:function(){
  940.                 this.vramAddress = (((((this.cntFV&7)<<4)|((this.cntV&1)<<3)|((this.cntH&1)<<2)|((this.cntVT>>3)&3))<<8)|(((this.cntVT&7)<<5)|(this.cntHT&31)))&0x7FFF;
  941.         },
  942.  
  943.         /**
  944.          * Updates the scroll registers from the temporary vram address created by regsToAddress().
  945.          */
  946.  
  947.         regsFromAddress:function(){
  948.                 //???
  949.                 var address = (this.vramTmpAddress>>8)&0xFF;
  950.                 this.regFV = (address>>4)&7;
  951.                 this.regV = (address>>3)&1;
  952.                 this.regH = (address>>2)&1;
  953.                 this.regVT = (this.regVT&7)|((address&3)<<3);
  954.                 //???
  955.                 var address = this.vramTmpAddress&0xFF;
  956.                 this.regVT = (this.regVT&24)|((address>>5)&7);
  957.                 this.regHT = address&31;
  958.         },
  959.  
  960.         /**
  961.          * Updates the scroll counters from the current vram address.
  962.          */
  963.  
  964.         cntsFromAddress:function(){
  965.                 //???
  966.                 var address = (this.vramAddress>>8)&0xFF;
  967.                 this.cntFV = (address>>4)&3;
  968.                 this.cntV = (address>>3)&1;
  969.                 this.cntH = (address>>2)&1;
  970.                 this.cntVT = (this.cntVT&7)|((address&3)<<3);
  971.                 //???
  972.                 var address = this.vramAddress&0xFF;
  973.                 this.cntVT = (this.cntVT&24)|((address>>5)&7);
  974.                 this.cntHT = address&31;
  975.         },
  976.  
  977.         /**
  978.          * Writes the passed value to the specified register.
  979.          * @param {Number} address
  980.          * @param {Number} value
  981.          */
  982.  
  983.         writeRegister:function(address,value){
  984.                 switch(address&7){
  985.  
  986.                         //Write 0x2000, PPU Control Register
  987.                         //Sets the Control Register.
  988.                         case 0:
  989.                                 //Set the value into the ram.
  990.                                 nes.cpu.mem[0x2000] = value;
  991.                                 //???
  992.                                 this.triggerRendering();
  993.                                 //Set the flags from the value.
  994.                                 this.f_nmiOnVblank = (value>>7)&1;
  995.                                 this.f_spriteSize = (value>>5)&1;
  996.                                 this.f_bgPatternTable = (value>>4)&1;
  997.                                 this.f_spPatternTable = (value>>3)&1;
  998.                                 this.f_addrInc = (value>>2)&1;
  999.                                 this.f_nTblAddress = value&3;
  1000.                                 //???
  1001.                                 this.regV = (value>>1)&1;
  1002.                                 this.regH = value&1;
  1003.                                 this.regS = (value>>4)&1;
  1004.                                 break;
  1005.  
  1006.                         //Write 0x2001, PPU Masking Register
  1007.                         //Sets the Masking Register.
  1008.                         case 1:
  1009.                                 //Set the value into the ram.
  1010.                                 nes.cpu.mem[0x2001] = value;
  1011.                                 //???
  1012.                                 this.triggerRendering();
  1013.                                 //Set the flags from the value.
  1014.                                 this.f_color = (value>>5)&7;
  1015.                                 this.f_spVisibility = (value>>4)&1;
  1016.                                 this.f_bgVisibility = (value>>3)&1;
  1017.                                 this.f_spClipping = (value>>2)&1;
  1018.                                 this.f_bgClipping = (value>>1)&1;
  1019.                                 this.f_dispType = value&1;
  1020.                                 //Set the color emphasis if the display type is monochrome.
  1021.                                 if(this.f_dispType === 0){
  1022.                                         this.colorPalette.setEmphasis(this.f_color);
  1023.                                 }
  1024.                                 //Update the image and sprite color palettes.
  1025.                                 this.updatePalettes();
  1026.                                 break;
  1027.  
  1028.                         //Write 0x2003, Sprite RAM Address
  1029.                         //Sets the address used when accessing sprite RAM.
  1030.                         case 3:
  1031.                                 //Set the address.
  1032.                                 this.sramAddress = value;
  1033.                                 break;
  1034.  
  1035.                         //Write 0x2004, Sprite RAM
  1036.                         //Writes to the sprite RAM at the address set to 0x2003, increments the address afterwards.
  1037.                         case 4:
  1038.                                 //Write the value to the sprite ram.
  1039.                                 this.writeSpriteMem(this.sramAddress,value);
  1040.                                 //Increment the address.
  1041.                                 this.sramAddress++;
  1042.                                 //Knock it back down below 256.
  1043.                                 this.sramAddress %= 0x100;
  1044.                                 break;
  1045.  
  1046.                         //Write 0x2005, Screen Scroll Offsets
  1047.                         //Sets the screen scrolling options?
  1048.                         case 5:
  1049.                                 //???
  1050.                                 this.triggerRendering();
  1051.                                 //Check if this is the first write.
  1052.                                 if(this.firstWrite){
  1053.                                         //Yes, set the horizontal scrolling.
  1054.                                         this.regHT = (value>>3)&31;
  1055.                                         this.regFH = value&7;
  1056.                                         this.firstWrite = false;
  1057.                                 }
  1058.                                 else{
  1059.                                         //No, set the vertical scrolling.
  1060.                                         this.regFV = value&7;
  1061.                                         this.regVT = (value>>3)&31;
  1062.                                         this.firstWrite = true;
  1063.                                 }
  1064.                                 break;
  1065.  
  1066.                         //Write 0x2006, VRAM Address
  1067.                         //Sets the address used when accessing VRAM.
  1068.                         case 6:
  1069.                                 //Check if this is the first write.
  1070.                                 if(this.firstWrite){
  1071.                                         //Yes, set the high byte.
  1072.                                         this.regFV = (value>>4)&3;
  1073.                                         this.regV = (value>>3)&1;
  1074.                                         this.regH = (value>>2)&1;
  1075.                                         this.regVT = (this.regVT&7)|((value&3)<<3);
  1076.                                         this.firstWrite = false;
  1077.                                 }
  1078.                                 else{
  1079.                                         //No, set the low byte.
  1080.                                         this.triggerRendering();
  1081.                                         this.regVT = (this.regVT&24)|((value>>5)&7);
  1082.                                         this.regHT = value&31;
  1083.                                         this.cntFV = this.regFV;
  1084.                                         this.cntV = this.regV;
  1085.                                         this.cntH = this.regH;
  1086.                                         this.cntVT = this.regVT;
  1087.                                         this.cntHT = this.regHT;
  1088.                                         this.checkSprite0(this.scanline);
  1089.                                         this.firstWrite = true;
  1090.                                 }
  1091.                                 //Set the VRAM address from the counters just set.
  1092.                                 this.cntsToAddress();
  1093.                                 //Invoke mapper latch.
  1094.                                 if(this.vramAddress < 0x2000){
  1095.                                         nes.mmc.latchAccess(this.vramAddress);
  1096.                                 }
  1097.                                 break;
  1098.  
  1099.                         //Write 0x2007, VRAM
  1100.                         case 7:
  1101.                                 //???
  1102.                                 this.triggerRendering();
  1103.                                 //???
  1104.                                 this.cntsToAddress();
  1105.                                 this.regsToAddress();
  1106.                                 //Check if in an unmirrored address.
  1107.                                 if(this.vramAddress < 0x2000){
  1108.                                         //Write to the vram.
  1109.                                         this.vramMem[this.vramAddress] = value;
  1110.                                         //???
  1111.                                         if(this.vramAddress%16 < 8){
  1112.                                                 this.ptTile[parseInt(this.vramAddress/16)].setScanline(this.vramAddress%16,value,this.vramMem[this.vramAddress+8]);
  1113.                                         }
  1114.                                         else{
  1115.                                                 this.ptTile[parseInt(this.vramAddress/16)].setScanline((this.vramAddress%16)-8,this.vramMem[this.vramAddress-8],value);
  1116.                                         }
  1117.                                         //Invoke mapper latch.
  1118.                                         nes.mmc.latchAccess(this.vramAddress);
  1119.                                 }
  1120.                                 //Check if the write is into the image or sprite color palettes.
  1121.                                 else if(this.vramAddress >= 0x3f00 && this.vramAddress < 0x3f20){
  1122.                                         //Check which address is being written into.
  1123.                                         if(this.vramAddress === 0x3F00 || this.vramAddress === 0x3F10){
  1124.                                                 //Addresses 0x3F00 and 0x3F10 are mirrored, write to both of them.
  1125.                                                 this.vramMem[0x3F00] = value;
  1126.                                                 this.vramMem[0x3F10] = value;
  1127.                                         }
  1128.                                         else if(this.vramAddress === 0x3F04 || this.vramAddress === 0x3F14){
  1129.                                                 //Addresses 0x3F04 and 0x3F14 are mirrored, write to both of them.
  1130.                                                 this.vramMem[0x3F04] = value;
  1131.                                                 this.vramMem[0x3F14] = value;
  1132.                                         }
  1133.                                         else if(this.vramAddress === 0x3F08 || this.vramAddress === 0x3F18){
  1134.                                                 //Addresses 0x3F08 and 0x3F18 are mirrored, write to both of them.
  1135.                                                 this.vramMem[0x3F08] = value;
  1136.                                                 this.vramMem[0x3F18] = value;
  1137.                                         }
  1138.                                         else if(this.vramAddress === 0x3F0C || this.vramAddress === 0x3F1C){
  1139.                                                 //Addresses 0x3F0C and 0x3F1C are mirrored, write to both of them.
  1140.                                                 this.vramMem[0x3F0C] = value;
  1141.                                                 this.vramMem[0x3F1C] = value;
  1142.                                         }
  1143.                                         else{
  1144.                                                 //Else an unmirrored palette entry, write normally.
  1145.                                                 this.vramMem[this.vramAddress] = value;
  1146.                                         }
  1147.                                         //Update the internally buffered image and sprite color palettes.
  1148.                                         this.updatePalettes();
  1149.                                 }
  1150.                                 //Else check if the write is in the mirror table.
  1151.                                 else if(this.vramAddress < this.vramMirrorTable.length){
  1152.                                         //Cache the address from the mirroring table.
  1153.                                         var address = this.vramMirrorTable[this.vramAddress];
  1154.                                         //Write to the vram.
  1155.                                         this.vramMem[address] = value;
  1156.                                         //Use a case structure to determine how to update the internally buffered data.
  1157.                                         if(address < 0x23c0){
  1158.                                                 //???
  1159.                                                 this.nameTable[this.ntable1[0]].tile[address-0x2000] = value;
  1160.                                                 //Update sprite 0 hit.
  1161.                                                 this.checkSprite0();
  1162.                                         }
  1163.                                         else if(address < 0x2400){
  1164.                                                 //Update the internal pattern table buffer with this new attribute table byte.
  1165.                                                 this.nameTable[this.ntable1[0]].writeAttrib(address-0x23c0,value);
  1166.                                         }
  1167.                                         else if(address < 0x27c0){
  1168.                                                 //???
  1169.                                                 this.nameTable[this.ntable1[1]].tile[address-0x2400] = value;
  1170.                                                 //Update sprite 0 hit.
  1171.                                                 this.checkSprite0();
  1172.                                         }
  1173.                                         else if(address < 0x2800){
  1174.                                                 //Update the internal pattern table buffer with this new attribute table byte.
  1175.                                                 this.nameTable[this.ntable1[1]].writeAttrib(address-0x27c0,value);
  1176.                                         }
  1177.                                         else if(address < 0x2bc0){
  1178.                                                 //???
  1179.                                                 this.nameTable[this.ntable1[2]].tile[address-0x2800] = value;
  1180.                                                 //Update sprite 0 hit.
  1181.                                                 this.checkSprite0();
  1182.                                         }
  1183.                                         else if(address < 0x2c00){
  1184.                                                 //Update the internal pattern table buffer with this new attribute table byte.
  1185.                                                 this.nameTable[this.ntable1[2]].writeAttrib(address-0x2bc0,value);
  1186.                                         }
  1187.                                         else if(address < 0x2fc0){
  1188.                                                 //???
  1189.                                                 this.nameTable[this.ntable1[3]].tile[address-0x2c00] = value;
  1190.                                                 //Update sprite 0 hit.
  1191.                                                 this.checkSprite0();
  1192.                                         }
  1193.                                         else if(address < 0x3000){
  1194.                                                 //Update the internal pattern table buffer with this new attribute table byte.
  1195.                                                 this.nameTable[this.ntable1[3]].writeAttrib(address-0x2fc0,value);
  1196.                                         }
  1197.                                 }
  1198.                                 //Increment the vram address by either 1 or 32, depending on bit 2 of the control register.
  1199.                                 this.vramAddress += 1+this.f_addrInc*31;
  1200.                                 //???
  1201.                                 this.regsFromAddress();
  1202.                                 this.cntsFromAddress();
  1203.                                 break;
  1204.  
  1205.                 }
  1206.         },
  1207.  
  1208.                 //
  1209.                 //  Rendering
  1210.                 //_____________//
  1211.  
  1212.         getRedFrom24BitColor:function(color){
  1213.                 return color&0xFF;
  1214.         },
  1215.  
  1216.         getGreenFrom24BitColor:function(color){
  1217.                 return (color>>8)&0xFF;
  1218.         },
  1219.  
  1220.         getBlueFrom24BitColor:function(color){
  1221.                 return (color>>16)&0xFF;
  1222.         },
  1223.  
  1224.         getRGBStyleFrom24BitColor:function(color){
  1225.                 return 'rgb('+(color&0xFF)+','+((color>>8)&0xFF)+','+((color>>16)&0xFF)+')';
  1226.         },
  1227.  
  1228.         /**
  1229.          * Renders scanlines up to the current one?
  1230.          */
  1231.  
  1232.         triggerRendering:function(){
  1233.                 //Check if this is a visible scanline.
  1234.                 if(this.scanline > 20 && this.scanline < 261){
  1235.                         //Render sprites, and combine.
  1236.                         this.renderFramePartially(this.scanline-this.lastRenderedScanline-1);
  1237.                         //Set last rendered scanline.
  1238.                         this.lastRenderedScanline = this.scanline-1;
  1239.                 }
  1240.         },
  1241.  
  1242.         /**
  1243.          * Renders the specified number of scanlines.
  1244.          * @param {Number} scanCount
  1245.          */
  1246.  
  1247.         renderFramePartially:function(scanCount){
  1248.                 //Cache the scanline to start rendering at.
  1249.                 var startScan = this.lastRenderedScanline-19;
  1250.                 //???
  1251.                 this.renderSpritesPartially(scanCount,true);
  1252.                 //Check if the bg is visible.
  1253.                 if(this.f_bgVisibility === 1){
  1254.                         //Cache the ending index.
  1255.                         var endIndex = Math.min((startScan+scanCount)<<8,0xF000);
  1256.                         //Loop through the specified pixels.
  1257.                         for(var destIndex=startScan<<8;destIndex<endIndex;destIndex++){
  1258.                                 //???
  1259.                                 if(this.pixRendered[destIndex] > 0xFF){
  1260.                                         //Set the pixel color from the background buffer.
  1261.                                         this.setPixelInBuffer(destIndex,this.bgbuffer[destIndex]);
  1262.                                 }
  1263.                         }
  1264.                 }
  1265.                 //???
  1266.                 this.renderSpritesPartially(scanCount,false);
  1267.                 //Invalidate tile data, for what reason who knows?
  1268.                 this.validTileData = false;
  1269.         },
  1270.  
  1271.         /**
  1272.          * Renders the background of the specified scanline onto the passed buffer.
  1273.          * @param {Number} scanline
  1274.          */
  1275.  
  1276.         renderBgScanlineOntoBgBuffer:function(scanline){
  1277.                 //Reduce the scanline value to the previous scale, -20 to 241 instead of 0 to 261.
  1278.                 scanline -= 20;
  1279.                 //???
  1280.                 var destIndex = (scanline<<8)-this.regFH;
  1281.                 //???
  1282.                 this.cntHT = this.regHT;
  1283.                 this.cntH = this.regH;
  1284.                 //???
  1285.                 if(scanline < 240 && (scanline-this.cntFV) >= 0){
  1286.                         //Cache the curent name table index.
  1287.                         var curNt = this.ntable1[2*this.cntV+this.cntH];
  1288.                         //???
  1289.                         var tscanoffset = this.cntFV<<3;
  1290.                         //Loop through the 32 tiles.
  1291.                         for(var tile=0;tile<32;tile++){
  1292.                                 //Check if the scanline is visible.
  1293.                                 if(scanline >= 0){
  1294.                                         //Check if the tile and attribute data is not cached.
  1295.                                         if(!this.validTileData){
  1296.                                                 //If not cache it.
  1297.                                                 this.scantile[tile] = this.ptTile[this.regS*256+this.nameTable[curNt].getTileIndex(this.cntHT,this.cntVT)];
  1298.                                                 this.attrib[tile] = this.nameTable[curNt].getAttrib(this.cntHT,this.cntVT);
  1299.                                         }
  1300.                                         //Get the tile and its attributes.
  1301.                                         var t = this.scantile[tile];
  1302.                                         var att = this.attrib[tile];
  1303.                                         //Render tile scanline.
  1304.                                         var x = (tile<<3)-this.regFH;
  1305.                                         if(x > -8){
  1306.                                                 //???
  1307.                                                 if(x < 0){
  1308.                                                         destIndex -= x;
  1309.                                                         var sx = -x;
  1310.                                                 }
  1311.                                                 else{
  1312.                                                         var sx = 0;
  1313.                                                 }
  1314.                                                 //???
  1315.                                                 if(t.opaque[this.cntFV]){
  1316.                                                         for(;sx<8;sx++){
  1317.                                                                 this.bgbuffer[destIndex] = this.imgPalette[t.pixelColor[tscanoffset+sx]+att];
  1318.                                                                 this.pixRendered[destIndex] |= 256;
  1319.                                                                 destIndex++;
  1320.                                                         }
  1321.                                                 }
  1322.                                                 else{
  1323.                                                         for(;sx<8;sx++){
  1324.                                                                 var col = t.pixelColor[tscanoffset+sx];
  1325.                                                                 if(col !== 0){
  1326.                                                                         this.bgbuffer[destIndex] = this.imgPalette[col+att];
  1327.                                                                         this.pixRendered[destIndex] |= 256;
  1328.                                                                 }
  1329.                                                                 destIndex++;
  1330.                                                         }
  1331.                                                 }
  1332.                                         }
  1333.                                 }
  1334.                                 //Increment the horizontal tile counter.
  1335.                                 this.cntHT++;
  1336.                                 //Check to reset the horizontal tile counter.
  1337.                                 if(this.cntHT === 32){
  1338.                                         this.cntHT = 0;
  1339.                                         this.cntH++;
  1340.                                         this.cntH %= 2;
  1341.                                 }
  1342.                         }
  1343.                         //Tile data for one row should now have been fetched, so the data in the array is valid.
  1344.                         this.validTileData = true;
  1345.                 }
  1346.                 //Update vertical scroll.
  1347.                 this.cntFV++;
  1348.                 if(this.cntFV === 8){
  1349.                         this.cntFV = 0;
  1350.                         this.cntVT++;
  1351.                         if(this.cntVT === 30){
  1352.                                 this.cntVT = 0;
  1353.                                 this.cntV++;
  1354.                                 this.cntV %= 2;
  1355.                         }
  1356.                         else if(this.cntVT === 32){
  1357.                                 this.cntVT = 0;
  1358.                         }
  1359.                         //Invalidate tile data, for what reason who knows?
  1360.                         this.validTileData = false;
  1361.                 }
  1362.         },
  1363.  
  1364.         renderBgScanlineOntoBuffer:function(scanline){
  1365.                 //Reduce the scanline value to the previous scale, -20 to 241 instead of 0 to 261.
  1366.                 scanline -= 20;
  1367.                 //???
  1368.                 var destIndex = (scanline<<8)-this.regFH;
  1369.                 //???
  1370.                 this.cntHT = this.regHT;
  1371.                 this.cntH = this.regH;
  1372.                 //???
  1373.                 if(scanline < 240 && (scanline-this.cntFV) >= 0){
  1374.                         //Cache the curent name table index.
  1375.                         var curNt = this.ntable1[2*this.cntV+this.cntH];
  1376.                         //???
  1377.                         var tscanoffset = this.cntFV<<3;
  1378.                         //Check if the scanline is visible.
  1379.                         if(scanline >= 0){
  1380.                                 //Loop through the 32 tiles.
  1381.                                 for(var tile=0;tile<32;tile++){
  1382.                                         //Check if the tile and attribute data is not cached.
  1383.                                         if(!this.validTileData){
  1384.                                                 //If not cache it.
  1385.                                                 this.scantile[tile] = this.ptTile[this.regS*256+this.nameTable[curNt].getTileIndex(this.cntHT,this.cntVT)];
  1386.                                                 this.attrib[tile] = this.nameTable[curNt].getAttrib(this.cntHT,this.cntVT);
  1387.                                         }
  1388.                                         //Get the tile and its attributes.
  1389.                                         var t = this.scantile[tile];
  1390.                                         var att = this.attrib[tile];
  1391.                                         //Render tile at this scanline.
  1392.                                         var x = (tile<<3)-this.regFH;
  1393.                                         if(x > -8){
  1394.                                                 //???
  1395.                                                 if(x < 0){
  1396.                                                         destIndex -= x;
  1397.                                                         var sx = -x;
  1398.                                                 }
  1399.                                                 else{
  1400.                                                         var sx = 0;
  1401.                                                 }
  1402.                                                 //???
  1403.                                                 if(t.opaque[this.cntFV]){
  1404.                                                         for(;sx<8;sx++){
  1405.                                                                 //Set the pixel to the buffer.
  1406.                                                                 this.setPixelInBuffer(destIndex,this.imgPalette[t.pixelColor[tscanoffset+sx]+att]);
  1407.                                                                 this.pixRendered[destIndex] |= 256;
  1408.                                                                 destIndex++;
  1409.                                                         }
  1410.                                                 }
  1411.                                                 else{
  1412.                                                         for(;sx<8;sx++){
  1413.                                                                 var col = t.pixelColor[tscanoffset+sx];
  1414.                                                                 if(col !== 0){
  1415.                                                                         //Set the pixel to the buffer.
  1416.                                                                         this.setPixelInBuffer(destIndex,this.imgPalette[col+att]);
  1417.                                                                         this.pixRendered[destIndex] |= 256;
  1418.                                                                 }
  1419.                                                                 destIndex++;
  1420.                                                         }
  1421.                                                 }
  1422.                                         }
  1423.                                         //Increment the horizontal tile counter.
  1424.                                         this.cntHT++;
  1425.                                         //Check to reset the horizontal tile counter.
  1426.                                         if(this.cntHT === 32){
  1427.                                                 this.cntHT = 0;
  1428.                                                 this.cntH++;
  1429.                                                 this.cntH %= 2;
  1430.                                         }
  1431.                                 }
  1432.                         }
  1433.                         //Tile data for one row should now have been fetched, so the data in the array is valid.
  1434.                         this.validTileData = true;
  1435.                 }
  1436.                 //Update vertical scroll.
  1437.                 this.cntFV++;
  1438.                 if(this.cntFV === 8){
  1439.                         this.cntFV = 0;
  1440.                         this.cntVT++;
  1441.                         if(this.cntVT === 30){
  1442.                                 this.cntVT = 0;
  1443.                                 this.cntV++;
  1444.                                 this.cntV %= 2;
  1445.                         }
  1446.                         else if(this.cntVT === 32){
  1447.                                 this.cntVT = 0;
  1448.                         }
  1449.                         //Invalidate tile data, for what reason who knows?
  1450.                         this.validTileData = false;
  1451.                 }
  1452.         },
  1453.  
  1454.         /**
  1455.          * Renders sprites onto the specified scanline, provided they match the specified bg priority.
  1456.          * @param {Number} scancount
  1457.          * @param {boolean} bgPri
  1458.          */
  1459.  
  1460.         renderSpritesPartially:function(scancount,bgPri){
  1461.                 //Cache the scanline to start at.
  1462.                 var startscan = this.lastRenderedScanline-19;
  1463.                 //Check if the sprites are visible.
  1464.                 if(this.f_spVisibility === 1){
  1465.                         //Check the sprite size.
  1466.                         if(this.f_spriteSize === 0){
  1467.                                 //8x8 Sprites
  1468.                                 for(var i=0;i<64;i++){
  1469.                                         //???
  1470.                                         if(this.bgPriority[i] === bgPri && this.sprX[i]>=0 && this.sprX[i]<256 && this.sprY[i]+8 >= startscan && this.sprY[i]<startscan+scancount){
  1471.                                                 //???
  1472.                                                 if(this.sprY[i] < startscan){
  1473.                                                         var srcy1 = startscan-this.sprY[i]-1;
  1474.                                                 }
  1475.                                                 else{
  1476.                                                         var srcy1 = 0;
  1477.                                                 }
  1478.                                                 //???
  1479.                                                 if(this.sprY[i]+8 > startscan+scancount){
  1480.                                                         var srcy2 = startscan+scancount-this.sprY[i]+1;
  1481.                                                 }
  1482.                                                 else{
  1483.                                                         var srcy2 = 8;
  1484.                                                 }
  1485.                                                 //Check the sprite pattern table address.
  1486.                                                 if(this.f_spPatternTable === 0){
  1487.                                                         //0x0000
  1488.                                                         this.ptTile[this.sprTile[i]].render(srcy1,srcy2,1,i);
  1489.                                                 }
  1490.                                                 else{
  1491.                                                         //0x1000
  1492.                                                         this.ptTile[this.sprTile[i]+256].render(srcy1,srcy2,1,i);
  1493.                                                 }
  1494.                                         }
  1495.                                 }
  1496.                         }
  1497.                         else{
  1498.                                 //8x16 Sprites
  1499.                                 for(var i=0;i<64;i++){
  1500.                                         //???
  1501.                                         if(this.bgPriority[i] === bgPri && this.sprX[i]>=0 && this.sprX[i]<256 && this.sprY[i]+8 >= startscan && this.sprY[i]<startscan+scancount){
  1502.                                                 //???
  1503.                                                 var top = this.sprTile[i];
  1504.                                                 if((top&1) !== 0){
  1505.                                                         top = this.sprTile[i]-1+256;
  1506.                                                 }
  1507.                                                 //???
  1508.                                                 if(this.sprY[i] < startscan){
  1509.                                                         var srcy1 = startscan-this.sprY[i]-1;
  1510.                                                 }
  1511.                                                 else{
  1512.                                                         var srcy1 = 0;
  1513.                                                 }
  1514.                                                 //???
  1515.                                                 if(this.sprY[i]+8 > startscan+scancount){
  1516.                                                         var srcy2 = startscan+scancount-this.sprY[i];
  1517.                                                 }
  1518.                                                 else{
  1519.                                                         var srcy2 = 8;
  1520.                                                 }
  1521.                                                 //???
  1522.                                                 this.ptTile[top+(this.vertFlip[i]?1:0)].render(srcy1,srcy2,1,i);
  1523.                                                 //???
  1524.                                                 if(this.sprY[i]+8 < startscan){
  1525.                                                         var srcy1 = startscan-(this.sprY[i]+9);
  1526.                                                 }
  1527.                                                 else{
  1528.                                                         var srcy1 = 0;
  1529.                                                 }
  1530.                                                 //???
  1531.                                                 if(this.sprY[i]+16 > startscan+scancount){
  1532.                                                         var srcy2 = startscan+scancount-(this.sprY[i]+8);
  1533.                                                 }
  1534.                                                 else{
  1535.                                                         var srcy2 = 8;
  1536.                                                 }
  1537.                                                 //???
  1538.                                                 this.ptTile[top+(this.vertFlip[i]?0:1)].render(srcy1,srcy2,9,i);
  1539.                                         }
  1540.                                 }
  1541.                         }
  1542.                 }
  1543.         },
  1544.  
  1545.         /**
  1546.          * Checks for a sprite 0 hit on the specified scanline.
  1547.          * @param {Number} scanline
  1548.          */
  1549.  
  1550.         checkSprite0:function(){
  1551.                 //Get the current scanline.
  1552.                 var scanline = this.scanline;
  1553.                 //Reduce the scanline value to the previous scale, -20 to 241 instead of 0 to 261.
  1554.                 scanline -= 20;
  1555.                 //Set the sprite collision to an invalid position.
  1556.                 this.spr0HitX = this.spr0HitY = -1;
  1557.                 //Cache the sprites position, why add 1 to the y value?
  1558.                 var x = this.sprX[0];
  1559.                 var y = this.sprY[0]+1;
  1560.                 //Check if sprite size is 8x8.
  1561.                 if(this.f_spriteSize === 0){
  1562.                         //Check if its in range.
  1563.                         if(y <= scanline && y+8 > scanline && x >= -7 && x < 256){
  1564.                                 //Cache the sprite.
  1565.                                 var t = this.ptTile[this.sprTile[0]+this.f_spPatternTable*256];
  1566.                                 //Check if vertically flipped.
  1567.                                 if(this.vertFlip[0]){
  1568.                                         //Yes
  1569.                                         var toffset = (7-scanline+y)*8;
  1570.                                 }
  1571.                                 else{
  1572.                                         //No
  1573.                                         var toffset = (scanline-y)*8;
  1574.                                 }
  1575.                                 //Cache the buffer index.
  1576.                                 var bufferIndex = scanline*256+x;
  1577.                                 //Check if horizontally flipped.
  1578.                                 if(this.horiFlip[0]){
  1579.                                         //Yes
  1580.                                         for(var i=7;i>=0;i--){
  1581.                                                 if(x >= 0 && x < 256){
  1582.                                                         if(bufferIndex >= 0 && bufferIndex < 61440 && this.pixRendered[bufferIndex] !== 0){
  1583.                                                                 if(t.pixelColor[toffset+i] !== 0){
  1584.                                                                         //Set the hit coordinates and return true.
  1585.                                                                         this.spr0HitX = bufferIndex%256;
  1586.                                                                         this.spr0HitY = scanline;
  1587.                                                                         return true;
  1588.                                                                 }
  1589.                                                         }
  1590.                                                 }
  1591.                                                 x++;
  1592.                                                 bufferIndex++;
  1593.                                         }
  1594.                                 }
  1595.                                 else{
  1596.                                         //No
  1597.                                         for(var i=0;i<8;i++){
  1598.                                                 if(x >= 0 && x < 256){
  1599.                                                         if(bufferIndex >= 0 && bufferIndex < 61440 && this.pixRendered[bufferIndex] !== 0){
  1600.                                                                 if(t.pixelColor[toffset+i] !== 0){
  1601.                                                                         //Set the hit coordinates and return true.
  1602.                                                                         this.spr0HitX = bufferIndex%256;
  1603.                                                                         this.spr0HitY = scanline;
  1604.                                                                         return true;
  1605.                                                                 }
  1606.                                                         }
  1607.                                                 }
  1608.                                                 x++;
  1609.                                                 bufferIndex++;
  1610.                                         }
  1611.                                 }
  1612.                         }
  1613.                 }
  1614.                 //Else 8x16 sprite, check if its in range.
  1615.                 else if(y <= scanline && y+16 > scanline && x >= -7 && x < 256){
  1616.                         //Get the relevant tile.
  1617.                         if(toffset < 8){
  1618.                                 //First half of sprite.
  1619.                                 var t = this.ptTile[this.sprTile[0]+(this.vertFlip[0]?1:0)+((this.sprTile[0]&1) !== 0?255:0)];
  1620.                                 //Check if vertically flipped.
  1621.                                 if(this.vertFlip[0]){
  1622.                                         //Yes
  1623.                                         var toffset = 15-scanline+y;
  1624.                                 }
  1625.                                 else{
  1626.                                         //No
  1627.                                         var toffset = scanline-y;
  1628.                                 }
  1629.                         }
  1630.                         else{
  1631.                                 //Second half of sprite.
  1632.                                 var t = this.ptTile[this.sprTile[0]+(this.vertFlip[0]?0:1)+((this.sprTile[0]&1) !== 0?255:0)];
  1633.                                 //Check if vertically flipped.
  1634.                                 if(this.vertFlip[0]){
  1635.                                         //Yes
  1636.                                         var toffset = -scanline+y;
  1637.                                 }
  1638.                                 else{
  1639.                                         //No
  1640.                                         var toffset = scanline-y-8;
  1641.                                 }
  1642.                         }
  1643.                         //???
  1644.                         toffset *= 8;
  1645.                         //Cache the buffer index.
  1646.                         var bufferIndex = scanline*256+x;
  1647.                         //Check if horizontally flipped.
  1648.                         if(this.horiFlip[0]){
  1649.                                 //Yes
  1650.                                 for(var i=7;i>=0;i--){
  1651.                                         if(x>=0 && x<256){
  1652.                                                 if(bufferIndex >= 0 && bufferIndex < 61440 && this.pixRendered[bufferIndex] !== 0){
  1653.                                                         if(t.pixelColor[toffset+i] !== 0){
  1654.                                                                 //Set the hit coordinates and return true.
  1655.                                                                 this.spr0HitX = bufferIndex%256;
  1656.                                                                 this.spr0HitY = scanline;
  1657.                                                                 return true;
  1658.                                                         }
  1659.                                                 }
  1660.                                         }
  1661.                                         x++;
  1662.                                         bufferIndex++;
  1663.                                 }
  1664.                         }
  1665.                         else{
  1666.                                 //No
  1667.                                 for(var i=0;i<8;i++){
  1668.                                         if(x>=0 && x<256){
  1669.                                                 if(bufferIndex >= 0 && bufferIndex < 61440 && this.pixRendered[bufferIndex] !== 0){
  1670.                                                         if(t.pixelColor[toffset+i] !== 0){
  1671.                                                                 //Set the hit coordinates and return true.
  1672.                                                                 this.spr0HitX = bufferIndex%256;
  1673.                                                                 this.spr0HitY = scanline;
  1674.                                                                 return true;
  1675.                                                         }
  1676.                                                 }
  1677.                                         }
  1678.                                         x++;
  1679.                                         bufferIndex++;
  1680.                                 }
  1681.                         }
  1682.                 }
  1683.                 //No collisions detected, return false.
  1684.                 return false;
  1685.         },
  1686.  
  1687.                 //
  1688.                 //  Memory
  1689.                 //__________//
  1690.  
  1691.         /**
  1692.          * Updates the internally buffered image and sprite color palettes.
  1693.          */
  1694.  
  1695.         updatePalettes:function(){
  1696.                 //Updates the image and sprite color palettes from 0x3f00 to 0x3f20.
  1697.                 //Check if monochrome.
  1698.                 if(this.f_dispType === 0){
  1699.                         //No, update normally.
  1700.                         for(var i=0;i<16;i++){
  1701.                                 this.imgPalette[i] = this.colorPalette.curTable[this.vramMem[0x3f00+i]];
  1702.                                 this.sprPalette[i] = this.colorPalette.curTable[this.vramMem[0x3f10+i]];
  1703.                         }
  1704.                 }
  1705.                 else{
  1706.                         //Yes, & every color byte with 48, taking away its hue.
  1707.                         for(var i=0;i<16;i++){
  1708.                                 this.imgPalette[i] = this.colorPalette.curTable[this.vramMem[0x3f00+i]&48];
  1709.                                 this.sprPalette[i] = this.colorPalette.curTable[this.vramMem[0x3f10+i]&48];
  1710.                         }
  1711.                 }
  1712.         },
  1713.  
  1714.         /**
  1715.          * Writes the passed value into sprite memory at the specified address and updates the internal buffers accordingly.
  1716.          * @param {Number} address
  1717.          * @param {Number} value
  1718.          */
  1719.  
  1720.         writeSpriteMem:function(address,value){
  1721.                 //Write the value into the sprite memory.
  1722.                 this.spriteMem[address] = value;
  1723.                 //???
  1724.                 var tIndex = parseInt(address/4);
  1725.                 //???
  1726.                 if(tIndex === 0){
  1727.                         //Check sprite 0 for a hit.
  1728.                         this.checkSprite0();
  1729.                 }
  1730.                 //???
  1731.                 address %= 4;
  1732.                 //???
  1733.                 if(address === 0){
  1734.                         //Y Coordinate
  1735.                         this.sprY[tIndex] = value;
  1736.                 }
  1737.                 else if(address === 1){
  1738.                         //Tile Index
  1739.                         this.sprTile[tIndex] = value;
  1740.                 }
  1741.                 else if(address === 2){
  1742.                         //Attributes
  1743.                         this.vertFlip[tIndex] = (value&0x80) !== 0;
  1744.                         this.horiFlip[tIndex] = (value&0x40) !== 0;
  1745.                         this.bgPriority[tIndex] = (value&0x20) !== 0;
  1746.                         this.sprCol[tIndex] = (value&3)<<2;
  1747.                 }
  1748.                 else if(address === 3){
  1749.                         //X Coordinate
  1750.                         this.sprX[tIndex] = value;
  1751.                 }
  1752.         },
  1753.  
  1754.         //
  1755.         //  Color Palette
  1756.         //_________________//
  1757.  
  1758.         /**
  1759.          * @namespace The nes color pallette, will probably be removed eventually.
  1760.          */
  1761.  
  1762.         colorPalette:{
  1763.  
  1764.         //Properties
  1765.  
  1766.                 /**
  1767.                  * The color emphasis tables for the background color settings, set with loadNTSCPalette() or loadDefaultPalette().
  1768.                  * @type Array
  1769.                  */
  1770.  
  1771.                 emphTable:[],
  1772.  
  1773.                 /**
  1774.                  * The current emphasis table, set with setEmphasis().
  1775.                  * @type Array
  1776.                  */
  1777.  
  1778.                 curTable:[],
  1779.  
  1780.         //Methods
  1781.  
  1782.                 /**
  1783.                  * Loads and sets the ntsc color palette emphasis tables.
  1784.                  */
  1785.  
  1786.                 loadNTSCPalette:function nes_ppu_colorPalette_loadNTSCPalette(){
  1787.                         //Construct the color emphasis tables from these colors.
  1788.                         this.makeTables([5395026,11796480,10485760,11599933,7602281,91,95,6208,12048,543240,26368,1196544,7153664,0,0,0,12899815,16728064,14421538,16729963,14090399,6818519,6588,21681,27227,35843,43776,2918400,10777088,0,0,0,16316664,16755516,16742785,16735173,16730354,14633471,4681215,46327,57599,58229,259115,7911470,15065624,7895160,0,0,16777215,16773822,16300216,16300248,16758527,16761855,13095423,10148607,8973816,8650717,12122296,16119980,16777136,16308472,0,0]);
  1789.                         //Set the emphasis to 0, no emphasis.
  1790.                         this.setEmphasis(0);
  1791.                 },
  1792.  
  1793.                 /**
  1794.                  * Loads and sets the default color palette emphasis tables.
  1795.                  */
  1796.  
  1797.                 loadDefaultPalette:function nes_ppu_colorPalette_loadDefaultPalette(){
  1798.                         //Construct the color emphasis tables from these colors.
  1799.                         this.makeTables([7697781,2562959,171,4653215,9371767,11206675,10944512,8325888,4402944,18176,20736,16151,1785695,0,0,0,12369084,29679,2309103,8585459,12517567,15138907,14363392,13324047,9138944,38656,43776,37691,33675,0,0,0,16777215,4177919,6264831,10980349,16219135,16742327,16742243,16751419,15974207,8639251,5234507,5830808,60379,0,0,0,16777215,11266047,13096959,14142463,16762879,16762843,16760755,16767915,16770979,14942115,11269055,11796431,10485747,0,0,0]);
  1800.                         //Set the emphasis to 0, no emphasis.
  1801.                         this.setEmphasis(0);
  1802.                 },
  1803.  
  1804.                 /**
  1805.                  * Creates the emphasis tables from the given color palette.
  1806.                  * @param {Array} palete
  1807.                  */
  1808.  
  1809.                 makeTables:function nes_ppu_colorPalette_makeTables(palette){
  1810.                         //Calculate a table for each possible emphasis setting:
  1811.                         for(var emph=0;emph<8;emph++){
  1812.                                 //Set the color factors to full.
  1813.                                 var rFactor = gFactor = bFactor = 1;
  1814.                                 //Check to "highlight" greens.
  1815.                                 if(emph&1){
  1816.                                         rFactor = 0.75;
  1817.                                         bFactor = 0.75;
  1818.                                 }
  1819.                                 //Check to "highlight" blues.
  1820.                                 if(emph&2){
  1821.                                         rFactor = 0.75;
  1822.                                         gFactor = 0.75;
  1823.                                 }
  1824.                                 //Check to "highlight" reds.
  1825.                                 if(emph&4){
  1826.                                         gFactor = 0.75;
  1827.                                         bFactor = 0.75;
  1828.                                 }
  1829.                                 //Create a new array for the color emphasis.
  1830.                                 this.emphTable[emph] = new Array(64);
  1831.                                 //Calculate the table.
  1832.                                 for(var i=0;i<64;i++){
  1833.                                         //Cache the color.
  1834.                                         var color = palette[i];
  1835.                                         //Add the color with modified rgb values into the emphasis array.
  1836.                                         this.emphTable[emph][i] = (parseInt(((color>>16)&0xFF)*rFactor)<<16)|(parseInt(((color>>8)&0xFF)*gFactor)<<8)|parseInt((color&0xFF)*bFactor);
  1837.                                 }
  1838.                         }
  1839.                 },
  1840.  
  1841.                 /**
  1842.                  * Sets the active color emphasis.
  1843.                  * @param {Number} emphasisSetting
  1844.                  */
  1845.  
  1846.                 setEmphasis:function nes_ppu_colorPalette_setEmphasis(emphasisSetting){
  1847.                         //Set the current color table from the emphasis tables.
  1848.                         this.curTable = this.emphTable[emphasisSetting];
  1849.                 }
  1850.  
  1851.         }
  1852.  
  1853. };
  1854.  
  1855. //
  1856. //  Name Table
  1857. //______________//
  1858.  
  1859. /**
  1860.  * @class Holds tiles and their attributes in a psuedo two-dimensional array.
  1861.  * @param {Number} width
  1862.  * @param {Number} height
  1863.  */
  1864.  
  1865. nes.ppu.NameTable = function nes_ppu_NameTable(width,height){
  1866.         //Save the width of the name table.
  1867.         this.width = width;
  1868.         //Create the tile and attribute arrays from the width and height.
  1869.         this.tile = new Array(width*height);
  1870.         this.attrib = new Array(width*height);
  1871. };
  1872.  
  1873. nes.ppu.NameTable.prototype = {
  1874.  
  1875.         /**
  1876.          * Returns the tile at the specified index.
  1877.          * @returns {Tile}
  1878.          * @param {Number} x
  1879.          * @param {Number} y
  1880.          */
  1881.  
  1882.         getTileIndex:function nes_ppu_NameTable_getTileIndex(x,y){
  1883.                 //Return the tile at the specified index.
  1884.                 return this.tile[y*this.width+x];
  1885.         },
  1886.  
  1887.         /**
  1888.          * Returns the tile attributes at the specified index.
  1889.          * @returns {Number}
  1890.          * @param {Number} x
  1891.          * @param {Number} y
  1892.          */
  1893.  
  1894.         getAttrib:function nes_ppu_NameTable_getAttrib(x,y){
  1895.                 //Return the attribute at the specified index.
  1896.                 return this.attrib[y*this.width+x];
  1897.         },
  1898.  
  1899.         /**
  1900.          * Sets the tile attributes at the specified index.
  1901.          * @param {Number} index
  1902.          * @param {Number} value
  1903.          */
  1904.  
  1905.         writeAttrib:function nes_ppu_NameTable_writeAttrib(index,value){
  1906.                 //Get the x and y position of the index.
  1907.                 var basex = (index%8)*4;
  1908.                 var basey = parseInt(index/8)*4;
  1909.                 //???
  1910.                 for(var sqy=0;sqy<2;sqy++){
  1911.                         for(var sqx=0;sqx<2;sqx++){
  1912.                                 var add = (value>>(2*(sqy*2+sqx)))&3;
  1913.                                 for(var y=0;y<2;y++){
  1914.                                         for(var x=0;x<2;x++){
  1915.                                                 var tx = basex+sqx*2+x;
  1916.                                                 var ty = basey+sqy*2+y;
  1917.                                                 var attindex = ty*this.width+tx;
  1918.                                                 this.attrib[ty*this.width+tx] = (add<<2)&12;
  1919.                                         }
  1920.                                 }
  1921.                         }
  1922.                 }
  1923.         }
  1924.  
  1925. };
  1926.  
  1927. //
  1928. //  Tile
  1929. //________//
  1930.  
  1931. /**
  1932.  * @class Standard 8x8 pixel tile, used as sprites.
  1933.  */
  1934.  
  1935. Tile = function Tile(){
  1936.         //Pixel Colors
  1937.         this.pixelColor = new Array(64);
  1938.         //Row Opacity
  1939.         this.opaque = new Array(8);
  1940. };
  1941.  
  1942. Tile.prototype = {
  1943.  
  1944.         isTransparent:function(x,y){
  1945.                 return (this.pixelColor[(y<<3)+x]==0);
  1946.         },
  1947.  
  1948.         /**
  1949.          * Sets the pixel color/transparency to the specified scanline?
  1950.          * @param {Number} scanline
  1951.          * @param {Number} b1
  1952.          * @param {Number} b2
  1953.          */
  1954.  
  1955.         setScanline:function(scanline,b1,b2){
  1956.                 //???
  1957.                 var tIndex = scanline<<3;
  1958.                 //Loop through the 8 pixels on a row.
  1959.                 for(var x=0;x<8;x++){
  1960.                         //Set the pixel color/transparency?
  1961.                         this.pixelColor[tIndex+x] = ((b1>>(7-x))&1)+(((b2>>(7-x))&1)<<1);
  1962.                         //Check if transparent the pixel is transparent.
  1963.                         if(this.pixelColor[tIndex+x] === 0){
  1964.                                 //If so, set the scanline as having a transparent pixel.
  1965.                                 this.opaque[scanline] = false;
  1966.                         }
  1967.                 }
  1968.         },
  1969.  
  1970.         /**
  1971.          * Renders the tile using the sprite data from the specified index.
  1972.          * @param {Number} srcy1 The top bound for the tile.
  1973.          * @param {Number} srcy2 The bottom bound for the tile.
  1974.          * @param {Number} yAdd Amount of pixels the tile is rendered below the sprY value.
  1975.          * @param {Number} spriteIndex The sprite that is being rendered for.
  1976.          */
  1977.  
  1978.         render:function(srcy1,srcy2,yAdd,spriteIndex){
  1979.                 //Set the pixel left and right bounds.
  1980.                 var srcx1 = 0;
  1981.                 var srcx2 = 8;
  1982.                 //Get the position to draw the tile at.
  1983.                 var dx = nes.ppu.sprX[spriteIndex];
  1984.                 var dy = nes.ppu.sprY[spriteIndex]+yAdd;
  1985.                 //Check for an invalid position, remove?
  1986.                 if(dx < -7 || dx >= 256 || dy < -7 || dy >= 240){
  1987.                         return;
  1988.                 }
  1989.                 //Move the clipping x right if the sprite is past the left of the screen.
  1990.                 if(dx < 0){
  1991.                         srcx1 -= dx;
  1992.                 }
  1993.                 //Move the clipping x2 left if the sprite is past the right of the screen.
  1994.                 if(dx+srcx2 >= 256){
  1995.                         srcx2 = 256-dx;
  1996.                 }
  1997.                 //Move the clipping y down if the sprite is past the top of the screen.
  1998.                 if(dy < 0){
  1999.                         srcy1 -= dy;
  2000.                 }
  2001.                 //Move the clipping y2 up if the sprite is past the bottom of the screen.
  2002.                 if(dy+srcy2 >= 240){
  2003.                         srcy2 = 240-dy;
  2004.                 }
  2005.                 //Get the pixel index to start drawing the sprite at.
  2006.                 var screenIndex = dy*256+dx;
  2007.                 //Check the sprite orientation.
  2008.                 if(nes.ppu.horiFlip[spriteIndex]){
  2009.                         //Draw the sprite flipped horizontally and vertically.
  2010.                         if(nes.ppu.vertFlip[spriteIndex]){
  2011.                                 //Start drawing from the last pixel.
  2012.                                 var pixelIndex = 63;
  2013.                                 //Loop through the 64 tile pixels.
  2014.                                 for(var y=0;y<8;y++){
  2015.                                         for(var x=0;x<8;x++){
  2016.                                                 //Check if the pixel is within bounds.
  2017.                                                 if(x >= srcx1 && x < srcx2 && y >= srcy1 && y < srcy2){
  2018.                                                         //Get the color index.
  2019.                                                         var colorIndex = this.pixelColor[pixelIndex];
  2020.                                                         var tpri = nes.ppu.pixRendered[screenIndex];
  2021.                                                         if(colorIndex !== 0 && spriteIndex <= (tpri&0xFF)){
  2022.                                                                 //Set the color from the nes.ppu.sprPalette to the frame buffer.
  2023.                                                                 nes.ppu.setPixelInBuffer(screenIndex,nes.ppu.sprPalette[colorIndex+nes.ppu.sprCol[spriteIndex]]);
  2024.                                                                 nes.ppu.pixRendered[screenIndex] = (tpri&0xF00)|spriteIndex;
  2025.                                                         }
  2026.                                                 }
  2027.                                                 //Move to the next pixel on screen.
  2028.                                                 screenIndex++;
  2029.                                                 //Move backwards across the tile pixels.
  2030.                                                 pixelIndex--;
  2031.                                         }
  2032.                                         screenIndex += 248;
  2033.                                 }
  2034.                         }
  2035.                         //Draw the sprite flipped horizontally.
  2036.                         else{
  2037.                                 //Start drawing from the last pixel on the first row.
  2038.                                 var pixelIndex = 7;
  2039.                                 //Loop through the 64 tile pixels.
  2040.                                 for(var y=0;y<8;y++){
  2041.                                         for(var x=0;x<8;x++){
  2042.                                                 //Check if the pixel is within bounds.
  2043.                                                 if(x >= srcx1 && x < srcx2 && y >= srcy1 && y < srcy2){
  2044.                                                         //Get the color index.
  2045.                                                         var colorIndex = this.pixelColor[pixelIndex];
  2046.                                                         var tpri = nes.ppu.pixRendered[screenIndex];
  2047.                                                         if(colorIndex !== 0 && spriteIndex <= (tpri&0xFF)){
  2048.                                                                 //Set the color from the nes.ppu.sprPalette to the frame buffer.
  2049.                                                                 nes.ppu.setPixelInBuffer(screenIndex,nes.ppu.sprPalette[colorIndex+nes.ppu.sprCol[spriteIndex]]);
  2050.                                                                 nes.ppu.pixRendered[screenIndex] = (tpri&0xF00)|spriteIndex;
  2051.                                                         }
  2052.                                                 }
  2053.                                                 //Move to the next pixel on screen.
  2054.                                                 screenIndex++;
  2055.                                                 //Move backwards across the tile pixels.
  2056.                                                 pixelIndex--;
  2057.                                         }
  2058.                                         //Move to the next row on the screen.
  2059.                                         screenIndex += 248;
  2060.                                         //Move the pixel index down a row.
  2061.                                         pixelIndex += 16;
  2062.                                 }
  2063.                         }
  2064.                 }
  2065.                 //Draw the sprite flipped vertically.
  2066.                 else if(nes.ppu.vertFlip[spriteIndex]){
  2067.                         //Start drawing from the first pixel on the last row.
  2068.                         var pixelIndex = 56;
  2069.                         //Loop through the 64 tile pixels.
  2070.                         for(var y=0;y<8;y++){
  2071.                                 for(var x=0;x<8;x++){
  2072.                                         //Check if the pixel is within bounds.
  2073.                                         if(x >= srcx1 && x < srcx2 && y >= srcy1 && y < srcy2){
  2074.                                                 //Get the color index.
  2075.                                                 var colorIndex = this.pixelColor[pixelIndex];
  2076.                                                 var tpri = nes.ppu.pixRendered[screenIndex];
  2077.                                                 if(colorIndex !== 0 && spriteIndex <= (tpri&0xFF)){
  2078.                                                         //Set the color from the nes.ppu.sprPalette to the frame buffer.
  2079.                                                         nes.ppu.setPixelInBuffer(screenIndex,nes.ppu.sprPalette[colorIndex+nes.ppu.sprCol[spriteIndex]]);
  2080.                                                         nes.ppu.pixRendered[screenIndex] = (tpri&0xF00)|spriteIndex;
  2081.                                                 }
  2082.                                         }
  2083.                                         //Move to the next pixel on screen.
  2084.                                         screenIndex++;
  2085.                                         //Move to the next pixel in the tile.
  2086.                                         pixelIndex++;
  2087.                                 }
  2088.                                 //Move to the next row on the screen.
  2089.                                 screenIndex += 248;
  2090.                                 //Move the pixel index up a row.
  2091.                                 pixelIndex -= 16;
  2092.                         }
  2093.                 }
  2094.                 //Draw the sprite normally.
  2095.                 else{
  2096.                         //Start drawing at the first pixel.
  2097.                         var pixelIndex = 0;
  2098.                         //Loop through the 64 tile pixels.
  2099.                         for(var y=0;y<8;y++){
  2100.                                 for(var x=0;x<8;x++){
  2101.                                         //Check if the pixel is within bounds.
  2102.                                         if(x >= srcx1 && x < srcx2 && y >= srcy1 && y < srcy2){
  2103.                                                 //Get the color index.
  2104.                                                 var colorIndex = this.pixelColor[pixelIndex];
  2105.                                                 var tpri = nes.ppu.pixRendered[screenIndex];
  2106.                                                 if(colorIndex !== 0 && spriteIndex <= (tpri&0xFF)){
  2107.                                                         //Set the color from the nes.ppu.sprPalette to the frame buffer.
  2108.                                                         nes.ppu.setPixelInBuffer(screenIndex,nes.ppu.sprPalette[colorIndex+nes.ppu.sprCol[spriteIndex]]);
  2109.                                                         nes.ppu.pixRendered[screenIndex] = (tpri&0xF00)|spriteIndex;
  2110.                                                 }
  2111.                                         }
  2112.                                         //Move to the next pixel on screen.
  2113.                                         screenIndex++;
  2114.                                         //Move to the next pixel in the tile.
  2115.                                         pixelIndex++;
  2116.                                 }
  2117.                                 //Move to the next row on the screen.
  2118.                                 screenIndex += 248;
  2119.                         }
  2120.                 }
  2121.         }
  2122.  
  2123. };

Raw Paste


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