Permalink
Browse files

Oh my! JSNES now runs at full speed on Chrome (thanks Connor!)

  • Loading branch information...
1 parent fefe8f9 commit ed8bb73e37b7b3e53f37f69a927e72dca7ef7b14 @bfirsh bfirsh committed Aug 11, 2009
Showing with 37 additions and 34 deletions.
  1. +9 −10 index.html
  2. +24 −20 ppu.js
  3. +4 −4 tile.js
View
@@ -1,15 +1,13 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<!DOCTYPE html>
+<html lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSNES: A Javascript NES emulator</title>
<link rel="stylesheet" href="/style.3.css" type="text/css" media="screen"
-charset="utf-8" />
+charset="utf-8">
<link rel="stylesheet" href="jsnes.1.css" type="text/css" media="screen"
-charset="utf-8" />
+charset="utf-8">
<script src="jquery-1.2.6.min.js" type="text/javascript" charset="utf-8"></script>
<script src="jquery.dimensions.min.js" type="text/javascript" charset="utf-8"></script>
@@ -54,8 +52,8 @@
<canvas id="screen" width="256" height="240"></canvas>
<div id="controls">
<select id="roms"></select>
- <input type="button" value="pause" id="pause" disabled="disabled" />
- <input type="button" value="restart" id="restart" disabled="disabled" />
+ <input type="button" value="pause" id="pause" disabled="disabled">
+ <input type="button" value="restart" id="restart" disabled="disabled">
</div>
<p id="status">Loading...</p>
</div>
@@ -110,8 +108,9 @@
<h2>About</h2>
<p>A few months ago, I stumbled across Matt Westcott's excellent <a href="http://matt.west.co.tt/spectrum/jsspeccy/">JSSpeccy</a>. I had seen some pretty imaginative <a href="http://en.wikipedia.org/wiki/Canvas_(HTML_element)">canvas</a> creations, but Javascript emulators? What a perfect idea for a daft new project.</p>
<p>I got underway shamelessly porting <a href="http://www.virtualnes.com/">vNES</a> into Javascript. Although not the most efficient, it didn't have any of the pointer memory mapping magic associated with emulators written in lower level languages. As such, it was more or less a direct port, bar a few tweaks to compensate for the lack of static typing, and obviously a rewrite of all the I/O.</p>
- <p>Thanks to some meticulous performance analysis made beforehand (it might work?!), JSNES is actually quite playable on modern Javascript engines with a reasonably fast CPU. I'd suggest you use <strong>Firefox 3.5</strong>, <strong>Safari 4</strong> or some recent Google Chrome build that supports canvas, otherwise it's far too slow.</p>
+ <p>I highly recommend you use <a href="http://www.google.com/chrome">Google Chrome</a> to play JSNES. Thanks to its high performance canvas element, and a clever optimisation by Connor Dunn, it runs at full speed on modern computers. <a href="http://build.chromium.org/buildbot/snapshots/chromium-rel-mac/">Mac builds</a> are also available. Otherwise, it just about runs on <a href="http://getfirefox.com/">Firefox 3.5</a> or <a href="http://www.apple.com/safari/">Safari 4</a>, but it's hardly playable.</p>
<p>The source is available on <a href="http://github.com/bfirsh/jsnes/">Github</a>, contributions welcome!</p>
+
<script type="text/javascript" charset="utf-8">
// Mouse events
$("#screen").mousedown(function(e){
View
@@ -114,6 +114,8 @@ function PPU(nes) {
// Variables used when rendering:
this.attrib = new Array(32);
+ this.buffer = new Array(256*240);
+ this.prevBuffer = new Array(256*240);
this.bgbuffer = new Array(256*240);
this.pixrendered = new Array(256*240);
this.spr0dummybuffer = new Array(256*240);
@@ -480,7 +482,7 @@ function PPU(nes) {
}
for(var i=0;i<256*240;i++) {
- this.writePixel(i, bgColor);
+ this.buffer[i] = bgColor;
}
for(var i=0;i<this.pixrendered.length;i++) {
this.pixrendered[i]=65;
@@ -495,19 +497,19 @@ function PPU(nes) {
// Spr 0 position:
if(this.sprX[0]>=0 && this.sprX[0]<256 && this.sprY[0]>=0 && this.sprY[0]<240){
for(var i=0;i<256;i++){
- this.writePixel((this.sprY[0]<<8)+i, 0xFF5555);
+ this.buffer[(this.sprY[0]<<8)+i] = 0xFF5555;
}
for(var i=0;i<240;i++){
- this.writePixel((i<<8)+this.sprX[0], 0xFF5555);
+ this.buffer[(i<<8)+this.sprX[0]] = 0xFF5555;
}
}
// Hit position:
if(this.spr0HitX>=0 && this.spr0HitX<256 && this.spr0HitY>=0 && this.spr0HitY<240){
for(var i=0;i<256;i++){
- this.writePixel((this.spr0HitY<<8)+i, 0x55FF55);
+ this.buffer[(this.spr0HitY<<8)+i] = 0x55FF55;
}
for(var i=0;i<240;i++){
- this.writePixel((i<<8)+this.spr0HitX, 0x55FF55);
+ this.buffer[(i<<8)+this.spr0HitX] = 0x55FF55;
}
}
}
@@ -519,7 +521,7 @@ function PPU(nes) {
// Clip left 8-pixels column:
for(var y=0;y<240;y++){
for(var x=0;x<8;x++){
- this.writePixel((y<<8)+x, 0);
+ this.buffer[(y<<8)+x] = 0;
}
}
}
@@ -528,7 +530,7 @@ function PPU(nes) {
// Clip right 8-pixels column too:
for(var y=0;y<240;y++){
for(var x=0;x<8;x++){
- this.writePixel((y<<8)+255-x, 0);
+ this.buffer[(y<<8)+255-x] = 0;
}
}
}
@@ -537,12 +539,21 @@ function PPU(nes) {
if(this.clipToTvSize){
for(var y=0;y<8;y++){
for(var x=0;x<256;x++){
- this.writePixel((y<<8)+x, 0);
- this.writePixel(((239-y)<<8)+x, 0);
+ this.buffer[(y<<8)+x] = 0;
+ this.buffer[((239-y)<<8)+x] = 0;
}
}
}
+ for (var i=0;i<256*240;i++) {
+ if (this.buffer[i] != this.prevBuffer[i]) {
+ var j = i*4;
+ this.nes.imageData.data[j] = this.buffer[i]&0xFF;
+ this.nes.imageData.data[j+1] = (this.buffer[i]>>8)&0xFF;
+ this.nes.imageData.data[j+2] = (this.buffer[i]>>16)&0xFF;
+ this.prevBuffer[i] = this.buffer[i];
+ }
+ }
}
this.updateControlReg1 = function(value){
@@ -946,8 +957,8 @@ function PPU(nes) {
for(this.destIndex=this.si;this.destIndex<this.ei;this.destIndex++){
if(this.pixrendered[this.destIndex]>0xFF){
//console.log("Writing "+this.imgPalette[this.col+this.att].toString(16)+" to buffer at "+this.destIndex.toString(16));
- this.writePixel(this.destIndex,
- this.bgbuffer[this.destIndex]);
+ this.buffer[this.destIndex] =
+ this.bgbuffer[this.destIndex];
}
}
}
@@ -1031,7 +1042,7 @@ function PPU(nes) {
this.bgbuffer[this.destIndex] = pix;
}
else {
- this.writePixel(this.destIndex, pix);
+ this.buffer[this.destIndex] = pix;
}
this.pixrendered[this.destIndex] |= 256;
this.destIndex++;
@@ -1047,7 +1058,7 @@ function PPU(nes) {
this.bgbuffer[this.destIndex] = pix;
}
else {
- this.writePixel(this.destIndex, pix);
+ this.buffer[this.destIndex] = pix;
}
this.pixrendered[this.destIndex] |= 256;
}
@@ -1492,13 +1503,6 @@ function PPU(nes) {
}
- this.writePixel = function(index, value){
- var j = index*4;
- this.nes.imageData.data[j] = value&0xFF;
- this.nes.imageData.data[j+1] = (value>>8)&0xFF;
- this.nes.imageData.data[j+2] = (value>>16)&0xFF;
- }
-
this.reset = function() {
this.vramBufferedReadValue = 0;
View
@@ -68,7 +68,7 @@ Tile.prototype.render = function(srcx1, srcy1, srcx2, srcy2, dx, dy, palAdd, pal
this.tpri = priTable[this.fbIndex];
if(this.palIndex!=0 && pri<=(this.tpri&0xFF)){
//console.log("Rendering upright tile to buffer");
- Globals.nes.ppu.writePixel(this.fbIndex, palette[this.palIndex+palAdd]);
+ Globals.nes.ppu.buffer[this.fbIndex] = palette[this.palIndex+palAdd];
this.tpri = (this.tpri&0xF00)|pri;
priTable[this.fbIndex] =this.tpri;
}
@@ -90,7 +90,7 @@ Tile.prototype.render = function(srcx1, srcy1, srcx2, srcy2, dx, dy, palAdd, pal
this.palIndex = this.pix[this.tIndex];
this.tpri = priTable[this.fbIndex];
if(this.palIndex!=0 && pri<=(this.tpri&0xFF)){
- Globals.nes.ppu.writePixel(this.fbIndex, palette[this.palIndex+palAdd]);
+ Globals.nes.ppu.buffer[this.fbIndex] = palette[this.palIndex+palAdd];
this.tpri = (this.tpri&0xF00)|pri;
priTable[this.fbIndex] =this.tpri;
}
@@ -113,7 +113,7 @@ Tile.prototype.render = function(srcx1, srcy1, srcx2, srcy2, dx, dy, palAdd, pal
this.palIndex = this.pix[this.tIndex];
this.tpri = priTable[this.fbIndex];
if(this.palIndex!=0 && pri<=(this.tpri&0xFF)){
- Globals.nes.ppu.writePixel(this.fbIndex, palette[this.palIndex+palAdd]);
+ Globals.nes.ppu.buffer[this.fbIndex] = palette[this.palIndex+palAdd];
this.tpri = (this.tpri&0xF00)|pri;
priTable[this.fbIndex] =this.tpri;
}
@@ -136,7 +136,7 @@ Tile.prototype.render = function(srcx1, srcy1, srcx2, srcy2, dx, dy, palAdd, pal
this.palIndex = this.pix[this.tIndex];
this.tpri = priTable[this.fbIndex];
if(this.palIndex!=0 && pri<=(this.tpri&0xFF)){
- Globals.nes.ppu.writePixel(this.fbIndex, palette[this.palIndex+palAdd]);
+ Globals.nes.ppu.buffer[this.fbIndex] = palette[this.palIndex+palAdd];
this.tpri = (this.tpri&0xF00)|pri;
priTable[this.fbIndex] =this.tpri;
}

0 comments on commit ed8bb73

Please sign in to comment.