cpu - Create sketch for a lower-level cpu simulator

- includes passing data through address and data buses, for example
- incomplete (and in need of some re-thinking), but instructive
- see dev notes from 2023-09-03 and 2023-09-04 for more commentary
This commit is contained in:
n loewen 2023-09-04 17:39:48 -07:00
parent 77a41d47c3
commit 871fecb9af
1 changed files with 198 additions and 0 deletions

198
src/sketch.cpu.js Normal file
View File

@ -0,0 +1,198 @@
/* CPU sketch */
class CPU {
constructor () {
this.reset();
}
IP = 0;
A = 0;
flags = 0b00000000;
instruction = 0x00;
operand = 0x00;
//What do I want for interrupts…?
// interruptRequest = 0;
// TODO: can it be a simple setup with fixed instruction cycle length pls?
_instructionCycleStep = 0;
_decodedOp = {};
_calculatedIP = 0;
tickClock () {
console.log('cpu tick');
if (connections.cpuWait) return false;
switch (this._instructionCycleStep) {
// Fetch - instruction
case 0:
// initiate read
connections.addressBus = this.IP;
connections.memoryReadSignal = 1;
this._calculatedIP += 2;
break;
case 1:
// complete read
connections.memoryReadSignal = 0;
this.instruction = connections.dataBus;
break;
// Fetch - operand
case 2:
// initiate read
connections.addressBus = this.IP;
connections.memoryReadSignal = 1;
break;
case 3:
// complete read
connections.memoryReadSignal = 0;
this.operand = connections.dataBus;
break;
// Decode
case 4:
// decode
this._decode();
break;
// Execute
case 5:
this._ops[this._decodedOp]();
break;
case 4:
// execute more
break;
}
this._instructionCycleStep = (this._instructionCycleStep + 1) % 4;
}
reset () {
this.IP = 0xFE;
this.A = 0;
this.flags = 0b00000000;
connections.dataBus = 0b00000000;
this._instructionCycleStep = 0;
this.debug.running = true;
}
_decode (instruction) {
/*
// gg aa iiii
let group = 0b11000000 & instruction;
let mode = 0b00110000 & instruction;
let instr = 0b00001111 & instruction;
console.log(group, mode, instr);
*/
let mode = (0b00110000 & instruction) === 16 ? 'indirect' : 'direct';
const instrs = {
0x00 : 'END',
0x01 : 'NOP',
0x50 : 'STO_indirect',
0x51 : 'LDA_indirect',
0x52 : 'ADD_indirect',
0x53 : 'SUB_indirect',
0x54 : 'HOP_indirect',
0x55 : 'JMP_indirect',
0x56 : 'FTG_indirect',
0x57 : 'FHP_indirect',
0x60 : 'STO_direct',
0x61 : 'LDA_direct',
0x62 : 'ADD_direct',
0x63 : 'SUB_direct',
0x64 : 'HOP_direct',
0x65 : 'JMP_direct',
0x66 : 'FTG_direct',
0x67 : 'FHP_direct',
};
// return { instruction: instrs[instruction], mode: mode };
this._decodedOp = instruction;
}
_ops = {
JMP_direct: (lit) => {
this.debug.currentMnemonic = 'JMP lit';
this._calculatedIP = lit;
},
JMP_indirect: (addr) => {
this.debug.currentMnemonic = 'JMP addr';
this._calculatedIP = addr;
},
}
debug = {
// TODO include the rest of the stuff that I have here in the current version
running: false,
}
}
class Memory {
// ROM format: { addr: nn, data: nn }
constructor (sizeInBytes, ROM) {
// memory format: [ { data: nn, type: str } ]
this.memory = new Array(sizeInBytes);
ROM.forEach((byte) => {
this.memory[byte.address] = { data: byte.data, type: 'ROM' };
})
}
tickClock () {
if (connections.memoryReadSignal) this._read();
if (connections.memoryWriteSignal) this._write();
}
_read () {
connections.dataBus = this.memory[connections.addressBus].data;
}
_write () {
if (this.memory[connections.addressBus].type === 'ROM') throw new Error('Attempted write to ROM');
this.memory[connections.addressBus].data = connections.dataBus;
}
}
/** DMA controller for screen, keypad... **/
class IO {
// TODO…
constructor (connections) {
// ...
}
}
class Connections {
dataBus = 0;
addressBus = 0;
memoryReadSignal = 0;
memoryWriteSignal = 0;
cpuWait = 0;
}
// let rom = // TODO read in…
const rom = [
{ address: 0xFE, data: 0x55, },
{ address: 0xFF, data: 0x00, },
{ address: 0x00, data: 0x51, },
{ address: 0x01, data: 0x0F, },
{ address: 0x02, data: 0x62, },
{ address: 0x03, data: 0x01, },
{ address: 0x04, data: 0x00, },
];
let connections = new Connections();
let memory = new Memory(256, rom);
let cpu = new CPU();
let intervalTimer = setInterval( () => {
cpu.tickClock();
memory.tickClock();
}, 500);
console.log(intervalTimer);