|
|
@@ -1,23 +1,196 @@ |
|
|
|
/* |
|
|
|
* Emulate a basic 6502 (MOS) chip. |
|
|
|
*/ |
|
|
|
|
|
|
|
const BIT = require('../../utils/bitman.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{ |
|
|
|
constructor(){ |
|
|
|
// Registers |
|
|
|
this.__PC = 0; // Program Counter (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.__YR = 0; // Y 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.__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. |
|
|
|
this.__mem = null; // Must be explicitly attached. |
|
|
|
|
|
|
@@ -25,6 +198,16 @@ class CPU{ |
|
|
|
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){ |
|
|
|
this.__nmi = (n === true); |
|
|
|
} |
|
|
@@ -40,20 +223,114 @@ class CPU{ |
|
|
|
// 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(){ |
|
|
|
if (this.__clkfn === null){ |
|
|
|
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); |
|
|
|
} |
|
|
|
return this.__clkfn; |
|
|
|
} |
|
|
|
|
|
|
|
memory(mem){ |
|
|
|
if (!(mem instanceof Memory)) |
|
|
|
throw new ValueError("Expected Memory instance object."); |
|
|
|
this.__mem = mem; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
module.exports = CPU; |