Introducing Breakpoints window
This commit is contained in:
parent
edab431a1f
commit
d636719775
|
@ -9,9 +9,16 @@ app {
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
(menu) Debug
|
(menu) Debug
|
||||||
|
breakpoints Breakpoints
|
||||||
console Console
|
console Console
|
||||||
cpu CPU
|
cpu CPU
|
||||||
memory Memory
|
memory Memory
|
||||||
|
# VIP section
|
||||||
|
backgrounds Backgrounds
|
||||||
|
characters Characters
|
||||||
|
frame_buffers Frame buffers
|
||||||
|
objects Objects
|
||||||
|
worlds Worlds
|
||||||
}
|
}
|
||||||
|
|
||||||
file {
|
file {
|
||||||
|
@ -32,6 +39,35 @@ app {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Breakpoints window
|
||||||
|
breakpoints {
|
||||||
|
address Address
|
||||||
|
condition Condition
|
||||||
|
default_name New breakpoint
|
||||||
|
delete Delete
|
||||||
|
enabled Enabled
|
||||||
|
exception Exception
|
||||||
|
execute Execute
|
||||||
|
name Name
|
||||||
|
new New
|
||||||
|
read Read
|
||||||
|
title Breakpoints
|
||||||
|
type Type
|
||||||
|
write Write
|
||||||
|
|
||||||
|
error {
|
||||||
|
badliteral_a Error:{err.position}: Unable to process address "{err.text}"
|
||||||
|
badliteral_c Error:{err.position}: Unable to process literal "{err.text}"
|
||||||
|
badoperand Error:{err.position}: Invalid operand to operator "{err.text}"
|
||||||
|
badtoken Error:{err.position}: Unrecognized token "{err.text}"
|
||||||
|
earlyeof Error:{err.position}: Unexpected end of input
|
||||||
|
invalid Error:{err.position}: Token "{err.text}" cannot appear here
|
||||||
|
nesting Error:{err.position}: Expected "{err.other}", but found "{err.text}"
|
||||||
|
unexpected Error:{err.position}: Unexpected "{err.text}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
# Console window
|
# Console window
|
||||||
console {
|
console {
|
||||||
title Console
|
title Console
|
||||||
|
|
|
@ -1126,6 +1126,10 @@ static vbool cpuExecute(Vue *vue) {
|
||||||
vue->cpu.exception = 0xFF90;
|
vue->cpu.exception = 0xFF90;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* An application break was requested */
|
||||||
|
if (vue->breakCode != 0)
|
||||||
|
return VUE_TRUE;
|
||||||
|
|
||||||
/* Common processing */
|
/* Common processing */
|
||||||
if (vue->cpu.exception == 0) {
|
if (vue->cpu.exception == 0) {
|
||||||
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
|
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
|
||||||
|
|
|
@ -0,0 +1,618 @@
|
||||||
|
package app;
|
||||||
|
|
||||||
|
// Java imports
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.util.*;
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
// Project imports
|
||||||
|
import util.*;
|
||||||
|
import vue.*;
|
||||||
|
|
||||||
|
// Memory viewer and hex editor window
|
||||||
|
class BreakpointsWindow extends ChildWindow {
|
||||||
|
|
||||||
|
// Private fields
|
||||||
|
private Font font; // Display font
|
||||||
|
private ArrayList<Item> items; // List items
|
||||||
|
private int selectedIndex; // Selected list item
|
||||||
|
|
||||||
|
// UI components
|
||||||
|
private JButton btnDelete; // Delete button
|
||||||
|
private JButton btnNew; // New button
|
||||||
|
private JCheckBox chkEnabled; // Enabled check box
|
||||||
|
private JCheckBox chkException; // Exception check box
|
||||||
|
private JCheckBox chkExecute; // Execute check box
|
||||||
|
private JCheckBox chkRead; // Read check box
|
||||||
|
private JCheckBox chkWrite; // Write check box
|
||||||
|
private JPanel client; // Client area
|
||||||
|
private JLabel errAddress; // Address error text
|
||||||
|
private JLabel errCondition; // Condition error text
|
||||||
|
private JPanel lstBreakpoints; // Breakpoint list
|
||||||
|
private JPanel spacer; // List termination spacer
|
||||||
|
private JTextField txtAddress; // Address text box
|
||||||
|
private JTextField txtCondition; // Condition text box
|
||||||
|
private JTextField txtName; // Name text box
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constants //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Address error keys
|
||||||
|
private static final String[] KEYS_ADDRESS = {
|
||||||
|
null , // NONE
|
||||||
|
"breakpoints.error.badliteral_a", // BADLITERAL
|
||||||
|
null , // BADOPERAND
|
||||||
|
null , // BADTOKEN
|
||||||
|
"breakpoints.error.earlyeof" , // EARLYEOF
|
||||||
|
null , // INVALID
|
||||||
|
null , // NESTING
|
||||||
|
"breakpoints.error.unexpected" // UNEXPECTED
|
||||||
|
};
|
||||||
|
|
||||||
|
// Condition error keys
|
||||||
|
private static final String[] KEYS_CONDITION = {
|
||||||
|
null , // NONE
|
||||||
|
"breakpoints.error.badliteral_c", // BADLITERAL
|
||||||
|
"breakpoints.error.badoperand" , // BADOPERAND
|
||||||
|
"breakpoints.error.badtoken" , // BADTOKEN
|
||||||
|
"breakpoints.error.earlyeof" , // EARLYEOF
|
||||||
|
"breakpoints.error.invalid" , // INVALID
|
||||||
|
"breakpoints.error.nesting" , // NESTING
|
||||||
|
"breakpoints.error.unexpected" // UNEXPECTED
|
||||||
|
};
|
||||||
|
|
||||||
|
// List constraints
|
||||||
|
private static final GridBagConstraints ENABLED;
|
||||||
|
private static final GridBagConstraints NAME;
|
||||||
|
private static final GridBagConstraints SPACER;
|
||||||
|
private static final GridBagConstraints STATUS;
|
||||||
|
|
||||||
|
// Static initializer
|
||||||
|
static {
|
||||||
|
ENABLED = new GridBagConstraints();
|
||||||
|
ENABLED.anchor = GridBagConstraints.CENTER;
|
||||||
|
ENABLED.insets = new Insets(1, 2, 1, 2);
|
||||||
|
NAME = new GridBagConstraints();
|
||||||
|
NAME.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
NAME.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
NAME.insets = new Insets(1, 0, 1, 2);
|
||||||
|
NAME.weightx = 1;
|
||||||
|
SPACER = new GridBagConstraints();
|
||||||
|
SPACER.fill = GridBagConstraints.BOTH;
|
||||||
|
SPACER.gridheight = GridBagConstraints.REMAINDER;
|
||||||
|
SPACER.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
SPACER.weighty = 1;
|
||||||
|
STATUS = new GridBagConstraints();
|
||||||
|
STATUS.fill = GridBagConstraints.BOTH;
|
||||||
|
STATUS.insets = new Insets(0, 0, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Classes //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Text box commit
|
||||||
|
private interface Commit {
|
||||||
|
void run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// List item
|
||||||
|
private class Item {
|
||||||
|
Breakpoint breakpoint;
|
||||||
|
JCheckBox chkEnabled;
|
||||||
|
JLabel lblName;
|
||||||
|
JLabel lblStatus;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Item(Breakpoint brk) {
|
||||||
|
var that = this;
|
||||||
|
breakpoint = brk;
|
||||||
|
brk.setFetch(false);
|
||||||
|
chkEnabled = new JCheckBox();
|
||||||
|
chkEnabled.setBorder(null);
|
||||||
|
chkEnabled.setFocusable(false);
|
||||||
|
chkEnabled.addActionListener(e->onEnabled(that));
|
||||||
|
parent.app.localizer.add(chkEnabled,"null","breakpoints.enabled");
|
||||||
|
lblName = new JLabel(brk.getName());
|
||||||
|
lblStatus = new JLabel("", SwingConstants.CENTER);
|
||||||
|
refresh(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the display
|
||||||
|
void refresh(Instruction inst, Access acc) {
|
||||||
|
if (
|
||||||
|
breakpoint.getAddressError ().code != Breakpoint.NONE ||
|
||||||
|
breakpoint.getConditionError().code != Breakpoint.NONE
|
||||||
|
) lblStatus.setText("\u00d7"); // Times symbol
|
||||||
|
else if (breakpoint.any() && breakpoint.evaluate(inst, acc))
|
||||||
|
lblStatus.setText("\u2605"); // Star
|
||||||
|
else lblStatus.setText(" ");
|
||||||
|
int lineHeight = lblStatus.getPreferredSize().height;
|
||||||
|
lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether or not the item has focus
|
||||||
|
void setFocused(boolean focused) {
|
||||||
|
var color = focused ? SystemColor.textHighlightText :
|
||||||
|
SystemColor.windowText;
|
||||||
|
lblStatus.setForeground(color);
|
||||||
|
lblName .setForeground(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructors //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Default constructor
|
||||||
|
BreakpointsWindow(MainWindow parent) {
|
||||||
|
super(parent, "breakpoints.title");
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
font = new Font(Util.fontFamily(new String[]
|
||||||
|
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
|
||||||
|
items = new ArrayList<Item>();
|
||||||
|
selectedIndex = -1;
|
||||||
|
|
||||||
|
// Configure client area
|
||||||
|
client = new JPanel(new BorderLayout());
|
||||||
|
client.setBackground(SystemColor.control);
|
||||||
|
client.setFocusable(true);
|
||||||
|
client.setPreferredSize(new Dimension(320, 240));
|
||||||
|
|
||||||
|
// Configure breakpoint list
|
||||||
|
lstBreakpoints = new JPanel(new GridBagLayout()) {
|
||||||
|
public void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
onPaint((Graphics2D) g, getWidth(), getHeight());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lstBreakpoints.setFocusable(true);
|
||||||
|
lstBreakpoints.setBackground(SystemColor.window);
|
||||||
|
lstBreakpoints.addFocusListener(Util.onFocus(
|
||||||
|
e->lstBreakpoints.repaint(), e->lstBreakpoints.repaint()));
|
||||||
|
lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null));
|
||||||
|
var scr = new JScrollPane(lstBreakpoints);
|
||||||
|
client.add(scr, BorderLayout.CENTER);
|
||||||
|
spacer = new JPanel(null);
|
||||||
|
spacer.setOpaque(false);
|
||||||
|
lstBreakpoints.add(spacer, SPACER);
|
||||||
|
|
||||||
|
// Configure properties pane
|
||||||
|
var props = new JPanel(new GridBagLayout());
|
||||||
|
props.setBackground(SystemColor.control);
|
||||||
|
props.addMouseListener(
|
||||||
|
Util.onMouse(e->client.requestFocus(), null));
|
||||||
|
client.add(props, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
// Configure properties
|
||||||
|
label(props, "breakpoints.name", 2);
|
||||||
|
txtName = textBox(props, false, 2, ()->onName());
|
||||||
|
//label(props, "breakpoints.enabled", 0);
|
||||||
|
//chkEnabled = checkBox(props, null, true);
|
||||||
|
chkEnabled = new JCheckBox();
|
||||||
|
chkEnabled.addActionListener(e->onEnabled(null));
|
||||||
|
label(props, "breakpoints.type", 0);
|
||||||
|
chkExecute = checkBox(props, "breakpoints.execute" , false);
|
||||||
|
chkExecute .addActionListener(e->onType(chkExecute ));
|
||||||
|
chkRead = checkBox(props, "breakpoints.read" , false);
|
||||||
|
chkRead .addActionListener(e->onType(chkRead ));
|
||||||
|
chkWrite = checkBox(props, "breakpoints.write" , false);
|
||||||
|
chkWrite .addActionListener(e->onType(chkWrite ));
|
||||||
|
chkException = checkBox(props, "breakpoints.exception", true );
|
||||||
|
chkException.addActionListener(e->onType(chkException));
|
||||||
|
label(props, "breakpoints.address", 0);
|
||||||
|
txtAddress = textBox(props, true, 0, ()->onAddress());
|
||||||
|
errAddress = error(props);
|
||||||
|
label(props, "breakpoints.condition", 0);
|
||||||
|
txtCondition = textBox(props, true, 0, ()->onCondition());
|
||||||
|
errCondition = error(props);
|
||||||
|
|
||||||
|
// Configure buttons
|
||||||
|
var buttons = new JPanel(new GridBagLayout());
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
props.add(buttons, gbc);
|
||||||
|
var fill = new JPanel(null);
|
||||||
|
fill.setOpaque(false);
|
||||||
|
fill.addMouseListener(Util.onMouse(e->onDebug(e), null));
|
||||||
|
gbc = new GridBagConstraints();
|
||||||
|
gbc.fill = GridBagConstraints.BOTH;
|
||||||
|
gbc.weightx = 1;
|
||||||
|
buttons.add(fill, gbc);
|
||||||
|
btnDelete = button(buttons, "breakpoints.delete", false);
|
||||||
|
btnDelete.setEnabled(false);
|
||||||
|
btnDelete.addActionListener(e->onDelete());
|
||||||
|
btnNew = button(buttons, "breakpoints.new", true);
|
||||||
|
btnNew.addActionListener(e->onNew());
|
||||||
|
|
||||||
|
// Configure component
|
||||||
|
setContentPane(client);
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Package Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Update the display
|
||||||
|
void refresh() {
|
||||||
|
var inst = new Instruction(parent.vue.read(
|
||||||
|
parent.vue.getRegister(Vue.PC, true), Vue.S32));
|
||||||
|
var acc = inst.access(parent.vue);
|
||||||
|
for (var item : items)
|
||||||
|
item.refresh(inst, acc);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Event Handlers //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Address text box commit
|
||||||
|
private void onAddress() {
|
||||||
|
if (selectedIndex == -1)
|
||||||
|
return;
|
||||||
|
var item = items.get(selectedIndex);
|
||||||
|
item.breakpoint.setAddresses(txtAddress.getText());
|
||||||
|
checkErrors(item);
|
||||||
|
item.refresh(null, null);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition text box commit
|
||||||
|
private void onCondition() {
|
||||||
|
if (selectedIndex == -1)
|
||||||
|
return;
|
||||||
|
var item = items.get(selectedIndex);
|
||||||
|
item.breakpoint.setCondition(txtCondition.getText());
|
||||||
|
checkErrors(item);
|
||||||
|
item.refresh(null, null);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug interaction
|
||||||
|
private void onDebug(MouseEvent e) {
|
||||||
|
client.requestFocus();
|
||||||
|
if (
|
||||||
|
selectedIndex == -1 ||
|
||||||
|
e.getButton() != MouseEvent.BUTTON1 ||
|
||||||
|
(e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == 0
|
||||||
|
) return;
|
||||||
|
var brk = items.get(selectedIndex).breakpoint;
|
||||||
|
System.out.println("Address");
|
||||||
|
System.out.println(brk.getAddresses());
|
||||||
|
System.out.println(brk.debugRanges());
|
||||||
|
System.out.println("Condition");
|
||||||
|
System.out.println(brk.getCondition());
|
||||||
|
System.out.println(brk.debugTokens());
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete button click
|
||||||
|
private void onDelete() {
|
||||||
|
var item = items.remove(selectedIndex);
|
||||||
|
parent.vue.remove(item.breakpoint);
|
||||||
|
lstBreakpoints.remove(item.chkEnabled);
|
||||||
|
lstBreakpoints.remove(item.lblName);
|
||||||
|
lstBreakpoints.remove(item.lblStatus);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
selectedIndex = -1;
|
||||||
|
select(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled check box toggle
|
||||||
|
private void onEnabled(Item item) {
|
||||||
|
boolean enabled;
|
||||||
|
|
||||||
|
// Clicked from the properties pane
|
||||||
|
if (item == null) {
|
||||||
|
enabled = chkEnabled.isSelected();
|
||||||
|
item = items.get(selectedIndex);
|
||||||
|
item.chkEnabled.setSelected(enabled);
|
||||||
|
client.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clicked from the breakpoints list
|
||||||
|
else {
|
||||||
|
enabled = item.chkEnabled.isSelected();
|
||||||
|
if (items.indexOf(item) == selectedIndex)
|
||||||
|
chkEnabled.setSelected(enabled);
|
||||||
|
lstBreakpoints.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common processing
|
||||||
|
item.breakpoint.setEnabled(enabled);
|
||||||
|
item.refresh(null, null);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// List mouse press
|
||||||
|
private void onMouseDown(MouseEvent e) {
|
||||||
|
int count = items.size();
|
||||||
|
|
||||||
|
// Left clicking
|
||||||
|
if (count > 0 && e.getButton() == MouseEvent.BUTTON1) {
|
||||||
|
int newIndex = e.getY() / items.get(0).lblStatus.getHeight();
|
||||||
|
if (newIndex < count)
|
||||||
|
select(newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update layout
|
||||||
|
lstBreakpoints.requestFocus();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name text box commit
|
||||||
|
private void onName() {
|
||||||
|
if (selectedIndex == -1)
|
||||||
|
return;
|
||||||
|
var item = items.get(selectedIndex);
|
||||||
|
item.lblName.setText(item.breakpoint.setName(txtName.getText()));
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
item.refresh(null, null);
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// New button click
|
||||||
|
private void onNew() {
|
||||||
|
|
||||||
|
var brk = parent.vue.breakpoint();
|
||||||
|
brk.setEnabled(true);
|
||||||
|
brk.setName (parent.app.localizer.get("breakpoints.default_name"));
|
||||||
|
|
||||||
|
var item = new Item(brk);
|
||||||
|
item.chkEnabled.setSelected(true);
|
||||||
|
item.lblName .setText(brk.getName());
|
||||||
|
items.add(item);
|
||||||
|
|
||||||
|
int count = items.size();
|
||||||
|
boolean first = count == 1;
|
||||||
|
lstBreakpoints.remove(spacer);
|
||||||
|
lstBreakpoints.add(item.chkEnabled, ENABLED);
|
||||||
|
lstBreakpoints.add(item.lblStatus , STATUS );
|
||||||
|
lstBreakpoints.add(item.lblName , NAME );
|
||||||
|
lstBreakpoints.add(spacer , SPACER );
|
||||||
|
|
||||||
|
select(count - 1);
|
||||||
|
lstBreakpoints.requestFocus();
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// List paint
|
||||||
|
private void onPaint(Graphics2D g, int width, int height) {
|
||||||
|
|
||||||
|
// There is no selected item
|
||||||
|
if (selectedIndex == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Select the display colors
|
||||||
|
var item = items.get(selectedIndex);
|
||||||
|
if (lstBreakpoints.isFocusOwner()) {
|
||||||
|
g.setColor(SystemColor.textHighlight);
|
||||||
|
item.setFocused(true);
|
||||||
|
} else {
|
||||||
|
g.setColor(SystemColor.control);
|
||||||
|
item.setFocused(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the selection background
|
||||||
|
height = item.lblStatus.getHeight();
|
||||||
|
g.fillRect(0, selectedIndex * height, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type check box toggle
|
||||||
|
private void onType(JCheckBox chk) {
|
||||||
|
var item = items.get(selectedIndex);
|
||||||
|
var brk = item.breakpoint;
|
||||||
|
boolean selected = chk.isSelected();
|
||||||
|
|
||||||
|
// Configure controls
|
||||||
|
if (chk == chkException)
|
||||||
|
brk.setException(selected);
|
||||||
|
else if (chk == chkExecute )
|
||||||
|
brk.setExecute (selected);
|
||||||
|
else if (chk == chkRead )
|
||||||
|
brk.setRead (selected);
|
||||||
|
else if (chk == chkWrite )
|
||||||
|
brk.setWrite (selected);
|
||||||
|
|
||||||
|
// Common processing
|
||||||
|
item.refresh(null, null);
|
||||||
|
client.requestFocus();
|
||||||
|
lstBreakpoints.revalidate();
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Add a button to the form
|
||||||
|
private JButton button(JPanel panel, String key, boolean last) {
|
||||||
|
var btn = new JButton();
|
||||||
|
if (key != null)
|
||||||
|
parent.app.localizer.add(btn, key);
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.WEST;
|
||||||
|
gbc.insets = new Insets(0, 0, 2, 2);
|
||||||
|
if (last)
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
panel.add(btn, gbc);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a check box to the form
|
||||||
|
private JCheckBox checkBox(JPanel panel, String key, boolean last) {
|
||||||
|
var chk = new JCheckBox();
|
||||||
|
if (key != null)
|
||||||
|
parent.app.localizer.add(chk, key);
|
||||||
|
chk.setBorder(null);
|
||||||
|
chk.setEnabled(false);
|
||||||
|
chk.setFocusable(false);
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.WEST;
|
||||||
|
gbc.insets = new Insets(0, 0, 2, last ? 2 : 6);
|
||||||
|
if (last)
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
panel.add(chk, gbc);
|
||||||
|
return chk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for errors in the selected breakpoint
|
||||||
|
private void checkErrors(Item item) {
|
||||||
|
var brk = item.breakpoint;
|
||||||
|
var loc = parent.app.localizer;
|
||||||
|
|
||||||
|
// Check for address errors
|
||||||
|
var err = brk.getAddressError();
|
||||||
|
if (err.code != Breakpoint.NONE) {
|
||||||
|
loc.add(errAddress, KEYS_ADDRESS[err.code]);
|
||||||
|
loc.put(errAddress, "err.position",
|
||||||
|
Integer.toString(err.position));
|
||||||
|
loc.put(errAddress, "err.text", err.text);
|
||||||
|
errAddress.setVisible(true);
|
||||||
|
} else errAddress.setVisible(false);
|
||||||
|
|
||||||
|
// Check for condition errors
|
||||||
|
err = brk.getConditionError();
|
||||||
|
if (err.code != Breakpoint.NONE) {
|
||||||
|
loc.add(errCondition, KEYS_CONDITION[err.code]);
|
||||||
|
loc.put(errCondition, "err.position",
|
||||||
|
Integer.toString(err.position));
|
||||||
|
loc.put(errCondition, "err.text", err.text);
|
||||||
|
if (err.code == Breakpoint.NESTING)
|
||||||
|
loc.put(errCondition, "err.other",
|
||||||
|
err.text.equals(")") ? "]" : ")");
|
||||||
|
errCondition.setVisible(true);
|
||||||
|
} else errCondition.setVisible(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error label to the form
|
||||||
|
private JLabel error(JPanel panel) {
|
||||||
|
var lbl = new JLabel();
|
||||||
|
lbl.setForeground(Color.red);
|
||||||
|
lbl.setVisible(false);
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.CENTER;
|
||||||
|
gbc.fill = GridBagConstraints.BOTH;
|
||||||
|
gbc.gridx = 1;
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gbc.insets = new Insets(0, 0, 2, 2);
|
||||||
|
panel.add(lbl, gbc);
|
||||||
|
return lbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a label to the form
|
||||||
|
private void label(JPanel panel, String key, int top) {
|
||||||
|
var lbl = new JLabel();
|
||||||
|
if (key != null)
|
||||||
|
parent.app.localizer.add(lbl, key);
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.WEST;
|
||||||
|
gbc.insets = new Insets(top, 2, 2, 4);
|
||||||
|
panel.add(lbl, gbc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select a particular list item
|
||||||
|
private void select(int newIndex) {
|
||||||
|
boolean enabled = newIndex != -1;
|
||||||
|
|
||||||
|
// The existing selection was de-selected
|
||||||
|
if (selectedIndex != -1 && newIndex != selectedIndex)
|
||||||
|
items.get(selectedIndex).setFocused(false);
|
||||||
|
|
||||||
|
// Selecting a new list item
|
||||||
|
if (newIndex != -1)
|
||||||
|
items.get(newIndex).setFocused(true);
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
selectedIndex = newIndex;
|
||||||
|
|
||||||
|
// Update control properties
|
||||||
|
if (enabled) {
|
||||||
|
var brk = items.get(selectedIndex).breakpoint;
|
||||||
|
chkEnabled .setSelected(brk.isEnabled ());
|
||||||
|
chkExecute .setSelected(brk.getExecute ());
|
||||||
|
chkRead .setSelected(brk.getRead ());
|
||||||
|
chkWrite .setSelected(brk.getWrite ());
|
||||||
|
chkException.setSelected(brk.getException());
|
||||||
|
txtAddress .setText (brk.getAddresses());
|
||||||
|
txtCondition.setText (brk.getCondition());
|
||||||
|
txtName .setText (brk.getName ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear control properties
|
||||||
|
else {
|
||||||
|
chkEnabled .setSelected(false);
|
||||||
|
chkExecute .setSelected(false);
|
||||||
|
chkRead .setSelected(false);
|
||||||
|
chkWrite .setSelected(false);
|
||||||
|
chkException.setSelected(false);
|
||||||
|
errAddress .setText(""); errAddress .setVisible(false);
|
||||||
|
errCondition.setText(""); errCondition.setVisible(false);
|
||||||
|
txtAddress .setText("");
|
||||||
|
txtCondition.setText("");
|
||||||
|
txtName .setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable controls
|
||||||
|
btnDelete .setEnabled(enabled);
|
||||||
|
chkEnabled .setEnabled(enabled);
|
||||||
|
chkExecute .setEnabled(enabled);
|
||||||
|
chkException.setEnabled(enabled);
|
||||||
|
chkRead .setEnabled(enabled);
|
||||||
|
chkWrite .setEnabled(enabled);
|
||||||
|
txtAddress .setEnabled(enabled);
|
||||||
|
txtCondition.setEnabled(enabled);
|
||||||
|
txtName .setEnabled(enabled);
|
||||||
|
|
||||||
|
// Common processing
|
||||||
|
if (selectedIndex != -1)
|
||||||
|
checkErrors(items.get(selectedIndex));
|
||||||
|
lstBreakpoints.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a text box to the form
|
||||||
|
private JTextField textBox(JPanel panel, boolean mono, int top,
|
||||||
|
Commit handler) {
|
||||||
|
var txt = new JTextField();
|
||||||
|
txt.setEnabled(false);
|
||||||
|
if (mono)
|
||||||
|
txt.setFont(font);
|
||||||
|
txt.addActionListener(e->client.requestFocus());
|
||||||
|
txt.addFocusListener (Util.onFocus(null, e->handler.run()));
|
||||||
|
var gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.CENTER;
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
gbc.insets = new Insets(top, 0, 2, 2);
|
||||||
|
gbc.weightx = 1;
|
||||||
|
panel.add(txt, gbc);
|
||||||
|
return txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ class MainWindow extends JFrame {
|
||||||
private File romFile; // Currently loaded ROM file
|
private File romFile; // Currently loaded ROM file
|
||||||
|
|
||||||
// UI components
|
// UI components
|
||||||
|
private BreakpointsWindow breakpoints; // Breakpoints window
|
||||||
private JPanel client; // Common client container
|
private JPanel client; // Common client container
|
||||||
private ConsoleWindow console; // Console window
|
private ConsoleWindow console; // Console window
|
||||||
private CPUWindow cpu; // CPU window
|
private CPUWindow cpu; // CPU window
|
||||||
|
@ -94,6 +95,7 @@ class MainWindow extends JFrame {
|
||||||
// Configure child windows
|
// Configure child windows
|
||||||
desktop = new JDesktopPane();
|
desktop = new JDesktopPane();
|
||||||
desktop.setBackground(SystemColor.controlShadow);
|
desktop.setBackground(SystemColor.controlShadow);
|
||||||
|
desktop.add(breakpoints = new BreakpointsWindow(this));
|
||||||
desktop.add(console = new ConsoleWindow (this));
|
desktop.add(console = new ConsoleWindow (this));
|
||||||
desktop.add(cpu = new CPUWindow (this));
|
desktop.add(cpu = new CPUWindow (this));
|
||||||
desktop.add(memory = new MemoryWindow (this));
|
desktop.add(memory = new MemoryWindow (this));
|
||||||
|
@ -130,20 +132,52 @@ class MainWindow extends JFrame {
|
||||||
loc.add(mnuDebug, "app.debug.(menu)");
|
loc.add(mnuDebug, "app.debug.(menu)");
|
||||||
mnuDebug.setVisible(false);
|
mnuDebug.setVisible(false);
|
||||||
|
|
||||||
|
var mnuDebugBreakpoints = new JMenuItem();
|
||||||
|
loc.add(mnuDebugBreakpoints, "app.debug.breakpoints");
|
||||||
|
mnuDebugBreakpoints.addActionListener(e->breakpoints.setVisible(true));
|
||||||
|
mnuDebug.add(mnuDebugBreakpoints);
|
||||||
|
|
||||||
var mnuDebugConsole = new JMenuItem();
|
var mnuDebugConsole = new JMenuItem();
|
||||||
loc.add(mnuDebugConsole, "app.debug.console");
|
loc.add(mnuDebugConsole, "app.debug.console");
|
||||||
mnuDebugConsole.addActionListener(e->console.setVisible(true));
|
mnuDebugConsole.addActionListener(e->console.setVisible(true));
|
||||||
mnuDebug.add(mnuDebugConsole);
|
mnuDebug.add(mnuDebugConsole);
|
||||||
|
|
||||||
|
var mnuDebugCPU = new JMenuItem();
|
||||||
|
loc.add(mnuDebugCPU, "app.debug.cpu");
|
||||||
|
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
|
||||||
|
mnuDebug.add(mnuDebugCPU);
|
||||||
|
|
||||||
var mnuDebugMemory = new JMenuItem();
|
var mnuDebugMemory = new JMenuItem();
|
||||||
loc.add(mnuDebugMemory, "app.debug.memory");
|
loc.add(mnuDebugMemory, "app.debug.memory");
|
||||||
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
|
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
|
||||||
mnuDebug.add(mnuDebugMemory);
|
mnuDebug.add(mnuDebugMemory);
|
||||||
|
|
||||||
var mnuDebugCPU = new JMenuItem();
|
mnuDebug.addSeparator();
|
||||||
loc.add(mnuDebugCPU, "app.debug.cpu");
|
|
||||||
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
|
var mnuDebugBackgrounds = new JMenuItem();
|
||||||
mnuDebug.add(mnuDebugCPU);
|
mnuDebugBackgrounds.setEnabled(false);
|
||||||
|
loc.add(mnuDebugBackgrounds, "app.debug.backgrounds");
|
||||||
|
mnuDebug.add(mnuDebugBackgrounds);
|
||||||
|
|
||||||
|
var mnuDebugCharacters = new JMenuItem();
|
||||||
|
mnuDebugCharacters.setEnabled(false);
|
||||||
|
loc.add(mnuDebugCharacters, "app.debug.characters");
|
||||||
|
mnuDebug.add(mnuDebugCharacters);
|
||||||
|
|
||||||
|
var mnuDebugFrameBuffers = new JMenuItem();
|
||||||
|
mnuDebugFrameBuffers.setEnabled(false);
|
||||||
|
loc.add(mnuDebugFrameBuffers, "app.debug.frame_buffers");
|
||||||
|
mnuDebug.add(mnuDebugFrameBuffers);
|
||||||
|
|
||||||
|
var mnuDebugObjects = new JMenuItem();
|
||||||
|
mnuDebugObjects.setEnabled(false);
|
||||||
|
loc.add(mnuDebugObjects, "app.debug.objects");
|
||||||
|
mnuDebug.add(mnuDebugObjects);
|
||||||
|
|
||||||
|
var mnuDebugWorlds = new JMenuItem();
|
||||||
|
mnuDebugWorlds.setEnabled(false);
|
||||||
|
loc.add(mnuDebugWorlds, "app.debug.worlds");
|
||||||
|
mnuDebug.add(mnuDebugWorlds);
|
||||||
|
|
||||||
return mnuDebug;
|
return mnuDebug;
|
||||||
}
|
}
|
||||||
|
@ -192,6 +226,7 @@ class MainWindow extends JFrame {
|
||||||
|
|
||||||
// Refresh all debug views
|
// Refresh all debug views
|
||||||
void refreshDebug(boolean seekToPC) {
|
void refreshDebug(boolean seekToPC) {
|
||||||
|
breakpoints.refresh();
|
||||||
cpu .refresh(seekToPC);
|
cpu .refresh(seekToPC);
|
||||||
memory .refresh();
|
memory .refresh();
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,8 @@ class Register {
|
||||||
parent.add(gbc, txtValue);
|
parent.add(gbc, txtValue);
|
||||||
|
|
||||||
// Value changed
|
// Value changed
|
||||||
txtValue.addActionListener(e->{
|
txtValue.addActionListener(e->parent.requestFocus());
|
||||||
|
txtValue.addFocusListener(Util.onFocus(null, e->{
|
||||||
String text = txtValue.getText();
|
String text = txtValue.getText();
|
||||||
int val = value;
|
int val = value;
|
||||||
try { switch (mode) {
|
try { switch (mode) {
|
||||||
|
@ -112,7 +113,7 @@ class Register {
|
||||||
Float.floatToIntBits(Float.parseFloat(text) ); break;
|
Float.floatToIntBits(Float.parseFloat(text) ); break;
|
||||||
}} catch (Exception x) { }
|
}} catch (Exception x) { }
|
||||||
setValue(val);
|
setValue(val);
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Expansion controls
|
// Expansion controls
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -208,10 +209,9 @@ class Register {
|
||||||
|
|
||||||
// Value text box
|
// Value text box
|
||||||
var txt = new JTextField();
|
var txt = new JTextField();
|
||||||
txt.addActionListener(e->{
|
txt.addActionListener(e->parent.requestFocus());
|
||||||
txt.setText((String) txt.getClientProperty("text"));
|
txt.addFocusListener(Util.onFocus(null,
|
||||||
parent.requestFocus();
|
e->txt.setText((String) txt.getClientProperty("text"))));
|
||||||
});
|
|
||||||
txt.putClientProperty("index",
|
txt.putClientProperty("index",
|
||||||
x == 0 ? Vue.JUMP_FROM : Vue.JUMP_TO);
|
x == 0 ? Vue.JUMP_FROM : Vue.JUMP_TO);
|
||||||
txt.setBorder(null);
|
txt.setBorder(null);
|
||||||
|
@ -479,13 +479,13 @@ class Register {
|
||||||
controls.add(ctrl);
|
controls.add(ctrl);
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
ctrl.addActionListener(e->{
|
ctrl.addActionListener(e->parent.requestFocus());
|
||||||
parent.requestFocus();
|
ctrl.addFocusListener(Util.onFocus(null, e->{
|
||||||
int val = value >> bit & mask;
|
int val = value >> bit & mask;
|
||||||
try { val = Integer.parseInt(ctrl.getText(), hex ? 16 : 10); }
|
try { val = Integer.parseInt(ctrl.getText(), hex ? 16 : 10); }
|
||||||
catch (Exception x) { }
|
catch (Exception x) { }
|
||||||
setValue(value & ~(mask << bit) | (val & mask) << bit);
|
setValue(value & ~(mask << bit) | (val & mask) << bit);
|
||||||
});
|
}));
|
||||||
|
|
||||||
// Configure expansion area
|
// Configure expansion area
|
||||||
var label = new JLabel(name);
|
var label = new JLabel(name);
|
||||||
|
|
|
@ -265,6 +265,7 @@ public class Localizer {
|
||||||
"Required key not found: 'locale.id'");
|
"Required key not found: 'locale.id'");
|
||||||
if (!ret.containsKey("locale.name")) throw new RuntimeException(
|
if (!ret.containsKey("locale.name")) throw new RuntimeException(
|
||||||
"Required key not found: 'locale.name'");
|
"Required key not found: 'locale.name'");
|
||||||
|
ret.put("null", "");
|
||||||
return new Locale(ret);
|
return new Locale(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -533,10 +533,11 @@ static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check all breakpoints
|
// Check all breakpoints
|
||||||
|
int32_t fetch = breakType == BRK_READ && vue->cpu.fetch != -1 ? 1 : 0;
|
||||||
for (int x = 0; x < core->numBreakpoints; x++) {
|
for (int x = 0; x < core->numBreakpoints; x++) {
|
||||||
Breakpoint *brk = &core->breakpoints[x];
|
Breakpoint *brk = &core->breakpoints[x];
|
||||||
if (
|
if (
|
||||||
brk->enabled &&
|
brk->enabled && (brk->fetch || !fetch) &&
|
||||||
(breakType == 0 || (breakType & brk->hooks) != 0) &&
|
(breakType == 0 || (breakType & brk->hooks) != 0) &&
|
||||||
(!ranged || brkInRange(brk, start, end)) &&
|
(!ranged || brkInRange(brk, start, end)) &&
|
||||||
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
|
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class Breakpoint {
|
||||||
private Error conditionError; // Condition parsing error
|
private Error conditionError; // Condition parsing error
|
||||||
private int dataType; // Condition evaluation data type
|
private int dataType; // Condition evaluation data type
|
||||||
private int depth; // Stack size for condition evaluation
|
private int depth; // Stack size for condition evaluation
|
||||||
|
private boolean fetch; // Breakpoint traps fetch reads
|
||||||
private int hooks; // Applied breakpoint types
|
private int hooks; // Applied breakpoint types
|
||||||
private boolean isEnabled; // Breakpoint is active
|
private boolean isEnabled; // Breakpoint is active
|
||||||
private String name; // Display name
|
private String name; // Display name
|
||||||
|
@ -452,6 +453,7 @@ public class Breakpoint {
|
||||||
addresses = "";
|
addresses = "";
|
||||||
condition = "";
|
condition = "";
|
||||||
conditionError = new Error(NONE, 0, "");
|
conditionError = new Error(NONE, 0, "");
|
||||||
|
fetch = true;
|
||||||
name = "";
|
name = "";
|
||||||
ranges = new int[0][];
|
ranges = new int[0][];
|
||||||
tokens = new Token[0];
|
tokens = new Token[0];
|
||||||
|
@ -464,36 +466,54 @@ public class Breakpoint {
|
||||||
// Public Methods //
|
// Public Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Evaluate the condition against the emulation context
|
// Determine whether the breakpoint applies to any break scenarios
|
||||||
public boolean evaluate() {
|
public boolean any() {
|
||||||
|
return hooks != 0;
|
||||||
// Error checking
|
|
||||||
if (vue == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Retrieve state objects
|
|
||||||
Access acc = vue.getAccess ();
|
|
||||||
int breakType = vue.getBreakType ();
|
|
||||||
Instruction inst = vue.getInstruction();
|
|
||||||
|
|
||||||
// Error checking
|
|
||||||
if (!appliesTo(breakType))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check address ranges for execute
|
|
||||||
if (breakType == EXECUTE) {
|
|
||||||
int pc = vue.getRegister(Vue.PC, true);
|
|
||||||
if (!inRange(pc, pc + inst.size - 1))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check address ranges for read and write
|
// Evaluate the condition against the emulation context
|
||||||
else if (breakType == READ || breakType == WRITE &&
|
public boolean evaluate() {
|
||||||
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
|
return evaluate(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the condition against the emulation context
|
||||||
|
public boolean evaluate(Instruction inst, Access acc) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (vue == null || !isEnabled || hooks == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
boolean applies = false;
|
||||||
|
int pc = vue.getRegister(Vue.PC, true);
|
||||||
|
|
||||||
|
// Resolve state objects
|
||||||
|
if (inst == null)
|
||||||
|
inst = new Instruction(vue.read(pc, Vue.S32));
|
||||||
|
if (acc == null)
|
||||||
|
acc = inst.access(vue);
|
||||||
|
|
||||||
|
// Check whether the breakpoint applies to the current state
|
||||||
|
if ((hooks & EXCEPTION) != 0)
|
||||||
|
applies = applies || vue.getExceptionCode() != 0;
|
||||||
|
if ((hooks & EXECUTE) != 0)
|
||||||
|
applies = applies || inRange(pc, pc + inst.size - 1);
|
||||||
|
if ((hooks & (READ | WRITE)) != 0 && acc != null) switch (inst.id) {
|
||||||
|
case Vue.CAXI :
|
||||||
|
case Vue.IN_B : case Vue.IN_H : case Vue.IN_W :
|
||||||
|
case Vue.LD_B : case Vue.LD_H : case Vue.LD_W :
|
||||||
|
case Vue.OUT_B: case Vue.OUT_H: case Vue.OUT_W:
|
||||||
|
case Vue.ST_B : case Vue.ST_H : case Vue.ST_W :
|
||||||
|
applies = applies || inRange(acc.address,
|
||||||
|
acc.address + JavaVue.TYPE_SIZES[acc.type] - 1);
|
||||||
|
}
|
||||||
|
if (!applies)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Evaluate the condition
|
// Evaluate the condition
|
||||||
return isTrue(new int[depth], breakType, inst, acc);
|
if (acc == null)
|
||||||
|
acc = new Access();
|
||||||
|
return isTrue(new int[depth], 0, inst, acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the most recent address text
|
// Retrieve the most recent address text
|
||||||
|
@ -526,6 +546,11 @@ public class Breakpoint {
|
||||||
return (hooks & EXECUTE) != 0;
|
return (hooks & EXECUTE) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine whether the breakpoint hooks fetch reads
|
||||||
|
public boolean getFetch() {
|
||||||
|
return fetch;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine whether the breakpoint hooks reads
|
// Determine whether the breakpoint hooks reads
|
||||||
public boolean getRead() {
|
public boolean getRead() {
|
||||||
return (hooks & READ) != 0;
|
return (hooks & READ) != 0;
|
||||||
|
@ -662,7 +687,7 @@ public class Breakpoint {
|
||||||
} // x
|
} // x
|
||||||
|
|
||||||
// Unexpected end of input
|
// Unexpected end of input
|
||||||
if (mode == 0 && ranges.size() > 0) {
|
if (mode == 0 && (ranges.size() > 0 || first != null)) {
|
||||||
addressError = new Error(EARLYEOF, x + 1, "");
|
addressError = new Error(EARLYEOF, x + 1, "");
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.updateRanges(this);
|
vue.updateRanges(this);
|
||||||
|
@ -759,9 +784,16 @@ public class Breakpoint {
|
||||||
vue.updateState(this);
|
vue.updateState(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint hooks fetch reads
|
||||||
|
public void setFetch(boolean fetch) {
|
||||||
|
this.fetch = fetch;
|
||||||
|
if (vue != null)
|
||||||
|
vue.updateState(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Specify the display name
|
// Specify the display name
|
||||||
public void setName(String name) {
|
public String setName(String name) {
|
||||||
this.name = name == null ? "" : name;
|
return this.name = name == null ? "" : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specify whether the breakpoint hooks reads
|
// Specify whether the breakpoint hooks reads
|
||||||
|
@ -785,8 +817,9 @@ public class Breakpoint {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Determine whether the breakpoint applies to a break scenario
|
// Determine whether the breakpoint applies to a break scenario
|
||||||
boolean appliesTo(int breakType) {
|
boolean appliesTo(int breakType, boolean fetch) {
|
||||||
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
|
return isActive() && (this.fetch || !fetch) &&
|
||||||
|
(breakType == 0 || (breakType & hooks) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a typed evaluation of the condition expression
|
// Perform a typed evaluation of the condition expression
|
||||||
|
@ -1145,7 +1178,6 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Not Logical
|
// Not Logical
|
||||||
case NOT_L:
|
case NOT_L:
|
||||||
newType = BOOL;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Round to Nearest
|
// Round to Nearest
|
||||||
|
@ -1289,7 +1321,7 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Select the ID of the conversion operation
|
// Select the ID of the conversion operation
|
||||||
int cvt = 0;
|
int cvt = 0;
|
||||||
switch (tok.dataType) {
|
switch (dataType) {
|
||||||
case BOOL : cvt = BOOL_ ; break;
|
case BOOL : cvt = BOOL_ ; break;
|
||||||
case FLOAT : cvt = FLOAT_; break;
|
case FLOAT : cvt = FLOAT_; break;
|
||||||
case SIGNED : cvt = S32 ; break;
|
case SIGNED : cvt = S32 ; break;
|
||||||
|
@ -1305,7 +1337,7 @@ public class Breakpoint {
|
||||||
// Insert a conversion token
|
// Insert a conversion token
|
||||||
else {
|
else {
|
||||||
var imp = new Token(UNARY, -1, "");
|
var imp = new Token(UNARY, -1, "");
|
||||||
switch (tok.dataType) {
|
switch (dataType) {
|
||||||
case BOOL : imp.text = "bool" ; break;
|
case BOOL : imp.text = "bool" ; break;
|
||||||
case FLOAT : imp.text = "float"; break;
|
case FLOAT : imp.text = "float"; break;
|
||||||
case SIGNED : imp.text = "s32" ; break;
|
case SIGNED : imp.text = "s32" ; break;
|
||||||
|
@ -1347,7 +1379,7 @@ public class Breakpoint {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|
||||||
// Locate the bounds of the innermost nested group
|
// Locate the bounds of the innermost nested group
|
||||||
for (int x = 0; x < end; x++) {
|
for (int x = 0; x <= end; x++) {
|
||||||
var tok = tokens.get(x);
|
var tok = tokens.get(x);
|
||||||
if (tok.type == OPEN)
|
if (tok.type == OPEN)
|
||||||
start = x + 1;
|
start = x + 1;
|
||||||
|
@ -1447,7 +1479,7 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A group was not closed, or a binary operation was incomplete
|
// A group was not closed, or a binary operation was incomplete
|
||||||
if (!stack.empty() || mode == 0) {
|
if (!stack.empty() || mode == 0 && tokens.size() != 0) {
|
||||||
conditionError =
|
conditionError =
|
||||||
new Error(EARLYEOF, condition.length(), "");
|
new Error(EARLYEOF, condition.length(), "");
|
||||||
return false;
|
return false;
|
||||||
|
@ -1927,6 +1959,10 @@ public class Breakpoint {
|
||||||
if (conditionError.code != NONE)
|
if (conditionError.code != NONE)
|
||||||
return "Error";
|
return "Error";
|
||||||
|
|
||||||
|
// There is no condition
|
||||||
|
if (tokens.length == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
// Determine the maximum width of the text fields
|
// Determine the maximum width of the text fields
|
||||||
int max = 0;
|
int max = 0;
|
||||||
for (var tok : tokens)
|
for (var tok : tokens)
|
||||||
|
|
|
@ -443,7 +443,7 @@ class CPU {
|
||||||
|
|
||||||
// First unit
|
// First unit
|
||||||
if (fetch == 0) {
|
if (fetch == 0) {
|
||||||
inst.bits = access.value << 16;
|
inst.bits = access.value & 0xFFFF;
|
||||||
if (Instruction.size(access.value >> 10 & 0x3F) == 4) {
|
if (Instruction.size(access.value >> 10 & 0x3F) == 4) {
|
||||||
fetch = 1;
|
fetch = 1;
|
||||||
return false;
|
return false;
|
||||||
|
@ -452,7 +452,7 @@ class CPU {
|
||||||
|
|
||||||
// Second unit
|
// Second unit
|
||||||
else {
|
else {
|
||||||
inst.bits |= access.value & 0xFFFF;
|
inst.bits |= access.value << 16;
|
||||||
fetch = -1;
|
fetch = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,11 @@ public class Instruction {
|
||||||
// Default constructor
|
// Default constructor
|
||||||
public Instruction() { }
|
public Instruction() { }
|
||||||
|
|
||||||
|
// Decoding constructor
|
||||||
|
public Instruction(int bits) {
|
||||||
|
decode(bits);
|
||||||
|
}
|
||||||
|
|
||||||
// Cloning constructor
|
// Cloning constructor
|
||||||
Instruction(Instruction o) {
|
Instruction(Instruction o) {
|
||||||
this();
|
this();
|
||||||
|
@ -104,21 +109,61 @@ public class Instruction {
|
||||||
// Public Methods //
|
// Public Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Decode an instruction from a byte array
|
// Produce an access descriptor from the current instruction state
|
||||||
public void decode(byte[] data, int offset) {
|
public Access access(Vue vue) {
|
||||||
bits = (data[offset + 1] & 0xFF) << 24 | (data[offset] & 0xFF) << 16;
|
boolean read = false;
|
||||||
if (size(bits >>> 26) == 4)
|
var ret = new Access();
|
||||||
bits |= (data[offset + 3] & 0xFF) << 8 | data[offset + 2] & 0xFF;
|
ret.address = vue.getRegister(reg1, false) + disp;
|
||||||
decode(bits);
|
ret.type = Vue.S32;
|
||||||
|
|
||||||
|
// Configure descriptor by ID
|
||||||
|
switch (id) {
|
||||||
|
case Vue.CAXI : read = true; break;
|
||||||
|
case Vue.IN_B : ret.type = Vue.U8 ; read = true; break;
|
||||||
|
case Vue.IN_H : ret.type = Vue.U16; read = true; break;
|
||||||
|
case Vue.IN_W : read = true; break;
|
||||||
|
case Vue.LD_B : ret.type = Vue.S8 ; read = true; break;
|
||||||
|
case Vue.LD_H : ret.type = Vue.S16; read = true; break;
|
||||||
|
case Vue.LD_W : read = true; break;
|
||||||
|
case Vue.OUT_B: ret.type = Vue.U8 ; break;
|
||||||
|
case Vue.OUT_H: ret.type = Vue.U16; break;
|
||||||
|
case Vue.OUT_W: break;
|
||||||
|
case Vue.ST_B : ret.type = Vue.S8 ; break;
|
||||||
|
case Vue.ST_H : ret.type = Vue.S16; break;
|
||||||
|
case Vue.ST_W : break;
|
||||||
|
// TODO: Bit strings
|
||||||
|
default: return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode an instruction from its binary encoding
|
// Read the value currently on the bus
|
||||||
public void decode(int bits) {
|
if (read)
|
||||||
|
ret.value = vue.read(ret.address, ret.type);
|
||||||
|
|
||||||
|
// Select the value to write to the bus
|
||||||
|
else ret.value = vue.getRegister(id == Vue.CAXI &&
|
||||||
|
vue.getRegister(reg2, false) == vue.read(ret.address, Vue.S32) ?
|
||||||
|
30 : reg2, false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode an instruction from a byte array
|
||||||
|
public Instruction decode(byte[] data, int offset) {
|
||||||
|
return decode(
|
||||||
|
(data[offset + 0] & 0xFF) |
|
||||||
|
(data[offset + 1] & 0xFF) << 8 |
|
||||||
|
(data[offset + 2] & 0xFF) << 16 |
|
||||||
|
(data[offset + 3] & 0xFF) << 24
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode an instruction from its binary encoding (swapped halfwords)
|
||||||
|
public Instruction decode(int bits) {
|
||||||
byte extend; // Sign-extend the immediate operand
|
byte extend; // Sign-extend the immediate operand
|
||||||
int x; // Working variable
|
int x; // Working variable
|
||||||
|
|
||||||
// Configure instance fields
|
// Configure instance fields
|
||||||
this.bits = bits;
|
this.bits = bits = bits << 16 | bits >>> 16;
|
||||||
opcode = bits >> 26 & 63;
|
opcode = bits >> 26 & 63;
|
||||||
x = opcode << 1;
|
x = opcode << 1;
|
||||||
id = LOOKUP_OPCODE[x ];
|
id = LOOKUP_OPCODE[x ];
|
||||||
|
@ -169,6 +214,7 @@ public class Instruction {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,10 +286,11 @@ class JavaVue extends Vue {
|
||||||
|
|
||||||
// Check all breakpoints
|
// Check all breakpoints
|
||||||
int count = breakpoints.size();
|
int count = breakpoints.size();
|
||||||
|
boolean fetch = breakType == Breakpoint.READ && cpu.fetch != -1;
|
||||||
for (int x = 0; breakCode == 0 && x < count; x++) {
|
for (int x = 0; breakCode == 0 && x < count; x++) {
|
||||||
var brk = breakpoints.get(x);
|
var brk = breakpoints.get(x);
|
||||||
if (
|
if (
|
||||||
brk.appliesTo(breakType) &&
|
brk.appliesTo(breakType, fetch) &&
|
||||||
(!ranged || brk.inRange(start, end)) &&
|
(!ranged || brk.inRange(start, end)) &&
|
||||||
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
|
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
|
||||||
) breakCode = x + 1;
|
) breakCode = x + 1;
|
||||||
|
|
|
@ -35,6 +35,7 @@ typedef struct {
|
||||||
// Precompiled breakpoint
|
// Precompiled breakpoint
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t enabled; // The breakpoint is enabled
|
int32_t enabled; // The breakpoint is enabled
|
||||||
|
int32_t fetch; // Breakpoint traps fetch reads
|
||||||
int32_t hooks; // Events hooked by the breakpoint
|
int32_t hooks; // Events hooked by the breakpoint
|
||||||
int32_t numRanges; // Number of address ranges
|
int32_t numRanges; // Number of address ranges
|
||||||
int32_t numTokens; // Number of condition tokens
|
int32_t numTokens; // Number of condition tokens
|
||||||
|
@ -106,7 +107,9 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_breakpoint
|
||||||
core->numBreakpoints++;
|
core->numBreakpoints++;
|
||||||
core->breakpoints =
|
core->breakpoints =
|
||||||
realloc(core->breakpoints,core->numBreakpoints * sizeof (Breakpoint));
|
realloc(core->breakpoints,core->numBreakpoints * sizeof (Breakpoint));
|
||||||
memset(&core->breakpoints[core->numBreakpoints-1], 0,sizeof (Breakpoint));
|
Breakpoint *brk = &core->breakpoints[core->numBreakpoints - 1];
|
||||||
|
memset(brk, 0 ,sizeof (Breakpoint));
|
||||||
|
brk->fetch = VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
|
@ -368,10 +371,11 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_updateRanges
|
||||||
// A breakpoint's enabled/hook state has changed
|
// A breakpoint's enabled/hook state has changed
|
||||||
JNIEXPORT void JNICALL Java_vue_NativeVue_updateState
|
JNIEXPORT void JNICALL Java_vue_NativeVue_updateState
|
||||||
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled,
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled,
|
||||||
jint hooks) {
|
jboolean fetch, jint hooks) {
|
||||||
Core *core = *(Core **)&handle;
|
Core *core = *(Core **)&handle;
|
||||||
Breakpoint *brk = &core->breakpoints[index];
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
brk->enabled = enabled;
|
brk->enabled = enabled;
|
||||||
|
brk->fetch = fetch;
|
||||||
brk->hooks = hooks;
|
brk->hooks = hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,13 +155,13 @@ class NativeVue extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's enabled/hook state has changed
|
// A breakpoint's enabled/hook state has changed
|
||||||
private native void updateState(long handle, int index, boolean isEnabled,
|
private native void updateState(long handle, int index, boolean enabled,
|
||||||
int hooks);
|
boolean fetch, int hooks);
|
||||||
void updateState(Breakpoint brk) {
|
void updateState(Breakpoint brk) {
|
||||||
super.updateState(brk);
|
super.updateState(brk);
|
||||||
int index = breakpoints.indexOf(brk);
|
int index = breakpoints.indexOf(brk);
|
||||||
updateState(handle, index, brk.isEnabled(),
|
updateState(handle, index,
|
||||||
brk.getHooks());
|
brk.isEnabled(), brk.getFetch(), brk.getHooks());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's condition tokens have changed
|
// A breakpoint's condition tokens have changed
|
||||||
|
|
|
@ -183,25 +183,20 @@ public abstract class Vue {
|
||||||
|
|
||||||
// Evaluate an expression
|
// Evaluate an expression
|
||||||
public Object evaluate(String expression) {
|
public Object evaluate(String expression) {
|
||||||
|
var inst = new Instruction(read(getRegister(PC, true), S32));
|
||||||
var brk = new Breakpoint(this);
|
var brk = new Breakpoint(this);
|
||||||
brk.setCondition(expression);
|
brk.setCondition(expression);
|
||||||
brk.setEnabled (true);
|
brk.setEnabled (true);
|
||||||
return brk.evaluateTyped(new int[brk.getDepth()], 0,
|
return brk.evaluateTyped(
|
||||||
getInstruction(), getAccess());
|
new int[brk.getDepth()], 0, inst, inst.access(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a snapshot of the current state's memory access
|
|
||||||
public abstract Access getAccess();
|
|
||||||
|
|
||||||
// Retrieve the most recent applicaiton break code
|
// Retrieve the most recent applicaiton break code
|
||||||
public abstract int getBreakCode();
|
public abstract int getBreakCode();
|
||||||
|
|
||||||
// Retrieve the most recent exception code
|
// Retrieve the most recent exception code
|
||||||
public abstract int getExceptionCode();
|
public abstract int getExceptionCode();
|
||||||
|
|
||||||
// Retrieve a snapshot of the current state's instruction
|
|
||||||
public abstract Instruction getInstruction();
|
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
public abstract int getRegister(int index, boolean system);
|
public abstract int getRegister(int index, boolean system);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue