|
|
@@ -0,0 +1,212 @@ |
|
|
|
|
|
|
|
var IMem = require('./imem'); |
|
|
|
|
|
|
|
class Listener{ |
|
|
|
constructor(){ |
|
|
|
this.__listeners = {}; |
|
|
|
} |
|
|
|
|
|
|
|
on(n, fn){ |
|
|
|
if (typeof(fn) !== 'function') |
|
|
|
throw new TypeErrpr("Expected a callback function."); |
|
|
|
if (!(n in this.__listeners)) |
|
|
|
this.__listeners[n] = []; |
|
|
|
this.__listeners[n].push(fn); |
|
|
|
} |
|
|
|
|
|
|
|
trigger(n, data){ |
|
|
|
if (n in this.__listeners){ |
|
|
|
this.__listeners[n].forEach((l)=>{ |
|
|
|
l(data); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class ROM extends IMem{ |
|
|
|
constructor(pages){ |
|
|
|
if (pages < 1) |
|
|
|
throw new RangeError("Memory requires positive page values."); |
|
|
|
super(); |
|
|
|
this.__addr = 0; |
|
|
|
this.__map = new Uint8Array(pages * 1024); |
|
|
|
this.__rlisteners = new Listener(); |
|
|
|
} |
|
|
|
|
|
|
|
get size(){return this.__map.length;} |
|
|
|
|
|
|
|
get address(){return this.__addr;} |
|
|
|
set address(a){ |
|
|
|
this.__addr = Math.min(this.__map.length, Math.max(0, a)); |
|
|
|
} |
|
|
|
|
|
|
|
get byte(){ |
|
|
|
this.__rlisteners.trigger(this.__addr); |
|
|
|
return this.__map[this.__addr]; |
|
|
|
} |
|
|
|
set byte(b){} |
|
|
|
|
|
|
|
|
|
|
|
onAddressRead(addr, fn){ |
|
|
|
if (addr < 0 || addr >= this.size) |
|
|
|
throw new RangeError("Memory address is out of bounds."); |
|
|
|
this.__rlisteners.on(addr, fn); |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
load(address, data){ |
|
|
|
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(dc, address); |
|
|
|
return dc.length; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clearPage(page){ |
|
|
|
let addr = (page << 8) & 0xFF00; |
|
|
|
if (addr < 0 || addr >= this.__map.length) |
|
|
|
throw new RangeError("Memory address out of range."); |
|
|
|
this.__map.fill(0, addr, addr + 256); |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clear(){ |
|
|
|
this.__map.fill(0); |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RAM extends ROM { |
|
|
|
constructor(pages){ |
|
|
|
super(pages); |
|
|
|
this.__wlisteners = new Listener(); |
|
|
|
} |
|
|
|
|
|
|
|
get size(){return this.__map.length;} |
|
|
|
get writable(){return true;} |
|
|
|
|
|
|
|
|
|
|
|
get address(){return this.__addr;} |
|
|
|
set address(a){ |
|
|
|
this.__addr = Math.min(this.__map.length, Math.max(0, a)); |
|
|
|
} |
|
|
|
|
|
|
|
get byte(){ |
|
|
|
this.__rlisteners.trigger(this.__addr); |
|
|
|
return this.__map[this.__addr]; |
|
|
|
} |
|
|
|
set byte(b){ |
|
|
|
this.__map[this.__addr] = b & 0xFF; |
|
|
|
this.__wlisteners.trigger(this.__addr, b & 0xFF); |
|
|
|
} |
|
|
|
|
|
|
|
onAddressWrite(addr, fn){ |
|
|
|
if (addr < 0 || addr >= this.size) |
|
|
|
throw new RangeError("Memory address is out of bounds."); |
|
|
|
this.__elisteners.on(addr, fn); |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class Shadow extends IMem { |
|
|
|
constructor(pages, rbc){ |
|
|
|
if (rbc > (pages * 1024)) |
|
|
|
throw RangeError("Register Byte Count exceeds available pages defined."); |
|
|
|
super(); |
|
|
|
this.__map = new Uint8Array(rbc); |
|
|
|
this.__addr = 0; |
|
|
|
this.__size = pages * 1024; |
|
|
|
|
|
|
|
this.__rlisteners = new Listener(); |
|
|
|
this.__wlisteners = new Listener(); |
|
|
|
} |
|
|
|
|
|
|
|
get size(){return this.__size;} |
|
|
|
get writable(){return true;} |
|
|
|
|
|
|
|
get address(){return this.__addr;} |
|
|
|
set address(a){ |
|
|
|
if (this.__map) |
|
|
|
this.__addr = Math.min(this.__size, Math.max(0, a)); |
|
|
|
} |
|
|
|
|
|
|
|
get byte(){ |
|
|
|
let a = this.__addr % this.__map.length; |
|
|
|
this.__rlisteners.trigger(this.__addr); |
|
|
|
return this.__map[a]; |
|
|
|
} |
|
|
|
set byte(b){ |
|
|
|
if (this.__addr >= 0 && this.__addr < this.__map.length){ |
|
|
|
this.__map[this.__addr] = b & 0xFF; |
|
|
|
this.__wlisteners.trigger(this.__addr, b & 0xFF); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
onAddressRead(addr, fn){ |
|
|
|
if (addr < 0 || addr >= this.size) |
|
|
|
throw new RangeError("Memory address is out of bounds."); |
|
|
|
this.__rlisteners.on(addr, fn); |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
onAddressWrite(addr, fn){ |
|
|
|
if (addr < 0 || addr >= this.size) |
|
|
|
throw new RangeError("Memory address is out of bounds."); |
|
|
|
let a = this.__addr % this.__map.length; |
|
|
|
this.__wlisteners.on(a, fn); |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
load(addr, data){ |
|
|
|
let dc = data; |
|
|
|
if (addr < 0 || addr >= this.__size) |
|
|
|
throw new RangeError("Memory address out of range."); |
|
|
|
// We simply quit here because, while it's legal to try to write, |
|
|
|
// there's actually no memory at this point. We're playing pretend :p |
|
|
|
if (addr >= this.__map.length){return 0;} |
|
|
|
|
|
|
|
if (this.__map.length - addr < dc.length) |
|
|
|
dc = dc.slice(0, this.__map.length - addr); |
|
|
|
this.__map.set(dc, addr); |
|
|
|
return dc.length; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clearPage(page){ |
|
|
|
let addr = (page << 8) & 0xFF00; |
|
|
|
if (addr < 0 || addr >= this.__size) |
|
|
|
throw new RangeError("Memory address out of range."); |
|
|
|
if (addr < this.__map.length){ |
|
|
|
let end = (addr + 256 <= this.__map.length) ? 256 : this.__map.length - addr; |
|
|
|
this.__map.fill(0, addr, end); |
|
|
|
} |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clear(){ |
|
|
|
if (this.__map) |
|
|
|
this.__map.fill(0); |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = Object.freeze({ |
|
|
|
RAM: RAM, |
|
|
|
ROM: ROM, |
|
|
|
Shadow: Shadow |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|