mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-09-27 08:54:48 +00:00
1996 lines
64 KiB
TypeScript
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);
|
|
}
|
|
}
|