Browse Source

Rewrote memory classes and put them in their own folder. Updated MOS6502/cpu and relavent tests.

master
Bryan Miller 5 years ago
parent
commit
beab01695c
8 changed files with 271 additions and 94 deletions
  1. +2
    -2
      src/chip/MOS6502/cpu.js
  2. +0
    -72
      src/common/bank.js
  3. +0
    -16
      src/common/memory.js
  4. +42
    -0
      src/memory/imem.js
  5. +8
    -0
      src/memory/index.js
  6. +212
    -0
      src/memory/memory.js
  7. +3
    -2
      src/memory/mmc.js
  8. +4
    -2
      test/unit.src.chip.MOS6502.cpu.spec.js

+ 2
- 2
src/chip/MOS6502/cpu.js View File

@@ -2,7 +2,7 @@
* Emulate a basic 6502 (MOS) chip.
*/
const BITM = require('../../utils/bitman.js');
var Memory = require('../../common/memory.js');
var IMem = require('../../memory').IMem;

// mode = 0 - Immediate
// mode = 1 - Zero Page
@@ -830,7 +830,7 @@ class CPU{
// Set and Get Memory property.
get memory(){return this.__mem;}
set memory(m){
if (!(m instanceof Memory))
if (!(m instanceof IMem))
throw new ValueError("Expected Memory instance object.");
this.__mem = m;
}

+ 0
- 72
src/common/bank.js View File

@@ -1,72 +0,0 @@

var Memory = require('./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);
}

load(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(dc, address);
return dc.length;
}
}

clearPage(page){
if (this.__map){
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);
}
}

clear(){
if (this.__map)
this.__map.fill(0);
}
}


module.exports = Bank;

+ 0
- 16
src/common/memory.js View File

@@ -1,16 +0,0 @@


class Memory{
constructor(){}

get size(){return 0;}

get address(){return 0;}
set address(a){}

get byte(){return -1;}
set byte(b){}
}


module.exports = Memory;

+ 42
- 0
src/memory/imem.js View File

@@ -0,0 +1,42 @@


class IMem{
constructor(){}

get size(){return 0;}
get writable(){return false;}

get address(){return 0;}
set address(a){}

get byte(){return -1;}
set byte(b){}

read(a){
this.address = a;
return this.byte;
}

write(a, b){
if (this.writable){
this.address = a;
this.byte = b & 0xFF;
}
return this;
}

load(address, data){
return this;
}

clearPage(page){
return this;
}

clear(){
return this;
}
}


module.exports = IMem;

+ 8
- 0
src/memory/index.js View File

@@ -0,0 +1,8 @@


module.exports = Object.freeze({
Memory: require("./memory.js"),
MMC: require("./mmc.js"),
IMem: require("./imem.js")
});


+ 212
- 0
src/memory/memory.js View File

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






src/common/mmc.js → src/memory/mmc.js View File

@@ -1,5 +1,5 @@

var Memory = require('src/memory');
var IMem = require('./imem.js');

class Switch{
constructor(mem){
@@ -28,7 +28,7 @@ class Switch{



class MMC extends Memory{
class MMC extends IMem{
constructor(){
super();
this.__switches = [];
@@ -102,3 +102,4 @@ class MMC extends Memory{
}

module.exports = MMC;


+ 4
- 2
test/unit.src.chip.MOS6502.cpu.spec.js View File

@@ -1,12 +1,12 @@
const expect = require('chai').expect;
const CPU = require('../src/chip/MOS6502/cpu.js');
const Bank = require('../src/common/bank.js');
const Mem = require('../src/memory');


describe("Testing MOS6502 CPU...", function(){
var cpu = new CPU();
var tick = cpu.clk();
cpu.memory = new Bank(65536);
cpu.memory = new Mem.Memory.RAM(64);
cpu.memory.load(0xFFFC, [0x00, 0x00]);
it("Resetting (IRQ Disabled flag must be on", function(){
@@ -23,8 +23,10 @@ describe("Testing MOS6502 CPU...", function(){
cpu.reset = true;
tick(); // reset.
tick(); tick(); // Two ticks to process opcode.
console.log(cpu);
expect(cpu.C).to.equal(0);
tick(); tick();
//console.log(cpu)
expect(cpu.C).to.equal(1);
});


Loading…
Cancel
Save