viewing: emulator.js.html     use J/K or arrows to scroll, SEMICOLON to change theme




/*
 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