/* * Emulate a basic 6502 (MOS) chip. */ const BITM = require('../../utils/bitman.js'); var Memory = require('../../common/memory.js'); // mode = 0 - Immediate // mode = 1 - Zero Page // mode = 2 - Zero Page, X // mode = 3 - Zero Page, Y // mode = 4 - Absolute // mode = 5 - Absolute, X // mode = 6 - Absolute, Y // mode = 7 - Indirect, X // mode = 8 - Indirect, Y function ProcessOp(cpu, mode){ switch(cpu.__cycle){ case 0: cpu.__mem.address = cpu.__PC; PCUp(cpu, 1); cpu.__opv = cpu.__mem.byte; return (mode === 0); case 1: switch(mode){ case 1: // Zero Page cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; return true; case 2: // Zero Page, X case 3: // Zero Page, Y cpu.__opv = (cpu.__opv + ((mode === 2) ? cpu.__XR : cpu.__YR)) & 0xFF; break; case 4: // Absolute case 5: // Absolute, X case 6: // Absolute, Y cpu.__mem.address = cpu.__PC; PCUp(cpu, 1); cpu.__opv |= cpu.__mem.byte << 8; break; case 7: // Indirect, X cpu.__opv = (cpu.__opv + cpu.__XR) & 0xFF; break; case 8: // Indirect, Y cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; break; } break; case 2: switch(mode){ case 2: // Zero Page, X case 3: // Zero Page, Y case 4: // Absolute cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; return true; case 5: // Absolute, X case 6: // Absolute, Y let s = (mode === 5) ? cpu.__XR : cpu.__YR; let l = (cpu.__opv & 0xFF) + s; cpu.__opv = (cpu.__opv & 0xFF00) | (l & 0xFF); if (l < 255){ cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; return true; } break; case 7: // Indirect, X cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; break; case 8: // Indirect, Y cpu.__mem.address += 1; cpu.__opv |= cpu.__mem.byte << 8; break; } break; case 3: switch(mode){ case 5: // Absolute, X case 6: // Absolute, Y let h = (cpu.__opv >> 8) + 1; cpu.__mem.address = (cpu.__opv & 0xFF) | (h << 8); cpu.__opv = cpu.__mem.byte; return true; case 7: // Indirect, X cpu.__mem.address += 1; cpu.__opv |= cpu.__mem.byte << 8; break; case 8: // Indirect, Y let l = (cpu.__opv & 0xFF) + cpu.__YR; cpu.__opv = (cpu.__opv & 0xFF00) | (l & 0xFF); if (l <= 255){ cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; return true; } break; } break; case 4: if (mode === 8){ let h = (cpu.__opv >> 8) + 1; cpu.__opv = (cpu.__opv & 0x00FF) | (h << 8); } cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; return true; } cpu.__cycle += 1; return false; } function ADC(cpu){ // To be used by both the ADC and SBC op codes. let pmode = [0x69, 0x65, 0x75, null, 0x6D, 0x7D, 0x79, 0x61, 0x71].indexOf(cpu.__op); if (ProcessOp(cpu, pmode) === true){ cpu.__op = -1; ALU(cpu, 0, cpu.__opv); } } /*function MATHC(cpu, m){ // To be used by both the ADC and SBC op codes. // m == 0 - Add // m == 1 - Subtract switch(cpu.__step){ case 0: cpu.__mem.address = cpu.__PC; PCUp(cpu, 1); cpu.__opv = cpu.__mem.byte; if (cpu.__op == 0x69){ // Immediate ALU(cpu, m, cpu.__opv); cpu.__op = -1; break; } case 1: switch (cpu.__op){ case 0x65: // Zero Page cpu.__mem.address = cpu.__opv; ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; break; case 0x75: // Zero Page, X cpu.__opv = (cpu.__opv + cpu.__XR) & 0xFF; break; case 0x6D: // Absolute case 0x7D: // Absolute, X case 0x79: // Absolute, Y cpu.__mem.address = cpu.__PC; PCUp(cpu, 1); cpu.__opv |= cpu.__mem.byte << 8; break; case 0x61: // Indirect, X cpu.__opv = (cpu.__opv + cpu.__XR) & 0xFF; break; case 0x71: // Indirect, Y cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; break; } break; case 2: switch (cpu.__op){ case 0x75: // Zero Page, X case 0x6D: // Absolute cpu.__mem.address = cpu.__opv; ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; break; case 0x7D: // Absolute, X case 0x79: // Absolute, Y let s = (cpu.__op === 0x7D) ? cpu.__XR : cpu.__YR; let l = (cpu.__opv & 0xFF) + s; cpu.__opv = (cpu.__opv & 0xFF00) | (l & 0xFF); if (l < 255){ cpu.__mem.address = cpu.__opv; ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; } break; case 0x61: // Indirect, X cpu.__mem.address = cpu.__opv; cpu.__opv = cpu.__mem.byte; break; case 0x71: // Indirect, Y cpu.__mem.address += 1; cpu.__opv |= cpu.__mem.byte << 8; break; } break; case 3: switch (cpu.__op){ case 0x7D: // Absolute, X case 0x79: // Absolute, Y let h = (cpu.__opv >> 8) + 1; cpu.__mem.address = (cpu.__opv & 0xFF) | (h << 8); ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; break; case 0x61: // Indirect, X cpu.__mem.address += 1; cpu.__opv |= cpu.__mem.byte << 8; break; case 0x71: // Indirect, Y let l = (cpu.__opv & 0xFF) + cpu.__YR; cpu.__opv = (cpu.__opv & 0xFF00) | (l & 0xFF); if (l <= 255){ cpu.__mem.address = cpu.__opv; ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; break; } } break; case 4: if (cpu.__op === 0x71){ let h = (cpu.__opv >> 8) + 1; cpu.__opv = (cpu.__opv & 0x00FF) | (h << 8); } cpu.__mem.address = cpu.__opv; ALU(cpu, m, cpu.__mem.byte); cpu.__op = -1; break; } cpu.__step += 1; }*/ function AND(cpu){ } function ASL(cpu){ } function BIT(cpu){ } function BRANCH(cpu){ switch(cpu.__cycle){ case 0: let branch = false; switch(cpu.__op){ case 0x10: // BPL branch = (cpu.N === 0); break; case 0x30: // BMI branch = (cpu.N === 1); break; case 0x50: // BVC branch = (cpu.V === 0); break; case 0x70: // BVS branch = (cpu.V === 1); break; case 0x90: // BCC branch = (cpu.C === 0); break; case 0xB0: // BCS branch = (cpu.C === 1); break; case 0xD0: // BNE branch = (cpu.Z === 0); break; case 0xF0: // BEQ branch = (cpu.Z === 1); break; } if (branch === false) PCUp(cpu, 1); case 1: if (cpu.__cycle === 1){ // TODO: Huh??? cpu.__mem.address = this.__PC; let v = cpu.__mem.byte; if (v > 128){ PCDown(cpu, 255 - v); } else { PCUp(cpu, v); } } cpu.__op = -1; } cpu.__cycle += 1; } function BRK(cpu){ } function CMP(cpu){ } function CPX(cpu){ } function CPY(cpu){ } function DEC(cpu){ } function EOR(cpu){ } function FLAG(cpu){ switch (cpu.__op){ case 0x18: // CLC cpu.C = 0; break; case 0x38: // SEC cpu.C = 1; break; case 0x58: // CLI cpu.I = 0; break; case 0x78: // SEI cpu.I = 1; break; case 0xB8: // CLV cpu.V = 0; break; case 0xD8: // CLD cpu.D = 0; break; case 0xF8: // SED cpu.D = 1; break; } cpu.__op = -1; } function INC(cpu){ } function JMP(cpu){ } function JRS(cpu){ } function LDA(cpu){ } function LDX(cpu){ } function LDY(cpu){ } function LSR(cpu){ } function ORA(cpu){ } function REGISTER(cpu){ let t = 0; switch(this.__op){ case 0xAA: // TAX cpu.__XR = cpu.__AR; t = cpu.__XR; break; case 0x8A: // TXA cpu.__AR = cpu.__XR; t = cpu.__AR; break; case 0xCA: // DEX cpu.__XR = (cpu.__XR === 0) ? 255 : cpu.__XR - 1; t = cpu.__XR; break; case 0xE8: // INX cpu.__XR = (cpu.__XR === 255) ? 0 : cpu.__XR + 1; t = cpu.__XR; break; case 0xA8: // TAY cpu.__YR = cpu.__AR; t = cpu.__YR; break; case 0x98: // TYA cpu.__AR = cpu.__YR; t = cpu.__AR; break; case 0x88: // DEY cpu.__YR = (cpu.__YR === 0) ? 255 : cpu.__YR - 1; t = cpu.__YR; break; case 0xC8: // INY cpu.__YR = (cpu.__YR === 255) ? 0 : cpu.__YR + 1; t = cpu.__YR; break; } cpu.N = (t >= 0x80); cpu.Z = (t === 0); cpu.__op = -1; } function ROL(cpu){ } function ROR(cpu){ } function RTI(cpu){ } function RTS(cpu){ } function SBC(cpu){ let pmode = [0xE9, 0xE5, 0xF5, null, 0xED, 0xFD, 0xF9, 0xE1, 0xF1].indexOf(cpu.__op); if (ProcessOp(cpu, pmode) === true){ cpu.__op = -1; ALU(cpu, 1, cpu.__opv); } } function STA(cpu){ } function STACK(cpu){ switch(cpu.__op){ case 0x9A: // TXS cpu.__SP = cpu.__XR; cpu.__op = -1; break; case 0xBA: // TSX cpu.__XR = cpu.__SP; cpu.__op = -1; break; case 0x48: // PHA if (cpu.__cycle === 0){ cpu.__mem.address = 0x0100 | cpu.__SP; } else if (cpu.__cycle === 1){ cpu.__mem.byte = cpu.__AR; cpu.__SP = (cpu.__SP === 0) ? 255 : cpu.__SP - 1; cpu.__op = -1; } break; case 0x68: // PLA if (cpu.__cycle === 0){ cpu.__mem.address = 0x0100 | cpu.__SP; } else if (cpu.__cycle === 1){ cpu.__AR = cpu.__mem.byte; cpu.Z = (cpu.__AR === 0); cpu.N = BITM.isOn(cpu.__AR, 7); } else if (cpu.__step === 2){ cpu.__SP = (cpu.__SP === 255) ? 0 : cpu.__SP + 1; cpu.__op = -1; } break; case 0x08: // PHP if (cpu.__cycle === 0){ cpu.__mem.address = 0x0100 | cpu.__SP; } else if (cpu.__cycle === 1){ cpu.__mem.byte = cpu.__PR; cpu.__SP = (cpu.__SP === 0) ? 255 : cpu.__SP - 1; cpu.__op = -1; } break; case 0x28: // PLP if (cpu.__cycle === 0){ cpu.__mem.address = 0x0100 | cpu.__SP; } else if (cpu.__cycle === 1){ cpu.__SP = cpu.__mem.byte; } else if (cpu.__cycle === 2){ cpu.__SP = (cpu.__SP === 255) ? 0 : cpu.__SP + 1; cpu.__op = -1; } break; } cpu.__cycle += 1; } function STX(cpu){ } function STY(cpu){ } // -------------------------------------------------------------------------------------------- // Test to see if both a and b's high byte is the same... same page. function SamePage(a, b){ return ((a >> 8) == (b >> 8)); } function PCHI(cpu, b){ cpu.__PC = (cpu.__PC & 0x00FF) | (b << 8); } function PCLOW(cpu, b){ cpu.__PC = (cpu.__PC & 0xFF00) | b; } function PCUp(cpu, amount){ if (cpu.__pcc === 1){ PCHI(cpu, ((cpu.__PC & 0xFF00) >> 8) + 1); cpu.__pcc = 0; } else if ((cpu.__PC & 0x00FF) + amount > 255){ cpu.__pcc = 1; PCLOW(cpu, ((cpu.__PC & 0x00FF) + amount) - 256); } else { PCLOW(cpu, (cpu.__PC & 0x00FF) + amount); } } function PCDown(cpu, amount){ if (cpu.__pcc === -1){ PCHI(cpu, ((cpu.__PC & 0xFF00) >> 8) - 1); cpu.__pcc = 0; } else if ((cpu.__PC & 0x00FF) - amount < 0){ cpu.__pcc = -1; PCLOW(cpu, ((cpu.__PC & 0x00FF) - amount) + 256); } else { PCLOW(cpu, (cpu.__PC & 0x00FF) - amount); } } function MemAddrFrom(cpu, addr){ cpu.__mem.address = addr; let v = cpu.__mem.byte; cpu.__mem.address = (addr + 1) & 0xFF; v |= cpu.__mem.byte << 8; return v; } function ALU(cpu, m, b){ let v = 0; switch(m){ case 0: // Addition cpu.__AR = (cpu.__AR + b) + ((cpu.C === 1) ? 1 : 0); cpu.C = (v >= 256); break; case 1: // Subtraction cpu.__AR = (cpu.__AR - b) - ((cpu.C === 0) ? 1 : 0); cpu.C = (v >= 0); break; } cpu.__AR &= 0xFF; cpu.V = (BITM.isOn(cpu.__AR, 7) === BITM.isOn(b, 7)) && (BITM.val(v, 7) !== BITM.val(cpu.__AR, 7)); cpu.N = BITM.val(cpu.__AR, 7); cpu.Z = (cpu.__AR === 0); return cpu.__AR; } // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- class CPU{ constructor(){ // Registers this.__PC = 0; // Program Counter (16 bit) this.__SP = 255; // Stack Pointer (8 bit) this.__PR = 32; // Status Register (8 bit, bit 5 is always 1) this.__XR = 0; // X Register (8 bit) this.__YR = 0; // Y Register (8 bit) this.__AR = 0; // Accumulator Register (8 bit) // Variables to watch for Hardware Interrupts. this.__nmi = false; this.__irq = false; // Variable for tracking tick operations. this.__op = -1; this.__opmem = 0; this.__opv = 0; this.__cycle = 0; this.__pcc = 0; // Program Counter Carry. // Memory module or controller. this.__mem = null; // Must be explicitly attached. // Hold any created CLK instances. this.__clkfn = null; } // ---------------------------------------- // CPU Registers. Here for debug purposes. get PC(){return this.__PC;} get SP(){return this.__SP;} get P(){return this.__PR;} get X(){return this.__XR;} get Y(){return this.__YR;} get A(){return this.__AR;} // ---------------------------------------- // Quick Flag Access get N(){return (BITM.isOn(this.__PR, 7)) ? 1 : 0;} set N(n){this.__PR = (n === true || n === 1) ? BITM.set(this.__PR, 7) : BITM.clear(this.__PR, 7);} get V(){return (BITM.isOn(this.__PR, 6)) ? 1 : 0;} set V(v){this.__PR = (v === true || v === 1) ? BITM.set(this.__PR, 6) : BITM.clear(this.__PR, 6);} get B(){return (BITM.isOn(this.__PR, 4)) ? 1 : 0;} set B(b){this.__PR = (b === true || b === 1) ? BITM.set(this.__PR, 4) : BITM.clear(this.__PR, 4);} get D(){return (BITM.isOn(this.__PR, 3)) ? 1 : 0;} set D(d){this.__PR = (d === true || d === 1) ? BITM.set(this.__PR, 3) : BITM.clear(this.__PR, 3);} get I(){return (BITM.isOn(this.__PR, 2)) ? 1 : 0;} set I(i){this.__PR = (i === true || i === 1) ? BITM.set(this.__PR, 2) : BITM.clear(this.__PR, 2);} get Z(){return (BITM.isOn(this.__PR, 1)) ? 1 : 0;} set Z(z){this.__PR = (z === true || z === 1) ? BITM.set(this.__PR, 1) : BITM.clear(this.__PR, 1);} get C(){return (BITM.isOn(this.__PR, 0)) ? 1 : 0;} set C(c){this.__PR = (c === true || c === 1) ? BITM.set(this.__PR, 0) : BITM.clear(this.__PR, 0);} // ---------------------------------------- // Hardware interrupt triggers. Settable only. set NMI(n){ this.__nmi = (n === true); } set IRQ(q){ // TODO: Verify this. // TODO: Do not set if the interrupt flag is off. this.__irq = (q === true); } reset(){ this.__mem.address = 0xFFFC; this.__PC = this.__mem.byte; this.__mem.address = 0xFFFD; this.__PC |= this.__mem.byte << 8; // Disabling the IRQ interrupts is the ONLY flag that must be set // during reset. The others are random. this.I = 1; } // ----------------------------------------- // Set and Get Memory property. get memory(){return this.__mem;} set memory(m){ if (!(m instanceof Memory)) throw new ValueError("Expected Memory instance object."); this.__mem = m; } // ----------------------------------------- clk(){ if (this.__clkfn === null){ this.__clkfn = (function(){ if (this.__pcc !== 0){ if (this.__pcc > 0) { PCUp(this, 1); } else { PCDown(this, 1); } } else if (this.__op < 0){ if (this.__nmi) { // TODO: Handle NMI Interrupt. } else if (this.__irq) { this.__irq = false; if (!BITM.isOn(this.__PR, 5)){ // TODO: Handle IRQ Interrupt. } } else { this.__cycle = 0; this.__mem.address = this.__PC; this.__op = this.__mem.byte; PCUp(this, 1); } } else { switch(this.__op){ case 0x69: case 0x65: case 0x75: case 0x6D: case 0x7D: case 0x79: case 0x61: case 0x71: ADC(this); break; case 0x29: case 0x25: case 0x35: case 0x2D: case 0x3D: case 0x39: case 0x21: case 0x31: AND(this); break; case 0x0A: case 0x06: case 0x16: case 0x0E: case 0x1E: ASL(this); break; case 0x24: case 0x2C: BIT(this); break; case 0x10: case 0x30: case 0x50: case 0x70: case 0x90: case 0xB0: case 0xD0: case 0xF0: BRANCH(this); break; case 0x00: BRK(this); break; case 0xC9: case 0xC5: case 0xD5: case 0xCD: case 0xDD: case 0xD9: case 0xC1: case 0xD1: CMP(this); break; case 0xE0: case 0xE4: case 0xEC: CPX(this); break; case 0xC0: case 0xC4: case 0xCC: CPY(this); break; case 0xC6: case 0xD6: case 0xCE: case 0xDE: DEC(this); break; case 0x49: case 0x45: case 0x55: case 0x4D: case 0x5D: case 0x59: case 0x41: case 0x51: EOR(this); break; case 0x18: case 0x38: case 0x58: case 0x78: case 0xB8: case 0xD8: case 0xF8: FLAG(this); break; case 0xE6: case 0xF6: case 0xEE: case 0xFE: INC(this); break; case 0x4C: case 0x6C: JMP(this); break; case 0x20: JSR(this); break; case 0xA9: case 0xA5: case 0xB5: case 0xAD: case 0xBD: case 0xB9: case 0xA1: case 0xB1: LDA(this); break; case 0xA2: case 0xA6: case 0xB6: case 0xAE: case 0xBE: LDX(this); break; case 0xA0: case 0xA4: case 0xB4: case 0xAC: case 0xBC: LDY(this); break; case 0x4A: case 0x46: case 0x56: case 0x4E: case 0x5E: LSR(this); break; case 0xEA: // NOP if (this.__step == 1){ this.__op = -1; } else {this.__step += 1;} break; case 0x09: case 0x05: case 0x15: case 0x0D: case 0x1D: case 0x19: case 0x01: case 0x11: ORA(this); break; case 0xAA: case 0x8A: case 0xCA: case 0xE8: case 0xA8: case 0x98: case 0x88: case 0xC8: REGISTER(this); break; case 0x2A: case 0x26: case 0x36: case 0x2E: case 0x3E: ROL(this); break; case 0x6A: case 0x66: case 0x76: case 0x6E: case 0x7E: ROR(this); break; case 0x40: RTI(this); break; case 0x60: RTS(this); break; case 0xE9: case 0xE5: case 0xF5: case 0xED: case 0xFD: case 0xF9: case 0xE1: case 0xF1: SBC(this); break; case 0x85: case 0x95: case 0x8D: case 0x9D: case 0x99: case 0x81: case 0x91: STA(this); break; case 0x9A: case 0xBA: case 0x48: case 0x68: case 0x08: case 0x28: STACK(this); break; case 0x86: case 0x96: case 0x8E: STX(this); break; case 0x84: case 0x94: case 0x8C: STY(this); break; } } }).bind(this); } return this.__clkfn; } } module.exports = CPU;