Jeremy Penner
8dc252d70f
subrepo: subdir: "jsnes" merged: "d8021d0" upstream: origin: "https://github.com/bfirsh/jsnes.git" branch: "master" commit: "d8021d0" git-subrepo: version: "0.4.9" origin: "???" commit: "???"
205 lines
5.5 KiB
JavaScript
205 lines
5.5 KiB
JavaScript
var Mappers = require("./mappers");
|
|
var Tile = require("./tile");
|
|
|
|
var ROM = function (nes) {
|
|
this.nes = nes;
|
|
|
|
this.mapperName = new Array(92);
|
|
|
|
for (var i = 0; i < 92; i++) {
|
|
this.mapperName[i] = "Unknown Mapper";
|
|
}
|
|
this.mapperName[0] = "Direct Access";
|
|
this.mapperName[1] = "Nintendo MMC1";
|
|
this.mapperName[2] = "UNROM";
|
|
this.mapperName[3] = "CNROM";
|
|
this.mapperName[4] = "Nintendo MMC3";
|
|
this.mapperName[5] = "Nintendo MMC5";
|
|
this.mapperName[6] = "FFE F4xxx";
|
|
this.mapperName[7] = "AOROM";
|
|
this.mapperName[8] = "FFE F3xxx";
|
|
this.mapperName[9] = "Nintendo MMC2";
|
|
this.mapperName[10] = "Nintendo MMC4";
|
|
this.mapperName[11] = "Color Dreams Chip";
|
|
this.mapperName[12] = "FFE F6xxx";
|
|
this.mapperName[15] = "100-in-1 switch";
|
|
this.mapperName[16] = "Bandai chip";
|
|
this.mapperName[17] = "FFE F8xxx";
|
|
this.mapperName[18] = "Jaleco SS8806 chip";
|
|
this.mapperName[19] = "Namcot 106 chip";
|
|
this.mapperName[20] = "Famicom Disk System";
|
|
this.mapperName[21] = "Konami VRC4a";
|
|
this.mapperName[22] = "Konami VRC2a";
|
|
this.mapperName[23] = "Konami VRC2a";
|
|
this.mapperName[24] = "Konami VRC6";
|
|
this.mapperName[25] = "Konami VRC4b";
|
|
this.mapperName[32] = "Irem G-101 chip";
|
|
this.mapperName[33] = "Taito TC0190/TC0350";
|
|
this.mapperName[34] = "32kB ROM switch";
|
|
|
|
this.mapperName[64] = "Tengen RAMBO-1 chip";
|
|
this.mapperName[65] = "Irem H-3001 chip";
|
|
this.mapperName[66] = "GNROM switch";
|
|
this.mapperName[67] = "SunSoft3 chip";
|
|
this.mapperName[68] = "SunSoft4 chip";
|
|
this.mapperName[69] = "SunSoft5 FME-7 chip";
|
|
this.mapperName[71] = "Camerica chip";
|
|
this.mapperName[78] = "Irem 74HC161/32-based";
|
|
this.mapperName[91] = "Pirate HK-SF3 chip";
|
|
};
|
|
|
|
ROM.prototype = {
|
|
// Mirroring types:
|
|
VERTICAL_MIRRORING: 0,
|
|
HORIZONTAL_MIRRORING: 1,
|
|
FOURSCREEN_MIRRORING: 2,
|
|
SINGLESCREEN_MIRRORING: 3,
|
|
SINGLESCREEN_MIRRORING2: 4,
|
|
SINGLESCREEN_MIRRORING3: 5,
|
|
SINGLESCREEN_MIRRORING4: 6,
|
|
CHRROM_MIRRORING: 7,
|
|
|
|
header: null,
|
|
rom: null,
|
|
vrom: null,
|
|
vromTile: null,
|
|
|
|
romCount: null,
|
|
vromCount: null,
|
|
mirroring: null,
|
|
batteryRam: null,
|
|
trainer: null,
|
|
fourScreen: null,
|
|
mapperType: null,
|
|
valid: false,
|
|
|
|
load: function (data) {
|
|
var i, j, v;
|
|
|
|
if (data.indexOf("NES\x1a") === -1) {
|
|
throw new Error("Not a valid NES ROM.");
|
|
}
|
|
this.header = new Array(16);
|
|
for (i = 0; i < 16; i++) {
|
|
this.header[i] = data.charCodeAt(i) & 0xff;
|
|
}
|
|
this.romCount = this.header[4];
|
|
this.vromCount = this.header[5] * 2; // Get the number of 4kB banks, not 8kB
|
|
this.mirroring = (this.header[6] & 1) !== 0 ? 1 : 0;
|
|
this.batteryRam = (this.header[6] & 2) !== 0;
|
|
this.trainer = (this.header[6] & 4) !== 0;
|
|
this.fourScreen = (this.header[6] & 8) !== 0;
|
|
this.mapperType = (this.header[6] >> 4) | (this.header[7] & 0xf0);
|
|
/* TODO
|
|
if (this.batteryRam)
|
|
this.loadBatteryRam();*/
|
|
// Check whether byte 8-15 are zero's:
|
|
var foundError = false;
|
|
for (i = 8; i < 16; i++) {
|
|
if (this.header[i] !== 0) {
|
|
foundError = true;
|
|
break;
|
|
}
|
|
}
|
|
if (foundError) {
|
|
this.mapperType &= 0xf; // Ignore byte 7
|
|
}
|
|
// Load PRG-ROM banks:
|
|
this.rom = new Array(this.romCount);
|
|
var offset = 16;
|
|
for (i = 0; i < this.romCount; i++) {
|
|
this.rom[i] = new Array(16384);
|
|
for (j = 0; j < 16384; j++) {
|
|
if (offset + j >= data.length) {
|
|
break;
|
|
}
|
|
this.rom[i][j] = data.charCodeAt(offset + j) & 0xff;
|
|
}
|
|
offset += 16384;
|
|
}
|
|
// Load CHR-ROM banks:
|
|
this.vrom = new Array(this.vromCount);
|
|
for (i = 0; i < this.vromCount; i++) {
|
|
this.vrom[i] = new Array(4096);
|
|
for (j = 0; j < 4096; j++) {
|
|
if (offset + j >= data.length) {
|
|
break;
|
|
}
|
|
this.vrom[i][j] = data.charCodeAt(offset + j) & 0xff;
|
|
}
|
|
offset += 4096;
|
|
}
|
|
|
|
// Create VROM tiles:
|
|
this.vromTile = new Array(this.vromCount);
|
|
for (i = 0; i < this.vromCount; i++) {
|
|
this.vromTile[i] = new Array(256);
|
|
for (j = 0; j < 256; j++) {
|
|
this.vromTile[i][j] = new Tile();
|
|
}
|
|
}
|
|
|
|
// Convert CHR-ROM banks to tiles:
|
|
var tileIndex;
|
|
var leftOver;
|
|
for (v = 0; v < this.vromCount; v++) {
|
|
for (i = 0; i < 4096; i++) {
|
|
tileIndex = i >> 4;
|
|
leftOver = i % 16;
|
|
if (leftOver < 8) {
|
|
this.vromTile[v][tileIndex].setScanline(
|
|
leftOver,
|
|
this.vrom[v][i],
|
|
this.vrom[v][i + 8]
|
|
);
|
|
} else {
|
|
this.vromTile[v][tileIndex].setScanline(
|
|
leftOver - 8,
|
|
this.vrom[v][i - 8],
|
|
this.vrom[v][i]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.valid = true;
|
|
},
|
|
|
|
getMirroringType: function () {
|
|
if (this.fourScreen) {
|
|
return this.FOURSCREEN_MIRRORING;
|
|
}
|
|
if (this.mirroring === 0) {
|
|
return this.HORIZONTAL_MIRRORING;
|
|
}
|
|
return this.VERTICAL_MIRRORING;
|
|
},
|
|
|
|
getMapperName: function () {
|
|
if (this.mapperType >= 0 && this.mapperType < this.mapperName.length) {
|
|
return this.mapperName[this.mapperType];
|
|
}
|
|
return "Unknown Mapper, " + this.mapperType;
|
|
},
|
|
|
|
mapperSupported: function () {
|
|
return typeof Mappers[this.mapperType] !== "undefined";
|
|
},
|
|
|
|
createMapper: function () {
|
|
if (this.mapperSupported()) {
|
|
return new Mappers[this.mapperType](this.nes);
|
|
} else {
|
|
throw new Error(
|
|
"This ROM uses a mapper not supported by JSNES: " +
|
|
this.getMapperName() +
|
|
"(" +
|
|
this.mapperType +
|
|
")"
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
module.exports = ROM;
|