| /* | /* | ||||
| * Emulate a basic 6502 (MOS) chip. | * Emulate a basic 6502 (MOS) chip. | ||||
| */ | */ | ||||
| const BIT = require('../../utils/bitman.js'); | |||||
| var Memory = require('../../common/memory.js'); | var Memory = require('../../common/memory.js'); | ||||
| function ADC(cpu){ | |||||
| } | |||||
| function AND(cpu){ | |||||
| } | |||||
| function ASL(cpu){ | |||||
| } | |||||
| function BIT(cpu){ | |||||
| } | |||||
| function BRANCH(cpu){ | |||||
| } | |||||
| function BRK(cpu){ | |||||
| } | |||||
| function CMP(cpu){ | |||||
| } | |||||
| function CPX(cpu){ | |||||
| } | |||||
| function CPY(cpu){ | |||||
| } | |||||
| function DEC(cpu){ | |||||
| } | |||||
| function EOR(cpu){ | |||||
| } | |||||
| function FLAG(cpu){ | |||||
| } | |||||
| 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){ | |||||
| } | |||||
| function ROL(cpu){ | |||||
| } | |||||
| function ROR(cpu){ | |||||
| } | |||||
| function RTI(cpu){ | |||||
| } | |||||
| function RTS(cpu){ | |||||
| } | |||||
| function SBC(cpu){ | |||||
| } | |||||
| function STA(cpu){ | |||||
| } | |||||
| function STACK(cpu){ | |||||
| } | |||||
| 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); | |||||
| } | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------- | |||||
| class CPU{ | class CPU{ | ||||
| constructor(){ | constructor(){ | ||||
| // Registers | // Registers | ||||
| this.__PC = 0; // Program Counter (16 bit) | this.__PC = 0; // Program Counter (16 bit) | ||||
| this.__IRQ = 0; // IRQ interrupt address code (16 bit) | this.__IRQ = 0; // IRQ interrupt address code (16 bit) | ||||
| this.__SR = 0; // Status Register (8 bit) | |||||
| this.__PR = 0; // Status Register (8 bit) | |||||
| this.__XR = 0; // X Register (8 bit) | this.__XR = 0; // X Register (8 bit) | ||||
| this.__YR = 0; // Y Register (8 bit) | this.__YR = 0; // Y Register (8 bit) | ||||
| this.__AR = 0; // Accumulator Register (8 bit) | this.__AR = 0; // Accumulator Register (8 bit) | ||||
| // Variables to watch for Interrupts. | |||||
| // Variables to watch for Hardware Interrupts. | |||||
| this.__nmi = false; | this.__nmi = false; | ||||
| this.__irq = false; | this.__irq = false; | ||||
| // Variable for tracking tick operations. | |||||
| this.__op = -1; | |||||
| this.__opmem = 0; | |||||
| this.__opv = 0; | |||||
| this.__step = 0; | |||||
| this.__pcc = 0; // Program Counter Carry. | |||||
| // Memory module or controller. | // Memory module or controller. | ||||
| this.__mem = null; // Must be explicitly attached. | this.__mem = null; // Must be explicitly attached. | ||||
| this.__clkfn = null; | this.__clkfn = null; | ||||
| } | } | ||||
| // ---------------------------------------- | |||||
| // CPU Registers. Here for debug purposes. | |||||
| get PC(){return this.__PC;} | |||||
| get P(){return this.__SR;} | |||||
| get X(){return this.__XR;} | |||||
| get Y(){return this.__YR;} | |||||
| get A(){return this.__AR;} | |||||
| // ---------------------------------------- | |||||
| // Hardware interrupt triggers. Settable only. | |||||
| set NMI(n){ | set NMI(n){ | ||||
| this.__nmi = (n === true); | this.__nmi = (n === true); | ||||
| } | } | ||||
| // TODO: Reset status registers that get changed on a reset. | // TODO: Reset status registers that get changed on a reset. | ||||
| } | } | ||||
| // ----------------------------------------- | |||||
| // Set and Get Memory property. | |||||
| get memory(){return this.__mem;} | |||||
| set memory(m){ | |||||
| if (!(mem instanceof Memory)) | |||||
| throw new ValueError("Expected Memory instance object."); | |||||
| this.__mem = m; | |||||
| } | |||||
| // ----------------------------------------- | |||||
| clk(){ | clk(){ | ||||
| if (this.__clkfn === null){ | if (this.__clkfn === null){ | ||||
| this.__clkfn = (function(){ | this.__clkfn = (function(){ | ||||
| // TODO: All the work!! | |||||
| 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 (!BIT.isOn(this.__PR, 5)){ | |||||
| // TODO: Handle IRQ Interrupt. | |||||
| } | |||||
| } else { | |||||
| this.__step = 0; | |||||
| 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); | }).bind(this); | ||||
| } | } | ||||
| return this.__clkfn; | return this.__clkfn; | ||||
| } | } | ||||
| memory(mem){ | |||||
| if (!(mem instanceof Memory)) | |||||
| throw new ValueError("Expected Memory instance object."); | |||||
| this.__mem = mem; | |||||
| } | |||||
| } | } | ||||
| module.exports = CPU; | module.exports = CPU; |