Introducing Breakpoints window
This commit is contained in:
parent
edab431a1f
commit
d636719775
|
@ -8,26 +8,62 @@ locale {
|
|||
app {
|
||||
|
||||
debug {
|
||||
(menu) Debug
|
||||
console Console
|
||||
cpu CPU
|
||||
memory Memory
|
||||
(menu) Debug
|
||||
breakpoints Breakpoints
|
||||
console Console
|
||||
cpu CPU
|
||||
memory Memory
|
||||
# VIP section
|
||||
backgrounds Backgrounds
|
||||
characters Characters
|
||||
frame_buffers Frame buffers
|
||||
objects Objects
|
||||
worlds Worlds
|
||||
}
|
||||
|
||||
file {
|
||||
(menu) File
|
||||
debug_mode Debug mode
|
||||
exit Exit
|
||||
game_mode Game mode
|
||||
load_rom Load ROM...
|
||||
new_window New window
|
||||
(menu) File
|
||||
debug_mode Debug mode
|
||||
exit Exit
|
||||
game_mode Game mode
|
||||
load_rom Load ROM...
|
||||
new_window New window
|
||||
}
|
||||
|
||||
title {
|
||||
default PVB Emulator
|
||||
mixed {ctrl.number} {ctrl.filename} - {app.title.default}
|
||||
number {ctrl.number} {app.title.default}
|
||||
rom {ctrl.filename} - {app.title.default}
|
||||
default PVB Emulator
|
||||
mixed {ctrl.number} {ctrl.filename} - {app.title.default}
|
||||
number {ctrl.number} {app.title.default}
|
||||
rom {ctrl.filename} - {app.title.default}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1126,6 +1126,10 @@ static vbool cpuExecute(Vue *vue) {
|
|||
vue->cpu.exception = 0xFF90;
|
||||
}
|
||||
|
||||
/* An application break was requested */
|
||||
if (vue->breakCode != 0)
|
||||
return VUE_TRUE;
|
||||
|
||||
/* Common processing */
|
||||
if (vue->cpu.exception == 0) {
|
||||
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,13 +28,14 @@ class MainWindow extends JFrame {
|
|||
private File romFile; // Currently loaded ROM file
|
||||
|
||||
// UI components
|
||||
private JPanel client; // Common client container
|
||||
private ConsoleWindow console; // Console window
|
||||
private CPUWindow cpu; // CPU window
|
||||
private JDesktopPane desktop; // Container for child windows
|
||||
private MemoryWindow memory; // Memory window
|
||||
private JMenu mnuDebug; // Debug menu
|
||||
private JPanel video; // Video output
|
||||
private BreakpointsWindow breakpoints; // Breakpoints window
|
||||
private JPanel client; // Common client container
|
||||
private ConsoleWindow console; // Console window
|
||||
private CPUWindow cpu; // CPU window
|
||||
private JDesktopPane desktop; // Container for child windows
|
||||
private MemoryWindow memory; // Memory window
|
||||
private JMenu mnuDebug; // Debug menu
|
||||
private JPanel video; // Video output
|
||||
private JMenuItem mnuFileDebugMode; // File -> Debug mode
|
||||
private JMenuItem mnuFileGameMode; // File -> Game mode
|
||||
|
||||
|
@ -94,9 +95,10 @@ class MainWindow extends JFrame {
|
|||
// Configure child windows
|
||||
desktop = new JDesktopPane();
|
||||
desktop.setBackground(SystemColor.controlShadow);
|
||||
desktop.add(console = new ConsoleWindow(this));
|
||||
desktop.add(cpu = new CPUWindow (this));
|
||||
desktop.add(memory = new MemoryWindow (this));
|
||||
desktop.add(breakpoints = new BreakpointsWindow(this));
|
||||
desktop.add(console = new ConsoleWindow (this));
|
||||
desktop.add(cpu = new CPUWindow (this));
|
||||
desktop.add(memory = new MemoryWindow (this));
|
||||
|
||||
// Configure internal breakpoints
|
||||
brkStep = vue.breakpoint();
|
||||
|
@ -130,20 +132,52 @@ class MainWindow extends JFrame {
|
|||
loc.add(mnuDebug, "app.debug.(menu)");
|
||||
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();
|
||||
loc.add(mnuDebugConsole, "app.debug.console");
|
||||
mnuDebugConsole.addActionListener(e->console.setVisible(true));
|
||||
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();
|
||||
loc.add(mnuDebugMemory, "app.debug.memory");
|
||||
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
|
||||
mnuDebug.add(mnuDebugMemory);
|
||||
|
||||
var mnuDebugCPU = new JMenuItem();
|
||||
loc.add(mnuDebugCPU, "app.debug.cpu");
|
||||
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
|
||||
mnuDebug.add(mnuDebugCPU);
|
||||
mnuDebug.addSeparator();
|
||||
|
||||
var mnuDebugBackgrounds = new JMenuItem();
|
||||
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;
|
||||
}
|
||||
|
@ -192,8 +226,9 @@ class MainWindow extends JFrame {
|
|||
|
||||
// Refresh all debug views
|
||||
void refreshDebug(boolean seekToPC) {
|
||||
cpu .refresh(seekToPC);
|
||||
memory.refresh();
|
||||
breakpoints.refresh();
|
||||
cpu .refresh(seekToPC);
|
||||
memory .refresh();
|
||||
}
|
||||
|
||||
// A window has been added to or removed from the program state
|
||||
|
|
|
@ -101,7 +101,8 @@ class Register {
|
|||
parent.add(gbc, txtValue);
|
||||
|
||||
// Value changed
|
||||
txtValue.addActionListener(e->{
|
||||
txtValue.addActionListener(e->parent.requestFocus());
|
||||
txtValue.addFocusListener(Util.onFocus(null, e->{
|
||||
String text = txtValue.getText();
|
||||
int val = value;
|
||||
try { switch (mode) {
|
||||
|
@ -112,7 +113,7 @@ class Register {
|
|||
Float.floatToIntBits(Float.parseFloat(text) ); break;
|
||||
}} catch (Exception x) { }
|
||||
setValue(val);
|
||||
});
|
||||
}));
|
||||
|
||||
// Expansion controls
|
||||
switch (type) {
|
||||
|
@ -208,10 +209,9 @@ class Register {
|
|||
|
||||
// Value text box
|
||||
var txt = new JTextField();
|
||||
txt.addActionListener(e->{
|
||||
txt.setText((String) txt.getClientProperty("text"));
|
||||
parent.requestFocus();
|
||||
});
|
||||
txt.addActionListener(e->parent.requestFocus());
|
||||
txt.addFocusListener(Util.onFocus(null,
|
||||
e->txt.setText((String) txt.getClientProperty("text"))));
|
||||
txt.putClientProperty("index",
|
||||
x == 0 ? Vue.JUMP_FROM : Vue.JUMP_TO);
|
||||
txt.setBorder(null);
|
||||
|
@ -479,13 +479,13 @@ class Register {
|
|||
controls.add(ctrl);
|
||||
|
||||
// Event handlers
|
||||
ctrl.addActionListener(e->{
|
||||
parent.requestFocus();
|
||||
ctrl.addActionListener(e->parent.requestFocus());
|
||||
ctrl.addFocusListener(Util.onFocus(null, e->{
|
||||
int val = value >> bit & mask;
|
||||
try { val = Integer.parseInt(ctrl.getText(), hex ? 16 : 10); }
|
||||
catch (Exception x) { }
|
||||
setValue(value & ~(mask << bit) | (val & mask) << bit);
|
||||
});
|
||||
}));
|
||||
|
||||
// Configure expansion area
|
||||
var label = new JLabel(name);
|
||||
|
|
|
@ -265,6 +265,7 @@ public class Localizer {
|
|||
"Required key not found: 'locale.id'");
|
||||
if (!ret.containsKey("locale.name")) throw new RuntimeException(
|
||||
"Required key not found: 'locale.name'");
|
||||
ret.put("null", "");
|
||||
return new Locale(ret);
|
||||
}
|
||||
|
||||
|
|
|
@ -533,10 +533,11 @@ static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) {
|
|||
}
|
||||
|
||||
// Check all breakpoints
|
||||
int32_t fetch = breakType == BRK_READ && vue->cpu.fetch != -1 ? 1 : 0;
|
||||
for (int x = 0; x < core->numBreakpoints; x++) {
|
||||
Breakpoint *brk = &core->breakpoints[x];
|
||||
if (
|
||||
brk->enabled &&
|
||||
brk->enabled && (brk->fetch || !fetch) &&
|
||||
(breakType == 0 || (breakType & brk->hooks) != 0) &&
|
||||
(!ranged || brkInRange(brk, start, end)) &&
|
||||
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
|
||||
|
|
|
@ -13,6 +13,7 @@ public class Breakpoint {
|
|||
private Error conditionError; // Condition parsing error
|
||||
private int dataType; // Condition evaluation data type
|
||||
private int depth; // Stack size for condition evaluation
|
||||
private boolean fetch; // Breakpoint traps fetch reads
|
||||
private int hooks; // Applied breakpoint types
|
||||
private boolean isEnabled; // Breakpoint is active
|
||||
private String name; // Display name
|
||||
|
@ -452,6 +453,7 @@ public class Breakpoint {
|
|||
addresses = "";
|
||||
condition = "";
|
||||
conditionError = new Error(NONE, 0, "");
|
||||
fetch = true;
|
||||
name = "";
|
||||
ranges = new int[0][];
|
||||
tokens = new Token[0];
|
||||
|
@ -464,36 +466,54 @@ public class Breakpoint {
|
|||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Determine whether the breakpoint applies to any break scenarios
|
||||
public boolean any() {
|
||||
return hooks != 0;
|
||||
}
|
||||
|
||||
// Evaluate the condition against the emulation context
|
||||
public boolean evaluate() {
|
||||
return evaluate(null, null);
|
||||
}
|
||||
|
||||
// Evaluate the condition against the emulation context
|
||||
public boolean evaluate(Instruction inst, Access acc) {
|
||||
|
||||
// Error checking
|
||||
if (vue == null)
|
||||
if (vue == null || !isEnabled || hooks == 0)
|
||||
return false;
|
||||
|
||||
// Retrieve state objects
|
||||
Access acc = vue.getAccess ();
|
||||
int breakType = vue.getBreakType ();
|
||||
Instruction inst = vue.getInstruction();
|
||||
// Working variables
|
||||
boolean applies = false;
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
|
||||
// Error checking
|
||||
if (!appliesTo(breakType))
|
||||
return false;
|
||||
// Resolve state objects
|
||||
if (inst == null)
|
||||
inst = new Instruction(vue.read(pc, Vue.S32));
|
||||
if (acc == null)
|
||||
acc = inst.access(vue);
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
|
||||
// Check address ranges for read and write
|
||||
else if (breakType == READ || breakType == WRITE &&
|
||||
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
|
||||
if (!applies)
|
||||
return false;
|
||||
|
||||
// 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
|
||||
|
@ -526,6 +546,11 @@ public class Breakpoint {
|
|||
return (hooks & EXECUTE) != 0;
|
||||
}
|
||||
|
||||
// Determine whether the breakpoint hooks fetch reads
|
||||
public boolean getFetch() {
|
||||
return fetch;
|
||||
}
|
||||
|
||||
// Determine whether the breakpoint hooks reads
|
||||
public boolean getRead() {
|
||||
return (hooks & READ) != 0;
|
||||
|
@ -662,7 +687,7 @@ public class Breakpoint {
|
|||
} // x
|
||||
|
||||
// Unexpected end of input
|
||||
if (mode == 0 && ranges.size() > 0) {
|
||||
if (mode == 0 && (ranges.size() > 0 || first != null)) {
|
||||
addressError = new Error(EARLYEOF, x + 1, "");
|
||||
if (vue != null)
|
||||
vue.updateRanges(this);
|
||||
|
@ -759,9 +784,16 @@ public class Breakpoint {
|
|||
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
|
||||
public void setName(String name) {
|
||||
this.name = name == null ? "" : name;
|
||||
public String setName(String name) {
|
||||
return this.name = name == null ? "" : name;
|
||||
}
|
||||
|
||||
// Specify whether the breakpoint hooks reads
|
||||
|
@ -785,8 +817,9 @@ public class Breakpoint {
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Determine whether the breakpoint applies to a break scenario
|
||||
boolean appliesTo(int breakType) {
|
||||
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
|
||||
boolean appliesTo(int breakType, boolean fetch) {
|
||||
return isActive() && (this.fetch || !fetch) &&
|
||||
(breakType == 0 || (breakType & hooks) != 0);
|
||||
}
|
||||
|
||||
// Perform a typed evaluation of the condition expression
|
||||
|
@ -1145,7 +1178,6 @@ public class Breakpoint {
|
|||
|
||||
// Not Logical
|
||||
case NOT_L:
|
||||
newType = BOOL;
|
||||
break;
|
||||
|
||||
// Round to Nearest
|
||||
|
@ -1289,7 +1321,7 @@ public class Breakpoint {
|
|||
|
||||
// Select the ID of the conversion operation
|
||||
int cvt = 0;
|
||||
switch (tok.dataType) {
|
||||
switch (dataType) {
|
||||
case BOOL : cvt = BOOL_ ; break;
|
||||
case FLOAT : cvt = FLOAT_; break;
|
||||
case SIGNED : cvt = S32 ; break;
|
||||
|
@ -1305,7 +1337,7 @@ public class Breakpoint {
|
|||
// Insert a conversion token
|
||||
else {
|
||||
var imp = new Token(UNARY, -1, "");
|
||||
switch (tok.dataType) {
|
||||
switch (dataType) {
|
||||
case BOOL : imp.text = "bool" ; break;
|
||||
case FLOAT : imp.text = "float"; break;
|
||||
case SIGNED : imp.text = "s32" ; break;
|
||||
|
@ -1347,7 +1379,7 @@ public class Breakpoint {
|
|||
int start = 0;
|
||||
|
||||
// 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);
|
||||
if (tok.type == OPEN)
|
||||
start = x + 1;
|
||||
|
@ -1447,7 +1479,7 @@ public class Breakpoint {
|
|||
}
|
||||
|
||||
// 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 =
|
||||
new Error(EARLYEOF, condition.length(), "");
|
||||
return false;
|
||||
|
@ -1927,6 +1959,10 @@ public class Breakpoint {
|
|||
if (conditionError.code != NONE)
|
||||
return "Error";
|
||||
|
||||
// There is no condition
|
||||
if (tokens.length == 0)
|
||||
return "";
|
||||
|
||||
// Determine the maximum width of the text fields
|
||||
int max = 0;
|
||||
for (var tok : tokens)
|
||||
|
|
|
@ -443,7 +443,7 @@ class CPU {
|
|||
|
||||
// First unit
|
||||
if (fetch == 0) {
|
||||
inst.bits = access.value << 16;
|
||||
inst.bits = access.value & 0xFFFF;
|
||||
if (Instruction.size(access.value >> 10 & 0x3F) == 4) {
|
||||
fetch = 1;
|
||||
return false;
|
||||
|
@ -452,7 +452,7 @@ class CPU {
|
|||
|
||||
// Second unit
|
||||
else {
|
||||
inst.bits |= access.value & 0xFFFF;
|
||||
inst.bits |= access.value << 16;
|
||||
fetch = -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,11 @@ public class Instruction {
|
|||
// Default constructor
|
||||
public Instruction() { }
|
||||
|
||||
// Decoding constructor
|
||||
public Instruction(int bits) {
|
||||
decode(bits);
|
||||
}
|
||||
|
||||
// Cloning constructor
|
||||
Instruction(Instruction o) {
|
||||
this();
|
||||
|
@ -104,27 +109,67 @@ public class Instruction {
|
|||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Decode an instruction from a byte array
|
||||
public void decode(byte[] data, int offset) {
|
||||
bits = (data[offset + 1] & 0xFF) << 24 | (data[offset] & 0xFF) << 16;
|
||||
if (size(bits >>> 26) == 4)
|
||||
bits |= (data[offset + 3] & 0xFF) << 8 | data[offset + 2] & 0xFF;
|
||||
decode(bits);
|
||||
// Produce an access descriptor from the current instruction state
|
||||
public Access access(Vue vue) {
|
||||
boolean read = false;
|
||||
var ret = new Access();
|
||||
ret.address = vue.getRegister(reg1, false) + disp;
|
||||
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;
|
||||
}
|
||||
|
||||
// Read the value currently on the bus
|
||||
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 its binary encoding
|
||||
public void decode(int bits) {
|
||||
// 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
|
||||
int x; // Working variable
|
||||
|
||||
// Configure instance fields
|
||||
this.bits = bits;
|
||||
opcode = bits >> 26 & 63;
|
||||
x = opcode << 1;
|
||||
id = LOOKUP_OPCODE[x ];
|
||||
extend = LOOKUP_OPCODE[x + 1];
|
||||
format = extend < 0 ? -extend : extend;
|
||||
size = format < 4 ? 2 : 4;
|
||||
this.bits = bits = bits << 16 | bits >>> 16;
|
||||
opcode = bits >> 26 & 63;
|
||||
x = opcode << 1;
|
||||
id = LOOKUP_OPCODE[x ];
|
||||
extend = LOOKUP_OPCODE[x + 1];
|
||||
format = extend < 0 ? -extend : extend;
|
||||
size = format < 4 ? 2 : 4;
|
||||
|
||||
// Decode by format
|
||||
switch (format) {
|
||||
|
@ -169,6 +214,7 @@ public class Instruction {
|
|||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -285,11 +285,12 @@ class JavaVue extends Vue {
|
|||
}
|
||||
|
||||
// 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++) {
|
||||
var brk = breakpoints.get(x);
|
||||
if (
|
||||
brk.appliesTo(breakType) &&
|
||||
brk.appliesTo(breakType, fetch) &&
|
||||
(!ranged || brk.inRange(start, end)) &&
|
||||
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
|
||||
) breakCode = x + 1;
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef struct {
|
|||
// Precompiled breakpoint
|
||||
typedef struct {
|
||||
int32_t enabled; // The breakpoint is enabled
|
||||
int32_t fetch; // Breakpoint traps fetch reads
|
||||
int32_t hooks; // Events hooked by the breakpoint
|
||||
int32_t numRanges; // Number of address ranges
|
||||
int32_t numTokens; // Number of condition tokens
|
||||
|
@ -106,7 +107,9 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_breakpoint
|
|||
core->numBreakpoints++;
|
||||
core->breakpoints =
|
||||
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
|
||||
|
@ -368,10 +371,11 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_updateRanges
|
|||
// A breakpoint's enabled/hook state has changed
|
||||
JNIEXPORT void JNICALL Java_vue_NativeVue_updateState
|
||||
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled,
|
||||
jint hooks) {
|
||||
jboolean fetch, jint hooks) {
|
||||
Core *core = *(Core **)&handle;
|
||||
Breakpoint *brk = &core->breakpoints[index];
|
||||
brk->enabled = enabled;
|
||||
brk->fetch = fetch;
|
||||
brk->hooks = hooks;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,13 +155,13 @@ class NativeVue extends Vue {
|
|||
}
|
||||
|
||||
// A breakpoint's enabled/hook state has changed
|
||||
private native void updateState(long handle, int index, boolean isEnabled,
|
||||
int hooks);
|
||||
private native void updateState(long handle, int index, boolean enabled,
|
||||
boolean fetch, int hooks);
|
||||
void updateState(Breakpoint brk) {
|
||||
super.updateState(brk);
|
||||
int index = breakpoints.indexOf(brk);
|
||||
updateState(handle, index, brk.isEnabled(),
|
||||
brk.getHooks());
|
||||
updateState(handle, index,
|
||||
brk.isEnabled(), brk.getFetch(), brk.getHooks());
|
||||
}
|
||||
|
||||
// A breakpoint's condition tokens have changed
|
||||
|
|
|
@ -183,25 +183,20 @@ public abstract class Vue {
|
|||
|
||||
// Evaluate an expression
|
||||
public Object evaluate(String expression) {
|
||||
var brk = new Breakpoint(this);
|
||||
var inst = new Instruction(read(getRegister(PC, true), S32));
|
||||
var brk = new Breakpoint(this);
|
||||
brk.setCondition(expression);
|
||||
brk.setEnabled (true);
|
||||
return brk.evaluateTyped(new int[brk.getDepth()], 0,
|
||||
getInstruction(), getAccess());
|
||||
return brk.evaluateTyped(
|
||||
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
|
||||
public abstract int getBreakCode();
|
||||
|
||||
// Retrieve the most recent exception code
|
||||
public abstract int getExceptionCode();
|
||||
|
||||
// Retrieve a snapshot of the current state's instruction
|
||||
public abstract Instruction getInstruction();
|
||||
|
||||
// Retrieve a register value
|
||||
public abstract int getRegister(int index, boolean system);
|
||||
|
||||
|
|
Loading…
Reference in New Issue