Add disassembled instructions to breakpoints
This commit is contained in:
1 parent
6b5976be8f
commit
634ea933f1
12 files changed
+460
-410
No files matched your search
@@ -24,7 +24,7 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.util.HexUtil;
|
||||
import com.loomcom.symon.util.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class Cpu implements InstructionTable {
|
||||
private Bus bus;
|
||||
|
||||
/* The CPU state */
|
||||
private static final CpuState state = new CpuState();
|
||||
private final CpuState state = new CpuState();
|
||||
|
||||
/* start time of op execution, needed for speed simulation */
|
||||
private long opBeginTime;
|
||||
@@ -114,7 +114,7 @@ public class Cpu implements InstructionTable {
|
||||
state.sp = 0xff;
|
||||
|
||||
// Set the PC to the address stored in the reset vector
|
||||
state.pc = address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
|
||||
state.pc = Utils.address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
|
||||
|
||||
// Clear instruction register.
|
||||
state.ir = 0;
|
||||
@@ -202,7 +202,7 @@ public class Cpu implements InstructionTable {
|
||||
case 2: // Accumulator - ignored
|
||||
break;
|
||||
case 3: // Absolute
|
||||
effectiveAddress = address(state.args[0], state.args[1]);
|
||||
effectiveAddress = Utils.address(state.args[0], state.args[1]);
|
||||
break;
|
||||
case 5: // Zero Page,X / Zero Page,Y
|
||||
if (state.ir == 0x96 || state.ir == 0xb6) {
|
||||
@@ -224,7 +224,7 @@ public class Cpu implements InstructionTable {
|
||||
switch (irAddressMode) {
|
||||
case 0: // (Zero Page,X)
|
||||
tmp = (state.args[0] + state.x) & 0xff;
|
||||
effectiveAddress = address(bus.read(tmp), bus.read(tmp + 1));
|
||||
effectiveAddress = Utils.address(bus.read(tmp), bus.read(tmp + 1));
|
||||
break;
|
||||
case 1: // Zero Page
|
||||
effectiveAddress = state.args[0];
|
||||
@@ -233,10 +233,10 @@ public class Cpu implements InstructionTable {
|
||||
effectiveAddress = -1;
|
||||
break;
|
||||
case 3: // Absolute
|
||||
effectiveAddress = address(state.args[0], state.args[1]);
|
||||
effectiveAddress = Utils.address(state.args[0], state.args[1]);
|
||||
break;
|
||||
case 4: // (Zero Page),Y
|
||||
tmp = address(bus.read(state.args[0]),
|
||||
tmp = Utils.address(bus.read(state.args[0]),
|
||||
bus.read((state.args[0] + 1) & 0xff));
|
||||
effectiveAddress = (tmp + state.y) & 0xffff;
|
||||
break;
|
||||
@@ -277,7 +277,7 @@ public class Cpu implements InstructionTable {
|
||||
case 0x20: // JSR - Jump to Subroutine - Implied
|
||||
stackPush((state.pc - 1 >> 8) & 0xff); // PC high byte
|
||||
stackPush(state.pc - 1 & 0xff); // PC low byte
|
||||
state.pc = address(state.args[0], state.args[1]);
|
||||
state.pc = Utils.address(state.args[0], state.args[1]);
|
||||
break;
|
||||
case 0x28: // PLP - Pull Processor Status - Implied
|
||||
setProcessorStatus(stackPop());
|
||||
@@ -294,7 +294,7 @@ public class Cpu implements InstructionTable {
|
||||
setProcessorStatus(stackPop());
|
||||
int lo = stackPop();
|
||||
int hi = stackPop();
|
||||
setProgramCounter(address(lo, hi));
|
||||
setProgramCounter(Utils.address(lo, hi));
|
||||
break;
|
||||
case 0x48: // PHA - Push Accumulator - Implied
|
||||
stackPush(state.a);
|
||||
@@ -310,7 +310,7 @@ public class Cpu implements InstructionTable {
|
||||
case 0x60: // RTS - Return from Subroutine - Implied
|
||||
lo = stackPop();
|
||||
hi = stackPop();
|
||||
setProgramCounter((address(lo, hi) + 1) & 0xffff);
|
||||
setProgramCounter((Utils.address(lo, hi) + 1) & 0xffff);
|
||||
break;
|
||||
case 0x68: // PLA - Pull Accumulator - Implied
|
||||
state.a = stackPop();
|
||||
@@ -398,20 +398,20 @@ public class Cpu implements InstructionTable {
|
||||
|
||||
/** JMP *****************************************************************/
|
||||
case 0x4c: // JMP - Absolute
|
||||
state.pc = address(state.args[0], state.args[1]);
|
||||
state.pc = Utils.address(state.args[0], state.args[1]);
|
||||
break;
|
||||
case 0x6c: // JMP - Indirect
|
||||
lo = address(state.args[0], state.args[1]); // Address of low byte
|
||||
lo = Utils.address(state.args[0], state.args[1]); // Address of low byte
|
||||
|
||||
if (state.args[0] == 0xff &&
|
||||
(behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG ||
|
||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
|
||||
hi = address(0x00, state.args[1]);
|
||||
hi = Utils.address(0x00, state.args[1]);
|
||||
} else {
|
||||
hi = lo + 1;
|
||||
}
|
||||
|
||||
state.pc = address(bus.read(lo), bus.read(hi));
|
||||
state.pc = Utils.address(bus.read(lo), bus.read(hi));
|
||||
/* TODO: For accuracy, allow a flag to enable broken behavior of early 6502s:
|
||||
*
|
||||
* "An original 6502 has does not correctly fetch the target
|
||||
@@ -770,7 +770,7 @@ public class Cpu implements InstructionTable {
|
||||
setIrqDisableFlag();
|
||||
|
||||
// Load interrupt vector address into PC
|
||||
state.pc = address(bus.read(vectorLow), bus.read(vectorHigh));
|
||||
state.pc = Utils.address(bus.read(vectorLow), bus.read(vectorHigh));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1198,23 +1198,23 @@ public class Cpu implements InstructionTable {
|
||||
}
|
||||
|
||||
public String getAccumulatorStatus() {
|
||||
return "$" + HexUtil.byteToHex(state.a);
|
||||
return "$" + Utils.byteToHex(state.a);
|
||||
}
|
||||
|
||||
public String getXRegisterStatus() {
|
||||
return "$" + HexUtil.byteToHex(state.x);
|
||||
return "$" + Utils.byteToHex(state.x);
|
||||
}
|
||||
|
||||
public String getYRegisterStatus() {
|
||||
return "$" + HexUtil.byteToHex(state.y);
|
||||
return "$" + Utils.byteToHex(state.y);
|
||||
}
|
||||
|
||||
public String getProgramCounterStatus() {
|
||||
return "$" + HexUtil.wordToHex(state.pc);
|
||||
return "$" + Utils.wordToHex(state.pc);
|
||||
}
|
||||
|
||||
public String getStackPointerStatus() {
|
||||
return "$" + HexUtil.byteToHex(state.sp);
|
||||
return "$" + Utils.byteToHex(state.sp);
|
||||
}
|
||||
|
||||
public int getProcessorStatus() {
|
||||
@@ -1298,19 +1298,12 @@ public class Cpu implements InstructionTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two bytes, return an address.
|
||||
*/
|
||||
int address(int lowByte, int hiByte) {
|
||||
return ((hiByte << 8) | lowByte) & 0xffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a hi byte and a low byte, return the Absolute,X
|
||||
* offset address.
|
||||
*/
|
||||
int xAddress(int lowByte, int hiByte) {
|
||||
return (address(lowByte, hiByte) + state.x) & 0xffff;
|
||||
return (Utils.address(lowByte, hiByte) + state.x) & 0xffff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1318,7 +1311,7 @@ public class Cpu implements InstructionTable {
|
||||
* offset address.
|
||||
*/
|
||||
int yAddress(int lowByte, int hiByte) {
|
||||
return (address(lowByte, hiByte) + state.y) & 0xffff;
|
||||
return (Utils.address(lowByte, hiByte) + state.y) & 0xffff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1362,249 +1355,81 @@ public class Cpu implements InstructionTable {
|
||||
} while (opBeginTime + interval >= end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatted string representing the last instruction and
|
||||
* operands that were executed.
|
||||
*
|
||||
* @return A string representing the mnemonic and operands of the instruction
|
||||
*/
|
||||
public static String disassembleOp(int opCode, int[] args) {
|
||||
String mnemonic = opcodeNames[opCode];
|
||||
|
||||
if (mnemonic == null) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(mnemonic);
|
||||
|
||||
switch (instructionModes[opCode]) {
|
||||
case ABS:
|
||||
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1])));
|
||||
break;
|
||||
case ABX:
|
||||
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X");
|
||||
break;
|
||||
case ABY:
|
||||
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",Y");
|
||||
break;
|
||||
case IMM:
|
||||
sb.append(" #$").append(Utils.byteToHex(args[0]));
|
||||
break;
|
||||
case IND:
|
||||
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(")");
|
||||
break;
|
||||
case XIN:
|
||||
sb.append(" ($").append(Utils.byteToHex(args[0])).append(",X)");
|
||||
break;
|
||||
case INY:
|
||||
sb.append(" ($").append(Utils.byteToHex(args[0])).append("),Y");
|
||||
break;
|
||||
case REL:
|
||||
case ZPG:
|
||||
sb.append(" $").append(Utils.byteToHex(args[0]));
|
||||
break;
|
||||
case ZPX:
|
||||
sb.append(" $").append(Utils.byteToHex(args[0])).append(",X");
|
||||
break;
|
||||
case ZPY:
|
||||
sb.append(" $").append(Utils.byteToHex(args[0])).append(",Y");
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A compact, struct-like representation of CPU state.
|
||||
* Return a formatted string representing the next instruction and
|
||||
* operands to be executed.
|
||||
*
|
||||
* @return A string representing the mnemonic and operands of the instruction
|
||||
*/
|
||||
public static class CpuState {
|
||||
/**
|
||||
* Accumulator
|
||||
*/
|
||||
public int a;
|
||||
/**
|
||||
* X index regsiter
|
||||
*/
|
||||
public int x;
|
||||
/**
|
||||
* Y index register
|
||||
*/
|
||||
public int y;
|
||||
/**
|
||||
* Stack Pointer
|
||||
*/
|
||||
public int sp;
|
||||
/**
|
||||
* Program Counter
|
||||
*/
|
||||
public int pc;
|
||||
/**
|
||||
* Last Loaded Instruction Register
|
||||
*/
|
||||
public int ir;
|
||||
/**
|
||||
* Peek-Ahead to next IR
|
||||
*/
|
||||
public int nextIr;
|
||||
public int[] args = new int[2];
|
||||
public int[] nextArgs = new int[2];
|
||||
public int instSize;
|
||||
public boolean opTrap;
|
||||
public boolean irqAsserted;
|
||||
public boolean nmiAsserted;
|
||||
public int lastPc;
|
||||
public String disassembleNextOp() {
|
||||
return Cpu.disassembleOp(state.nextIr, state.nextArgs);
|
||||
}
|
||||
|
||||
/* Status Flag Register bits */
|
||||
public boolean carryFlag;
|
||||
public boolean negativeFlag;
|
||||
public boolean zeroFlag;
|
||||
public boolean irqDisableFlag;
|
||||
public boolean decimalModeFlag;
|
||||
public boolean breakFlag;
|
||||
public boolean overflowFlag;
|
||||
public long stepCounter = 0L;
|
||||
|
||||
/**
|
||||
* Create an empty CPU State.
|
||||
*/
|
||||
public CpuState() {}
|
||||
|
||||
/**
|
||||
* Snapshot a copy of the CpuState.
|
||||
*
|
||||
* (This is a copy constructor rather than an implementation of <code>Cloneable</code>
|
||||
* based on Josh Bloch's recommendation)
|
||||
*
|
||||
* @param s The CpuState to copy.
|
||||
*/
|
||||
public CpuState(CpuState s) {
|
||||
this.a = s.a;
|
||||
this.x = s.x;
|
||||
this.y = s.y;
|
||||
this.sp = s.sp;
|
||||
this.pc = s.pc;
|
||||
this.ir = s.ir;
|
||||
this.nextIr = s.nextIr;
|
||||
this.lastPc = s.lastPc;
|
||||
this.args[0] = s.args[0];
|
||||
this.args[1] = s.args[1];
|
||||
this.nextArgs[0] = s.nextArgs[0];
|
||||
this.nextArgs[1] = s.nextArgs[1];
|
||||
this.instSize = s.instSize;
|
||||
this.opTrap = s.opTrap;
|
||||
this.irqAsserted = s.irqAsserted;
|
||||
this.carryFlag = s.carryFlag;
|
||||
this.negativeFlag = s.negativeFlag;
|
||||
this.zeroFlag = s.zeroFlag;
|
||||
this.irqDisableFlag = s.irqDisableFlag;
|
||||
this.decimalModeFlag = s.decimalModeFlag;
|
||||
this.breakFlag = s.breakFlag;
|
||||
this.overflowFlag = s.overflowFlag;
|
||||
this.stepCounter = s.stepCounter;
|
||||
/**
|
||||
* @param address Address to disassemble
|
||||
* @return String containing the disassembled instruction and operands.
|
||||
*/
|
||||
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
|
||||
int opCode = bus.read(address);
|
||||
int args[] = new int[2];
|
||||
int size = Cpu.instructionSizes[opCode];
|
||||
for (int i = 1; i < size; i++) {
|
||||
int nextRead = (address + i) % bus.endAddress();
|
||||
args[i-1] = bus.read(nextRead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string formatted for the trace log.
|
||||
*
|
||||
* @return a string formatted for the trace log.
|
||||
*/
|
||||
public String toTraceEvent() {
|
||||
String opcode = disassembleLastOp();
|
||||
return getInstructionByteStatus() + " " +
|
||||
String.format("%-14s", opcode) +
|
||||
"A:" + HexUtil.byteToHex(a) + " " +
|
||||
"X:" + HexUtil.byteToHex(x) + " " +
|
||||
"Y:" + HexUtil.byteToHex(y) + " " +
|
||||
"F:" + HexUtil.byteToHex(getStatusFlag()) + " " +
|
||||
"S:1" + HexUtil.byteToHex(sp) + " " +
|
||||
getProcessorStatusString() + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The value of the Process Status Register, as a byte.
|
||||
*/
|
||||
public int getStatusFlag() {
|
||||
int status = 0x20;
|
||||
if (carryFlag) {
|
||||
status |= P_CARRY;
|
||||
}
|
||||
if (zeroFlag) {
|
||||
status |= P_ZERO;
|
||||
}
|
||||
if (irqDisableFlag) {
|
||||
status |= P_IRQ_DISABLE;
|
||||
}
|
||||
if (decimalModeFlag) {
|
||||
status |= P_DECIMAL;
|
||||
}
|
||||
if (breakFlag) {
|
||||
status |= P_BREAK;
|
||||
}
|
||||
if (overflowFlag) {
|
||||
status |= P_OVERFLOW;
|
||||
}
|
||||
if (negativeFlag) {
|
||||
status |= P_NEGATIVE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getInstructionByteStatus() {
|
||||
switch (Cpu.instructionSizes[ir]) {
|
||||
case 0:
|
||||
case 1:
|
||||
return HexUtil.wordToHex(lastPc) + " " +
|
||||
HexUtil.byteToHex(ir) + " ";
|
||||
case 2:
|
||||
return HexUtil.wordToHex(lastPc) + " " +
|
||||
HexUtil.byteToHex(ir) + " " +
|
||||
HexUtil.byteToHex(args[0]) + " ";
|
||||
case 3:
|
||||
return HexUtil.wordToHex(lastPc) + " " +
|
||||
HexUtil.byteToHex(ir) + " " +
|
||||
HexUtil.byteToHex(args[0]) + " " +
|
||||
HexUtil.byteToHex(args[1]);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatted string representing the last instruction and
|
||||
* operands that were executed.
|
||||
*
|
||||
* @return A string representing the mnemonic and operands of the instruction
|
||||
*/
|
||||
private String disassembleOp(int ir, int[] args) {
|
||||
String mnemonic = opcodeNames[ir];
|
||||
|
||||
if (mnemonic == null) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(mnemonic);
|
||||
|
||||
switch (instructionModes[ir]) {
|
||||
case ABS:
|
||||
sb.append(" $").append(HexUtil.wordToHex(address(args[0], args[1])));
|
||||
break;
|
||||
case ABX:
|
||||
sb.append(" $").append(HexUtil.wordToHex(address(args[0], args[1]))).append(",X");
|
||||
break;
|
||||
case ABY:
|
||||
sb.append(" $").append(HexUtil.wordToHex(address(args[0], args[1]))).append(",Y");
|
||||
break;
|
||||
case IMM:
|
||||
sb.append(" #$").append(HexUtil.byteToHex(args[0]));
|
||||
break;
|
||||
case IND:
|
||||
sb.append(" ($").append(HexUtil.wordToHex(address(args[0], args[1]))).append(")");
|
||||
break;
|
||||
case XIN:
|
||||
sb.append(" ($").append(HexUtil.byteToHex(args[0])).append(",X)");
|
||||
break;
|
||||
case INY:
|
||||
sb.append(" ($").append(HexUtil.byteToHex(args[0])).append("),Y");
|
||||
break;
|
||||
case REL:
|
||||
case ZPG:
|
||||
sb.append(" $").append(HexUtil.byteToHex(args[0]));
|
||||
break;
|
||||
case ZPX:
|
||||
sb.append(" $").append(HexUtil.byteToHex(args[0])).append(",X");
|
||||
break;
|
||||
case ZPY:
|
||||
sb.append(" $").append(HexUtil.byteToHex(args[0])).append(",Y");
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String disassembleLastOp() {
|
||||
return disassembleOp(ir, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatted string representing the next instruction and
|
||||
* operands to be executed.
|
||||
*
|
||||
* @return A string representing the mnemonic and operands of the instruction
|
||||
*/
|
||||
public String disassembleNextOp() {
|
||||
return disassembleOp(nextIr, nextArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two bytes, return an address.
|
||||
*/
|
||||
private int address(int lowByte, int hiByte) {
|
||||
return ((hiByte << 8) | lowByte) & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return A string representing the current status register state.
|
||||
*/
|
||||
public String getProcessorStatusString() {
|
||||
return "[" + (negativeFlag ? 'N' : '.') +
|
||||
(overflowFlag ? 'V' : '.') +
|
||||
"-" +
|
||||
(breakFlag ? 'B' : '.') +
|
||||
(decimalModeFlag ? 'D' : '.') +
|
||||
(irqDisableFlag ? 'I' : '.') +
|
||||
(zeroFlag ? 'Z' : '.') +
|
||||
(carryFlag ? 'C' : '.') +
|
||||
"]";
|
||||
}
|
||||
return disassembleOp(opCode, args);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user