Execution Trace Log
Introduces an execution trace window that will keep track of the most recent 10000 execution steps. I'm not entirely happy with the implementation, yet.
This commit is contained in:
1 parent
38a4458aff
commit
bc3de80892
8 files changed
+504
-492
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -33,6 +33,7 @@ import com.loomcom.symon.exceptions.SymonException;
|
|||||||
import com.loomcom.symon.ui.Console;
|
import com.loomcom.symon.ui.Console;
|
||||||
import com.loomcom.symon.ui.PreferencesDialog;
|
import com.loomcom.symon.ui.PreferencesDialog;
|
||||||
import com.loomcom.symon.ui.StatusPanel;
|
import com.loomcom.symon.ui.StatusPanel;
|
||||||
|
import com.loomcom.symon.ui.TraceLog;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -118,7 +119,7 @@ public class Simulator implements Observer {
|
|||||||
/**
|
/**
|
||||||
* The Trace Window shows the most recent 50,000 CPU states.
|
* The Trace Window shows the most recent 50,000 CPU states.
|
||||||
*/
|
*/
|
||||||
private JFrame traceWindow;
|
private TraceLog traceLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Zero Page Window shows the contents of page 0.
|
* The Zero Page Window shows the contents of page 0.
|
||||||
@@ -229,6 +230,9 @@ public class Simulator implements Observer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Prepare the log window
|
||||||
|
traceLog = new TraceLog();
|
||||||
|
|
||||||
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
console.requestFocus();
|
console.requestFocus();
|
||||||
@@ -264,8 +268,9 @@ public class Simulator implements Observer {
|
|||||||
statusPane.updateState(cpu);
|
statusPane.updateState(cpu);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (traceLog.isVisible()) {
|
||||||
// TODO: Write to Log window, if frame is visible.
|
traceLog.refresh();
|
||||||
|
}
|
||||||
// TODO: Update memory window, if frame is visible.
|
// TODO: Update memory window, if frame is visible.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +322,11 @@ public class Simulator implements Observer {
|
|||||||
private void step() throws MemoryAccessException {
|
private void step() throws MemoryAccessException {
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
|
// TODO: We need to profile this for performance. Possibly allow
|
||||||
|
// a flag to turn trace on/off
|
||||||
|
synchronized(traceLog) {
|
||||||
|
traceLog.append(cpu.getCpuState());
|
||||||
|
}
|
||||||
|
|
||||||
// Read from the ACIA and immediately update the console if there's
|
// Read from the ACIA and immediately update the console if there's
|
||||||
// output ready.
|
// output ready.
|
||||||
@@ -607,6 +617,24 @@ public class Simulator implements Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ToggleTraceWindowAction extends AbstractAction {
|
||||||
|
public ToggleTraceWindowAction() {
|
||||||
|
super("Trace Log", null);
|
||||||
|
putValue(SHORT_DESCRIPTION, "Show or Hide the Trace Log Window");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
synchronized(traceLog) {
|
||||||
|
if (traceLog.isVisible()) {
|
||||||
|
traceLog.setVisible(false);
|
||||||
|
} else {
|
||||||
|
traceLog.refresh();
|
||||||
|
traceLog.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SimulatorMenu extends JMenuBar {
|
class SimulatorMenu extends JMenuBar {
|
||||||
// Menu Items
|
// Menu Items
|
||||||
private JMenuItem loadProgramItem;
|
private JMenuItem loadProgramItem;
|
||||||
@@ -675,6 +703,9 @@ public class Simulator implements Observer {
|
|||||||
makeFontSizeMenuItem(20, fontSubMenu, group);
|
makeFontSizeMenuItem(20, fontSubMenu, group);
|
||||||
|
|
||||||
viewMenu.add(fontSubMenu);
|
viewMenu.add(fontSubMenu);
|
||||||
|
|
||||||
|
JMenuItem showTraceLog = new JMenuItem(new ToggleTraceWindowAction());
|
||||||
|
viewMenu.add(showTraceLog);
|
||||||
add(viewMenu);
|
add(viewMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,19 +177,21 @@ public class StatusPanel extends JPanel {
|
|||||||
* @param cpu The simulated 6502 CPU.
|
* @param cpu The simulated 6502 CPU.
|
||||||
*/
|
*/
|
||||||
public void updateState(Cpu cpu) {
|
public void updateState(Cpu cpu) {
|
||||||
// Update the Processor Status Flag display
|
Cpu.CpuState cpuState = cpu.getCpuState();
|
||||||
int state = cpu.getProcessorStatus();
|
|
||||||
|
|
||||||
carryFlagLabel.setIcon(iconForFlag(state, 0));
|
// Update the Processor Status Flag display
|
||||||
zeroFlagLabel.setIcon(iconForFlag(state, 1));
|
int status = cpu.getCpuState().getStatusFlag();
|
||||||
irqDisableFlagLabel.setIcon(iconForFlag(state, 2));
|
|
||||||
decimalModeFlagLabel.setIcon(iconForFlag(state, 3));
|
carryFlagLabel.setIcon(iconForFlag(status, 0));
|
||||||
breakFlagLabel.setIcon(iconForFlag(state, 4));
|
zeroFlagLabel.setIcon(iconForFlag(status, 1));
|
||||||
overflowFlagLabel.setIcon(iconForFlag(state, 6));
|
irqDisableFlagLabel.setIcon(iconForFlag(status, 2));
|
||||||
negativeFlagLabel.setIcon(iconForFlag(state, 7));
|
decimalModeFlagLabel.setIcon(iconForFlag(status, 3));
|
||||||
|
breakFlagLabel.setIcon(iconForFlag(status, 4));
|
||||||
|
overflowFlagLabel.setIcon(iconForFlag(status, 6));
|
||||||
|
negativeFlagLabel.setIcon(iconForFlag(status, 7));
|
||||||
|
|
||||||
// Update the register and address displays
|
// Update the register and address displays
|
||||||
opcodeField.setText(cpu.getOpcodeStatus());
|
opcodeField.setText(cpu.getCpuState().disassembleOp());
|
||||||
pcField.setText(cpu.getProgramCounterStatus());
|
pcField.setText(cpu.getProgramCounterStatus());
|
||||||
spField.setText(cpu.getStackPointerStatus());
|
spField.setText(cpu.getStackPointerStatus());
|
||||||
aField.setText(cpu.getAccumulatorStatus());
|
aField.setText(cpu.getAccumulatorStatus());
|
||||||
|
|||||||
@@ -24,17 +24,61 @@
|
|||||||
|
|
||||||
package com.loomcom.symon.ui;
|
package com.loomcom.symon.ui;
|
||||||
|
|
||||||
|
import com.loomcom.symon.Cpu;
|
||||||
import com.loomcom.symon.util.FifoRingBuffer;
|
import com.loomcom.symon.util.FifoRingBuffer;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This frame displays a trace of CPU execution. The most recent TRACE_LENGTH lines
|
* This frame displays a trace of CPU execution. The most recent TRACE_LENGTH lines
|
||||||
* are captured in a buffer and rendered to the JFrame's main text area upon request.
|
* are captured in a buffer and rendered to the JFrame's main text area upon request.
|
||||||
*/
|
*/
|
||||||
public class TraceLog extends JFrame {
|
public class TraceLog {
|
||||||
|
|
||||||
private FifoRingBuffer<String> traceLog;
|
private FifoRingBuffer<Cpu.CpuState> traceLog;
|
||||||
|
private JFrame traceLogWindow;
|
||||||
|
private JTextArea logArea;
|
||||||
|
|
||||||
|
private static final Dimension SIZE = new Dimension(640, 480);
|
||||||
|
private static final int MAX_LOG_LENGTH = 10000;
|
||||||
|
|
||||||
|
public TraceLog() {
|
||||||
|
traceLog = new FifoRingBuffer<Cpu.CpuState>(MAX_LOG_LENGTH);
|
||||||
|
traceLogWindow = new JFrame();
|
||||||
|
traceLogWindow.setPreferredSize(SIZE);
|
||||||
|
traceLogWindow.setResizable(true);
|
||||||
|
|
||||||
|
traceLogWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
|
||||||
|
|
||||||
|
logArea = new JTextArea();
|
||||||
|
logArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||||
|
|
||||||
|
JScrollPane scrollableView = new JScrollPane(logArea);
|
||||||
|
|
||||||
|
traceLogWindow.getContentPane().add(scrollableView);
|
||||||
|
traceLogWindow.pack();
|
||||||
|
// Don't show the frame. That action is controlled by the Simulator.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
StringBuilder logString = new StringBuilder();
|
||||||
|
for (Cpu.CpuState state : traceLog) {
|
||||||
|
logString.append(state.toString());
|
||||||
|
logString.append("\n");
|
||||||
|
}
|
||||||
|
logArea.setText(logString.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append(Cpu.CpuState state) {
|
||||||
|
traceLog.push(new Cpu.CpuState(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return traceLogWindow.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisible(boolean b) {
|
||||||
|
traceLogWindow.setVisible(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.loomcom.symon;
|
|||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class CpuIndexedIndirectModeTest {
|
public class CpuIndexedIndirectModeTest {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.loomcom.symon;
|
|||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class CpuIndirectIndexedModeTest {
|
public class CpuIndirectIndexedModeTest {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.loomcom.symon;
|
|||||||
|
|
||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.InstructionTable;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|||||||
@@ -392,27 +392,4 @@ public class CpuTest extends TestCase {
|
|||||||
cpu.setYRegister(0x95);
|
cpu.setYRegister(0x95);
|
||||||
assertEquals(0x15, cpu.zpyAddress(0x80));
|
assertEquals(0x15, cpu.zpyAddress(0x80));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPcStatus() {
|
|
||||||
cpu.setProgramCounter(0x03fa);
|
|
||||||
assertEquals("$03FA", cpu.getProgramCounterStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testOpcodeStatus() throws MemoryAccessException {
|
|
||||||
// LDA (immediate)
|
|
||||||
bus.write(0x0200, 0xa9);
|
|
||||||
bus.write(0x0201, 0xef);
|
|
||||||
cpu.step();
|
|
||||||
assertEquals("LDA #$EF", cpu.getOpcodeStatus());
|
|
||||||
|
|
||||||
// BRK instruction
|
|
||||||
bus.write(0x0202, 0x00);
|
|
||||||
cpu.step();
|
|
||||||
assertEquals("BRK", cpu.getOpcodeStatus());
|
|
||||||
|
|
||||||
// Illegal opcode
|
|
||||||
cpu.step();
|
|
||||||
assertEquals("BRK", cpu.getOpcodeStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user