Skip to content

Instantly share code, notes, and snippets.

@roxwize
Created December 14, 2022 14:19
Show Gist options
  • Select an option

  • Save roxwize/80a06089d6acf242b52ee8a1c8757304 to your computer and use it in GitHub Desktop.

Select an option

Save roxwize/80a06089d6acf242b52ee8a1c8757304 to your computer and use it in GitHub Desktop.
abandoned LMC implementation
// this was originally intended to be a full implementation of a little man computer including assembling, but i got to the assembly part and gave up.
// i might remake this at some point, but itll probably require manual program assembly.
const INT_MAX = 999;
const INT_MIN = -999;
const zeroPad = (num, places) => {
const res = String(Math.abs(num)).padStart(places, "0");
return num < 0 ? "-" + res : res;
};
const mem = new Array(100).fill(0);
mem[7] = 0;
mem[99] = 25;
let drawMem, drawRegs, drawOut;
let stepButton, runButton, assembleButton;
let stepping = true;
let programDone = true;
let looping = false;
const CPU = {
programCounter: 0,
instructionRegister: 0,
addressRegister: 0,
accumulator: 0,
error: "Good to go",
output: [],
ticks: 0, // This is to prevent crashes
labels: {},
};
CPU.opcodes = {
ADD: 1,
SUB: 2,
STA: 3,
LDA: 5,
BRA: 6,
BRZ: 7,
BRP: 8,
INP: 901,
OUT: 902,
OTC: 922,
HLT: 0,
};
CPU.reset = function () {
CPU.programCounter = 0;
CPU.instructionRegister = 0;
CPU.addressRegister = 0;
CPU.accumulator = 0;
CPU.error = "Good to go";
CPU.output = [];
CPU.ticks = 0;
CPU.labels = {};
};
CPU.throwError = function (msg) {
programDone = true;
CPU.reset();
CPU.error = zeroPad(CPU.programCounter, 2) + ": " + msg;
};
// If the branch goes to a place in memory before or at the current one it counts as looping
CPU.loopCheck = function () {
if (CPU.addressRegister <= CPU.programCounter) looping = true;
else looping = false;
};
CPU.accOverflow = function () {
const n = CPU.accumulator - 999;
};
CPU.fetchCycle = function () {
if (CPU.ticks >= 9999)
CPU.throwError("9,999 tick limit reached (check your loops)");
const newInp = zeroPad(mem[CPU.programCounter].toString(), 3);
if (newInp.length > 3) CPU.throwError("Improper address");
CPU.instructionRegister = parseInt(newInp.slice(0, 1));
CPU.addressRegister = parseInt(newInp.slice(1));
CPU.programCounter++;
};
CPU.handleInstruction = function (instruction) {
switch (instruction) {
case 0:
programDone = true;
return;
case 1:
// ADD
CPU.accumulator += mem[CPU.addressRegister];
// Overflow
while (CPU.accumulator > INT_MAX) {
CPU.accumulator =
((CPU.accumulator + INT_MAX) % (INT_MAX * 2)) - INT_MAX - 1;
}
break;
case 2:
// SUB
CPU.accumulator -= mem[CPU.addressRegister];
// Underflow
while (CPU.accumulator < INT_MIN) {
// TODO make this work right
}
break;
case 3:
// STA
mem[CPU.addressRegister] = CPU.accumulator;
break;
// There is no 4xx opcode
case 5:
// LDA
CPU.accumulator = mem[CPU.addressRegister];
break;
case 6:
// BRA
CPU.loopCheck();
CPU.programCounter = CPU.addressRegister;
break;
case 7:
// BRZ
CPU.loopCheck();
if (CPU.accumulator === 0) CPU.programCounter = CPU.addressRegister;
break;
case 8:
// BRP
CPU.loopCheck();
if (CPU.accumulator >= 0) CPU.programCounter = CPU.addressRegister;
break;
case 9:
// INP / OUT / OTC
if (CPU.addressRegister === 1) {
let val = parseInt(prompt("Input required"));
while (isNaN(val)) {
val = parseInt(prompt("Input required (must be a number)"));
}
if (val > INT_MAX) CPU.throwError("Input out of range");
else CPU.accumulator = val;
} else if (CPU.addressRegister === 2) {
CPU.output.push(CPU.accumulator);
} else if (CPU.addressRegister === 22) {
CPU.output.push(String.fromCharCode(CPU.accumulator));
} else {
CPU.throwError("Improper input/output operation");
}
break;
default:
CPU.throwError("Unknown operation");
break;
}
};
CPU.findVariables = function (l, opcode, counter) {
if (l.length === 3) {
if (l[1] === "DAT") {
CPU.labels[l[0]] = counter;
mem[counter] = l[2];
console.log(mem[counter]); // AUGHUHUGJG HG find how to make this work
// Look at the two implementations inspect element when you get home and just
// PRay to god you kee pthe motivation to work on this while you dont do just
// that for like 10 hours
} else if (!(l[1] in CPU.opcodes)) {
CPU.throwError("Assembly: Unknown data storage");
}
}
if (!(l[0] in CPU.opcodes) && l[1] in CPU.opcodes) {
// e.g. test LDA 50
mem[counter] = CPU.opcodes[l[1]] + l[2];
CPU.labels[l[0]] = counter;
} else if (!(l[0] in CPU.opcodes) && l[1] === "DAT") {
// e.g. test DAT
mem[counter] = 0;
CPU.labels[l[0]] = counter;
}
};
CPU.parseInstructions = function(txt) {
// let counter = 0;
const lines = txt.toUpperCase().split("\n");
for (let line of lines) {
if (line.trim() === "") continue;
const l = line.trim().split(/\s+/i);
let opcode;
if (l.length === 3) opcode = l[1];
else if (l.length <= 2) {
opcode = l[0] in CPU.opcodes ? l[0] : l[1]
}
else CPU.throwError("Assembly: Improper instruction length")
const numCode = CPU.opcodes[opcode];
if (!numCode) continue;
console.log(numCode)
if (opcode == 901 || opcode == 902 || opcode == 922 || opcode == 0) {
// Special instructions that don't need parameters
console.log("Okay")
mem[counter] = opcode;
}
}
}
CPU.scanLines = function(txt) {
// Initial passthrough: go through instructions and add data variables at approrpiate locations
let counter = 0;
const lines = txt.toUpperCase().split("\n");
for (let line of lines) {
if (line.trim() === "") continue;
const l = line.trim().split(/\s+/i);
if (l.length === 2 && (l[1] === "DAT" || !(l[0] in CPU.opcodes))) {
// e.g. val DAT or val INP
CPU.labels[l[0]] = counter;
if (l[1] === "DAT") mem[counter] = 0; // val DAT
}
if (l.length === 3 && (l[1] === "DAT" || !(l[0] in CPU.opcodes))) {
// e.g. val LDA 55 or val DAT 55
CPU.labels[l[0]] = counter;
if (l[1] === "DAT") mem[counter] = l[2]; // val DAT 55
}
counter++;
}
}
CPU.assemble = function (txt) {
CPU.scanLines(txt);
CPU.parseInstructions(txt);
};
function setup() {
createCanvas(600, 300);
runButton = createButton("RUN");
runButton.mousePressed(() => {
CPU.reset();
programDone = false;
});
stepButton = createButton("STEP");
stepButton.mousePressed(() => {
if (!stepping) stepping = true;
console.log(stepping); // IMplement this once cpu logic is Done
});
runButton = createButton("ASSEMBLE");
runButton.mousePressed(() => {
CPU.assemble(`INP
thing LDA 20
pab INP
HLT
test DAT 99`);
});
const drawValue = (txt, value, x, y, size = 20) => {
push();
fill(0);
textStyle(BOLD);
textAlign(CENTER);
textSize(size);
text(txt, x, y);
textStyle(NORMAL);
text(value, x, y + 20);
pop();
};
drawMem = function (sx, sy) {
let x = 0;
let y = 0;
const w = 30;
const h = 20;
textAlign(CENTER);
textSize(8);
for (let addr in mem) {
const n = parseInt(addr);
rect(sx + x, sy + y, w, h);
textStyle(BOLD);
text(zeroPad(addr, 2), x + w * 10 + w / 2, y + h / 2);
textStyle(NORMAL);
text(zeroPad(mem[addr], 3), x + w * 10 + w / 2, y + h - 2);
x += w;
if ((n + 1) % 10 === 0) {
y += h;
x = 0;
}
}
};
drawRegs = function (sx, sy) {
let w = width / 2;
let h = height;
push();
fill(200);
rect(sx, sy, w, h);
pop();
// Write program counter
drawValue(
"Program counter",
zeroPad(CPU.programCounter, 2),
w / 2,
sy + 20
);
drawValue(
"Instruction register",
CPU.instructionRegister,
w / 4,
sy + 60,
14
);
drawValue(
"Address register",
zeroPad(CPU.addressRegister, 2),
w / 2 + w / 4,
sy + 60,
14
);
drawValue("Accumulator", zeroPad(CPU.accumulator, 3), w / 2, sy + 120);
drawValue("Error (if any)", CPU.error, w / 2, sy + 160, 10);
};
drawOut = function () {
let w = width;
let h = 100;
push();
fill(50);
rect(0, 200, w, h);
textStyle(BOLDITALIC);
textAlign(LEFT);
textSize(20);
fill(255);
text("Output", 10, 225);
textFont("Consolas");
textSize(10);
textStyle(NORMAL);
for (let line in CPU.output) {
text(CPU.output[line], 10, 240 + 10 * line); // Make it wrap when it reaches the bottom (maybe)
}
pop();
};
}
function draw() {
background(0);
drawMem(width - 300, 0);
drawRegs(0, 0);
drawOut();
while (!programDone) {
CPU.ticks++;
CPU.fetchCycle();
CPU.handleInstruction(CPU.instructionRegister);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment