From 51581e687910f75d6e9e06058ca8c2a3370ad290 Mon Sep 17 00:00:00 2001 From: n loewen Date: Tue, 25 Jul 2023 15:05:11 +0100 Subject: [PATCH] Initial proof-of-concept -- a working but incomplete simulator --- readme.md | 52 ++++++++++++++++++ simulator-sketch.js | 131 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 readme.md create mode 100644 simulator-sketch.js diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..067c419 --- /dev/null +++ b/readme.md @@ -0,0 +1,52 @@ +# Paper computer simulator experiment + +## ISA + +0 END +1 STO lit# ; store ... mem[lit#] <- A +2 STO addr ; store ... mem[mem[addr]] <- A +3 LDA lit# ; load ... A <- lit# +4 LDA addr ; load ... A <- mem[addr] +5 ADD lit# ; add ... A <- A + lit# ... and un/set carry flag +6 ADD addr ; add ... A <- A + mem[addr] ... and un/set carry flag +7 SUB lit# ; sub ... A <- A - lit# ... and un/set carry flag +8 SUB addr ; sub ... A <- A - mem[addr] ... and un/set carry flag +9 HOP lit# ; skip next instruction if A == lit# ... when true: IP <- PC + 2 +A HOP addr ; skip next instruction if A == addr ... when true: IP <- PC + 2 +B JMP lit# ; ... IP <- lit# +C JMP addr ; ... IP <- addr +D CAT ---- ; Carry Flag Toggle ... CF <- !CF +E CHP ---- ; skip next instruction if Carry Flag is set ... when true: IP <- PC + 2 +F + +### Nice features that didn't fit + +- hop IF< and hop IF> +- MUL and DIV +- rotates and shifts + +## Registers + +- A -- accumulator +- IP -- instruction pointer (aka program counter) + +## Flags + +- CF -- carry flag + +## Memory-mapped peripherals + +- 4x4 display +- hex keypad (details TBD) +- ? bank selector (for bank-switching) + +### Maybe someday + +- timer (for a version in software/electronic-hardware) + +## Tentative memory map + +00-0F - display +10-1F - keypad? (details TBD) +20 - initial value for IP ? +21-FF - free \ No newline at end of file diff --git a/simulator-sketch.js b/simulator-sketch.js new file mode 100644 index 0000000..3a8b91e --- /dev/null +++ b/simulator-sketch.js @@ -0,0 +1,131 @@ +function CPU(mem) { + this.memory = mem; + this.running = false; + this.instructionPointer = 0; + this.carryFlag = 0; + this.acc = 0; + + this.instructions = { + end: () => { + console.log('END'); + this.running = false + }, + store_lit: (lit) => { + console.log('STO lit#'); + this.memory[lit] = this.acc; + log_memory(); + this.instructionPointer = this.instructionPointer += 1; + }, + store_addr: (addr) => { + console.log('STO addr'); + this.memory[this.memory[addr]] = this.acc; + log_memory(); + this.instructionPointer = this.instructionPointer += 1; + }, + load_lit: (lit) => { + console.log('LDA lit#'); + this.acc = lit; + this.instructionPointer = this.instructionPointer += 1; + }, + load_addr: (addr) => { + console.log('LDA addr'); + console.log('mem at addr: ', this.memory[addr]); + this.acc = this.memory[addr]; + this.instructionPointer = this.instructionPointer += 1; + }, + }; + + log_debug_state = () => { + console.log(); + console.group('CPU state'); + console.log( `IP: ${this.instructionPointer} Acc: ${this.acc}  CF: ${this.carryFlag}  ${this.running ? "running" : "halted" }` ); + console.log(); + console.groupEnd('CPU state'); + }; + + log_memory = () => { + console.log(); + console.group('Memory'); + console.table(this.memory); + console.groupEnd('Memory'); + } + + this.perform_operation = (opcode, arg) => { + switch (opcode) { + case 0: + this.instructions.end(arg); + break; + + case 1: + this.instructions.store_lit(arg); + break; + + case 2: + this.instructions.store_addr(arg); + break; + + case 3: + this.instructions.load_lit(arg); + break; + + case 4: + this.instructions.load_addr(arg); + break; + + default: + console.error( `Invalid opcode: ${opcode} with argument ${arg}` ); + } + }; + + this.run_program = () => { + console.log(); + console.log( "Running program..." ); + log_debug_state(); + + this.running = true; + + for (let i = 1; i < 16; i++) { + if ( this.running && + (this.instructionPointer < this.memory.length) ) { + let op_arg_tuple = this.memory[this.instructionPointer]; + console.group("Proccessing instruction"); + console.log( op_arg_tuple ); + // console.log( `processing opcode ${op_arg_tuple[0]} with arg ${op_arg_tuple[1]}` ); + this.perform_operation(op_arg_tuple[0], op_arg_tuple[1]); + log_debug_state(); + console.groupEnd("Processing instruction"); + } + } + } +}; + +// UNIMPLEMENTED + let add_lit = function(lit) { return; } + let add_addr = function(addr) { return; } + let subtract_lit = function(lit) { return; } + let subtract_addr = function(addr) { return; } + let hop_lit = function(lit) { return; } + let hop_addr = function(addr) { return; } + let jump_lit = function(lit) { return; } + let jump_addr = function(addr) { return; } + let carry_toggle = function() { return; } + let carry_hop = function() { return; } + + +// TEST + +let halt_and_catch_fire = [ + [0, 0], + [1, 0], +]; + +let basic_test = [ + [3, 8], // LDA lit + [1, 5], // STO lit + [4, 5], // LDA addr + [2, 6], // STO addr + [0, 0], // END +]; + +let comp = new CPU(basic_test); +comp.run_program(); \ No newline at end of file