meowio/jsnes/src/rom.js
Jeremy Penner 8dc252d70f git subrepo clone https://github.com/bfirsh/jsnes.git
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:   "???"
2024-10-29 20:12:13 -04:00

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;