const expect = require('chai').expect; const sinon = require('sinon'); const Mem = require('../src/memory'); describe("Testing Memory Module", function(){ describe("Core Memory Classes...", function(){ describe("ROM Class", function(){ var m1 = new Mem.ROM(1); var m2 = new Mem.ROM(4); it("Bytes match 256 byte page sizes", function(){ expect(m1.size).to.equal(256); expect(m2.size).to.equal(1024); }); it("Loading Bytes", function(){ m1.load(0x80, [0x10, 0x44, 0xAB, 0x11, 0x9C]); expect(m1.__map[0x80]).to.equal(0x10); expect(m1.__map[0x81]).to.equal(0x44); expect(m1.__map[0x82]).to.equal(0xAB); expect(m1.__map[0x83]).to.equal(0x11); expect(m1.__map[0x84]).to.equal(0x9C); m2.load(0x0100, [0x88, 0x77, 0x66, 0x55]); m2.load(0x0200, [0xAA, 0xBB, 0xCC, 0xDD]); expect(m2.__map[0x0100]).to.equal(0x88); expect(m2.__map[0x0101]).to.equal(0x77); expect(m2.__map[0x0102]).to.equal(0x66); expect(m2.__map[0x0103]).to.equal(0x55); expect(m2.__map[0x0200]).to.equal(0xAA); expect(m2.__map[0x0201]).to.equal(0xBB); expect(m2.__map[0x0202]).to.equal(0xCC); expect(m2.__map[0x0203]).to.equal(0xDD); }); it("Read Check", function(){ expect(m1.read(0x81)).to.equal(0x44); expect(m1.address).to.equal(0x81); expect(m1.read(0x84)).to.equal(0x9C); expect(m1.address).to.equal(0x84); expect(m2.read(0x0102)).to.equal(0x66); expect(m2.address).to.equal(0x0102); expect(m2.read(0x0203)).to.equal(0xDD); expect(m2.address).to.equal(0x0203); }); it("Address Within Memory Bounds", function(){ m1.address = 0x0100; expect(m1.address).to.equal(0xFF); m2.address = -1; expect(m2.address).to.equal(0x00); m2.address = 0x0400; expect(m2.address).to.equal(0x03FF); }); it("Set Address / Get Byte", function(){ m1.address = 0x82; expect(m1.byte).to.equal(0xAB); m1.address = 0x83; expect(m1.byte).to.equal(0x11); m2.address = 0x0101; expect(m2.byte).to.equal(0x77); m2.address = 0x0202; expect(m2.byte).to.equal(0xCC); }); it("Address Read Callback", function(){ var cb = sinon.fake(); m2.onAddressRead(0x0100, cb); m2.read(0x0100); m2.read(0x0201); m2.address = 0x0101; m2.byte; m2.address = 0x0100; m2.byte; 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); expect(m2.read(0x0201)).to.equal(0x00); expect(m2.read(0x0202)).to.equal(0x00); expect(m2.read(0x0203)).to.equal(0x00); }); it("Clear", function(){ m1.clear(); expect(m1.read(0x80)).to.equal(0x00); expect(m1.read(0x81)).to.equal(0x00); expect(m1.read(0x82)).to.equal(0x00); expect(m1.read(0x83)).to.equal(0x00); }); }); describe("RAM Class", function(){ var m1 = new Mem.RAM(1); var m2 = new Mem.RAM(4); it("Bytes match 256 byte page sizes", function(){ expect(m1.size).to.equal(256); expect(m2.size).to.equal(1024); }); it("Loading Bytes", function(){ m1.load(0x80, [0x10, 0x44, 0xAB, 0x11, 0x9C]); expect(m1.__map[0x80]).to.equal(0x10); expect(m1.__map[0x81]).to.equal(0x44); expect(m1.__map[0x82]).to.equal(0xAB); expect(m1.__map[0x83]).to.equal(0x11); expect(m1.__map[0x84]).to.equal(0x9C); m2.load(0x0100, [0x88, 0x77, 0x66, 0x55]); m2.load(0x0200, [0xAA, 0xBB, 0xCC, 0xDD]); expect(m2.__map[0x0100]).to.equal(0x88); expect(m2.__map[0x0101]).to.equal(0x77); expect(m2.__map[0x0102]).to.equal(0x66); expect(m2.__map[0x0103]).to.equal(0x55); expect(m2.__map[0x0200]).to.equal(0xAA); expect(m2.__map[0x0201]).to.equal(0xBB); expect(m2.__map[0x0202]).to.equal(0xCC); expect(m2.__map[0x0203]).to.equal(0xDD); }); it("Read Check", function(){ expect(m1.read(0x81)).to.equal(0x44); expect(m1.address).to.equal(0x81); expect(m1.read(0x84)).to.equal(0x9C); expect(m1.address).to.equal(0x84); expect(m2.read(0x0102)).to.equal(0x66); expect(m2.address).to.equal(0x0102); expect(m2.read(0x0203)).to.equal(0xDD); expect(m2.address).to.equal(0x0203); }); it("Write Check", function(){ expect(m1.read(0x85)).to.equal(0x00); m1.write(0x85, 0x12); expect(m1.read(0x85)).to.equal(0x12); m1.write(0x85, 0xDC); expect(m1.byte).to.equal(0xDC); }); it("Set Address / Get Byte", function(){ m1.address = 0x82; expect(m1.byte).to.equal(0xAB); m1.address = 0x83; expect(m1.byte).to.equal(0x11); m2.address = 0x0101; expect(m2.byte).to.equal(0x77); m2.address = 0x0202; expect(m2.byte).to.equal(0xCC); }); it("Set Address / Set Byte", function(){ m1.address = 0x86; m1.byte = 0xFE; m1.address = 0x87; m1.byte = 0xEF; expect(m1.read(0x86)).to.equal(0xFE); expect(m1.read(0x87)).to.equal(0xEF); }); it("Address Within Memory Bounds", function(){ m1.address = 0x0100; expect(m1.address).to.equal(0xFF); m2.address = -1; expect(m2.address).to.equal(0x00); m2.address = 0x0400; expect(m2.address).to.equal(0x03FF); }); it("Address Read Callback", function(){ var cb = sinon.fake(); m2.onAddressRead(0x0100, cb); m2.read(0x0100); m2.read(0x0201); m2.address = 0x0101; m2.byte; m2.address = 0x0100; m2.byte; expect(cb.callCount).to.equal(2); }); it("Address Write Callback", function(){ var cb = sinon.fake(); m1.onAddressWrite(0x86, cb); m1.write(0x86, 0x12); expect(cb.lastArg).to.equal(0x12); m1.write(0x86, 0x22); expect(cb.lastArg).to.equal(0x22); 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); expect(m2.read(0x0201)).to.equal(0x00); expect(m2.read(0x0202)).to.equal(0x00); expect(m2.read(0x0203)).to.equal(0x00); }); it("Clear", function(){ m1.clear(); expect(m1.read(0x80)).to.equal(0x00); expect(m1.read(0x81)).to.equal(0x00); expect(m1.read(0x82)).to.equal(0x00); expect(m1.read(0x83)).to.equal(0x00); }); }); describe("Shadow Class", function(){ var sm = new Mem.Shadow(1, 4); it("Bytes Match 256 Size", function(){ expect(sm.size).to.equal(256); }); it("Load Bytes", function(){ var stored = sm.load(0, [0x10, 0x20, 0x30, 0x40, 0x50]); expect(stored).to.equal(4); expect(sm.__map[0]).to.equal(0x10); expect(sm.__map[1]).to.equal(0x20); expect(sm.__map[2]).to.equal(0x30); expect(sm.__map[3]).to.equal(0x40); }); it("Read Check", function(){ expect(sm.read(0x01)).to.equal(0x20); expect(sm.read(0x05)).to.equal(0x20); expect(sm.read(0x09)).to.equal(0x20); expect(sm.read(0x0D)).to.equal(0x20); }); it("Write Check", function(){ sm.write(0x01, 0x11); expect(sm.read(0x01)).to.equal(0x11); expect(sm.read(0x05)).to.equal(0x11); sm.write(0x05, 0x12); expect(sm.read(0x05)).to.equal(0x11); }); it("Set Address / Get Byte", function(){ sm.address = 0x02; expect(sm.byte).to.equal(0x30); sm.address = 0x03; expect(sm.byte).to.equal(0x40); sm.address = 0x04; expect(sm.byte).to.equal(0x10); sm.address = 0x05; expect(sm.byte).to.equal(0x11); }); it("Set Address / Set Byte", function(){ sm.address = 0x02; sm.byte = 0x31; expect(sm.byte).to.equal(0x31); sm.address = 0x06; expect(sm.byte).to.equal(0x31); sm.byte = 0x32; expect(sm.byte).to.equal(0x31); }); it("Address Within Memory Bounds", function(){ sm.address = 0x0100; expect(sm.address).to.equal(0xFF); sm.address = -1; expect(sm.address).to.equal(0x00); }); it("Address Read Callback", function(){ var cb1 = sinon.fake(); var cb2 = sinon.fake(); sm.onAddressRead(0x01, cb1); sm.onAddressRead(0x05, cb2); sm.read(0x01); expect(cb1.callCount).to.equal(1); expect(cb2.callCount).to.equal(0); sm.read(0x05); expect(cb1.callCount).to.equal(1); expect(cb2.callCount).to.equal(1); }); it("Address Write Callback", function(){ var cb1 = sinon.fake(); var cb2 = sinon.fake(); sm.onAddressWrite(0x01, cb1); sm.write(0x01, 0x21); expect(cb1.callCount).to.equal(1); expect(cb1.lastArg).to.equal(0x21); sm.onAddressWrite(0x05, cb2); sm.write(0x05, 0x22); expect(cb2.callCount).to.equal(0); sm.write(0x01, 0x22); expect(cb2.callCount).to.equal(1); expect(cb2.lastArg).to.equal(0x22); expect(cb1.callCount).to.equal(2); expect(cb1.lastArg).to.equal(0x22); }); it("Peek"); it("Poke"); it("Clear Page", function(){ sm.clearPage(0); expect(sm.read(0x00)).to.equal(0x00); expect(sm.read(0x01)).to.equal(0x00); expect(sm.read(0x05)).to.equal(0x00); }); it("Clear", function(){ sm.load(0, [0x10, 0x20, 0x30, 0x40]); sm.clear(); expect(sm.read(0x00)).to.equal(0x00); expect(sm.read(0x01)).to.equal(0x00); expect(sm.read(0x05)).to.equal(0x00); }); }); }); describe("Memory Management Controller (MMC) Class", function(){ var mmc = new Mem.MMC(); var rcb = sinon.fake(); var wcb = sinon.fake(); let m1 = new Mem.RAM(1); m1.onAddressRead(0x31, rcb); m1.onAddressWrite(0x31, wcb); mmc.connectMemory(m1); mmc.connectMemory(new Mem.RAM(2)); mmc.connectMemory(new Mem.RAM(1)); it("Reports 4 pages", function(){ expect(mmc.pages).to.equal(4); }); it("Bytes match 4 pages", function(){ expect(mmc.size).to.equal(1024); }); it("Load / Read Check", function(){ mmc.load(0, [0x01, 0x02, 0x03, 0x04]); mmc.load(0x0200, [0x11, 0x22, 0x33, 0x44]); mmc.load(0x0300, [0xAA, 0xBB, 0xCC, 0xDD]); mmc.load(0xFE, [0x41, 0x42, 0x43, 0x44]); expect(mmc.read(0x01)).to.equal(0x02); expect(mmc.read(0x0202)).to.equal(0x33); expect(mmc.read(0x0303)).to.equal(0xDD); expect(mmc.read(0xFF)).to.equal(0x42); expect(mmc.read(0x0101)).to.equal(0x44); }); it("Write check", function(){ mmc.write(0x02, 0x06); expect(mmc.read(0x02)).to.equal(0x06); mmc.write(0x0115, 0x15); expect(mmc.read(0x0115)).to.equal(0x15); mmc.write(0x0207, 0x07); expect(mmc.read(0x0207)).to.equal(0x07); }); it("Adding Switchable Banks", function(){ let b2 = new Mem.ROM(1); expect(function(){ mmc.connectMemory(b2, 0x0100); }).to.throw("Memory modules assigned to the same bank must be the same byte size."); expect(function(){ mmc.connectMemory(b2, 0xEE) }).to.throw("Cannot align memory module to bank at address " + (0xEE)); mmc.connectMemory(b2, 0x00); expect(mmc.read(0x00)).to.equal(0x01); expect(mmc.read(0x01)).to.equal(0x02); mmc.switchBank(0x01); expect(mmc.read(0x01)).to.equal(0x00); mmc.switchBank(0x00); expect(mmc.read(0x01)).to.equal(0x02); mmc.connectMemory(new Mem.ROM(2), 0x0100); expect(mmc.read(0x0115)).to.equal(0x15); mmc.switchBank(0x11); expect(mmc.read(0x0115)).to.equal(0x00); mmc.switchBank(0x10); expect(mmc.read(0x0115)).to.equal(0x15); }); it("Write Check 2 (switch between RAM and ROM)", function(){ // Disable memory write passthrough for the initial tests... mmc.writePassthrough = false; mmc.write(0x0115, 0x16); expect(mmc.read(0x0115)).to.equal(0x16); mmc.switchBank(0x11); expect(mmc.read(0x0115)).to.equal(0x00); mmc.write(0x0115, 0x17); expect(mmc.read(0x0115)).to.equal(0x00); mmc.switchBank(0x10); expect(mmc.read(0x0115)).to.equal(0x16); // Reenable write passthrough! mmc.writePassthrough = true; mmc.switchBank(0x11); mmc.write(0x0115, 0x17); expect(mmc.read(0x0115)).to.equal(0x00); mmc.switchBank(0x10); expect(mmc.read(0x0115)).to.equal(0x17); }); it("Set Address / Get Byte", function(){ mmc.address = 0x0200; expect(mmc.byte).to.equal(0x11); mmc.address = 0x0301; expect(mmc.byte).to.equal(0xBB); mmc.address = 0x03; expect(mmc.byte).to.equal(0x04); }); it("Set Address / Set Byte", function(){ mmc.address = 0xEE; mmc.byte = 0x1A; mmc.address = 0xEF; mmc.byte = 0x2B; mmc.address = 0xF0; mmc.byte = 0x3C; expect(mmc.read(0xEE)).to.equal(0x1A); expect(mmc.read(0xEF)).to.equal(0x2B); expect(mmc.read(0xF0)).to.equal(0x3C); }); it("Address Within Memory Bounds", function(){ mmc.address = 0x05; expect(mmc.address).to.equal(0x05); mmc.address = 0x0201 expect(mmc.address).to.equal(0x0201); mmc.address = -1; expect(mmc.address).to.equal(0); mmc.address = 0x0400; 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); mmc.clearPage(2); expect(mmc.read(0x0200)).to.equal(0x00); expect(mmc.read(0x0201)).to.equal(0x00); expect(mmc.read(0xEE)).to.equal(0x1A); mmc.clearPage(0); expect(mmc.read(0x00)).to.equal(0x00); expect(mmc.read(0xEE)).to.equal(0x00); expect(mmc.read(0xEF)).to.equal(0x00); }); it("Clear", function(){ mmc.clear(); for (let i=0; i < 10; i++){ expect(mmc.read(0xF6 + i)).to.equal(0x00); } }); }); });