| var Memory = require('src/memory'); | |||||
| class Bank extends Memory{ | |||||
| constructor(size, ro){ | |||||
| super(); | |||||
| this.__ro = (ro === true); | |||||
| this.__map = null; | |||||
| this.__addr = 0; | |||||
| if (size > 0){ | |||||
| this.__map = new Uint8Array(size); | |||||
| } | |||||
| this.__listeners = {}; | |||||
| } | |||||
| get size(){return (this.__map) ? this.__map.length : 0;} | |||||
| get address(){return this.__addr;} | |||||
| set address(a){ | |||||
| if (this.__map) | |||||
| this.__addr = Math.min(this.__map.length, Math.max(0, a)); | |||||
| } | |||||
| get byte(){return (this.__map) ? this.__map[this.__addr] : -1;} | |||||
| set byte(b){ | |||||
| if (!this.__ro && this.__map){ | |||||
| this.__map[this.__addr] = b; | |||||
| if (this.__addr in this.__listeners) | |||||
| this.__listeners[this.__addr].forEach((fn)=>{fn(b);}); | |||||
| } | |||||
| } | |||||
| onAddressWrite(addr, fn){ | |||||
| if (addr < 0 || addr >= this.size) | |||||
| throw new RangeError("Memory address is out of bounds."); | |||||
| if (typeof(fn) !== 'function') | |||||
| throw new TypeErrpr("Expected a callback function."); | |||||
| if (!(addr in this.__listeners)) | |||||
| this.__listeners[addr] = []; | |||||
| // WARNING: Not testing to see if using the same function more than once. | |||||
| this.__listeners[addr].push(fn); | |||||
| } | |||||
| // This method is intended for the emulator to fill a Read-Only memory module with data prior to | |||||
| // the start of execution. This method should never be used in a running system. | |||||
| sysStore(address, data){ | |||||
| if (this.__map){ | |||||
| let dc = data; | |||||
| if (address < 0 || address >= this.__map.length) | |||||
| throw new RangeError("Memory address out of range."); | |||||
| if (this.__map.length - address < dc.length) | |||||
| dc = dc.slice(0, this.__map.length - address); | |||||
| this.__map.set(address, dc); | |||||
| return dc.length; | |||||
| } | |||||
| } | |||||
| } | |||||
| module.exports = Bank; |
| class Memory{ | class Memory{ | ||||
| constructor(size, ro){ | |||||
| this.__ro = (ro === true); | |||||
| this.__map = null; | |||||
| this.__addr = 0; | |||||
| if (size > 0){ | |||||
| this.__map = new Uint8Array(size); | |||||
| } | |||||
| this.__listeners = {}; | |||||
| } | |||||
| constructor(){} | |||||
| get size(){return (this.__map) ? this.__map.length : 0;} | |||||
| get size(){return 0;} | |||||
| get address(){return this.__addr;} | |||||
| set address(a){ | |||||
| if (this.__map) | |||||
| this.__addr = Math.min(this.__map.length, Math.max(0, a)); | |||||
| } | |||||
| get address(){return 0;} | |||||
| set address(a){} | |||||
| get byte(){return (this.__map) ? this.__map[this.__addr] : -1;} | |||||
| set byte(b){ | |||||
| if (!this.__ro && this.__map){ | |||||
| this.__map[this.__addr] = b; | |||||
| if (this.__addr in this.__listeners) | |||||
| this.__listeners[this.__addr].forEach((fn)=>{fn(b);}); | |||||
| } | |||||
| } | |||||
| onAddressWrite(addr, fn){ | |||||
| if (addr < 0 || addr >= this.size) | |||||
| throw new RangeError("Memory address is out of bounds."); | |||||
| if (typeof(fn) !== 'function') | |||||
| throw new TypeErrpr("Expected a callback function."); | |||||
| if (!(addr in this.__listeners)) | |||||
| this.__listeners[addr] = []; | |||||
| // WARNING: Not testing to see if using the same function more than once. | |||||
| this.__listeners[addr].push(fn); | |||||
| } | |||||
| // This method is intended for the emulator to fill a Read-Only memory module with data prior to | |||||
| // the start of execution. This method should never be used in a running system. | |||||
| sysStore(address, data){ | |||||
| if (this.__map){ | |||||
| let dc = data; | |||||
| if (address < 0 || address >= this.__map.length) | |||||
| throw new RangeError("Memory address out of range."); | |||||
| if (this.__map.length - address < dc.length) | |||||
| dc = dc.slice(0, this.__map.length - address); | |||||
| this.__map.set(address, dc); | |||||
| return dc.length; | |||||
| } | |||||
| } | |||||
| get byte(){return -1;} | |||||
| set byte(b){} | |||||
| } | } | ||||
| var Memory = require('chip/memory'); | |||||
| var Memory = require('src/memory'); | |||||
| class Bank{ | |||||
| class Switch{ | |||||
| constructor(mem){ | constructor(mem){ | ||||
| this.__mem = [mem]; | this.__mem = [mem]; | ||||
| this.__idx = 0; | this.__idx = 0; | ||||
| class MMC{ | |||||
| class MMC extends Memory{ | |||||
| constructor(){ | constructor(){ | ||||
| this.__banks = []; | |||||
| super(); | |||||
| this.__switches = []; | |||||
| this.__addr = 0; | this.__addr = 0; | ||||
| this.__bnkidx = 0; | |||||
| this.__sidx = 0; | |||||
| } | } | ||||
| get size(){ | get size(){ | ||||
| return this.__banks.reduce((acc, b)=>{ | |||||
| acc += b.mem.size; | |||||
| return this.__switches.reduce((acc, s)=>{ | |||||
| acc += s.mem.size; | |||||
| }, 0); | }, 0); | ||||
| } | } | ||||
| get banks(){return this.__banks.length;} | |||||
| get switches(){return this.__switches.length;} | |||||
| get address(){return this.__addr;} | get address(){return this.__addr;} | ||||
| set address(a){ | set address(a){ | ||||
| if (a >= 0 && a < this.size){ | if (a >= 0 && a < this.size){ | ||||
| this.__addr = a; | this.__addr = a; | ||||
| offset = 0; | offset = 0; | ||||
| for (let b=0; b < this.__banks.length; b++){ | |||||
| if (a >= offset && a < offset + this.__banks[b].mem.size){ | |||||
| this.__bnkidx = b; | |||||
| this.__banks[b].mem.address = a - offset; | |||||
| for (let s=0; s < this.__switches.length; s++){ | |||||
| if (a >= offset && a < offset + this.__switches[s].mem.size){ | |||||
| this.__sidx = b; | |||||
| this.__switches[s].mem.address = a - offset; | |||||
| break; | break; | ||||
| } else { | } else { | ||||
| offset += this.__banks[b].mem.size; | |||||
| offset += this.__switches[s].mem.size; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| get byte(){return (this.__banks.length > 0) ? this.__banks[this.__bnkidx].mem.byte : -1;} | |||||
| get byte(){return (this.__switches.length > 0) ? this.__switches[this.__sidx].mem.byte : -1;} | |||||
| set byte(b){ | set byte(b){ | ||||
| if (this.__banks.length > 0){ | |||||
| this.__banks[this.__bnkidx].mem.byte = b; | |||||
| if (this.__switches.length > 0){ | |||||
| this.__switches[this.__sidx].mem.byte = b; | |||||
| } | } | ||||
| } | } | ||||
| connectMemory(mem, addroff){ | connectMemory(mem, addroff){ | ||||
| addroff = (typeof(addroff) === 'number' && addroff >= 0) ? addroff : -1; | addroff = (typeof(addroff) === 'number' && addroff >= 0) ? addroff : -1; | ||||
| if (addroff < 0 || addroff === this.size){ | if (addroff < 0 || addroff === this.size){ | ||||
| this.__banks.push(new Bank(mem)); | |||||
| this.__switches.push(new Switch(mem)); | |||||
| } else { | } else { | ||||
| offset = 0; | offset = 0; | ||||
| for (let b=0; b < this.__banks.length; b++){ | |||||
| for (let s=0; s < this.__switches.length; s++){ | |||||
| if (addroff === offset){ | if (addroff === offset){ | ||||
| if (this.__banks[b].mem.size !== mem.size) | |||||
| if (this.__switches[s].mem.size !== mem.size) | |||||
| throw new RangeError("Memory modules assigned to the same bank must be the same byte size."); | throw new RangeError("Memory modules assigned to the same bank must be the same byte size."); | ||||
| this.__banks[b].addMemModule(mem); | |||||
| this.__switches[s].addMemModule(mem); | |||||
| offset = -1; | offset = -1; | ||||
| break; | break; | ||||
| } else { | } else { | ||||
| offset += this.__banks[b].mem.size; | |||||
| offset += this.__switches[s].mem.size; | |||||
| } | } | ||||
| } | } | ||||
| if (offset >= 0) | if (offset >= 0) | ||||
| mmSwitchRegister(){ | mmSwitchRegister(){ | ||||
| return (function(byte){ | return (function(byte){ | ||||
| let mmcidx = (byte & 0xF0) >> 4; | let mmcidx = (byte & 0xF0) >> 4; | ||||
| if (mmcidx < this.__banks.length){ | |||||
| this.__banks[mmcidx].idx = (byte & 0x0F); | |||||
| if (mmcidx < this.__switches.length){ | |||||
| this.__switches[mmcidx].idx = (byte & 0x0F); | |||||
| } | } | ||||
| }).bind(this); | }).bind(this); | ||||
| } | } |