@@ -13,11 +13,25 @@ class IMem{ | |||
get byte(){return -1;} | |||
set byte(b){} | |||
// Shorthand method for accessing an address and returning a byte. | |||
// Same as... | |||
// | |||
// mem.address = <16 bit value> | |||
// let res = mem.byte | |||
// | |||
// NOTE: This will trigger read listeners. | |||
read(a){ | |||
this.address = a; | |||
return this.byte; | |||
} | |||
// Shorthand method for accessing an address and setting it's byte. | |||
// Same as... | |||
// | |||
// mem.address = <16 bit value> | |||
// mem.byte = <1 byte value> | |||
// | |||
// NOTE: This will trigger write listeners. | |||
write(a, b){ | |||
if (this.writable){ | |||
this.address = a; | |||
@@ -26,14 +40,26 @@ class IMem{ | |||
return this; | |||
} | |||
// Same as .read() but will NOT trigger read listeners. | |||
peek(a){return 0;} | |||
// Same as .write() but will NOT trigger write listeners. | |||
poke(a, b){return this;} | |||
// Starting at <address> will fill memory (regardless of whether | |||
// it's ROM or RAM) with the values in <data> | |||
// NOTE: <data> is assumed to only contain byte values. | |||
load(address, data){ | |||
return this; | |||
} | |||
// Clears 256 bytes of memory starting from <page> * 256 | |||
// NOTE: This affects both RAM and ROM. | |||
clearPage(page){ | |||
return this; | |||
} | |||
// Clears all memory | |||
// NOTE: This affects both RAM and ROM. | |||
clear(){ | |||
return this; | |||
} |
@@ -56,6 +56,12 @@ class ROM extends IMem{ | |||
} | |||
peek(a){ | |||
a = Math.min(this.__map.length - 1, Math.max(0, a)); | |||
return this.__map[a]; | |||
} | |||
load(address, data){ | |||
let dc = data; | |||
if (address < 0 || address >= this.__map.length) | |||
@@ -115,6 +121,12 @@ class RAM extends ROM { | |||
this.__wlisteners.on(addr, fn); | |||
return this; | |||
} | |||
poke(a, b){ | |||
a = Math.min(this.__map.length - 1, Math.max(0, a)); | |||
this.__map[a] = b & 0xFF; | |||
return this; | |||
} | |||
} | |||
@@ -169,6 +181,17 @@ class Shadow extends IMem { | |||
return this; | |||
} | |||
peek(a){ | |||
a = Math.min(this.__size - 1, Math.max(0, a)) % this.__map.length; | |||
return this.__map[a]; | |||
} | |||
poke(a, b){ | |||
if (a >= 0 && a < this.__map.length) | |||
this.__map[a] = b & 0xFF; | |||
return this; | |||
} | |||
load(addr, data){ | |||
let dc = data; | |||
if (addr < 0 || addr >= this.__size) |
@@ -85,6 +85,29 @@ class MMC extends IMem{ | |||
} | |||
} | |||
peek(a){ | |||
let v = 0; | |||
if (this.__switches.length > 0){ | |||
let oa = this.address; | |||
this.address = a; // Let this do ALL the work for me. | |||
a = this.__switches[this.__sidx].mem.address; | |||
v = this.__switches[this.__sidx].mem.peek(a); | |||
this.address = oa; | |||
} | |||
return v; | |||
} | |||
poke(a, b){ | |||
if (this.__switches.length > 0){ | |||
let oa = this.address; | |||
this.address = a; | |||
a = this.__switches[this.__sidx].mem.address; | |||
this.__switches[this.__sidx].mem.poke(a, b); | |||
this.address = oa; | |||
} | |||
return this; | |||
} | |||
load(addr, data){ | |||
if (addr < 0 || addr > this.size) | |||
throw new RangeError("Memory address out of range."); |
@@ -79,6 +79,17 @@ describe("Testing Memory Module", function(){ | |||
expect(cb.callCount).to.equal(2); | |||
}); | |||
it("Peek", function(){ | |||
var cb = sinon.fake(); | |||
m2.onAddressRead(0x0101, cb); | |||
expect(m2.peek(0x0101)).to.equal(0x77); | |||
expect(cb.callCount).to.equal(0); | |||
expect(m2.read(0x0101)).to.equal(0x77); | |||
expect(cb.callCount).to.equal(1); | |||
m2.peek(0x0101); | |||
expect(cb.callCount).to.equal(1); | |||
}); | |||
it("Clear Page", function(){ | |||
m2.clearPage(2); | |||
expect(m2.read(0x0200)).to.equal(0x00); | |||
@@ -198,6 +209,30 @@ describe("Testing Memory Module", function(){ | |||
expect(cb.callCount).to.equal(2); | |||
}); | |||
it("Peek", function(){ | |||
var cb = sinon.fake(); | |||
m2.onAddressRead(0x0101, cb); | |||
expect(m2.peek(0x0101)).to.equal(0x77); | |||
expect(cb.callCount).to.equal(0); | |||
expect(m2.read(0x0101)).to.equal(0x77); | |||
expect(cb.callCount).to.equal(1); | |||
m2.peek(0x0101); | |||
expect(cb.callCount).to.equal(1); | |||
}); | |||
it("Poke", function(){ | |||
var cb = sinon.fake(); | |||
m2.onAddressWrite(0x0130, cb); | |||
m2.poke(0x0130, 0x42); | |||
expect(cb.callCount).to.equal(0); | |||
expect(m2.peek(0x0130)).to.equal(0x42); | |||
m2.write(0x0130, 0x46); | |||
expect(cb.callCount).to.equal(1); | |||
expect(m2.peek(0x0130)).to.equal(0x46); | |||
m2.poke(0x0130, 0x42); | |||
expect(cb.callCount).to.equal(1); | |||
}); | |||
it("Clear Page", function(){ | |||
m2.clearPage(2); | |||
expect(m2.read(0x0200)).to.equal(0x00); | |||
@@ -304,6 +339,9 @@ describe("Testing Memory Module", function(){ | |||
expect(cb1.lastArg).to.equal(0x22); | |||
}); | |||
it("Peek"); | |||
it("Poke"); | |||
it("Clear Page", function(){ | |||
sm.clearPage(0); | |||
expect(sm.read(0x00)).to.equal(0x00); | |||
@@ -324,7 +362,12 @@ describe("Testing Memory Module", function(){ | |||
describe("Memory Management Controller (MMC) Class", function(){ | |||
var mmc = new Mem.MMC(); | |||
mmc.connectMemory(new Mem.Memory.RAM(1)); | |||
var rcb = sinon.fake(); | |||
var wcb = sinon.fake(); | |||
let m1 = new Mem.Memory.RAM(1); | |||
m1.onAddressRead(0x31, rcb); | |||
m1.onAddressWrite(0x31, wcb); | |||
mmc.connectMemory(m1); | |||
mmc.connectMemory(new Mem.Memory.RAM(2)); | |||
mmc.connectMemory(new Mem.Memory.RAM(1)); | |||
@@ -425,6 +468,9 @@ describe("Testing Memory Module", function(){ | |||
expect(mmc.address).to.equal(0x03FF); | |||
}); | |||
it("Peek"); | |||
it("Poke"); | |||
it("Clear Page", function(){ | |||
expect(mmc.read(0x0200)).to.equal(0x11); | |||
expect(mmc.read(0x0201)).to.equal(0x22); |