8bitworkshop/src/common/cpu/MOS6502.ts

1996 lines
64 KiB
TypeScript

import { CPU, Bus, ClockBased, SavesState, Interruptable } from "../devices";
// Copyright 2015 by Paulo Augusto Peccin. See license.txt distributed with this file.
export var _MOS6502 = function() {
var self = this;
this.powerOn = function() {
this.reset();
};
this.powerOff = function() {
};
this.clockPulse = function() {
if (!RDY) return; // TODO Should be ignored in the last cycle of the instruction
T++;
instruction[T]();
};
this.connectBus = function(aBus) {
bus = aBus;
};
this.setRDY = function(boo) {
RDY = boo;
};
this.isRDY = function() {
return RDY;
}
this.reset = function() {
I = 1;
T = -1;
instruction = [ fetchOpcodeAndDecodeInstruction ]; // Bootstrap instruction
PC = bus.read(RESET_VECTOR) | (bus.read(RESET_VECTOR + 1) << 8);
this.setRDY(true);
};
// Interfaces
var bus : Bus;
var RDY : boolean = false;
// Registers
var PC : number = 0;
var SP : number = 0;
var A : number = 0;
var X : number = 0;
var Y : number = 0;
// Status Bits
var N : number = 0;
var V : number = 0;
var D : number = 0;
var I : number = 0;
var Z : number = 0;
var C : number = 0;
// Internal decoding registers
var T : number = -1;
var opcode : number = -1;
var instruction : (() => void)[];
var data : number = 0;
var AD : number = 0;
var BA : number = 0;
var BALCrossed : boolean = false;
var IA : number = 0;
var branchOffset : number = 0;
var branchOffsetCrossAdjust : number = 0;
// Vectors
const NMI_VECTOR = 0xfffa;
const RESET_VECTOR = 0xfffc;
const IRQ_VECTOR = 0xfffe;
// Index registers names
const rX = 0;
const rY = 1;
// Status bits names
const bN = 7;
const bV = 6;
// const bE = 5; // Not used
// const bB = 4; // Not used
// const bD = 3; // Not used
// const bI = 2; // Not used
const bZ = 1;
const bC = 0;
// Auxiliary variables
// TODO
//noinspection JSUnusedGlobalSymbols
this.debug = false;
//noinspection JSUnusedGlobalSymbols
this.trace = false;
// Internal operations
var fetchOpcodeAndDecodeInstruction = function() {
opcode = bus.read(PC);
instruction = instructions[opcode];
T = 0;
// if (self.trace) self.breakpoint("TRACE");
// console.log("PC: " + PC + ", op: " + opcode + ": " + opcodes[opcode]);
PC++;
};
var fetchNextOpcode = fetchOpcodeAndDecodeInstruction;
var fetchOpcodeAndDiscard = function() {
bus.read(PC);
};
var fetchBranchOffset = function() {
branchOffset = bus.read(PC);
PC++;
};
var fetchADL = function() {
AD = bus.read(PC);
PC++;
};
var fetchADH = function() {
AD |= bus.read(PC) << 8;
PC++;
};
var fetchADLFromBA = function() {
AD = bus.read(BA);
};
var fetchADHFromBA = function() {
AD |= bus.read(BA) << 8;
};
var fetchBAL = function() {
BA = bus.read(PC);
PC++;
};
var fetchBAH = function() {
BA |= bus.read(PC) << 8;
PC++;
};
var fetchBALFromIA = function() {
BA = bus.read(IA);
};
var fetchBAHFromIA = function() {
BA |= bus.read(IA) << 8;
};
var addXtoBAL = function() {
var low = (BA & 255) + X;
BALCrossed = low > 255;
BA = (BA & 0xff00) | (low & 255);
};
var addYtoBAL = function() {
var low = (BA & 255) + Y;
BALCrossed = low > 255;
BA = (BA & 0xff00) | (low & 255);
};
var add1toBAL = function() {
var low = (BA & 255) + 1;
BALCrossed = low > 255;
BA = (BA & 0xff00) | (low & 255);
};
var add1toBAHifBALCrossed = function() {
if (BALCrossed)
BA = (BA + 0x0100) & 0xffff;
};
var fetchIAL = function() {
IA = bus.read(PC);
PC++;
};
var fetchIAH = function() {
IA |= bus.read(PC) << 8;
PC++;
};
var add1toIAL = function() {
var low = (IA & 255) + 1;
IA = (IA & 0xff00) | (low & 255);
};
var fetchDataFromImmediate = function() {
data = bus.read(PC);
PC++;
};
var fetchDataFromAD = function() {
data = bus.read(AD);
};
var fetchDataFromBA = function() {
data = bus.read(BA);
};
var writeDataToAD = function() {
bus.write(AD, data);
};
var writeDataToBA = function() {
bus.write(BA, data);
};
var addBranchOffsetToPCL = function() {
var oldLow = (PC & 0x00ff);
var newLow = (oldLow + branchOffset) & 255;
// Negative offset?
if (branchOffset > 127)
branchOffsetCrossAdjust = (newLow > oldLow) ? -0x0100 : 0;
else
branchOffsetCrossAdjust = (newLow < oldLow) ? 0x0100 : 0;
PC = (PC & 0xff00) | newLow;
};
var adjustPCHForBranchOffsetCross = function() {
PC = (PC + branchOffsetCrossAdjust) & 0xffff;
};
var setZ = function(val) {
Z = (val === 0) ? 1 : 0;
};
var setN = function(val) {
N = (val & 0x080) ? 1 : 0;
};
var setV = function(boo) {
V = boo ? 1 : 0;
};
var setC = function(boo) {
C = boo ? 1 : 0;
};
var popFromStack = function() {
SP = (SP + 1) & 255;
return bus.read(0x0100 + SP);
};
var peekFromStack = function() {
return bus.read(0x0100 + SP);
};
var pushToStack = function(val) {
bus.write(0x0100 + SP, val);
SP = (SP - 1) & 255;
};
var getStatusBits = function() {
return N << 7 | V << 6 | 0x30 // Always push with E (bit 5) and B (bit 4) ON
| D << 3 | I << 2 | Z << 1 | C;
};
var setStatusBits = function(val) {
N = val >>> 7; V = val >>> 6 & 1; // E and B flags actually do not exist as real flags, so ignore
D = val >>> 3 & 1; I = val >>> 2 & 1; Z = val >>> 1 & 1; C = val & 1;
};
var illegalOpcode = function(op) {
if (self.debug) self.breakpoint("Illegal Opcode: " + op);
};
// Addressing routines
var implied = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
function() {
operation();
fetchNextOpcode();
}
];
};
var immediateRead = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchDataFromImmediate,
function() {
operation();
fetchNextOpcode();
}
];
};
var zeroPageRead = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL, // ADH will be zero
fetchDataFromAD,
function() {
operation();
fetchNextOpcode();
}
];
};
var absoluteRead = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL,
fetchADH,
fetchDataFromAD,
function() {
operation();
fetchNextOpcode();
}
];
};
var indirectXRead = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function() {
addXtoBAL();
fetchADLFromBA();
},
function() {
add1toBAL();
fetchADHFromBA();
},
fetchDataFromAD,
function() {
operation();
fetchNextOpcode();
}
];
};
var absoluteIndexedRead = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL,
fetchBAH,
function() {
addIndex();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
function() {
if (BALCrossed) {
fetchDataFromBA();
} else {
operation();
fetchNextOpcode();
}
},
function() {
operation();
fetchNextOpcode();
}
];
};
};
var zeroPageIndexedRead = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function() {
addIndex();
fetchDataFromBA();
},
function() {
operation();
fetchNextOpcode();
}
];
};
};
var indirectYRead = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchIAL, // IAH will be zero
fetchBALFromIA,
function() {
add1toIAL();
fetchBAHFromIA();
},
function() {
addYtoBAL();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
function() {
if(BALCrossed) {
fetchDataFromBA();
} else {
operation();
fetchNextOpcode();
}
},
function() {
operation();
fetchNextOpcode();
}
];
};
var zeroPageWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL, // ADH will be zero
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var absoluteWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL,
fetchADH,
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var indirectXWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function() {
addXtoBAL();
fetchADLFromBA();
},
function() {
add1toBAL();
fetchADHFromBA();
},
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var absoluteIndexedWrite = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL,
fetchBAH,
function() {
addIndex();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
function() {
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
};
var zeroPageIndexedWrite = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function() {
addIndex();
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
};
var indirectYWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchIAL, // IAH will be zero
fetchBALFromIA,
function() {
add1toIAL();
fetchBAHFromIA();
},
function() {
addYtoBAL();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
function() {
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
var zeroPageReadModifyWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL, // ADH will be zero
fetchDataFromAD,
writeDataToAD,
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var absoluteReadModifyWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL,
fetchADH,
fetchDataFromAD,
writeDataToAD,
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var zeroPageIndexedReadModifyWrite = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function () {
addIndex();
fetchDataFromBA();
},
writeDataToBA,
function () {
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
};
var absoluteIndexedReadModifyWrite = function(index) {
var addIndex = index === rX ? addXtoBAL : addYtoBAL;
return function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL,
fetchBAH,
function () {
addIndex();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
fetchDataFromBA,
writeDataToBA,
function () {
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
};
var indirectXReadModifyWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchBAL, // BAH will be zero
fetchDataFromBA,
function() {
addXtoBAL();
fetchADLFromBA();
},
function() {
add1toBAL();
fetchADHFromBA();
},
fetchDataFromAD,
writeDataToAD,
function() {
operation();
writeDataToAD();
},
fetchNextOpcode
];
};
var indirectYReadModifyWrite = function(operation) {
return [
fetchOpcodeAndDecodeInstruction,
fetchIAL, // IAH will be zero
fetchBALFromIA,
function() {
add1toIAL();
fetchBAHFromIA();
},
function() {
addYtoBAL();
fetchDataFromBA();
add1toBAHifBALCrossed();
},
fetchDataFromBA,
writeDataToBA,
function() {
operation();
writeDataToBA();
},
fetchNextOpcode
];
};
// Instructions ========================================================================================
// Complete instruction set
var opcodes = new Array(256);
var instructions = new Array(256);
opcodes[0x00] = "BRK"; instructions[0x00] = BRK();
opcodes[0x01] = "ORA"; instructions[0x01] = ORA(indirectXRead);
opcodes[0x02] = "uKIL"; instructions[0x02] = uKIL();
opcodes[0x03] = "uSLO"; instructions[0x03] = uSLO(indirectXReadModifyWrite);
opcodes[0x04] = "uNOP"; instructions[0x04] = uNOP(zeroPageRead);
opcodes[0x05] = "ORA"; instructions[0x05] = ORA(zeroPageRead);
opcodes[0x06] = "ASL"; instructions[0x06] = ASL(zeroPageReadModifyWrite);
opcodes[0x07] = "uSLO"; instructions[0x07] = uSLO(zeroPageReadModifyWrite);
opcodes[0x08] = "PHP"; instructions[0x08] = PHP();
opcodes[0x09] = "ORA"; instructions[0x09] = ORA(immediateRead);
opcodes[0x0a] = "ASL"; instructions[0x0a] = ASL_ACC();
opcodes[0x0b] = "uANC"; instructions[0x0b] = uANC(immediateRead);
opcodes[0x0c] = "uNOP"; instructions[0x0c] = uNOP(absoluteRead);
opcodes[0x0d] = "ORA"; instructions[0x0d] = ORA(absoluteRead);
opcodes[0x0e] = "ASL"; instructions[0x0e] = ASL(absoluteReadModifyWrite);
opcodes[0x0f] = "uSLO"; instructions[0x0f] = uSLO(absoluteReadModifyWrite);
opcodes[0x10] = "BPL"; instructions[0x10] = Bxx(bN, 0); // BPL
opcodes[0x11] = "ORA"; instructions[0x11] = ORA(indirectYRead);
opcodes[0x12] = "uKIL"; instructions[0x12] = uKIL();
opcodes[0x13] = "uSLO"; instructions[0x13] = uSLO(indirectYReadModifyWrite);
opcodes[0x14] = "uNOP"; instructions[0x14] = uNOP(zeroPageIndexedRead(rX));
opcodes[0x15] = "ORA"; instructions[0x15] = ORA(zeroPageIndexedRead(rX));
opcodes[0x16] = "ASL"; instructions[0x16] = ASL(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x17] = "uSLO"; instructions[0x17] = uSLO(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x18] = "CLC"; instructions[0x18] = CLC();
opcodes[0x19] = "ORA"; instructions[0x19] = ORA(absoluteIndexedRead(rY));
opcodes[0x1a] = "uNOP"; instructions[0x1a] = uNOP(implied);
opcodes[0x1b] = "uSLO"; instructions[0x1b] = uSLO(absoluteIndexedReadModifyWrite(rY));
opcodes[0x1c] = "uNOP"; instructions[0x1c] = uNOP(absoluteIndexedRead(rX));
opcodes[0x1d] = "ORA"; instructions[0x1d] = ORA(absoluteIndexedRead(rX));
opcodes[0x1e] = "ASL"; instructions[0x1e] = ASL(absoluteIndexedReadModifyWrite(rX));
opcodes[0x1f] = "uSLO"; instructions[0x1f] = uSLO(absoluteIndexedReadModifyWrite(rX));
opcodes[0x20] = "JSR"; instructions[0x20] = JSR();
opcodes[0x21] = "AND"; instructions[0x21] = AND(indirectXRead);
opcodes[0x22] = "uKIL"; instructions[0x22] = uKIL();
opcodes[0x23] = "uRLA"; instructions[0x23] = uRLA(indirectXReadModifyWrite);
opcodes[0x24] = "BIT"; instructions[0x24] = BIT(zeroPageRead);
opcodes[0x25] = "AND"; instructions[0x25] = AND(zeroPageRead);
opcodes[0x26] = "ROL"; instructions[0x26] = ROL(zeroPageReadModifyWrite);
opcodes[0x27] = "uRLA"; instructions[0x27] = uRLA(zeroPageReadModifyWrite);
opcodes[0x28] = "PLP"; instructions[0x28] = PLP();
opcodes[0x29] = "AND"; instructions[0x29] = AND(immediateRead);
opcodes[0x2a] = "ROL"; instructions[0x2a] = ROL_ACC();
opcodes[0x2b] = "uANC"; instructions[0x2b] = uANC(immediateRead);
opcodes[0x2c] = "BIT"; instructions[0x2c] = BIT(absoluteRead);
opcodes[0x2d] = "AND"; instructions[0x2d] = AND(absoluteRead);
opcodes[0x2e] = "ROL"; instructions[0x2e] = ROL(absoluteReadModifyWrite);
opcodes[0x2f] = "uRLA"; instructions[0x2f] = uRLA(absoluteReadModifyWrite);
opcodes[0x30] = "BMI"; instructions[0x30] = Bxx(bN, 1); // BMI
opcodes[0x31] = "AND"; instructions[0x31] = AND(indirectYRead);
opcodes[0x32] = "uKIL"; instructions[0x32] = uKIL();
opcodes[0x33] = "uRLA"; instructions[0x33] = uRLA(indirectYReadModifyWrite);
opcodes[0x34] = "uNOP"; instructions[0x34] = uNOP(zeroPageIndexedRead(rX));
opcodes[0x35] = "AND"; instructions[0x35] = AND(zeroPageIndexedRead(rX));
opcodes[0x36] = "ROL"; instructions[0x36] = ROL(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x37] = "uRLA"; instructions[0x37] = uRLA(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x38] = "SEC"; instructions[0x38] = SEC();
opcodes[0x39] = "AND"; instructions[0x39] = AND(absoluteIndexedRead(rY));
opcodes[0x3a] = "uNOP"; instructions[0x3a] = uNOP(implied);
opcodes[0x3b] = "uRLA"; instructions[0x3b] = uRLA(absoluteIndexedReadModifyWrite(rY));
opcodes[0x3c] = "uNOP"; instructions[0x3c] = uNOP(absoluteIndexedRead(rX));
opcodes[0x3d] = "AND"; instructions[0x3d] = AND(absoluteIndexedRead(rX));
opcodes[0x3e] = "ROL"; instructions[0x3e] = ROL(absoluteIndexedReadModifyWrite(rX));
opcodes[0x3f] = "uRLA"; instructions[0x3f] = uRLA(absoluteIndexedReadModifyWrite(rX));
opcodes[0x40] = "RTI"; instructions[0x40] = RTI();
opcodes[0x41] = "EOR"; instructions[0x41] = EOR(indirectXRead);
opcodes[0x42] = "uKIL"; instructions[0x42] = uKIL();
opcodes[0x43] = "uSRE"; instructions[0x43] = uSRE(indirectXReadModifyWrite);
opcodes[0x44] = "uNOP"; instructions[0x44] = uNOP(zeroPageRead);
opcodes[0x45] = "EOR"; instructions[0x45] = EOR(zeroPageRead);
opcodes[0x46] = "LSR"; instructions[0x46] = LSR(zeroPageReadModifyWrite);
opcodes[0x47] = "uSRE"; instructions[0x47] = uSRE(zeroPageReadModifyWrite);
opcodes[0x48] = "PHA"; instructions[0x48] = PHA();
opcodes[0x49] = "EOR"; instructions[0x49] = EOR(immediateRead);
opcodes[0x4a] = "LSR"; instructions[0x4a] = LSR_ACC();
opcodes[0x4b] = "uASR"; instructions[0x4b] = uASR(immediateRead);
opcodes[0x4c] = "JMP"; instructions[0x4c] = JMP_ABS();
opcodes[0x4d] = "EOR"; instructions[0x4d] = EOR(absoluteRead);
opcodes[0x4e] = "LSR"; instructions[0x4e] = LSR(absoluteReadModifyWrite);
opcodes[0x4f] = "uSRE"; instructions[0x4f] = uSRE(absoluteReadModifyWrite);
opcodes[0x50] = "BVC"; instructions[0x50] = Bxx(bV, 0); // BVC
opcodes[0x51] = "EOR"; instructions[0x51] = EOR(indirectYRead);
opcodes[0x52] = "uKIL"; instructions[0x52] = uKIL();
opcodes[0x53] = "uSRE"; instructions[0x53] = uSRE(indirectYReadModifyWrite);
opcodes[0x54] = "uNOP"; instructions[0x54] = uNOP(zeroPageIndexedRead(rX));
opcodes[0x55] = "EOR"; instructions[0x55] = EOR(zeroPageIndexedRead(rX));
opcodes[0x56] = "LSR"; instructions[0x56] = LSR(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x57] = "uSRE"; instructions[0x57] = uSRE(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x58] = "CLI"; instructions[0x58] = CLI();
opcodes[0x59] = "EOR"; instructions[0x59] = EOR(absoluteIndexedRead(rY));
opcodes[0x5a] = "uNOP"; instructions[0x5a] = uNOP(implied);
opcodes[0x5b] = "uSRE"; instructions[0x5b] = uSRE(absoluteIndexedReadModifyWrite(rY));
opcodes[0x5c] = "uNOP"; instructions[0x5c] = uNOP(absoluteIndexedRead(rX));
opcodes[0x5d] = "EOR"; instructions[0x5d] = EOR(absoluteIndexedRead(rX));
opcodes[0x5e] = "LSR"; instructions[0x5e] = LSR(absoluteIndexedReadModifyWrite(rX));
opcodes[0x5f] = "uSRE"; instructions[0x5f] = uSRE(absoluteIndexedReadModifyWrite(rX));
opcodes[0x60] = "RTS"; instructions[0x60] = RTS();
opcodes[0x61] = "ADC"; instructions[0x61] = ADC(indirectXRead);
opcodes[0x62] = "uKIL"; instructions[0x62] = uKIL();
opcodes[0x63] = "uRRA"; instructions[0x63] = uRRA(indirectXReadModifyWrite);
opcodes[0x64] = "uNOP"; instructions[0x64] = uNOP(zeroPageRead);
opcodes[0x65] = "ADC"; instructions[0x65] = ADC(zeroPageRead);
opcodes[0x66] = "ROR"; instructions[0x66] = ROR(zeroPageReadModifyWrite);
opcodes[0x67] = "uRRA"; instructions[0x67] = uRRA(zeroPageReadModifyWrite);
opcodes[0x68] = "PLA"; instructions[0x68] = PLA();
opcodes[0x69] = "ADC"; instructions[0x69] = ADC(immediateRead);
opcodes[0x6a] = "ROR"; instructions[0x6a] = ROR_ACC();
opcodes[0x6b] = "uARR"; instructions[0x6b] = uARR(immediateRead);
opcodes[0x6c] = "JMP"; instructions[0x6c] = JMP_IND();
opcodes[0x6d] = "ADC"; instructions[0x6d] = ADC(absoluteRead);
opcodes[0x6e] = "ROR"; instructions[0x6e] = ROR(absoluteReadModifyWrite);
opcodes[0x6f] = "uRRA"; instructions[0x6f] = uRRA(absoluteReadModifyWrite);
opcodes[0x70] = "BVS"; instructions[0x70] = Bxx(bV, 1); // BVS
opcodes[0x71] = "ADC"; instructions[0x71] = ADC(indirectYRead);
opcodes[0x72] = "uKIL"; instructions[0x72] = uKIL();
opcodes[0x73] = "uRRA"; instructions[0x73] = uRRA(indirectYReadModifyWrite);
opcodes[0x74] = "uNOP"; instructions[0x74] = uNOP(zeroPageIndexedRead(rX));
opcodes[0x75] = "ADC"; instructions[0x75] = ADC(zeroPageIndexedRead(rX));
opcodes[0x76] = "ROR"; instructions[0x76] = ROR(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x77] = "uRRA"; instructions[0x77] = uRRA(zeroPageIndexedReadModifyWrite(rX));
opcodes[0x78] = "SEI"; instructions[0x78] = SEI();
opcodes[0x79] = "ADC"; instructions[0x79] = ADC(absoluteIndexedRead(rY));
opcodes[0x7a] = "uNOP"; instructions[0x7a] = uNOP(implied);
opcodes[0x7b] = "uRRA"; instructions[0x7b] = uRRA(absoluteIndexedReadModifyWrite(rY));
opcodes[0x7c] = "uNOP"; instructions[0x7c] = uNOP(absoluteIndexedRead(rX));
opcodes[0x7d] = "ADC"; instructions[0x7d] = ADC(absoluteIndexedRead(rX));
opcodes[0x7e] = "ROR"; instructions[0x7e] = ROR(absoluteIndexedReadModifyWrite(rX));
opcodes[0x7f] = "uRRA"; instructions[0x7f] = uRRA(absoluteIndexedReadModifyWrite(rX));
opcodes[0x80] = "uNOP"; instructions[0x80] = uNOP(immediateRead);
opcodes[0x81] = "STA"; instructions[0x81] = STA(indirectXWrite);
opcodes[0x82] = "uNOP"; instructions[0x82] = uNOP(immediateRead);
opcodes[0x83] = "uSAX"; instructions[0x83] = uSAX(indirectXWrite);
opcodes[0x84] = "STY"; instructions[0x84] = STY(zeroPageWrite);
opcodes[0x85] = "STA"; instructions[0x85] = STA(zeroPageWrite);
opcodes[0x86] = "STX"; instructions[0x86] = STX(zeroPageWrite);
opcodes[0x87] = "uSAX"; instructions[0x87] = uSAX(zeroPageWrite);
opcodes[0x88] = "DEY"; instructions[0x88] = DEY();
opcodes[0x89] = "uNOP"; instructions[0x89] = uNOP(immediateRead);
opcodes[0x8a] = "TXA"; instructions[0x8a] = TXA();
opcodes[0x8b] = "uANE"; instructions[0x8b] = uANE(immediateRead);
opcodes[0x8c] = "STY"; instructions[0x8c] = STY(absoluteWrite);
opcodes[0x8d] = "STA"; instructions[0x8d] = STA(absoluteWrite);
opcodes[0x8e] = "STX"; instructions[0x8e] = STX(absoluteWrite);
opcodes[0x8f] = "uSAX"; instructions[0x8f] = uSAX(absoluteWrite);
opcodes[0x90] = "BCC"; instructions[0x90] = Bxx(bC, 0); // BCC
opcodes[0x91] = "STA"; instructions[0x91] = STA(indirectYWrite);
opcodes[0x92] = "uKIL"; instructions[0x92] = uKIL();
opcodes[0x93] = "uSHA"; instructions[0x93] = uSHA(indirectYWrite);
opcodes[0x94] = "STY"; instructions[0x94] = STY(zeroPageIndexedWrite(rX));
opcodes[0x95] = "STA"; instructions[0x95] = STA(zeroPageIndexedWrite(rX));
opcodes[0x96] = "STX"; instructions[0x96] = STX(zeroPageIndexedWrite(rY));
opcodes[0x97] = "uSAX"; instructions[0x97] = uSAX(zeroPageIndexedWrite(rY));
opcodes[0x98] = "TYA"; instructions[0x98] = TYA();
opcodes[0x99] = "STA"; instructions[0x99] = STA(absoluteIndexedWrite(rY));
opcodes[0x9a] = "TXS"; instructions[0x9a] = TXS();
opcodes[0x9b] = "uSHS"; instructions[0x9b] = uSHS(absoluteIndexedWrite(rY));
opcodes[0x9c] = "uSHY"; instructions[0x9c] = uSHY(absoluteIndexedWrite(rX));
opcodes[0x9d] = "STA"; instructions[0x9d] = STA(absoluteIndexedWrite(rX));
opcodes[0x9e] = "uSHX"; instructions[0x9e] = uSHX(absoluteIndexedWrite(rY));
opcodes[0x9f] = "uSHA"; instructions[0x9f] = uSHA(absoluteIndexedWrite(rY));
opcodes[0xa0] = "LDY"; instructions[0xa0] = LDY(immediateRead);
opcodes[0xa1] = "LDA"; instructions[0xa1] = LDA(indirectXRead);
opcodes[0xa2] = "LDX"; instructions[0xa2] = LDX(immediateRead);
opcodes[0xa3] = "uLAX"; instructions[0xa3] = uLAX(indirectXRead);
opcodes[0xa4] = "LDY"; instructions[0xa4] = LDY(zeroPageRead);
opcodes[0xa5] = "LDA"; instructions[0xa5] = LDA(zeroPageRead);
opcodes[0xa6] = "LDX"; instructions[0xa6] = LDX(zeroPageRead);
opcodes[0xa7] = "uLAX"; instructions[0xa7] = uLAX(zeroPageRead);
opcodes[0xa8] = "TAY"; instructions[0xa8] = TAY();
opcodes[0xa9] = "LDA"; instructions[0xa9] = LDA(immediateRead);
opcodes[0xaa] = "TAX"; instructions[0xaa] = TAX();
opcodes[0xab] = "uLXA"; instructions[0xab] = uLXA(immediateRead);
opcodes[0xac] = "LDY"; instructions[0xac] = LDY(absoluteRead);
opcodes[0xad] = "LDA"; instructions[0xad] = LDA(absoluteRead);
opcodes[0xae] = "LDX"; instructions[0xae] = LDX(absoluteRead);
opcodes[0xaf] = "uLAX"; instructions[0xaf] = uLAX(absoluteRead);
opcodes[0xb0] = "BCS"; instructions[0xb0] = Bxx(bC, 1); // BCS
opcodes[0xb1] = "LDA"; instructions[0xb1] = LDA(indirectYRead);
opcodes[0xb2] = "uKIL"; instructions[0xb2] = uKIL();
opcodes[0xb3] = "uLAX"; instructions[0xb3] = uLAX(indirectYRead);
opcodes[0xb4] = "LDY"; instructions[0xb4] = LDY(zeroPageIndexedRead(rX));
opcodes[0xb5] = "LDA"; instructions[0xb5] = LDA(zeroPageIndexedRead(rX));
opcodes[0xb6] = "LDX"; instructions[0xb6] = LDX(zeroPageIndexedRead(rY));
opcodes[0xb7] = "uLAX"; instructions[0xb7] = uLAX(zeroPageIndexedRead(rY));
opcodes[0xb8] = "CLV"; instructions[0xb8] = CLV();
opcodes[0xb9] = "LDA"; instructions[0xb9] = LDA(absoluteIndexedRead(rY));
opcodes[0xba] = "TSX"; instructions[0xba] = TSX();
opcodes[0xbb] = "uLAS"; instructions[0xbb] = uLAS(absoluteIndexedRead(rY));
opcodes[0xbc] = "LDY"; instructions[0xbc] = LDY(absoluteIndexedRead(rX));
opcodes[0xbd] = "LDA"; instructions[0xbd] = LDA(absoluteIndexedRead(rX));
opcodes[0xbe] = "LDX"; instructions[0xbe] = LDX(absoluteIndexedRead(rY));
opcodes[0xbf] = "uLAX"; instructions[0xbf] = uLAX(absoluteIndexedRead(rY));
opcodes[0xc0] = "CPY"; instructions[0xc0] = CPY(immediateRead);
opcodes[0xc1] = "CMP"; instructions[0xc1] = CMP(indirectXRead);
opcodes[0xc2] = "uNOP"; instructions[0xc2] = uNOP(immediateRead);
opcodes[0xc3] = "uDCP"; instructions[0xc3] = uDCP(indirectXReadModifyWrite);
opcodes[0xc4] = "CPY"; instructions[0xc4] = CPY(zeroPageRead);
opcodes[0xc5] = "CMP"; instructions[0xc5] = CMP(zeroPageRead);
opcodes[0xc6] = "DEC"; instructions[0xc6] = DEC(zeroPageReadModifyWrite);
opcodes[0xc7] = "uDCP"; instructions[0xc7] = uDCP(zeroPageReadModifyWrite);
opcodes[0xc8] = "INY"; instructions[0xc8] = INY();
opcodes[0xc9] = "CMP"; instructions[0xc9] = CMP(immediateRead);
opcodes[0xca] = "DEX"; instructions[0xca] = DEX();
opcodes[0xcb] = "uSBX"; instructions[0xcb] = uSBX(immediateRead);
opcodes[0xcc] = "CPY"; instructions[0xcc] = CPY(absoluteRead);
opcodes[0xcd] = "CMP"; instructions[0xcd] = CMP(absoluteRead);
opcodes[0xce] = "DEC"; instructions[0xce] = DEC(absoluteReadModifyWrite);
opcodes[0xcf] = "uDCP"; instructions[0xcf] = uDCP(absoluteReadModifyWrite);
opcodes[0xd0] = "BNE"; instructions[0xd0] = Bxx(bZ, 0); // BNE
opcodes[0xd1] = "CMP"; instructions[0xd1] = CMP(indirectYRead);
opcodes[0xd2] = "uKIL"; instructions[0xd2] = uKIL();
opcodes[0xd3] = "uDCP"; instructions[0xd3] = uDCP(indirectYReadModifyWrite);
opcodes[0xd4] = "uNOP"; instructions[0xd4] = uNOP(zeroPageIndexedRead(rX));
opcodes[0xd5] = "CMP"; instructions[0xd5] = CMP(zeroPageIndexedRead(rX));
opcodes[0xd6] = "DEC"; instructions[0xd6] = DEC(zeroPageIndexedReadModifyWrite(rX));
opcodes[0xd7] = "uDCP"; instructions[0xd7] = uDCP(zeroPageIndexedReadModifyWrite(rX));
opcodes[0xd8] = "CLD"; instructions[0xd8] = CLD();
opcodes[0xd9] = "CMP"; instructions[0xd9] = CMP(absoluteIndexedRead(rY));
opcodes[0xda] = "uNOP"; instructions[0xda] = uNOP(implied);
opcodes[0xdb] = "uDCP"; instructions[0xdb] = uDCP(absoluteIndexedReadModifyWrite(rY));
opcodes[0xdc] = "uNOP"; instructions[0xdc] = uNOP(absoluteIndexedRead(rX));
opcodes[0xdd] = "CMP"; instructions[0xdd] = CMP(absoluteIndexedRead(rX));
opcodes[0xde] = "DEC"; instructions[0xde] = DEC(absoluteIndexedReadModifyWrite(rX));
opcodes[0xdf] = "uDCP"; instructions[0xdf] = uDCP(absoluteIndexedReadModifyWrite(rX));
opcodes[0xe0] = "CPX"; instructions[0xe0] = CPX(immediateRead);
opcodes[0xe1] = "SBC"; instructions[0xe1] = SBC(indirectXRead);
opcodes[0xe2] = "uNOP"; instructions[0xe2] = uNOP(immediateRead);
opcodes[0xe3] = "uISB"; instructions[0xe3] = uISB(indirectXReadModifyWrite);
opcodes[0xe4] = "CPX"; instructions[0xe4] = CPX(zeroPageRead);
opcodes[0xe5] = "SBC"; instructions[0xe5] = SBC(zeroPageRead);
opcodes[0xe6] = "INC"; instructions[0xe6] = INC(zeroPageReadModifyWrite);
opcodes[0xe7] = "uISB"; instructions[0xe7] = uISB(zeroPageReadModifyWrite);
opcodes[0xe8] = "INX"; instructions[0xe8] = INX();
opcodes[0xe9] = "SBC"; instructions[0xe9] = SBC(immediateRead);
opcodes[0xea] = "NOP"; instructions[0xea] = NOP();
opcodes[0xeb] = "SBC"; instructions[0xeb] = SBC(immediateRead);
opcodes[0xec] = "CPX"; instructions[0xec] = CPX(absoluteRead);
opcodes[0xed] = "SBC"; instructions[0xed] = SBC(absoluteRead);
opcodes[0xee] = "INC"; instructions[0xee] = INC(absoluteReadModifyWrite);
opcodes[0xef] = "uISB"; instructions[0xef] = uISB(absoluteReadModifyWrite);
opcodes[0xf0] = "BEQ"; instructions[0xf0] = Bxx(bZ, 1); // BEQ
opcodes[0xf1] = "SBC"; instructions[0xf1] = SBC(indirectYRead);
opcodes[0xf2] = "uKIL"; instructions[0xf2] = uKIL();
opcodes[0xf3] = "uISB"; instructions[0xf3] = uISB(indirectYReadModifyWrite);
opcodes[0xf4] = "uNOP"; instructions[0xf4] = uNOP(zeroPageIndexedRead(rX));
opcodes[0xf5] = "SBC"; instructions[0xf5] = SBC(zeroPageIndexedRead(rX));
opcodes[0xf6] = "INC"; instructions[0xf6] = INC(zeroPageIndexedReadModifyWrite(rX));
opcodes[0xf7] = "uISB"; instructions[0xf7] = uISB(zeroPageIndexedReadModifyWrite(rX));
opcodes[0xf8] = "SED"; instructions[0xf8] = SED();
opcodes[0xf9] = "SBC"; instructions[0xf9] = SBC(absoluteIndexedRead(rY));
opcodes[0xfa] = "uNOP"; instructions[0xfa] = uNOP(implied);
opcodes[0xfb] = "uISB"; instructions[0xfb] = uISB(absoluteIndexedReadModifyWrite(rY));
opcodes[0xfc] = "uNOP"; instructions[0xfc] = uNOP(absoluteIndexedRead(rX));
opcodes[0xfd] = "SBC"; instructions[0xfd] = SBC(absoluteIndexedRead(rX));
opcodes[0xfe] = "INC"; instructions[0xfe] = INC(absoluteIndexedReadModifyWrite(rX));
opcodes[0xff] = "uISB"; instructions[0xff] = uISB(absoluteIndexedReadModifyWrite(rX));
// Single Byte instructions
function ASL_ACC() {
return implied(function() {
setC(A > 127);
A = (A << 1) & 255;
setZ(A);
setN(A);
});
}
function CLC() {
return implied(function() {
C = 0;
});
}
function CLD() {
return implied(function() {
D = 0;
});
}
function CLI() {
return implied(function() {
I = 0;
});
}
function CLV() {
return implied(function() {
V = 0;
});
}
function DEX() {
return implied(function() {
X = (X - 1) & 255;
setZ(X);
setN(X);
});
}
function DEY() {
return implied(function() {
Y = (Y - 1) & 255;
setZ(Y);
setN(Y);
});
}
function INX() {
return implied(function() {
X = (X + 1) & 255;
setZ(X);
setN(X);
});
}
function INY() {
return implied(function() {
Y = (Y + 1) & 255;
setZ(Y);
setN(Y);
});
}
function LSR_ACC() {
return implied(function() {
C = A & 0x01;
A >>>= 1;
setZ(A);
N = 0;
});
}
function NOP() {
return implied(function() {
// nothing
});
}
function ROL_ACC() {
return implied(function() {
var newC = A > 127;
A = ((A << 1) | C) & 255;
setC(newC);
setZ(A);
setN(A);
});
}
function ROR_ACC() {
return implied(function() {
var newC = A & 0x01;
A = (A >>> 1) | (C << 7);
setC(newC);
setZ(A);
setN(A);
});
}
function SEC() {
return implied(function() {
C = 1;
});
}
function SED() {
return implied(function() {
D = 1;
});
}
function SEI() {
return implied(function() {
I = 1;
});
}
function TAX() {
return implied(function() {
X = A;
setZ(X);
setN(X);
});
}
function TAY() {
return implied(function() {
Y = A;
setZ(Y);
setN(Y);
});
}
function TSX() {
return implied(function() {
X = SP;
setZ(X);
setN(X);
});
}
function TXA() {
return implied(function() {
A = X;
setZ(A);
setN(A);
});
}
function TXS() {
return implied(function() {
SP = X;
});
}
function TYA() {
return implied(function() {
A = Y;
setZ(A);
setN(A);
});
}
function uKIL() {
return [
fetchOpcodeAndDecodeInstruction,
function() {
illegalOpcode("KIL/HLT/JAM");
},
function() {
T--; // Causes the processor to be stuck in this instruction forever
}
];
}
function uNOP(addressing) {
return addressing(function() {
illegalOpcode("NOP/DOP");
// nothing
});
}
// Internal Execution on Memory Data
function ADC(addressing) {
return addressing(function() {
if (D) {
var operand = data;
var AL = (A & 15) + (operand & 15) + C;
if (AL > 9) { AL += 6; }
var AH = ((A >> 4) + (operand >> 4) + ((AL > 15)?1:0)) << 4;
setZ((A + operand + C) & 255);
setN(AH);
setV(((A ^AH) & ~(A ^ operand)) & 128);
if (AH > 0x9f) { AH += 0x60; }
setC(AH > 255);
A = (AH | (AL & 15)) & 255;
} else {
var add = A + data + C;
setC(add > 255);
setV(((A ^ add) & (data ^ add)) & 0x80);
A = add & 255;
setZ(A);
setN(A);
}
});
}
function AND(addressing) {
return addressing(function() {
A &= data;
setZ(A);
setN(A);
});
}
function BIT(addressing) {
return addressing(function() {
var par = data;
setZ(A & par);
setV(par & 0x40);
setN(par);
});
}
function CMP(addressing) {
return addressing(function() {
var val = (A - data) & 255;
setC(A >= data);
setZ(val);
setN(val);
});
}
function CPX(addressing) {
return addressing(function() {
var val = (X - data) & 255;
setC(X >= data);
setZ(val);
setN(val);
});
}
function CPY(addressing) {
return addressing(function() {
var val = (Y - data) & 255;
setC(Y >= data);
setZ(val);
setN(val);
});
}
function EOR(addressing) {
return addressing(function() {
A ^= data;
setZ(A);
setN(A);
});
}
function LDA(addressing) {
return addressing(function() {
A = data;
setZ(A);
setN(A);
});
}
function LDX(addressing) {
return addressing(function() {
X = data;
setZ(X);
setN(X);
});
}
function LDY(addressing) {
return addressing(function() {
Y = data;
setZ(Y);
setN(Y);
});
}
function ORA(addressing) {
return addressing(function() {
A |= data;
setZ(A);
setN(A);
});
}
function SBC(addressing) {
return addressing(function() {
if (D) {
var operand = data;
var AL = (A & 15) - (operand & 15) - (1-C);
var AH = (A >> 4) - (operand >> 4) - ((AL < 0)?1:0);
if (AL < 0) { AL -= 6; }
if (AH < 0) { AH -= 6; }
var sub = A - operand - (1-C);
setC(~sub & 256);
setV(((A ^ operand) & (A ^ sub)) & 128);
setZ(sub & 255);
setN(sub);
A = ((AH << 4) | (AL & 15)) & 255;
} else {
operand = (~data) & 255;
sub = A + operand + C;
setC(sub > 255);
setV(((A ^ sub) & (operand ^ sub) & 0x80));
A = sub & 255;
setZ(A);
setN(A);
}
});
}
function uANC(addressing) {
return addressing(function() {
illegalOpcode("ANC");
A &= data;
setZ(A);
N = C = (A & 0x080) ? 1 : 0;
});
}
function uANE(addressing) {
return addressing(function() {
illegalOpcode("ANE");
// Exact operation unknown. Do nothing
});
}
function uARR(addressing) {
// Some sources say flags are affected per ROR, others say its more complex. The complex one is chosen
return addressing(function() {
illegalOpcode("ARR");
var val = A & data;
var oldC = C ? 0x80 : 0;
val = (val >>> 1) | oldC;
A = val;
setZ(val);
setN(val);
var comp = A & 0x60;
if (comp == 0x60) { C = 1; V = 0; }
else if (comp == 0x00) { C = 0; V = 0; }
else if (comp == 0x20) { C = 0; V = 1; }
else if (comp == 0x40) { C = 1; V = 1; }
});
}
function uASR(addressing) {
return addressing(function() {
illegalOpcode("ASR");
var val = A & data;
C = (val & 0x01); // bit 0
val = val >>> 1;
A = val;
setZ(val);
N = 0;
});
}
function uLAS(addressing) {
return addressing(function() {
illegalOpcode("LAS");
var val = SP & data;
A = val;
X = val;
SP = val;
setZ(val);
setN(val);
});
}
function uLAX(addressing) {
return addressing(function() {
illegalOpcode("LAX");
var val = data;
A = val;
X = val;
setZ(val);
setN(val);
});
}
function uLXA(addressing) {
return addressing(function() {
// Some sources say its an OR with $EE then AND with IMM, others exclude the OR,
// others exclude both the OR and the AND. Excluding just the OR...
illegalOpcode("LXA");
var val = A /* | 0xEE) */ & data;
A = val;
X = val;
setZ(val);
setN(val);
});
}
function uSBX(addressing) {
return addressing(function() {
illegalOpcode("SBX");
var par = A & X;
var val = data;
var newX = (par - val) & 255;
X = newX;
setC(par >= val);
setZ(newX);
setN(newX);
});
}
// Store operations
function STA(addressing) {
return addressing(function() {
data = A;
});
}
function STX(addressing) {
return addressing(function() {
data = X;
});
}
function STY(addressing) {
return addressing(function() {
data = Y;
});
}
function uSAX(addressing) {
return addressing(function() {
// Some sources say it would affect N and Z flags, some say it wouldn't. Chose not to affect
illegalOpcode("SAX");
data = A & X;
});
}
function uSHA(addressing) {
return addressing(function() {
illegalOpcode("SHA");
data = A & X & ((BA >>> 8) + 1) & 255; // A & X & (High byte of effective address + 1) !!!
// data would also be stored BAH if page boundary is crossed. Unobservable, not needed here
});
}
function uSHS(addressing) {
return addressing(function() {
illegalOpcode("SHS");
var val = A & X;
SP = val;
data = val & ((BA >>> 8) + 1) & 255; // A & X & (High byte of effective address + 1) !!!
// data would also be stored BAH if page boundary is crossed. Unobservable, not needed here
});
}
function uSHX(addressing) {
return addressing(function() {
illegalOpcode("SHX");
data = X & ((BA >>> 8) + 1) & 255; // X & (High byte of effective address + 1) !!!
// data would also be stored BAH if page boundary is crossed. Unobservable, not needed here
});
}
function uSHY(addressing) {
return addressing(function() {
illegalOpcode("SHY");
data = Y & ((BA >>> 8) + 1) & 255; // Y & (High byte of effective address + 1) !!!
// data would also be stored BAH if page boundary is crossed. Unobservable, not needed here
});
}
// Read-Modify-Write operations
function ASL(addressing) {
return addressing(function() {
setC(data > 127);
var par = (data << 1) & 255;
data = par;
setZ(par);
setN(par);
});
}
function DEC(addressing) {
return addressing(function() {
var par = (data - 1) & 255;
data = par;
setZ(par);
setN(par);
});
}
function INC(addressing) {
return addressing(function() {
var par = (data + 1) & 255;
data = par;
setZ(par);
setN(par);
});
}
function LSR(addressing) {
return addressing(function() {
C = data & 0x01;
data >>>= 1;
setZ(data);
N = 0;
});
}
function ROL(addressing) {
return addressing(function() {
var newC = data > 127;
var par = ((data << 1) | C) & 255;
data = par;
setC(newC);
setZ(par);
setN(par);
});
}
function ROR(addressing) {
return addressing(function() {
var newC = data & 0x01;
var par = (data >>> 1) | (C << 7);
data = par;
setC(newC);
setZ(par);
setN(par);
});
}
function uDCP(addressing) {
return addressing(function() {
illegalOpcode("DCP");
var par = (data - 1) & 255;
data = par;
par = A - par;
setC(par >= 0);
setZ(par);
setN(par);
});
}
function uISB(addressing) {
return addressing(function() {
illegalOpcode("ISB");
data = (data + 1) & 255; // ISB is the same as SBC but incs the operand first
if (D) {
var operand = data;
var AL = (A & 15) - (operand & 15) - (1-C);
var AH = (A >> 4) - (operand >> 4) - ((AL < 0)?1:0);
if (AL < 0) { AL -= 6; }
if (AH < 0) { AH -= 6; }
var sub = A - operand - (1-C);
setC(~sub & 256);
setV(((A ^ operand) & (A ^ sub)) & 128);
setZ(sub & 255);
setN(sub);
A = ((AH << 4) | (AL & 15)) & 255;
} else {
operand = (~data) & 255;
sub = A + operand + C;
setC(sub > 255);
setV(((A ^ sub) & (operand ^ sub) & 0x80));
A = sub & 255;
setZ(A);
setN(A);
}
});
}
function uRLA(addressing) {
return addressing(function() {
illegalOpcode("RLA");
var val = data;
var oldC = C;
setC(val & 0x80); // bit 7 was set
val = ((val << 1) | oldC) & 255;
data = val;
A &= val;
setZ(val); // TODO Verify. May be A instead of val in the flags setting
setN(val);
});
}
function uRRA(addressing) {
return addressing(function() {
illegalOpcode("RRA");
var val = data;
var oldC = C ? 0x80 : 0;
setC(val & 0x01); // bit 0 was set
val = (val >>> 1) | oldC;
data = val;
// RRA is the same as ADC from here
if (D) {
var operand = data;
var AL = (A & 15) + (operand & 15) + C;
if (AL > 9) { AL += 6; }
var AH = ((A >> 4) + (operand >> 4) + ((AL > 15)?1:0)) << 4;
setZ((A + operand + C) & 255);
setN(AH);
setV(((A ^AH) & ~(A ^ operand)) & 128);
if (AH > 0x9f) { AH += 0x60; }
setC(AH > 255);
A = (AH | (AL & 15)) & 255;
} else {
var add = A + data + C;
setC(add > 255);
setV(((A ^ add) & (data ^ add)) & 0x80);
A = add & 255;
setZ(A);
setN(A);
}
});
}
function uSLO(addressing) {
return addressing(function() {
illegalOpcode("SLO");
var val = data;
setC(val & 0x80); // bit 7 was set
val = (val << 1) & 255;
data = val;
val = A | val;
A = val;
setZ(val);
setN(val);
});
}
function uSRE(addressing) {
return addressing(function() {
illegalOpcode("SRE");
var val = data;
setC(val & 0x01); // bit 0 was set
val = val >>> 1;
data = val;
val = (A ^ val) & 255;
A = val;
setZ(val);
setN(val);
});
}
// Miscellaneous operations
function PHA() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
function() { pushToStack(A); },
fetchNextOpcode
];
}
function PHP() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
function() { pushToStack(getStatusBits()); },
fetchNextOpcode
];
}
function PLA() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
peekFromStack,
function() {
A = popFromStack();
setZ(A);
setN(A);
},
fetchNextOpcode
];
}
function PLP() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
peekFromStack,
function() { setStatusBits(popFromStack()); },
fetchNextOpcode
];
}
function JSR() {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL,
peekFromStack,
function() { pushToStack((PC >>> 8) & 0xff); },
function() { pushToStack(PC & 0xff); },
fetchADH,
function() { PC = AD; fetchNextOpcode(); }
];
}
function BRK() {
return [
fetchOpcodeAndDecodeInstruction,
fetchDataFromImmediate, // For debugging purposes, use operand as an arg for BRK!
function() {
if (self.debug) self.breakpoint("BRK " + data);
pushToStack((PC >>> 8) & 0xff);
},
function() { pushToStack(PC & 0xff); },
function() { pushToStack(getStatusBits()); }, // set B flag
function() { AD = bus.read(IRQ_VECTOR); },
function() { AD |= bus.read(IRQ_VECTOR + 1) << 8; },
function() { PC = AD; I = 1; fetchNextOpcode(); }
];
}
function IRQ() {
return [
fetchOpcodeAndDecodeInstruction,
fetchDataFromImmediate, // For debugging purposes, use operand as an arg for BRK!
function() {
if (self.debug) self.breakpoint("IRQ " + data);
pushToStack((PC >>> 8) & 0xff);
},
function() { pushToStack(PC & 0xff); },
function() { pushToStack(getStatusBits() & ~0x10); }, // no BRK flag
function() { AD = bus.read(IRQ_VECTOR); },
function() { AD |= bus.read(IRQ_VECTOR + 1) << 8; },
function() { PC = AD; fetchNextOpcode(); }
];
}
function NMI() {
return [
fetchOpcodeAndDecodeInstruction,
fetchDataFromImmediate,
function() {
if (self.debug) self.breakpoint("NMI " + data);
pushToStack((PC >>> 8) & 0xff);
},
function() { pushToStack(PC & 0xff); },
function() { pushToStack(getStatusBits() & ~0x10); }, // no BRK flag
function() { AD = bus.read(NMI_VECTOR); },
function() { AD |= bus.read(NMI_VECTOR + 1) << 8; },
function() { PC = AD; fetchNextOpcode(); }
];
}
function RTI() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
peekFromStack,
function() { setStatusBits(popFromStack()); },
function() { AD = popFromStack(); },
function() { AD |= popFromStack() << 8; },
function() { PC = AD; fetchNextOpcode(); }
];
}
function RTS() {
return [
fetchOpcodeAndDecodeInstruction,
fetchOpcodeAndDiscard,
peekFromStack,
function() { AD = popFromStack(); },
function() { AD |= popFromStack() << 8; },
function() { PC = AD; fetchDataFromImmediate(); },
fetchNextOpcode
];
}
function JMP_ABS() {
return [
fetchOpcodeAndDecodeInstruction,
fetchADL,
fetchADH,
function() { PC = AD; fetchNextOpcode(); }
];
}
function JMP_IND() {
return [
fetchOpcodeAndDecodeInstruction,
fetchIAL, // IAH will be zero
fetchIAH,
fetchBALFromIA,
function() {
add1toIAL();
fetchBAHFromIA();
},
function() { PC = BA; fetchNextOpcode(); }
];
}
function Bxx(reg, cond) {
var branchTaken;
if (reg === bZ) branchTaken = function() { return Z === cond; };
else if (reg === bN) branchTaken = function() { return N === cond; };
else if (reg === bC) branchTaken = function() { return C === cond; };
else branchTaken = function() { return V === cond; };
return [
fetchOpcodeAndDecodeInstruction,
fetchBranchOffset,
function() {
if (branchTaken()) {
fetchOpcodeAndDiscard();
addBranchOffsetToPCL();
} else {
fetchNextOpcode();
}
},
function() {
if(branchOffsetCrossAdjust) {
fetchOpcodeAndDiscard();
adjustPCHForBranchOffsetCross();
} else {
fetchNextOpcode();
}
},
fetchNextOpcode
];
}
// Savestate -------------------------------------------
this.saveState = function():MOS6502State {
return {
PC: (PC-1) & 0xffff,
A: A, X: X, Y: Y, SP: SP,
N: N, V: V, D: D, I: I, Z: Z, C: C,
T: T, o: opcode, R: RDY?1:0,
d: data, AD: AD, BA: BA, BC: BALCrossed?1:0, IA: IA,
bo: branchOffset, boa: branchOffsetCrossAdjust
};
};
this.loadState = function(state:MOS6502State) {
PC = (state.PC+1) & 0xffff;
A = state.A; X = state.X; Y = state.Y; SP = state.SP;
N = state.N; V = state.V; D = state.D; I = state.I; Z = state.Z; C = state.C;
T = state.T; opcode = state.o; RDY = !!state.R;
data = state.d; AD = state.AD; BA = state.BA; BALCrossed = !!state.BC; IA = state.IA;
branchOffset = state.bo; branchOffsetCrossAdjust = state.boa;
instruction = opcode < 0 ? [ fetchOpcodeAndDecodeInstruction ] : instructions[opcode];
};
// Accessory methods
this.toString = function() {
return "CPU " +
" PC: " + PC.toString(16) + " op: " + opcode.toString() + " T: " + T + " data: " + data + "\n" +
" A: " + A.toString(16) + " X: " + X.toString(16) + " Y: " + Y.toString(16) + " SP: " + SP.toString(16) + " " +
"N" + N + " " + "V" + V + " " + "D" + D + " " + "I" + I + " " + "Z" + Z + " " + "C" + C + " ";
};
this.breakpoint = function(mes) {
//jt.Util.log(mes);
if (this.trace) {
var text = "CPU Breakpoint! " + (mes ? "(" + mes + ")" : "") + "\n\n" + this.toString();
//jt.Util.message(text);
}
};
var cycletime = [
7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
6, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
0, 6, 0, 6, 3, 3, 3, 3, 2, 0, 2, 0, 4, 4, 4, 4,
2, 6, 0, 0, 4, 4, 4, 4, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 0, 4, 4, 4, 4,
2, 5, 0, 5, 4, 4, 4, 4, 2, 4, 2, 0, 4, 4, 4, 4,
2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 0, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7
];
var extracycles = [
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1
];
var insnlengths = [
1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3,
3, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3,
1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3,
1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3,
0, 2, 0, 2, 2, 2, 2, 2, 1, 0, 1, 0, 3, 3, 3, 3,
2, 2, 0, 0, 2, 2, 2, 3, 1, 3, 1, 0, 0, 3, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 1, 0, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3,
2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3
];
var validinsns = [
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
];
this.getOpcodeMetadata = function(opcode, address) {
// TODO: more intelligent maximum cycles
//var i = instructions[opcode];
return {
opcode:opcode,
mnenomic:opcodes[opcode],
minCycles:cycletime[opcode],
maxCycles:cycletime[opcode] + extracycles[opcode],
insnlength:insnlengths[opcode]
};
}
// only call when isPCStable() is true
this.setNMI = function() {
instruction = NMI();
T = 1;
PC = (PC-1) & 0xffff;
}
this.setIRQ = function() {
if (!I) { // only if not disabled
instruction = IRQ();
T = 1;
PC = (PC-1) & 0xffff;
}
}
this.getSP = function() { return SP; }
this.getPC = function() { return (PC-1) & 0xffff; }
this.getT = function() { return T; }
this.isHalted = function() { return opcodes[opcode] == "uKIL"; }
this.isPCStable = function() {
return T == 0;
}
};
export interface MOS6502State {
PC : number;
SP : number;
A : number;
X : number;
Y : number;
N : number;
V : number;
D : number;
I : number;
Z : number;
C : number;
T : number;
o : number;
R : number;
d : number;
AD : number;
BA : number;
BC : number;
IA : number;
bo : number;
boa : number;
}
export enum MOS6502Interrupts { None=0, NMI=1, IRQ=2 };
export class MOS6502 implements CPU, ClockBased, SavesState<MOS6502State>, Interruptable<MOS6502Interrupts> {
cpu = new _MOS6502();
interruptType : MOS6502Interrupts = MOS6502Interrupts.None;
connectMemoryBus(bus:Bus) {
this.cpu.connectBus(bus);
}
advanceClock() {
if (this.interruptType && this.isStable()) {
switch (this.interruptType) {
case MOS6502Interrupts.NMI: this.cpu.setNMI(); break;
case MOS6502Interrupts.IRQ: this.cpu.setIRQ(); break;
}
this.interruptType = 0;
}
this.cpu.clockPulse();
}
advanceInsn() {
do {
this.advanceClock();
} while (!this.isStable());
}
reset() {
this.cpu.reset();
this.interruptType = 0;
}
interrupt(itype:number) {
if (this.interruptType != MOS6502Interrupts.NMI) {
this.interruptType = itype;
}
}
NMI() {
this.interrupt(MOS6502Interrupts.NMI);
}
IRQ() {
this.interrupt(MOS6502Interrupts.IRQ);
}
getSP() {
return this.cpu.getSP();
}
getPC() {
return this.cpu.getPC();
}
isHalted() {
return this.cpu.isHalted();
}
saveState() {
var s = this.cpu.saveState();
s.it = this.interruptType;
return s;
}
loadState(s) {
this.cpu.loadState(s);
this.interruptType = s.it;
}
isStable() : boolean {
return this.cpu.isPCStable();
}
getOpcodeMetadata(op: number) {
return this.cpu.getOpcodeMetadata(op);
}
}