UNPKG

nes-js

Version:
388 lines (311 loc) 8.16 kB
import {Memory} from './Memory.js'; import {Mapper, MapperFactory} from './Mapper.js'; import {Utility} from './Utility.js'; /** * */ function Rom(arrayBuffer) { Memory.call(this, arrayBuffer); this.header = new RomHeader(this); if(this.isNes() === false) throw new Error('This rom doesn\'t seem iNES format.'); this.mapperFactory = new MapperFactory(); this.mapper = this.mapperFactory.create(this.header.getMapperNum(), this); } // Rom.MIRRORINGS = { SINGLE_SCREEN: 0, HORIZONTAL: 1, VERTICAL: 2, FOUR_SCREEN: 3 }; // Rom.prototype = Object.assign(Object.create(Memory.prototype), { isRom: true, // MIRRORINGS: Rom.MIRRORINGS, // /** * */ load: function(address) { if(address < 0x2000) { var offset = this.header.getPRGROMBanksNum() * 0x4000 + this.getHeaderSize(); return this.data[this.mapper.mapForChrRom(address) + offset]; } else { var offset = -0x8000 + this.getHeaderSize(); return this.data[this.mapper.map(address) + offset]; } }, /** * */ store: function(address, value) { this.mapper.store(address, value); }, /** * */ isNes: function() { return this.header.isNes(); }, /** * */ getHeaderSize: function() { return this.header.getSize(); }, /** * */ hasChrRom: function() { return this.header.hasChrRom(); }, /** * */ getMirroringType: function() { return this.mapper.getMirroringType(); }, // dump methods /** * */ dumpHeader: function() { return this.header.dump(); }, /** * */ _getStartDumpAddress: function() { return this.getHeaderSize(); }, /** * */ _getEndDumpAddress: function() { return this.getCapacity(); } }); /** * */ function RomHeader(rom) { this.rom = rom; } Object.assign(RomHeader.prototype, { isRomHeader: true, size: 16, // 16bytes // VALID_SIGNATURE: 'NES', VALID_MAGIC_NUMBER: 0x1a, // SIGNATURE_ADDRESS: 0, SIGNATURE_SIZE: 3, MAGIC_NUMBER_ADDRESS: 3, MAGIC_NUMBER_SIZE: 1, PRG_ROM_BANKS_NUM_ADDRESS: 4, PRG_ROM_BANKS_NUM_SIZE: 1, CHR_ROM_BANKS_NUM_ADDRESS: 5, CHR_ROM_BANKS_NUM_SIZE: 1, CONTROL_BYTE1_ADDRESS: 6, CONTROL_BYTE1_SIZE: 1, CONTROL_BYTE2_ADDRESS: 7, CONTROL_BYTE2_SIZE: 1, RAM_BANKS_NUM_ADDRESS: 8, RAM_BANKS_NUM_SIZE: 1, UNUSED_ADDRESS: 9, UNUSED_SIZE: 7, // MIRRORING_TYPE_BIT: 0, MIRRORING_TYPE_BITS_WIDTH: 1, MIRRORING_TYPE_HORIZONTAL: 0, MIRRORING_TYPE_VERTICAL: 1, BATTERY_BACKED_RAM_BIT: 1, BATTERY_BACKED_RAM_BITS_WIDTH: 1, TRAINER_512BYTES_BIT: 2, TRAINER_512BYTES_BITS_WIDTH: 1, FOUR_SCREEN_MIRRORING_BIT: 3, FOUR_SCREEN_MIRRORING_BITS_WIDTH: 1, MAPPER_LOWER_BIT: 4, MAPPER_LOWER_BITS_WIDTH: 4, MAPPER_HIGHER_BIT: 4, MAPPER_HIGHER_BITS_WIDTH: 4, // /** * */ getSize: function() { return this.size; }, /** * */ isNes: function() { if(this.getSignature() !== this.VALID_SIGNATURE) return false; if(this.getMagicNumber() !== this.VALID_MAGIC_NUMBER) return false; return true; }, // /** * */ load: function(address) { return this.rom.loadWithoutMapping(address); }, /** * */ getSignature: function() { var str = ''; for(var i = 0; i < this.SIGNATURE_SIZE; i++) str += String.fromCharCode(this.load(this.SIGNATURE_ADDRESS + i)); return str; }, /** * */ getMagicNumber: function() { return this.load(this.MAGIC_NUMBER_ADDRESS); }, /** * */ getPRGROMBanksNum: function() { return this.load(this.PRG_ROM_BANKS_NUM_ADDRESS); }, /** * */ getCHRROMBanksNum: function() { return this.load(this.CHR_ROM_BANKS_NUM_ADDRESS); }, /** * */ hasChrRom: function() { return this.getCHRROMBanksNum() > 0; }, /** * */ getControlByte1: function() { return this.load(this.CONTROL_BYTE1_ADDRESS); }, /** * */ getControlByte2: function() { return this.load(this.CONTROL_BYTE2_ADDRESS); }, /** * */ getRAMBanksNum: function() { return this.load(this.RAM_BANKS_NUM_ADDRESS); }, /** * */ getUnusedField: function() { var value = 0; for(var i = 0; i < this.UNUSED_SIZE; i++) value = (value << 8) | this.load(this.UNUSED_ADDRESS + i); return value; }, // /** * */ extractBits: function(value, offset, size) { return (value >> offset) & ((1 << size) - 1); }, /** * */ getMirroringType: function() { return this.extractBits(this.getControlByte1(), this.MIRRORING_TYPE_BIT, this.MIRRORING_TYPE_BITS_WIDTH); }, /** * */ getMirroringTypeAsStrings: function() { return (this.getMirroringType() === this.MIRRORING_TYPE_HORIZONTAL) ? 'horizontal' : 'vertical'; }, /** * */ isHorizontalMirroring: function() { return this.getMirroringType() === this.MIRRORING_TYPE_HORIZONTAL; }, /** * */ getBatteryBackedRAM: function() { return this.extractBits(this.getControlByte1(), this.BATTERY_BACKED_RAM_BIT, this.BATTERY_BACKED_RAM_BITS_WIDTH); }, /** * */ getTrainer512Bytes: function() { return this.extractBits(this.getControlByte1(), this.TRAINER_512BYTES_BIT, this.TRAINER_512BYTES_BITS_WIDTH); }, /** * */ getFourScreenMirroring: function() { return this.extractBits(this.getControlByte1(), this.FOUR_SCREEN_MIRRORING_BIT, this.FOUR_SCREEN_MIRRORING_BITS_WIDTH); }, /** * */ getMapperNum: function() { var lowerBits = this.extractBits(this.getControlByte1(), this.MAPPER_LOWER_BIT, this.MAPPER_LOWER_BITS_WIDTH); var higherBits = this.extractBits(this.getControlByte2(), this.MAPPER_HIGHER_BIT, this.MAPPER_HIGHER_BITS_WIDTH); return (higherBits << this.MAPPER_LOWER_BITS_WIDTH) | lowerBits; }, /** * */ dump: function() { var buffer = ''; buffer += '0x '; for(var i = 0; i < this.getSize(); i++) { buffer += Utility.convertDecToHexString(this.load(i), 2, true) + ' '; } buffer += '\n\n'; buffer += 'Signature: ' + this.getSignature() + '\n'; buffer += 'Magic Number: ' + Utility.convertDecToHexString(this.getMagicNumber(), 2) + '\n'; buffer += 'PRG-ROM banks num: ' + Utility.convertDecToHexString(this.getPRGROMBanksNum(), 2) + '\n'; buffer += 'CHR-ROM banks num: ' + Utility.convertDecToHexString(this.getCHRROMBanksNum(), 2) + '\n'; buffer += 'Control1: ' + Utility.convertDecToHexString(this.getControlByte1(), 2) + '\n'; buffer += 'Control2: ' + Utility.convertDecToHexString(this.getControlByte2(), 2) + '\n'; buffer += 'RAM banks num: ' + Utility.convertDecToHexString(this.getRAMBanksNum(), 2) + '\n'; buffer += 'Unused field: ' + Utility.convertDecToHexString(this.getUnusedField(), 14) + '\n'; buffer += '\n'; buffer += 'In control bytes\n'; buffer += 'Mirroring type: ' + Utility.convertDecToHexString(this.getMirroringType()) + '(' + this.getMirroringTypeAsStrings() + ')\n'; buffer += 'Battery-backed RAM: ' + Utility.convertDecToHexString(this.getBatteryBackedRAM()) + '\n'; buffer += '512-byte trainer: ' + Utility.convertDecToHexString(this.getTrainer512Bytes()) + '\n'; buffer += 'Four screen mirroring: ' + Utility.convertDecToHexString(this.getFourScreenMirroring()) + '\n'; buffer += 'Mapper number: ' + Utility.convertDecToHexString(this.getMapperNum(), 2) + '(' + this.rom.mapperFactory.getName(this.getMapperNum()) + ')'; return buffer; } }); export {Rom};