@@ -0,0 +1,60 @@ | |||
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; |
@@ -1,57 +1,15 @@ | |||
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){} | |||
} | |||
@@ -1,7 +1,7 @@ | |||
var Memory = require('chip/memory'); | |||
var Memory = require('src/memory'); | |||
class Bank{ | |||
class Switch{ | |||
constructor(mem){ | |||
this.__mem = [mem]; | |||
this.__idx = 0; | |||
@@ -28,60 +28,61 @@ class Bank{ | |||
class MMC{ | |||
class MMC extends Memory{ | |||
constructor(){ | |||
this.__banks = []; | |||
super(); | |||
this.__switches = []; | |||
this.__addr = 0; | |||
this.__bnkidx = 0; | |||
this.__sidx = 0; | |||
} | |||
get size(){ | |||
return this.__banks.reduce((acc, b)=>{ | |||
acc += b.mem.size; | |||
return this.__switches.reduce((acc, s)=>{ | |||
acc += s.mem.size; | |||
}, 0); | |||
} | |||
get banks(){return this.__banks.length;} | |||
get switches(){return this.__switches.length;} | |||
get address(){return this.__addr;} | |||
set address(a){ | |||
if (a >= 0 && a < this.size){ | |||
this.__addr = a; | |||
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; | |||
} 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){ | |||
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){ | |||
addroff = (typeof(addroff) === 'number' && addroff >= 0) ? addroff : -1; | |||
if (addroff < 0 || addroff === this.size){ | |||
this.__banks.push(new Bank(mem)); | |||
this.__switches.push(new Switch(mem)); | |||
} else { | |||
offset = 0; | |||
for (let b=0; b < this.__banks.length; b++){ | |||
for (let s=0; s < this.__switches.length; s++){ | |||
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."); | |||
this.__banks[b].addMemModule(mem); | |||
this.__switches[s].addMemModule(mem); | |||
offset = -1; | |||
break; | |||
} else { | |||
offset += this.__banks[b].mem.size; | |||
offset += this.__switches[s].mem.size; | |||
} | |||
} | |||
if (offset >= 0) | |||
@@ -93,8 +94,8 @@ class MMC{ | |||
mmSwitchRegister(){ | |||
return (function(byte){ | |||
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); | |||
} |