2019-06-11 15:27:11 +08:00

2025 lines
53 KiB
JavaScript

var utils = require("./utils");
var CPU = function(nes) {
this.nes = nes;
// Keep Chrome happy
this.mem = null;
this.REG_ACC = null;
this.REG_X = null;
this.REG_Y = null;
this.REG_SP = null;
this.REG_PC = null;
this.REG_PC_NEW = null;
this.REG_STATUS = null;
this.F_CARRY = null;
this.F_DECIMAL = null;
this.F_INTERRUPT = null;
this.F_INTERRUPT_NEW = null;
this.F_OVERFLOW = null;
this.F_SIGN = null;
this.F_ZERO = null;
this.F_NOTUSED = null;
this.F_NOTUSED_NEW = null;
this.F_BRK = null;
this.F_BRK_NEW = null;
this.opdata = null;
this.cyclesToHalt = null;
this.crash = null;
this.irqRequested = null;
this.irqType = null;
this.reset();
};
CPU.prototype = {
// IRQ Types
IRQ_NORMAL: 0,
IRQ_NMI: 1,
IRQ_RESET: 2,
reset: function() {
// Main memory
this.mem = new Array(0x10000);
for (var i = 0; i < 0x2000; i++) {
this.mem[i] = 0xff;
}
for (var p = 0; p < 4; p++) {
var j = p * 0x800;
this.mem[j + 0x008] = 0xf7;
this.mem[j + 0x009] = 0xef;
this.mem[j + 0x00a] = 0xdf;
this.mem[j + 0x00f] = 0xbf;
}
for (var k = 0x2001; k < this.mem.length; k++) {
this.mem[k] = 0;
}
// CPU Registers:
this.REG_ACC = 0;
this.REG_X = 0;
this.REG_Y = 0;
// Reset Stack pointer:
this.REG_SP = 0x01ff;
// Reset Program counter:
this.REG_PC = 0x8000 - 1;
this.REG_PC_NEW = 0x8000 - 1;
// Reset Status register:
this.REG_STATUS = 0x28;
this.setStatus(0x28);
// Set flags:
this.F_CARRY = 0;
this.F_DECIMAL = 0;
this.F_INTERRUPT = 1;
this.F_INTERRUPT_NEW = 1;
this.F_OVERFLOW = 0;
this.F_SIGN = 0;
this.F_ZERO = 1;
this.F_NOTUSED = 1;
this.F_NOTUSED_NEW = 1;
this.F_BRK = 1;
this.F_BRK_NEW = 1;
this.opdata = new OpData().opdata;
this.cyclesToHalt = 0;
// Reset crash flag:
this.crash = false;
// Interrupt notification:
this.irqRequested = false;
this.irqType = null;
},
// Emulates a single CPU instruction, returns the number of cycles
emulate: function() {
var temp;
var add;
// Check interrupts:
if (this.irqRequested) {
temp =
this.F_CARRY |
((this.F_ZERO === 0 ? 1 : 0) << 1) |
(this.F_INTERRUPT << 2) |
(this.F_DECIMAL << 3) |
(this.F_BRK << 4) |
(this.F_NOTUSED << 5) |
(this.F_OVERFLOW << 6) |
(this.F_SIGN << 7);
this.REG_PC_NEW = this.REG_PC;
this.F_INTERRUPT_NEW = this.F_INTERRUPT;
switch (this.irqType) {
case 0: {
// Normal IRQ:
if (this.F_INTERRUPT !== 0) {
// console.log("Interrupt was masked.");
break;
}
this.doIrq(temp);
// console.log("Did normal IRQ. I="+this.F_INTERRUPT);
break;
}
case 1: {
// NMI:
this.doNonMaskableInterrupt(temp);
break;
}
case 2: {
// Reset:
this.doResetInterrupt();
break;
}
}
this.REG_PC = this.REG_PC_NEW;
this.F_INTERRUPT = this.F_INTERRUPT_NEW;
this.F_BRK = this.F_BRK_NEW;
this.irqRequested = false;
}
var opinf = this.opdata[this.nes.mmap.load(this.REG_PC + 1)];
var cycleCount = opinf >> 24;
var cycleAdd = 0;
// Find address mode:
var addrMode = (opinf >> 8) & 0xff;
// Increment PC by number of op bytes:
var opaddr = this.REG_PC;
this.REG_PC += (opinf >> 16) & 0xff;
var addr = 0;
switch (addrMode) {
case 0: {
// Zero Page mode. Use the address given after the opcode,
// but without high byte.
addr = this.load(opaddr + 2);
break;
}
case 1: {
// Relative mode.
addr = this.load(opaddr + 2);
if (addr < 0x80) {
addr += this.REG_PC;
} else {
addr += this.REG_PC - 256;
}
break;
}
case 2: {
// Ignore. Address is implied in instruction.
break;
}
case 3: {
// Absolute mode. Use the two bytes following the opcode as
// an address.
addr = this.load16bit(opaddr + 2);
break;
}
case 4: {
// Accumulator mode. The address is in the accumulator
// register.
addr = this.REG_ACC;
break;
}
case 5: {
// Immediate mode. The value is given after the opcode.
addr = this.REG_PC;
break;
}
case 6: {
// Zero Page Indexed mode, X as index. Use the address given
// after the opcode, then add the
// X register to it to get the final address.
addr = (this.load(opaddr + 2) + this.REG_X) & 0xff;
break;
}
case 7: {
// Zero Page Indexed mode, Y as index. Use the address given
// after the opcode, then add the
// Y register to it to get the final address.
addr = (this.load(opaddr + 2) + this.REG_Y) & 0xff;
break;
}
case 8: {
// Absolute Indexed Mode, X as index. Same as zero page
// indexed, but with the high byte.
addr = this.load16bit(opaddr + 2);
if ((addr & 0xff00) !== ((addr + this.REG_X) & 0xff00)) {
cycleAdd = 1;
}
addr += this.REG_X;
break;
}
case 9: {
// Absolute Indexed Mode, Y as index. Same as zero page
// indexed, but with the high byte.
addr = this.load16bit(opaddr + 2);
if ((addr & 0xff00) !== ((addr + this.REG_Y) & 0xff00)) {
cycleAdd = 1;
}
addr += this.REG_Y;
break;
}
case 10: {
// Pre-indexed Indirect mode. Find the 16-bit address
// starting at the given location plus
// the current X register. The value is the contents of that
// address.
addr = this.load(opaddr + 2);
if ((addr & 0xff00) !== ((addr + this.REG_X) & 0xff00)) {
cycleAdd = 1;
}
addr += this.REG_X;
addr &= 0xff;
addr = this.load16bit(addr);
break;
}
case 11: {
// Post-indexed Indirect mode. Find the 16-bit address
// contained in the given location
// (and the one following). Add to that address the contents
// of the Y register. Fetch the value
// stored at that adress.
addr = this.load16bit(this.load(opaddr + 2));
if ((addr & 0xff00) !== ((addr + this.REG_Y) & 0xff00)) {
cycleAdd = 1;
}
addr += this.REG_Y;
break;
}
case 12: {
// Indirect Absolute mode. Find the 16-bit address contained
// at the given location.
addr = this.load16bit(opaddr + 2); // Find op
if (addr < 0x1fff) {
addr =
this.mem[addr] +
(this.mem[(addr & 0xff00) | (((addr & 0xff) + 1) & 0xff)] << 8); // Read from address given in op
} else {
addr =
this.nes.mmap.load(addr) +
(this.nes.mmap.load(
(addr & 0xff00) | (((addr & 0xff) + 1) & 0xff)
) <<
8);
}
break;
}
}
// Wrap around for addresses above 0xFFFF:
addr &= 0xffff;
// ----------------------------------------------------------------------------------------------------
// Decode & execute instruction:
// ----------------------------------------------------------------------------------------------------
// This should be compiled to a jump table.
switch (opinf & 0xff) {
case 0: {
// *******
// * ADC *
// *******
// Add with carry.
temp = this.REG_ACC + this.load(addr) + this.F_CARRY;
if (
((this.REG_ACC ^ this.load(addr)) & 0x80) === 0 &&
((this.REG_ACC ^ temp) & 0x80) !== 0
) {
this.F_OVERFLOW = 1;
} else {
this.F_OVERFLOW = 0;
}
this.F_CARRY = temp > 255 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
this.REG_ACC = temp & 255;
cycleCount += cycleAdd;
break;
}
case 1: {
// *******
// * AND *
// *******
// AND memory with accumulator.
this.REG_ACC = this.REG_ACC & this.load(addr);
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 2: {
// *******
// * ASL *
// *******
// Shift left one bit
if (addrMode === 4) {
// ADDR_ACC = 4
this.F_CARRY = (this.REG_ACC >> 7) & 1;
this.REG_ACC = (this.REG_ACC << 1) & 255;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
} else {
temp = this.load(addr);
this.F_CARRY = (temp >> 7) & 1;
temp = (temp << 1) & 255;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
this.write(addr, temp);
}
break;
}
case 3: {
// *******
// * BCC *
// *******
// Branch on carry clear
if (this.F_CARRY === 0) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 4: {
// *******
// * BCS *
// *******
// Branch on carry set
if (this.F_CARRY === 1) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 5: {
// *******
// * BEQ *
// *******
// Branch on zero
if (this.F_ZERO === 0) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 6: {
// *******
// * BIT *
// *******
temp = this.load(addr);
this.F_SIGN = (temp >> 7) & 1;
this.F_OVERFLOW = (temp >> 6) & 1;
temp &= this.REG_ACC;
this.F_ZERO = temp;
break;
}
case 7: {
// *******
// * BMI *
// *******
// Branch on negative result
if (this.F_SIGN === 1) {
cycleCount++;
this.REG_PC = addr;
}
break;
}
case 8: {
// *******
// * BNE *
// *******
// Branch on not zero
if (this.F_ZERO !== 0) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 9: {
// *******
// * BPL *
// *******
// Branch on positive result
if (this.F_SIGN === 0) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 10: {
// *******
// * BRK *
// *******
this.REG_PC += 2;
this.push((this.REG_PC >> 8) & 255);
this.push(this.REG_PC & 255);
this.F_BRK = 1;
this.push(
this.F_CARRY |
((this.F_ZERO === 0 ? 1 : 0) << 1) |
(this.F_INTERRUPT << 2) |
(this.F_DECIMAL << 3) |
(this.F_BRK << 4) |
(this.F_NOTUSED << 5) |
(this.F_OVERFLOW << 6) |
(this.F_SIGN << 7)
);
this.F_INTERRUPT = 1;
//this.REG_PC = load(0xFFFE) | (load(0xFFFF) << 8);
this.REG_PC = this.load16bit(0xfffe);
this.REG_PC--;
break;
}
case 11: {
// *******
// * BVC *
// *******
// Branch on overflow clear
if (this.F_OVERFLOW === 0) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 12: {
// *******
// * BVS *
// *******
// Branch on overflow set
if (this.F_OVERFLOW === 1) {
cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1;
this.REG_PC = addr;
}
break;
}
case 13: {
// *******
// * CLC *
// *******
// Clear carry flag
this.F_CARRY = 0;
break;
}
case 14: {
// *******
// * CLD *
// *******
// Clear decimal flag
this.F_DECIMAL = 0;
break;
}
case 15: {
// *******
// * CLI *
// *******
// Clear interrupt flag
this.F_INTERRUPT = 0;
break;
}
case 16: {
// *******
// * CLV *
// *******
// Clear overflow flag
this.F_OVERFLOW = 0;
break;
}
case 17: {
// *******
// * CMP *
// *******
// Compare memory and accumulator:
temp = this.REG_ACC - this.load(addr);
this.F_CARRY = temp >= 0 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
cycleCount += cycleAdd;
break;
}
case 18: {
// *******
// * CPX *
// *******
// Compare memory and index X:
temp = this.REG_X - this.load(addr);
this.F_CARRY = temp >= 0 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
break;
}
case 19: {
// *******
// * CPY *
// *******
// Compare memory and index Y:
temp = this.REG_Y - this.load(addr);
this.F_CARRY = temp >= 0 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
break;
}
case 20: {
// *******
// * DEC *
// *******
// Decrement memory by one:
temp = (this.load(addr) - 1) & 0xff;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
this.write(addr, temp);
break;
}
case 21: {
// *******
// * DEX *
// *******
// Decrement index X by one:
this.REG_X = (this.REG_X - 1) & 0xff;
this.F_SIGN = (this.REG_X >> 7) & 1;
this.F_ZERO = this.REG_X;
break;
}
case 22: {
// *******
// * DEY *
// *******
// Decrement index Y by one:
this.REG_Y = (this.REG_Y - 1) & 0xff;
this.F_SIGN = (this.REG_Y >> 7) & 1;
this.F_ZERO = this.REG_Y;
break;
}
case 23: {
// *******
// * EOR *
// *******
// XOR Memory with accumulator, store in accumulator:
this.REG_ACC = (this.load(addr) ^ this.REG_ACC) & 0xff;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
cycleCount += cycleAdd;
break;
}
case 24: {
// *******
// * INC *
// *******
// Increment memory by one:
temp = (this.load(addr) + 1) & 0xff;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
this.write(addr, temp & 0xff);
break;
}
case 25: {
// *******
// * INX *
// *******
// Increment index X by one:
this.REG_X = (this.REG_X + 1) & 0xff;
this.F_SIGN = (this.REG_X >> 7) & 1;
this.F_ZERO = this.REG_X;
break;
}
case 26: {
// *******
// * INY *
// *******
// Increment index Y by one:
this.REG_Y++;
this.REG_Y &= 0xff;
this.F_SIGN = (this.REG_Y >> 7) & 1;
this.F_ZERO = this.REG_Y;
break;
}
case 27: {
// *******
// * JMP *
// *******
// Jump to new location:
this.REG_PC = addr - 1;
break;
}
case 28: {
// *******
// * JSR *
// *******
// Jump to new location, saving return address.
// Push return address on stack:
this.push((this.REG_PC >> 8) & 255);
this.push(this.REG_PC & 255);
this.REG_PC = addr - 1;
break;
}
case 29: {
// *******
// * LDA *
// *******
// Load accumulator with memory:
this.REG_ACC = this.load(addr);
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
cycleCount += cycleAdd;
break;
}
case 30: {
// *******
// * LDX *
// *******
// Load index X with memory:
this.REG_X = this.load(addr);
this.F_SIGN = (this.REG_X >> 7) & 1;
this.F_ZERO = this.REG_X;
cycleCount += cycleAdd;
break;
}
case 31: {
// *******
// * LDY *
// *******
// Load index Y with memory:
this.REG_Y = this.load(addr);
this.F_SIGN = (this.REG_Y >> 7) & 1;
this.F_ZERO = this.REG_Y;
cycleCount += cycleAdd;
break;
}
case 32: {
// *******
// * LSR *
// *******
// Shift right one bit:
if (addrMode === 4) {
// ADDR_ACC
temp = this.REG_ACC & 0xff;
this.F_CARRY = temp & 1;
temp >>= 1;
this.REG_ACC = temp;
} else {
temp = this.load(addr) & 0xff;
this.F_CARRY = temp & 1;
temp >>= 1;
this.write(addr, temp);
}
this.F_SIGN = 0;
this.F_ZERO = temp;
break;
}
case 33: {
// *******
// * NOP *
// *******
// No OPeration.
// Ignore.
break;
}
case 34: {
// *******
// * ORA *
// *******
// OR memory with accumulator, store in accumulator.
temp = (this.load(addr) | this.REG_ACC) & 255;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
this.REG_ACC = temp;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 35: {
// *******
// * PHA *
// *******
// Push accumulator on stack
this.push(this.REG_ACC);
break;
}
case 36: {
// *******
// * PHP *
// *******
// Push processor status on stack
this.F_BRK = 1;
this.push(
this.F_CARRY |
((this.F_ZERO === 0 ? 1 : 0) << 1) |
(this.F_INTERRUPT << 2) |
(this.F_DECIMAL << 3) |
(this.F_BRK << 4) |
(this.F_NOTUSED << 5) |
(this.F_OVERFLOW << 6) |
(this.F_SIGN << 7)
);
break;
}
case 37: {
// *******
// * PLA *
// *******
// Pull accumulator from stack
this.REG_ACC = this.pull();
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
break;
}
case 38: {
// *******
// * PLP *
// *******
// Pull processor status from stack
temp = this.pull();
this.F_CARRY = temp & 1;
this.F_ZERO = ((temp >> 1) & 1) === 1 ? 0 : 1;
this.F_INTERRUPT = (temp >> 2) & 1;
this.F_DECIMAL = (temp >> 3) & 1;
this.F_BRK = (temp >> 4) & 1;
this.F_NOTUSED = (temp >> 5) & 1;
this.F_OVERFLOW = (temp >> 6) & 1;
this.F_SIGN = (temp >> 7) & 1;
this.F_NOTUSED = 1;
break;
}
case 39: {
// *******
// * ROL *
// *******
// Rotate one bit left
if (addrMode === 4) {
// ADDR_ACC = 4
temp = this.REG_ACC;
add = this.F_CARRY;
this.F_CARRY = (temp >> 7) & 1;
temp = ((temp << 1) & 0xff) + add;
this.REG_ACC = temp;
} else {
temp = this.load(addr);
add = this.F_CARRY;
this.F_CARRY = (temp >> 7) & 1;
temp = ((temp << 1) & 0xff) + add;
this.write(addr, temp);
}
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
break;
}
case 40: {
// *******
// * ROR *
// *******
// Rotate one bit right
if (addrMode === 4) {
// ADDR_ACC = 4
add = this.F_CARRY << 7;
this.F_CARRY = this.REG_ACC & 1;
temp = (this.REG_ACC >> 1) + add;
this.REG_ACC = temp;
} else {
temp = this.load(addr);
add = this.F_CARRY << 7;
this.F_CARRY = temp & 1;
temp = (temp >> 1) + add;
this.write(addr, temp);
}
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp;
break;
}
case 41: {
// *******
// * RTI *
// *******
// Return from interrupt. Pull status and PC from stack.
temp = this.pull();
this.F_CARRY = temp & 1;
this.F_ZERO = ((temp >> 1) & 1) === 0 ? 1 : 0;
this.F_INTERRUPT = (temp >> 2) & 1;
this.F_DECIMAL = (temp >> 3) & 1;
this.F_BRK = (temp >> 4) & 1;
this.F_NOTUSED = (temp >> 5) & 1;
this.F_OVERFLOW = (temp >> 6) & 1;
this.F_SIGN = (temp >> 7) & 1;
this.REG_PC = this.pull();
this.REG_PC += this.pull() << 8;
if (this.REG_PC === 0xffff) {
return;
}
this.REG_PC--;
this.F_NOTUSED = 1;
break;
}
case 42: {
// *******
// * RTS *
// *******
// Return from subroutine. Pull PC from stack.
this.REG_PC = this.pull();
this.REG_PC += this.pull() << 8;
if (this.REG_PC === 0xffff) {
return; // return from NSF play routine:
}
break;
}
case 43: {
// *******
// * SBC *
// *******
temp = this.REG_ACC - this.load(addr) - (1 - this.F_CARRY);
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
if (
((this.REG_ACC ^ temp) & 0x80) !== 0 &&
((this.REG_ACC ^ this.load(addr)) & 0x80) !== 0
) {
this.F_OVERFLOW = 1;
} else {
this.F_OVERFLOW = 0;
}
this.F_CARRY = temp < 0 ? 0 : 1;
this.REG_ACC = temp & 0xff;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 44: {
// *******
// * SEC *
// *******
// Set carry flag
this.F_CARRY = 1;
break;
}
case 45: {
// *******
// * SED *
// *******
// Set decimal mode
this.F_DECIMAL = 1;
break;
}
case 46: {
// *******
// * SEI *
// *******
// Set interrupt disable status
this.F_INTERRUPT = 1;
break;
}
case 47: {
// *******
// * STA *
// *******
// Store accumulator in memory
this.write(addr, this.REG_ACC);
break;
}
case 48: {
// *******
// * STX *
// *******
// Store index X in memory
this.write(addr, this.REG_X);
break;
}
case 49: {
// *******
// * STY *
// *******
// Store index Y in memory:
this.write(addr, this.REG_Y);
break;
}
case 50: {
// *******
// * TAX *
// *******
// Transfer accumulator to index X:
this.REG_X = this.REG_ACC;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
break;
}
case 51: {
// *******
// * TAY *
// *******
// Transfer accumulator to index Y:
this.REG_Y = this.REG_ACC;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
break;
}
case 52: {
// *******
// * TSX *
// *******
// Transfer stack pointer to index X:
this.REG_X = this.REG_SP - 0x0100;
this.F_SIGN = (this.REG_SP >> 7) & 1;
this.F_ZERO = this.REG_X;
break;
}
case 53: {
// *******
// * TXA *
// *******
// Transfer index X to accumulator:
this.REG_ACC = this.REG_X;
this.F_SIGN = (this.REG_X >> 7) & 1;
this.F_ZERO = this.REG_X;
break;
}
case 54: {
// *******
// * TXS *
// *******
// Transfer index X to stack pointer:
this.REG_SP = this.REG_X + 0x0100;
this.stackWrap();
break;
}
case 55: {
// *******
// * TYA *
// *******
// Transfer index Y to accumulator:
this.REG_ACC = this.REG_Y;
this.F_SIGN = (this.REG_Y >> 7) & 1;
this.F_ZERO = this.REG_Y;
break;
}
case 56: {
// *******
// * ALR *
// *******
// Shift right one bit after ANDing:
temp = this.REG_ACC & this.load(addr);
this.F_CARRY = temp & 1;
this.REG_ACC = this.F_ZERO = temp >> 1;
this.F_SIGN = 0;
break;
}
case 57: {
// *******
// * ANC *
// *******
// AND accumulator, setting carry to bit 7 result.
this.REG_ACC = this.F_ZERO = this.REG_ACC & this.load(addr);
this.F_CARRY = this.F_SIGN = (this.REG_ACC >> 7) & 1;
break;
}
case 58: {
// *******
// * ARR *
// *******
// Rotate right one bit after ANDing:
temp = this.REG_ACC & this.load(addr);
this.REG_ACC = this.F_ZERO = (temp >> 1) + (this.F_CARRY << 7);
this.F_SIGN = this.F_CARRY;
this.F_CARRY = (temp >> 7) & 1;
this.F_OVERFLOW = ((temp >> 7) ^ (temp >> 6)) & 1;
break;
}
case 59: {
// *******
// * AXS *
// *******
// Set X to (X AND A) - value.
temp = (this.REG_X & this.REG_ACC) - this.load(addr);
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
if (
((this.REG_X ^ temp) & 0x80) !== 0 &&
((this.REG_X ^ this.load(addr)) & 0x80) !== 0
) {
this.F_OVERFLOW = 1;
} else {
this.F_OVERFLOW = 0;
}
this.F_CARRY = temp < 0 ? 0 : 1;
this.REG_X = temp & 0xff;
break;
}
case 60: {
// *******
// * LAX *
// *******
// Load A and X with memory:
this.REG_ACC = this.REG_X = this.F_ZERO = this.load(addr);
this.F_SIGN = (this.REG_ACC >> 7) & 1;
cycleCount += cycleAdd;
break;
}
case 61: {
// *******
// * SAX *
// *******
// Store A AND X in memory:
this.write(addr, this.REG_ACC & this.REG_X);
break;
}
case 62: {
// *******
// * DCP *
// *******
// Decrement memory by one:
temp = (this.load(addr) - 1) & 0xff;
this.write(addr, temp);
// Then compare with the accumulator:
temp = this.REG_ACC - temp;
this.F_CARRY = temp >= 0 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 63: {
// *******
// * ISC *
// *******
// Increment memory by one:
temp = (this.load(addr) + 1) & 0xff;
this.write(addr, temp);
// Then subtract from the accumulator:
temp = this.REG_ACC - temp - (1 - this.F_CARRY);
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
if (
((this.REG_ACC ^ temp) & 0x80) !== 0 &&
((this.REG_ACC ^ this.load(addr)) & 0x80) !== 0
) {
this.F_OVERFLOW = 1;
} else {
this.F_OVERFLOW = 0;
}
this.F_CARRY = temp < 0 ? 0 : 1;
this.REG_ACC = temp & 0xff;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 64: {
// *******
// * RLA *
// *******
// Rotate one bit left
temp = this.load(addr);
add = this.F_CARRY;
this.F_CARRY = (temp >> 7) & 1;
temp = ((temp << 1) & 0xff) + add;
this.write(addr, temp);
// Then AND with the accumulator.
this.REG_ACC = this.REG_ACC & temp;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 65: {
// *******
// * RRA *
// *******
// Rotate one bit right
temp = this.load(addr);
add = this.F_CARRY << 7;
this.F_CARRY = temp & 1;
temp = (temp >> 1) + add;
this.write(addr, temp);
// Then add to the accumulator
temp = this.REG_ACC + this.load(addr) + this.F_CARRY;
if (
((this.REG_ACC ^ this.load(addr)) & 0x80) === 0 &&
((this.REG_ACC ^ temp) & 0x80) !== 0
) {
this.F_OVERFLOW = 1;
} else {
this.F_OVERFLOW = 0;
}
this.F_CARRY = temp > 255 ? 1 : 0;
this.F_SIGN = (temp >> 7) & 1;
this.F_ZERO = temp & 0xff;
this.REG_ACC = temp & 255;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 66: {
// *******
// * SLO *
// *******
// Shift one bit left
temp = this.load(addr);
this.F_CARRY = (temp >> 7) & 1;
temp = (temp << 1) & 255;
this.write(addr, temp);
// Then OR with the accumulator.
this.REG_ACC = this.REG_ACC | temp;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 67: {
// *******
// * SRE *
// *******
// Shift one bit right
temp = this.load(addr) & 0xff;
this.F_CARRY = temp & 1;
temp >>= 1;
this.write(addr, temp);
// Then XOR with the accumulator.
this.REG_ACC = this.REG_ACC ^ temp;
this.F_SIGN = (this.REG_ACC >> 7) & 1;
this.F_ZERO = this.REG_ACC;
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
case 68: {
// *******
// * SKB *
// *******
// Do nothing
break;
}
case 69: {
// *******
// * IGN *
// *******
// Do nothing but load.
// TODO: Properly implement the double-reads.
this.load(addr);
if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11
break;
}
default: {
// *******
// * ??? *
// *******
this.nes.stop();
this.nes.crashMessage =
"Game crashed, invalid opcode at address $" + opaddr.toString(16);
break;
}
} // end of switch
return cycleCount;
},
load: function(addr) {
if (addr < 0x2000) {
return this.mem[addr & 0x7ff];
} else {
return this.nes.mmap.load(addr);
}
},
load16bit: function(addr) {
if (addr < 0x1fff) {
return this.mem[addr & 0x7ff] | (this.mem[(addr + 1) & 0x7ff] << 8);
} else {
return this.nes.mmap.load(addr) | (this.nes.mmap.load(addr + 1) << 8);
}
},
write: function(addr, val) {
if (addr < 0x2000) {
this.mem[addr & 0x7ff] = val;
} else {
this.nes.mmap.write(addr, val);
}
},
requestIrq: function(type) {
if (this.irqRequested) {
if (type === this.IRQ_NORMAL) {
return;
}
// console.log("too fast irqs. type="+type);
}
this.irqRequested = true;
this.irqType = type;
},
push: function(value) {
this.nes.mmap.write(this.REG_SP, value);
this.REG_SP--;
this.REG_SP = 0x0100 | (this.REG_SP & 0xff);
},
stackWrap: function() {
this.REG_SP = 0x0100 | (this.REG_SP & 0xff);
},
pull: function() {
this.REG_SP++;
this.REG_SP = 0x0100 | (this.REG_SP & 0xff);
return this.nes.mmap.load(this.REG_SP);
},
pageCrossed: function(addr1, addr2) {
return (addr1 & 0xff00) !== (addr2 & 0xff00);
},
haltCycles: function(cycles) {
this.cyclesToHalt += cycles;
},
doNonMaskableInterrupt: function(status) {
if ((this.nes.mmap.load(0x2000) & 128) !== 0) {
// Check whether VBlank Interrupts are enabled
this.REG_PC_NEW++;
this.push((this.REG_PC_NEW >> 8) & 0xff);
this.push(this.REG_PC_NEW & 0xff);
//this.F_INTERRUPT_NEW = 1;
this.push(status);
this.REG_PC_NEW =
this.nes.mmap.load(0xfffa) | (this.nes.mmap.load(0xfffb) << 8);
this.REG_PC_NEW--;
}
},
doResetInterrupt: function() {
this.REG_PC_NEW =
this.nes.mmap.load(0xfffc) | (this.nes.mmap.load(0xfffd) << 8);
this.REG_PC_NEW--;
},
doIrq: function(status) {
this.REG_PC_NEW++;
this.push((this.REG_PC_NEW >> 8) & 0xff);
this.push(this.REG_PC_NEW & 0xff);
this.push(status);
this.F_INTERRUPT_NEW = 1;
this.F_BRK_NEW = 0;
this.REG_PC_NEW =
this.nes.mmap.load(0xfffe) | (this.nes.mmap.load(0xffff) << 8);
this.REG_PC_NEW--;
},
getStatus: function() {
return (
this.F_CARRY |
(this.F_ZERO << 1) |
(this.F_INTERRUPT << 2) |
(this.F_DECIMAL << 3) |
(this.F_BRK << 4) |
(this.F_NOTUSED << 5) |
(this.F_OVERFLOW << 6) |
(this.F_SIGN << 7)
);
},
setStatus: function(st) {
this.F_CARRY = st & 1;
this.F_ZERO = (st >> 1) & 1;
this.F_INTERRUPT = (st >> 2) & 1;
this.F_DECIMAL = (st >> 3) & 1;
this.F_BRK = (st >> 4) & 1;
this.F_NOTUSED = (st >> 5) & 1;
this.F_OVERFLOW = (st >> 6) & 1;
this.F_SIGN = (st >> 7) & 1;
},
JSON_PROPERTIES: [
"mem",
"cyclesToHalt",
"irqRequested",
"irqType",
// Registers
"REG_ACC",
"REG_X",
"REG_Y",
"REG_SP",
"REG_PC",
"REG_PC_NEW",
"REG_STATUS",
// Status
"F_CARRY",
"F_DECIMAL",
"F_INTERRUPT",
"F_INTERRUPT_NEW",
"F_OVERFLOW",
"F_SIGN",
"F_ZERO",
"F_NOTUSED",
"F_NOTUSED_NEW",
"F_BRK",
"F_BRK_NEW"
],
toJSON: function() {
return utils.toJSON(this);
},
fromJSON: function(s) {
utils.fromJSON(this, s);
}
};
// Generates and provides an array of details about instructions
var OpData = function() {
this.opdata = new Array(256);
// Set all to invalid instruction (to detect crashes):
for (var i = 0; i < 256; i++) this.opdata[i] = 0xff;
// Now fill in all valid opcodes:
// ADC:
this.setOp(this.INS_ADC, 0x69, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_ADC, 0x65, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_ADC, 0x75, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_ADC, 0x6d, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_ADC, 0x7d, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_ADC, 0x79, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_ADC, 0x61, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_ADC, 0x71, this.ADDR_POSTIDXIND, 2, 5);
// AND:
this.setOp(this.INS_AND, 0x29, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_AND, 0x25, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_AND, 0x35, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_AND, 0x2d, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_AND, 0x3d, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_AND, 0x39, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_AND, 0x21, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_AND, 0x31, this.ADDR_POSTIDXIND, 2, 5);
// ASL:
this.setOp(this.INS_ASL, 0x0a, this.ADDR_ACC, 1, 2);
this.setOp(this.INS_ASL, 0x06, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_ASL, 0x16, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_ASL, 0x0e, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_ASL, 0x1e, this.ADDR_ABSX, 3, 7);
// BCC:
this.setOp(this.INS_BCC, 0x90, this.ADDR_REL, 2, 2);
// BCS:
this.setOp(this.INS_BCS, 0xb0, this.ADDR_REL, 2, 2);
// BEQ:
this.setOp(this.INS_BEQ, 0xf0, this.ADDR_REL, 2, 2);
// BIT:
this.setOp(this.INS_BIT, 0x24, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_BIT, 0x2c, this.ADDR_ABS, 3, 4);
// BMI:
this.setOp(this.INS_BMI, 0x30, this.ADDR_REL, 2, 2);
// BNE:
this.setOp(this.INS_BNE, 0xd0, this.ADDR_REL, 2, 2);
// BPL:
this.setOp(this.INS_BPL, 0x10, this.ADDR_REL, 2, 2);
// BRK:
this.setOp(this.INS_BRK, 0x00, this.ADDR_IMP, 1, 7);
// BVC:
this.setOp(this.INS_BVC, 0x50, this.ADDR_REL, 2, 2);
// BVS:
this.setOp(this.INS_BVS, 0x70, this.ADDR_REL, 2, 2);
// CLC:
this.setOp(this.INS_CLC, 0x18, this.ADDR_IMP, 1, 2);
// CLD:
this.setOp(this.INS_CLD, 0xd8, this.ADDR_IMP, 1, 2);
// CLI:
this.setOp(this.INS_CLI, 0x58, this.ADDR_IMP, 1, 2);
// CLV:
this.setOp(this.INS_CLV, 0xb8, this.ADDR_IMP, 1, 2);
// CMP:
this.setOp(this.INS_CMP, 0xc9, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_CMP, 0xc5, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_CMP, 0xd5, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_CMP, 0xcd, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_CMP, 0xdd, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_CMP, 0xd9, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_CMP, 0xc1, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_CMP, 0xd1, this.ADDR_POSTIDXIND, 2, 5);
// CPX:
this.setOp(this.INS_CPX, 0xe0, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_CPX, 0xe4, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_CPX, 0xec, this.ADDR_ABS, 3, 4);
// CPY:
this.setOp(this.INS_CPY, 0xc0, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_CPY, 0xc4, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_CPY, 0xcc, this.ADDR_ABS, 3, 4);
// DEC:
this.setOp(this.INS_DEC, 0xc6, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_DEC, 0xd6, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_DEC, 0xce, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_DEC, 0xde, this.ADDR_ABSX, 3, 7);
// DEX:
this.setOp(this.INS_DEX, 0xca, this.ADDR_IMP, 1, 2);
// DEY:
this.setOp(this.INS_DEY, 0x88, this.ADDR_IMP, 1, 2);
// EOR:
this.setOp(this.INS_EOR, 0x49, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_EOR, 0x45, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_EOR, 0x55, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_EOR, 0x4d, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_EOR, 0x5d, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_EOR, 0x59, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_EOR, 0x41, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_EOR, 0x51, this.ADDR_POSTIDXIND, 2, 5);
// INC:
this.setOp(this.INS_INC, 0xe6, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_INC, 0xf6, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_INC, 0xee, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_INC, 0xfe, this.ADDR_ABSX, 3, 7);
// INX:
this.setOp(this.INS_INX, 0xe8, this.ADDR_IMP, 1, 2);
// INY:
this.setOp(this.INS_INY, 0xc8, this.ADDR_IMP, 1, 2);
// JMP:
this.setOp(this.INS_JMP, 0x4c, this.ADDR_ABS, 3, 3);
this.setOp(this.INS_JMP, 0x6c, this.ADDR_INDABS, 3, 5);
// JSR:
this.setOp(this.INS_JSR, 0x20, this.ADDR_ABS, 3, 6);
// LDA:
this.setOp(this.INS_LDA, 0xa9, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_LDA, 0xa5, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_LDA, 0xb5, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_LDA, 0xad, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_LDA, 0xbd, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_LDA, 0xb9, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_LDA, 0xa1, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_LDA, 0xb1, this.ADDR_POSTIDXIND, 2, 5);
// LDX:
this.setOp(this.INS_LDX, 0xa2, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_LDX, 0xa6, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_LDX, 0xb6, this.ADDR_ZPY, 2, 4);
this.setOp(this.INS_LDX, 0xae, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_LDX, 0xbe, this.ADDR_ABSY, 3, 4);
// LDY:
this.setOp(this.INS_LDY, 0xa0, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_LDY, 0xa4, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_LDY, 0xb4, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_LDY, 0xac, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_LDY, 0xbc, this.ADDR_ABSX, 3, 4);
// LSR:
this.setOp(this.INS_LSR, 0x4a, this.ADDR_ACC, 1, 2);
this.setOp(this.INS_LSR, 0x46, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_LSR, 0x56, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_LSR, 0x4e, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_LSR, 0x5e, this.ADDR_ABSX, 3, 7);
// NOP:
this.setOp(this.INS_NOP, 0x1a, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0x3a, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0x5a, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0x7a, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0xda, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0xea, this.ADDR_IMP, 1, 2);
this.setOp(this.INS_NOP, 0xfa, this.ADDR_IMP, 1, 2);
// ORA:
this.setOp(this.INS_ORA, 0x09, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_ORA, 0x05, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_ORA, 0x15, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_ORA, 0x0d, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_ORA, 0x1d, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_ORA, 0x19, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_ORA, 0x01, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_ORA, 0x11, this.ADDR_POSTIDXIND, 2, 5);
// PHA:
this.setOp(this.INS_PHA, 0x48, this.ADDR_IMP, 1, 3);
// PHP:
this.setOp(this.INS_PHP, 0x08, this.ADDR_IMP, 1, 3);
// PLA:
this.setOp(this.INS_PLA, 0x68, this.ADDR_IMP, 1, 4);
// PLP:
this.setOp(this.INS_PLP, 0x28, this.ADDR_IMP, 1, 4);
// ROL:
this.setOp(this.INS_ROL, 0x2a, this.ADDR_ACC, 1, 2);
this.setOp(this.INS_ROL, 0x26, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_ROL, 0x36, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_ROL, 0x2e, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_ROL, 0x3e, this.ADDR_ABSX, 3, 7);
// ROR:
this.setOp(this.INS_ROR, 0x6a, this.ADDR_ACC, 1, 2);
this.setOp(this.INS_ROR, 0x66, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_ROR, 0x76, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_ROR, 0x6e, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_ROR, 0x7e, this.ADDR_ABSX, 3, 7);
// RTI:
this.setOp(this.INS_RTI, 0x40, this.ADDR_IMP, 1, 6);
// RTS:
this.setOp(this.INS_RTS, 0x60, this.ADDR_IMP, 1, 6);
// SBC:
this.setOp(this.INS_SBC, 0xe9, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_SBC, 0xe5, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_SBC, 0xf5, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_SBC, 0xed, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_SBC, 0xfd, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_SBC, 0xf9, this.ADDR_ABSY, 3, 4);
this.setOp(this.INS_SBC, 0xe1, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_SBC, 0xf1, this.ADDR_POSTIDXIND, 2, 5);
// SEC:
this.setOp(this.INS_SEC, 0x38, this.ADDR_IMP, 1, 2);
// SED:
this.setOp(this.INS_SED, 0xf8, this.ADDR_IMP, 1, 2);
// SEI:
this.setOp(this.INS_SEI, 0x78, this.ADDR_IMP, 1, 2);
// STA:
this.setOp(this.INS_STA, 0x85, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_STA, 0x95, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_STA, 0x8d, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_STA, 0x9d, this.ADDR_ABSX, 3, 5);
this.setOp(this.INS_STA, 0x99, this.ADDR_ABSY, 3, 5);
this.setOp(this.INS_STA, 0x81, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_STA, 0x91, this.ADDR_POSTIDXIND, 2, 6);
// STX:
this.setOp(this.INS_STX, 0x86, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_STX, 0x96, this.ADDR_ZPY, 2, 4);
this.setOp(this.INS_STX, 0x8e, this.ADDR_ABS, 3, 4);
// STY:
this.setOp(this.INS_STY, 0x84, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_STY, 0x94, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_STY, 0x8c, this.ADDR_ABS, 3, 4);
// TAX:
this.setOp(this.INS_TAX, 0xaa, this.ADDR_IMP, 1, 2);
// TAY:
this.setOp(this.INS_TAY, 0xa8, this.ADDR_IMP, 1, 2);
// TSX:
this.setOp(this.INS_TSX, 0xba, this.ADDR_IMP, 1, 2);
// TXA:
this.setOp(this.INS_TXA, 0x8a, this.ADDR_IMP, 1, 2);
// TXS:
this.setOp(this.INS_TXS, 0x9a, this.ADDR_IMP, 1, 2);
// TYA:
this.setOp(this.INS_TYA, 0x98, this.ADDR_IMP, 1, 2);
// ALR:
this.setOp(this.INS_ALR, 0x4b, this.ADDR_IMM, 2, 2);
// ANC:
this.setOp(this.INS_ANC, 0x0b, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_ANC, 0x2b, this.ADDR_IMM, 2, 2);
// ARR:
this.setOp(this.INS_ARR, 0x6b, this.ADDR_IMM, 2, 2);
// AXS:
this.setOp(this.INS_AXS, 0xcb, this.ADDR_IMM, 2, 2);
// LAX:
this.setOp(this.INS_LAX, 0xa3, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_LAX, 0xa7, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_LAX, 0xaf, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_LAX, 0xb3, this.ADDR_POSTIDXIND, 2, 5);
this.setOp(this.INS_LAX, 0xb7, this.ADDR_ZPY, 2, 4);
this.setOp(this.INS_LAX, 0xbf, this.ADDR_ABSY, 3, 4);
// SAX:
this.setOp(this.INS_SAX, 0x83, this.ADDR_PREIDXIND, 2, 6);
this.setOp(this.INS_SAX, 0x87, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_SAX, 0x8f, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_SAX, 0x97, this.ADDR_ZPY, 2, 4);
// DCP:
this.setOp(this.INS_DCP, 0xc3, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_DCP, 0xc7, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_DCP, 0xcf, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_DCP, 0xd3, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_DCP, 0xd7, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_DCP, 0xdb, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_DCP, 0xdf, this.ADDR_ABSX, 3, 7);
// ISC:
this.setOp(this.INS_ISC, 0xe3, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_ISC, 0xe7, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_ISC, 0xef, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_ISC, 0xf3, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_ISC, 0xf7, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_ISC, 0xfb, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_ISC, 0xff, this.ADDR_ABSX, 3, 7);
// RLA:
this.setOp(this.INS_RLA, 0x23, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_RLA, 0x27, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_RLA, 0x2f, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_RLA, 0x33, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_RLA, 0x37, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_RLA, 0x3b, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_RLA, 0x3f, this.ADDR_ABSX, 3, 7);
// RRA:
this.setOp(this.INS_RRA, 0x63, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_RRA, 0x67, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_RRA, 0x6f, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_RRA, 0x73, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_RRA, 0x77, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_RRA, 0x7b, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_RRA, 0x7f, this.ADDR_ABSX, 3, 7);
// SLO:
this.setOp(this.INS_SLO, 0x03, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_SLO, 0x07, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_SLO, 0x0f, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_SLO, 0x13, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_SLO, 0x17, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_SLO, 0x1b, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_SLO, 0x1f, this.ADDR_ABSX, 3, 7);
// SRE:
this.setOp(this.INS_SRE, 0x43, this.ADDR_PREIDXIND, 2, 8);
this.setOp(this.INS_SRE, 0x47, this.ADDR_ZP, 2, 5);
this.setOp(this.INS_SRE, 0x4f, this.ADDR_ABS, 3, 6);
this.setOp(this.INS_SRE, 0x53, this.ADDR_POSTIDXIND, 2, 8);
this.setOp(this.INS_SRE, 0x57, this.ADDR_ZPX, 2, 6);
this.setOp(this.INS_SRE, 0x5b, this.ADDR_ABSY, 3, 7);
this.setOp(this.INS_SRE, 0x5f, this.ADDR_ABSX, 3, 7);
// SKB:
this.setOp(this.INS_SKB, 0x80, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_SKB, 0x82, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_SKB, 0x89, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_SKB, 0xc2, this.ADDR_IMM, 2, 2);
this.setOp(this.INS_SKB, 0xe2, this.ADDR_IMM, 2, 2);
// SKB:
this.setOp(this.INS_IGN, 0x0c, this.ADDR_ABS, 3, 4);
this.setOp(this.INS_IGN, 0x1c, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0x3c, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0x5c, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0x7c, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0xdc, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0xfc, this.ADDR_ABSX, 3, 4);
this.setOp(this.INS_IGN, 0x04, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_IGN, 0x44, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_IGN, 0x64, this.ADDR_ZP, 2, 3);
this.setOp(this.INS_IGN, 0x14, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_IGN, 0x34, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_IGN, 0x54, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_IGN, 0x74, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_IGN, 0xd4, this.ADDR_ZPX, 2, 4);
this.setOp(this.INS_IGN, 0xf4, this.ADDR_ZPX, 2, 4);
// prettier-ignore
this.cycTable = new Array(
/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,
/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,
/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,
/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7
);
this.instname = new Array(70);
// Instruction Names:
this.instname[0] = "ADC";
this.instname[1] = "AND";
this.instname[2] = "ASL";
this.instname[3] = "BCC";
this.instname[4] = "BCS";
this.instname[5] = "BEQ";
this.instname[6] = "BIT";
this.instname[7] = "BMI";
this.instname[8] = "BNE";
this.instname[9] = "BPL";
this.instname[10] = "BRK";
this.instname[11] = "BVC";
this.instname[12] = "BVS";
this.instname[13] = "CLC";
this.instname[14] = "CLD";
this.instname[15] = "CLI";
this.instname[16] = "CLV";
this.instname[17] = "CMP";
this.instname[18] = "CPX";
this.instname[19] = "CPY";
this.instname[20] = "DEC";
this.instname[21] = "DEX";
this.instname[22] = "DEY";
this.instname[23] = "EOR";
this.instname[24] = "INC";
this.instname[25] = "INX";
this.instname[26] = "INY";
this.instname[27] = "JMP";
this.instname[28] = "JSR";
this.instname[29] = "LDA";
this.instname[30] = "LDX";
this.instname[31] = "LDY";
this.instname[32] = "LSR";
this.instname[33] = "NOP";
this.instname[34] = "ORA";
this.instname[35] = "PHA";
this.instname[36] = "PHP";
this.instname[37] = "PLA";
this.instname[38] = "PLP";
this.instname[39] = "ROL";
this.instname[40] = "ROR";
this.instname[41] = "RTI";
this.instname[42] = "RTS";
this.instname[43] = "SBC";
this.instname[44] = "SEC";
this.instname[45] = "SED";
this.instname[46] = "SEI";
this.instname[47] = "STA";
this.instname[48] = "STX";
this.instname[49] = "STY";
this.instname[50] = "TAX";
this.instname[51] = "TAY";
this.instname[52] = "TSX";
this.instname[53] = "TXA";
this.instname[54] = "TXS";
this.instname[55] = "TYA";
this.instname[56] = "ALR";
this.instname[57] = "ANC";
this.instname[58] = "ARR";
this.instname[59] = "AXS";
this.instname[60] = "LAX";
this.instname[61] = "SAX";
this.instname[62] = "DCP";
this.instname[63] = "ISC";
this.instname[64] = "RLA";
this.instname[65] = "RRA";
this.instname[66] = "SLO";
this.instname[67] = "SRE";
this.instname[68] = "SKB";
this.instname[69] = "IGN";
this.addrDesc = new Array(
"Zero Page ",
"Relative ",
"Implied ",
"Absolute ",
"Accumulator ",
"Immediate ",
"Zero Page,X ",
"Zero Page,Y ",
"Absolute,X ",
"Absolute,Y ",
"Preindexed Indirect ",
"Postindexed Indirect",
"Indirect Absolute "
);
};
OpData.prototype = {
INS_ADC: 0,
INS_AND: 1,
INS_ASL: 2,
INS_BCC: 3,
INS_BCS: 4,
INS_BEQ: 5,
INS_BIT: 6,
INS_BMI: 7,
INS_BNE: 8,
INS_BPL: 9,
INS_BRK: 10,
INS_BVC: 11,
INS_BVS: 12,
INS_CLC: 13,
INS_CLD: 14,
INS_CLI: 15,
INS_CLV: 16,
INS_CMP: 17,
INS_CPX: 18,
INS_CPY: 19,
INS_DEC: 20,
INS_DEX: 21,
INS_DEY: 22,
INS_EOR: 23,
INS_INC: 24,
INS_INX: 25,
INS_INY: 26,
INS_JMP: 27,
INS_JSR: 28,
INS_LDA: 29,
INS_LDX: 30,
INS_LDY: 31,
INS_LSR: 32,
INS_NOP: 33,
INS_ORA: 34,
INS_PHA: 35,
INS_PHP: 36,
INS_PLA: 37,
INS_PLP: 38,
INS_ROL: 39,
INS_ROR: 40,
INS_RTI: 41,
INS_RTS: 42,
INS_SBC: 43,
INS_SEC: 44,
INS_SED: 45,
INS_SEI: 46,
INS_STA: 47,
INS_STX: 48,
INS_STY: 49,
INS_TAX: 50,
INS_TAY: 51,
INS_TSX: 52,
INS_TXA: 53,
INS_TXS: 54,
INS_TYA: 55,
INS_ALR: 56,
INS_ANC: 57,
INS_ARR: 58,
INS_AXS: 59,
INS_LAX: 60,
INS_SAX: 61,
INS_DCP: 62,
INS_ISC: 63,
INS_RLA: 64,
INS_RRA: 65,
INS_SLO: 66,
INS_SRE: 67,
INS_SKB: 68,
INS_IGN: 69,
INS_DUMMY: 70, // dummy instruction used for 'halting' the processor some cycles
// -------------------------------- //
// Addressing modes:
ADDR_ZP: 0,
ADDR_REL: 1,
ADDR_IMP: 2,
ADDR_ABS: 3,
ADDR_ACC: 4,
ADDR_IMM: 5,
ADDR_ZPX: 6,
ADDR_ZPY: 7,
ADDR_ABSX: 8,
ADDR_ABSY: 9,
ADDR_PREIDXIND: 10,
ADDR_POSTIDXIND: 11,
ADDR_INDABS: 12,
setOp: function(inst, op, addr, size, cycles) {
this.opdata[op] =
(inst & 0xff) |
((addr & 0xff) << 8) |
((size & 0xff) << 16) |
((cycles & 0xff) << 24);
}
};
module.exports = CPU;