summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/vm/core.js51
-rw-r--r--src/vm/enum.js27
-rw-r--r--src/vm/instruction.js116
3 files changed, 194 insertions, 0 deletions
diff --git a/src/vm/core.js b/src/vm/core.js
new file mode 100644
index 0000000..292ceb0
--- /dev/null
+++ b/src/vm/core.js
@@ -0,0 +1,51 @@
+'use strict';
+
+const { Op, AddrMode} = require('./enum.js');
+
+
+class Core {
+ constructor(size) {
+ this.data = new Array(size);
+
+ // initialize core to all DAT 0, 0
+ for (let i=0; i<size; i++) {
+ this.data[i] = {
+ opcode: Op.DAT,
+ a: { value: 0, mode: AddrMode.Direct },
+ b: { value: 0, mode: AddrMode.Direct },
+ };
+ }
+ }
+
+
+ normalize(pc, value) {
+ return (pc + value) % this.data.length;
+ }
+
+
+ getLocation(pc, address) {
+ switch(address.mode) {
+ case AddrMode.Immediate:
+ throw "Cannot get location from immediate-mode address";
+ case AddrMode.Direct:
+ return this.normalize(pc, address.value);
+ case AddrMode.Indirect: {
+ let loc = this.normalize(pc, address.value);
+ let b = this.data[loc];
+ return this.normalize(loc, b);
+ }
+ case AddrMode.Predecrement: {
+ let loc = this.normalize(pc, address.value);
+ this.data[loc].b -= 1;
+ let b = this.data[loc].b;
+ return this.normalize(loc, b);
+ }
+ default:
+ throw `Invalid addressing mode "${address.mode}"`;
+ }
+ }
+
+ getValue(pc, address) {
+ return this.data[this.getLocation(pc, address)];
+ }
+}
diff --git a/src/vm/enum.js b/src/vm/enum.js
new file mode 100644
index 0000000..5271991
--- /dev/null
+++ b/src/vm/enum.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const Op = {
+ DAT: 'DAT',
+ MOV: 'MOV',
+ ADD: 'ADD',
+ SUB: 'SUB',
+ JMP: 'JMP',
+ JMZ: 'JMZ',
+ JMN: 'JMN',
+ CMP: 'CMP',
+ SLT: 'SLT',
+ DJN: 'DJN',
+ SPL: 'SPL',
+};
+
+
+const AddrMode = {
+ Immediate: 'immediate',
+ Indirect: 'indirect',
+ Predecrement: 'predecrement',
+ Direct: 'direct',
+};
+
+
+exports.Op = Op;
+exports.AddrMode = AddrMode;
diff --git a/src/vm/instruction.js b/src/vm/instruction.js
new file mode 100644
index 0000000..0fe922d
--- /dev/null
+++ b/src/vm/instruction.js
@@ -0,0 +1,116 @@
+'use strict';
+
+const { AddrMode } = require('./enum.js');
+
+
+function DoDat(core, pc, ins) {
+ // do nothing
+ return [];
+}
+
+
+function DoMov(core, pc, ins) {
+ if (ins.a.mode === AddrMode.Immediate) {
+ let mem = core.getValue(pc, ins.b);
+ mem.b.value = ins.a.value;
+ } else {
+ const src = core.getValue(pc, ins.a);
+ const dstLoc = core.getLocation(pc, ins.b);
+
+ // hacky deep copy
+ core.data[dstLoc] = JSON.parse(JSON.stringify(src));
+ }
+ return [pc + 1];
+}
+
+
+function DoAdd(core, pc, ins) {
+ if (ins.a.mode === AddrMode.Immediate) {
+ let dst = core.getValue(pc, ins.b);
+ dst.b += ins.a.value;
+ } else {
+ let src = core.getValue(pc, ins.a);
+ let dst = core.getValue(pc, ins.b);
+ dst.a.value += src.a.value;
+ dst.b.value += src.b.value;
+ }
+ return [pc + 1];
+}
+
+
+function DoSub(core, pc, ins) {
+ if (ins.a.mode === AddrMode.Immediate) {
+ let dst = core.getValue(pc, ins.b);
+ dst.b -= ins.a.value;
+ } else {
+ let src = core.getValue(pc, ins.a);
+ let dst = core.getValue(pc, ins.b);
+ dst.a.value -= src.a.value;
+ dst.b.value -= src.b.value;
+ }
+ return [pc + 1];
+}
+
+
+function DoJmp(core, pc, ins) {
+ return [core.getLocation(pc, ins.a)];
+}
+
+
+function DoJmz(core, pc, ins) {
+ let test
+ if (ins.b.mode === AddrMode.Immediate) {
+ test = ins;
+ } else {
+ test = core.getValue(pc, ins.b);
+ }
+
+ if (test.b.value === 0) {
+ return [core.getLocation(pc, ins.a)];
+ } else {
+ return [pc + 1];
+ }
+}
+
+
+function DoJmn(core, pc, ins) {
+ let test
+ if (ins.b.mode === AddrMode.Immediate) {
+ test = ins;
+ } else {
+ test = core.getValue(pc, ins.b);
+ }
+
+ if (test.b.value !== 0) {
+ return [core.getLocation(pc, ins.a)];
+ } else {
+ return [pc + 1];
+ }
+}
+
+
+function DoCmp(core, pc, ins) {
+ if (ins.a.mode === AddrMode.Immediate) {
+ const test = core.getValue(pc, ins.b);
+ if (ins.a.value === test.b.value) {
+ return [pc + 2];
+ } else {
+ return [pc + 1];
+ }
+ } else {
+ const a = core.getValue(pc, ins.a);
+ const b = core.getValue(pc, ins.b);
+
+ if (
+ a.opcode === b.opcode &&
+ a.a.value === b.a.value &&
+ a.b.value === b.b.value)
+ ) {
+ return [pc + 2];
+ } else {
+ return [pc + 1];
+ }
+ }
+}
+
+