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