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