/*
this is an emulator of uffg, a 16bit cpu i designed but never build
(yes the name is just keyboard spam)
*/
const panel = document.getElementById("controlpanel_out");
const log = document.getElementById("logOutput");
const rom = document.getElementById("rom");
const clockbox = document.getElementById("clock");
//the original uffg was supossed to have much more advanced and universal i/o bus but for this emulator i will just use simple memory-mapped i/o
const stdin = document.getElementById("stdin");
const stdout = document.getElementById("stdout");
//used by the "stop" button
var shouldBeStopped = 0;
var sys = 16;
//fuck john javascript i shouldnt have to do some black magic fuckery to create a fucking FIXED SIZE ARRAY
var registers = new Array(16);
registers.fill(0);
Object.seal(registers);
var memory = new Array(0xffff);
//this might take a while
memory.fill(0);
Object.seal(memory);
function stop() {
shouldBeStopped = 1;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function io() {
memory[0xfefe] = Number(stdin.value);
stdout.innerHTML = "terminal: "
for (var i = 0; i < 0xff; i++)
stdout.innerHTML += String.fromCharCode(memory[0xff00+i]);
}
async function execute() {
shouldBeStopped = 0;
let pc;
let reason;
log.innerHTML = " ";
let program = rom.value.split(/\r?\n/);
let instruction = [];
let clock = Number(clockbox.value);
for (registers[15] = 0; registers[15] < 65535; registers[15]++) {
if (shouldBeStopped) {
log.innerHTML = "at pc=" + registers[15] + " SIGNAL_STOP";
return;
}
//the proper instruction format is for example 0 7 3 0859
instruction = program[registers[15]].split(" ");
for (var i = 0; i < 4; i++) {
instruction[i] = Number("0x" + instruction[i]);
}
await sleep(clock);
switch (instruction[1]) {
case 0x0:
//nop
break;
case 0x1:
//or
if (instruction[0] == 1)
registers[instruction[2]] = registers[instruction[2]] | registers[instruction[3]];
else
registers[instruction[2]] = registers[instruction[2]] | instruction[3];
break;
case 0x2:
//and
if (instruction[0] == 1)
registers[instruction[2]] = registers[instruction[2]] & registers[instruction[3]];
else
registers[instruction[2]] = registers[instruction[2]] & instruction[3];
break;
case 0x3:
//xor
if (instruction[0] == 1)
registers[instruction[2]] = registers[instruction[2]] ^ registers[instruction[3]];
else
registers[instruction[2]] = registers[instruction[2]] ^ instruction[3];
break;
case 0x4:
//nor
if (instruction[0] == 1)
registers[instruction[2]] = ~(registers[instruction[2]] | registers[instruction[3]]);
else
registers[instruction[2]] = ~(registers[instruction[2]] | instruction[3]);
break;
case 0x5:
//nand
if (instruction[0] == 1)
registers[instruction[2]] = ~(registers[instruction[2]] & registers[instruction[3]]);
else
registers[instruction[2]] = ~(registers[instruction[2]] & instruction[3]);
break;
case 0x6:
//xnor
if (instruction[0] == 1)
registers[instruction[2]] = ~(registers[instruction[2]] ^ registers[instruction[3]]);
else
registers[instruction[2]] = ~(registers[instruction[2]] ^ instruction[3]);
break;
case 0x7:
//add
if (instruction[0] == 1)
registers[instruction[2]] += registers[instruction[3]];
else
registers[instruction[2]] += instruction[3];
break;
case 0x8:
//sub
if (instruction[0] == 1)
registers[instruction[2]] -= registers[instruction[3]];
else
registers[instruction[2]] -= instruction[3];
break;
case 0x9:
//now this is getting complicated. signle operand instructions
switch (instruction[2]) {
case 0x0:
//jump on zero
if (registers[1] == 0 && instruction[0] == 1)
registers[15] = registers[instruction[3]];
else if (registers[1] == 0)
registers[15] = instruction[3];
break;
case 0x1:
//right shift
//the av bit doesnt matter in here
registers[instruction[3]] = registers[instruction[3]] >>> 1;
break;
case 0x2:
//left shift
registers[instruction[3]] = registers[instruction[3]] << 1;
break;
default:
log.innerHTML += "instruction not yet implemented or invalid";
reason = "ERR_INVALLID_INSTRUCTION";
return;
//break;
}
case 0xa:
//load byte
if (instruction[0] == 1)
//who said you cant have pointer hell in js
registers[instruction[2]] = memory[registers[instruction[3]]];
else
registers[instruction[2]] = memory[instruction[3]];
break;
case 0xb:
//store byte
if (instruction[0] == 1)
memory[registers[instruction[3]]] = registers[instruction[2]];
else
memory[instruction[3]] = registers[instruction[2]];
break;
case 0xc:
//load word. this is a bit tricky
if (instruction[0] == 1)
registers[instruction[2]] = Number("" + memory[registers[instruction[3]]] + memory[registers[instruction[3]+1]]);
else
registers[instruction[2]] = Number("" + memory[instruction[3]] + memory[instruction[3]+1]);
break
case 0xd:
//store word
//it requiers splitting register
var num2 = registers[instruction[2]] >>> 16;
var num1 = registers[instruction[2]] & 0x00ff;
if (instruction[0] == 1) {
memory[registers[instruction[3]]] = num1;
memory[registers[instruction[3]+1]] = num2;
} else {
memory[instruction[3]] = num1;
memory[instruction[3]+1] = num2;
}
break;
case 0xe:
if (instruction[0] == 1)
registers[instruction[2]] = registers[instruction[3]];
else
registers[instruction[2]] = instruction[3];
break;
case 0xf:
log.innerHTML = "at pc=" + registers[15] + " CPU_HALT";
return;
break;
default:
log.innerHTML = "at pc=" + registers[15] + " ERR_INVALID_INSTRUCTION";
return;
break;
}
//temporary i/o solution
io();
panel.innerHTML = "<br>pc=rf=" + registers[15] + "<br>";
for (var i = 1; i < 16; i++)
panel.innerHTML += "r" + i.toString(sys) + " " + registers[i].toString(sys) + "<br>";
}
if (reason == undefined)
reason = "ERR_PC_OVERFLOW";
log.innerHTML += "execution stopped at PC=" + registers[15] + " reason: " + reason;
console.log("execution stooped at pc=" + registers[15] + " reason");
}
note: this website is best viewed with unix v4 on a pdp-11/45 system