Initial proof-of-concept -- a working but incomplete simulator
This commit is contained in:
commit
51581e6879
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
Loading…
Reference in New Issue