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); | ||||
} | } |