Merge remote-tracking branch 'origin/GP-3049_ghidragon_fixing_improving_register_manager--SQUASHED'
@@ -127,8 +127,7 @@ public class BSimSearchDialog extends AbstractBSimSearchDialog {
|
||||
maxResultsField = new IntegerTextField(10);
|
||||
maxResultsField.setValue(100);
|
||||
maxResultsField.setMinValue(BigInteger.ONE);
|
||||
maxResultsField.setAllowNegativeValues(false);
|
||||
maxResultsField.setAllowsHexPrefix(false);
|
||||
maxResultsField.setUseNumberPrefix(false);
|
||||
maxResultsField.setShowNumberMode(false);
|
||||
|
||||
JComponent maxResultsComponent = maxResultsField.getComponent();
|
||||
|
||||
@@ -71,24 +71,27 @@
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H3><A name="tool_buttons"></A>Tool Buttons</H3>
|
||||
</BLOCKQUOTE>
|
||||
<H3><A name="tool_buttons"></A>Toolbar Buttons</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="images/locationIn.gif">Toggles whether or not to select the row in the
|
||||
<P><IMG src="images/locationIn.gif"> Toggles whether or not to select the row in the
|
||||
currently selected register value table whose address range contains the current address
|
||||
of the cursor in the listing view. For example, in the Register Manager image show above,
|
||||
if the user clicks on any address between 804c12 and 804c24, then the first row of the
|
||||
table will be selected if this action toggle is on.</P>
|
||||
|
||||
<P><IMG src="images/edit-delete.png">Deletes the register value associations for all the
|
||||
<P><IMG src="Icons.ADD_ICON"> Brings up the <I>Add Register Value Range</I> dialog for setting a
|
||||
value over an address range for the selected register. See the
|
||||
<A href="">Edit Address Range</A> section below for dialog details as this action shares the
|
||||
same dialog as the edit range action.</P>
|
||||
|
||||
<P><IMG src="images/edit-delete.png"> Deletes the register value associations for all the
|
||||
selected ranges in the table.</P>
|
||||
|
||||
<P><IMG src="Icons.MAKE_SELECTION_ICON">Creates a selection in the browser for all the address
|
||||
<P><IMG src="Icons.MAKE_SELECTION_ICON"> Creates a selection in the browser for all the address
|
||||
ranges selected in the register values table.</P>
|
||||
|
||||
<P><IMG src="images/view-filter.png">Filters out all registers in the register tree that
|
||||
<P><IMG src="images/view-filter.png"> Filters out all registers in the register tree that
|
||||
don't have any associated values (default or otherwise).</P>
|
||||
|
||||
<H3><BR>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.2 KiB |
@@ -33,6 +33,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.dialogs.StringChoices;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.BigEndianDataConverter;
|
||||
@@ -783,7 +784,8 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private void updateHexMode() {
|
||||
intHexModeMap.put(rowobject.definition.getName(), intTextField.isHexMode());
|
||||
intHexModeMap.put(rowobject.definition.getName(),
|
||||
intTextField.getFormat() == IntegerFormat.HEX);
|
||||
}
|
||||
|
||||
private Number getNumber() {
|
||||
@@ -854,14 +856,14 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
||||
mode = NUMBER;
|
||||
NumberSettingsDefinition def = (NumberSettingsDefinition) rowobject.definition;
|
||||
if (def.isHexModePreferred() || isHexModeEnabled(def)) {
|
||||
intTextField.setHexMode();
|
||||
intTextField.setFormat(IntegerFormat.HEX);
|
||||
}
|
||||
else {
|
||||
intTextField.setDecimalMode();
|
||||
intTextField.setFormat(IntegerFormat.DEC);
|
||||
}
|
||||
|
||||
intTextField.setMaxValue(def.getMaxValue());
|
||||
intTextField.setAllowNegativeValues(def.allowNegativeValue());
|
||||
intTextField.setMinValue(def.allowNegativeValue() ? null : BigInteger.ZERO);
|
||||
|
||||
if (value == null) {
|
||||
intTextField.setValue(null);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
import docking.widgets.table.FocusableEditor;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.program.model.address.Address;
|
||||
@@ -163,7 +164,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor
|
||||
|
||||
private Component createStackOffsetEditor(VarnodeInfo varnode) {
|
||||
offsetInput = new IntegerTextField();
|
||||
offsetInput.setHexMode();
|
||||
offsetInput.setFormat(IntegerFormat.HEX);
|
||||
Address address = varnode.getAddress();
|
||||
if (address != null) {
|
||||
offsetInput.setValue(address.getOffset());
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -24,6 +24,7 @@ import javax.swing.*;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
|
||||
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||
@@ -56,8 +57,8 @@ class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEdito
|
||||
int row, int column) {
|
||||
|
||||
input = new IntegerTextField();
|
||||
input.setAllowNegativeValues(false);
|
||||
input.setDecimalMode();
|
||||
input.setMinValue(BigInteger.ZERO);
|
||||
input.setFormat(IntegerFormat.DEC);
|
||||
Integer size = (Integer) value;
|
||||
if (size != null) {
|
||||
input.setValue(size.longValue());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package ghidra.app.plugin.core.memory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -28,6 +29,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
|
||||
import ghidra.app.plugin.core.misc.RegisterField;
|
||||
import ghidra.app.util.AddressInput;
|
||||
@@ -474,16 +476,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
||||
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
schemeDestByteCountField = new IntegerTextField(4, 1);
|
||||
schemeDestByteCountField.setAllowNegativeValues(false);
|
||||
schemeDestByteCountField.setAllowsHexPrefix(false);
|
||||
schemeDestByteCountField.setDecimalMode();
|
||||
schemeDestByteCountField.setMinValue(BigInteger.ZERO);
|
||||
schemeDestByteCountField.setUseNumberPrefix(false);
|
||||
schemeDestByteCountField.setFormat(IntegerFormat.DEC);
|
||||
schemeDestByteCountField.addChangeListener(ev -> schemeDestByteCountChanged());
|
||||
schemeDestByteCountField.setAccessibleName("Mapping Ratio: Destination Size");
|
||||
|
||||
schemeSrcByteCountField = new IntegerTextField(4, 1);
|
||||
schemeSrcByteCountField.setAllowNegativeValues(false);
|
||||
schemeSrcByteCountField.setAllowsHexPrefix(false);
|
||||
schemeSrcByteCountField.setDecimalMode();
|
||||
schemeSrcByteCountField.setMinValue(BigInteger.ZERO);
|
||||
schemeSrcByteCountField.setUseNumberPrefix(false);
|
||||
schemeSrcByteCountField.setFormat(IntegerFormat.DEC);
|
||||
schemeSrcByteCountField.addChangeListener(ev -> schemeSrcByteCountChanged());
|
||||
schemeSrcByteCountField.setAccessibleName("Mapping Ratio: Source Size");
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
@@ -36,7 +36,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
|
||||
private AddressInput startAddrField;
|
||||
private AddressInput endAddrField;
|
||||
private FixedBitSizeValueField registerValueField;
|
||||
private FixedSizeIntegerTextField registerValueField;
|
||||
private boolean wasCancelled = true;
|
||||
|
||||
EditRegisterValueDialog(Register register, Address start, Address end, BigInteger value,
|
||||
@@ -61,10 +61,16 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
startAddrField = new AddressInput(program, addressChangeListener);
|
||||
endAddrField = new AddressInput(program, addressChangeListener);
|
||||
|
||||
registerValueField = new FixedBitSizeValueField(register.getBitLength(), true, false);
|
||||
registerValueField.getAccessibleContext().setAccessibleName("Register Value");
|
||||
startAddrField.setAddress(start);
|
||||
endAddrField.setAddress(end);
|
||||
registerValueField = new FixedSizeIntegerTextField(16, register.getBitLength());
|
||||
registerValueField.getComponent()
|
||||
.getAccessibleContext()
|
||||
.setAccessibleName("Register Value");
|
||||
if (start != null) {
|
||||
startAddrField.setAddress(start);
|
||||
}
|
||||
if (end != null) {
|
||||
endAddrField.setAddress(end);
|
||||
}
|
||||
registerValueField.setValue(value);
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(5, 1));
|
||||
@@ -77,7 +83,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
panel.add(new GLabel("End Address:"));
|
||||
panel.add(endAddrField);
|
||||
panel.add(new GLabel("Value:"));
|
||||
panel.add(registerValueField);
|
||||
panel.add(registerValueField.getComponent());
|
||||
panel.getAccessibleContext().setAccessibleName("Edit Register Value");
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -19,14 +19,19 @@ import static ghidra.framework.model.DomainObjectEvent.*;
|
||||
import static ghidra.program.util.ProgramEvent.*;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.*;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.cmd.register.SetRegisterCmd;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
@@ -52,16 +57,13 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
private RegisterTree tree;
|
||||
private RegisterValuesPanel values;
|
||||
|
||||
private DockingAction deleteRegisterValuesAction;
|
||||
|
||||
private DockingAction selectRegisterValuesAction;
|
||||
private ToggleDockingAction showDefaultRegisterValuesAction;
|
||||
private ToggleDockingAction showDefaultValuesAction;
|
||||
private DomainObjectListener domainObjectListener;
|
||||
private ToggleDockingAction filterRegistersAction;
|
||||
private SwingUpdateManager updateMgr;
|
||||
|
||||
private ToggleDockingAction followLocationToggleAction;
|
||||
protected boolean followLocation;
|
||||
private boolean followLocation = false;
|
||||
private Address currentAddress;
|
||||
|
||||
RegisterManagerProvider(PluginTool tool, String owner) {
|
||||
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
||||
@@ -86,9 +88,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
|
||||
tree.addGTreeSelectionListener(e -> showRegister());
|
||||
values.getTable().getSelectionModel().addListSelectionListener(e -> {
|
||||
JTable table = values.getTable();
|
||||
deleteRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
||||
selectRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
||||
contextChanged();
|
||||
});
|
||||
|
||||
tree.setAccessibleNamePrefix("Register Manager");
|
||||
@@ -100,80 +100,91 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
|
||||
void createActions() {
|
||||
HelpLocation helpLocation = new HelpLocation("RegisterPlugin", "tool_buttons");
|
||||
deleteRegisterValuesAction = new DockingAction("Delete Register Value Ranges", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
values.deleteSelectedRanges();
|
||||
}
|
||||
};
|
||||
deleteRegisterValuesAction.setEnabled(false);
|
||||
deleteRegisterValuesAction.setToolBarData(new ToolBarData(DELETE_REGISTER_VALUES_ICON));
|
||||
deleteRegisterValuesAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Delete Register Value Ranges" }));
|
||||
deleteRegisterValuesAction.setDescription("Delete Register Value Ranges");
|
||||
deleteRegisterValuesAction.setHelpLocation(helpLocation);
|
||||
tool.addLocalAction(this, deleteRegisterValuesAction);
|
||||
|
||||
selectRegisterValuesAction = new DockingAction("Select Register Value Ranges", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
values.selectedRanges();
|
||||
}
|
||||
};
|
||||
selectRegisterValuesAction.setEnabled(false);
|
||||
selectRegisterValuesAction.setToolBarData(new ToolBarData(SELECT_REGISTER_VALUES_ICON));
|
||||
selectRegisterValuesAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Select Register Value Ranges" }));
|
||||
selectRegisterValuesAction.setDescription("Select Register Value Ranges");
|
||||
selectRegisterValuesAction.setHelpLocation(helpLocation);
|
||||
tool.addLocalAction(this, selectRegisterValuesAction);
|
||||
new ActionBuilder("Add Register Value Range", getOwner())
|
||||
.toolBarIcon(Icons.ADD_ICON)
|
||||
.popupMenuPath("Add Value Range")
|
||||
.popupMenuIcon(Icons.ADD_ICON)
|
||||
.description("Add a new register value range")
|
||||
.helpLocation(helpLocation)
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.getSelectedRegister() != null)
|
||||
.onAction(c -> addRange(c.getSelectedRegister()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
showDefaultRegisterValuesAction =
|
||||
new ToggleDockingAction("Show default register values", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
values.setShowDefaultValues(showDefaultRegisterValuesAction.isSelected());
|
||||
}
|
||||
};
|
||||
showDefaultRegisterValuesAction.setSelected(false);
|
||||
showDefaultRegisterValuesAction
|
||||
.setDescription("Toggles showing of default register values");
|
||||
showDefaultRegisterValuesAction
|
||||
.setMenuBarData(new MenuData(new String[] { "Show Default Values" }));
|
||||
showDefaultRegisterValuesAction
|
||||
.setHelpLocation(new HelpLocation("RegisterPlugin", "menu_actions"));
|
||||
tool.addLocalAction(this, showDefaultRegisterValuesAction);
|
||||
new ActionBuilder("Delete Register Value Ranges", getOwner())
|
||||
.toolBarIcon(DELETE_REGISTER_VALUES_ICON)
|
||||
.popupMenuPath("Delete Register Value Ranges")
|
||||
.popupMenuIcon(DELETE_REGISTER_VALUES_ICON)
|
||||
.description("Delete selected register value ranges")
|
||||
.helpLocation(helpLocation)
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||
.onAction(c -> values.deleteSelectedRanges())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
filterRegistersAction = new ToggleDockingAction("Filter Registers", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
tree.setFiltered(filterRegistersAction.isSelected());
|
||||
}
|
||||
};
|
||||
filterRegistersAction.setSelected(false);
|
||||
filterRegistersAction.setDescription(
|
||||
"Toggles filtering out registers that don't have values or default values.");
|
||||
filterRegistersAction.setToolBarData(new ToolBarData(FILTER_ICON));
|
||||
filterRegistersAction.setHelpLocation(helpLocation);
|
||||
tool.addLocalAction(this, filterRegistersAction);
|
||||
new ActionBuilder("Select Register Value Ranges", getOwner())
|
||||
.toolBarIcon(SELECT_REGISTER_VALUES_ICON)
|
||||
.popupMenuPath("Select Register Value Ranges")
|
||||
.popupMenuIcon(SELECT_REGISTER_VALUES_ICON)
|
||||
.helpLocation(helpLocation)
|
||||
.description("Create a program selection from the selected rows")
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||
.onAction(c -> values.selectRanges())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
followLocationToggleAction =
|
||||
new ToggleDockingAction("Follow location changes", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
followLocation = followLocationToggleAction.isSelected();
|
||||
}
|
||||
};
|
||||
followLocationToggleAction.setEnabled(true);
|
||||
followLocationToggleAction.setHelpLocation(helpLocation);
|
||||
followLocationToggleAction.setToolBarData(new ToolBarData(RECV_LOCATION_ICON, "NavAction"));
|
||||
tool.addLocalAction(this, followLocationToggleAction);
|
||||
showDefaultValuesAction =
|
||||
new ToggleActionBuilder("Show Default Register Values", getOwner())
|
||||
.menuPath("Show Default Values")
|
||||
.description("Toggles showing of default register values")
|
||||
.helpLocation(helpLocation)
|
||||
.selected(false)
|
||||
.onAction(c -> updateShowingDefaultRegisterValues())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
filterRegistersAction = new ToggleActionBuilder("Filter Registers", getOwner())
|
||||
.toolBarIcon(FILTER_ICON)
|
||||
.description("Toggles showing only registers with values or default values")
|
||||
.helpLocation(helpLocation)
|
||||
.selected(false)
|
||||
.onAction(c -> tree.setFiltered(filterRegistersAction.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
new ToggleActionBuilder("Follow location changes", getOwner())
|
||||
.toolBarIcon(RECV_LOCATION_ICON)
|
||||
.toolBarGroup("NavAction")
|
||||
.description(
|
||||
"If selected, auto select register and value range from listing location")
|
||||
.helpLocation(helpLocation)
|
||||
.selected(false)
|
||||
.onAction(c -> followLocation = !followLocation)
|
||||
.buildAndInstallLocal(this);
|
||||
}
|
||||
|
||||
private void updateShowingDefaultRegisterValues() {
|
||||
values.setShowDefaultValues(showDefaultValuesAction.isSelected());
|
||||
}
|
||||
|
||||
private void addRange(Register register) {
|
||||
EditRegisterValueDialog dialog =
|
||||
new EditRegisterValueDialog(register, currentAddress, currentAddress, null, program);
|
||||
dialog.setTitle("Add Value Range");
|
||||
tool.showDialog(dialog, this);
|
||||
|
||||
if (!dialog.wasCancelled()) {
|
||||
Address start = dialog.getStartAddress();
|
||||
Address end = dialog.getEndAddress();
|
||||
BigInteger value = dialog.getValue();
|
||||
Command<Program> command = new SetRegisterCmd(register, start, end, value);
|
||||
tool.execute(command, program);
|
||||
}
|
||||
}
|
||||
|
||||
private void showRegister() {
|
||||
Register register = tree.getSelectedRegister();
|
||||
values.setRegister(register);
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,7 +202,8 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
return new RegisterManagerContext(program, tree.getSelectedRegister(),
|
||||
values.hasSelectedRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,6 +257,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
public void setLocation(Register register, Address address) {
|
||||
currentAddress = address;
|
||||
if (!followLocation) {
|
||||
return;
|
||||
}
|
||||
@@ -254,4 +267,23 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
values.setAddress(address);
|
||||
}
|
||||
|
||||
private class RegisterManagerContext extends ProgramActionContext {
|
||||
private Register register;
|
||||
private boolean hasSelectedRows;
|
||||
|
||||
RegisterManagerContext(Program program, Register register, boolean hasSelectedRows) {
|
||||
super(RegisterManagerProvider.this, program);
|
||||
this.register = register;
|
||||
this.hasSelectedRows = hasSelectedRows;
|
||||
}
|
||||
|
||||
public boolean hasSelectedRegisterValueRanges() {
|
||||
return hasSelectedRows;
|
||||
}
|
||||
|
||||
public Register getSelectedRegister() {
|
||||
return register;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -358,15 +358,13 @@ public class RegisterPlugin extends ProgramPlugin {
|
||||
|
||||
@Override
|
||||
protected void locationChanged(ProgramLocation loc) {
|
||||
if (loc instanceof RegisterTransitionFieldLocation) {
|
||||
RegisterTransitionFieldLocation regLoc = (RegisterTransitionFieldLocation) loc;
|
||||
if (loc instanceof RegisterTransitionFieldLocation regLoc) {
|
||||
Register reg = regLoc.getRegister();
|
||||
if (reg != null) {
|
||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||
}
|
||||
}
|
||||
else if (loc instanceof RegisterFieldLocation) {
|
||||
RegisterFieldLocation regLoc = (RegisterFieldLocation) loc;
|
||||
else if (loc instanceof RegisterFieldLocation regLoc) {
|
||||
Register reg = regLoc.getRegister();
|
||||
if (reg != null) {
|
||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||
|
||||
@@ -83,6 +83,10 @@ class RegisterValuesPanel extends JPanel {
|
||||
|
||||
}
|
||||
|
||||
boolean hasSelectedRows() {
|
||||
return table.getSelectedRowCount() > 0;
|
||||
}
|
||||
|
||||
private void editRow(int row) {
|
||||
RegisterValueRange range = model.values.get(row);
|
||||
Address start = range.getStartAddress();
|
||||
@@ -275,7 +279,7 @@ class RegisterValuesPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
void selectedRanges() {
|
||||
void selectRanges() {
|
||||
int[] rows = table.getSelectedRows();
|
||||
AddressSet set = new AddressSet();
|
||||
for (int element : rows) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.register;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
@@ -28,19 +29,21 @@ import javax.swing.event.ChangeListener;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||
import generic.theme.GThemeDefaults.Ids.Fonts;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.VariableHeightPairLayout;
|
||||
|
||||
public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
private JComboBox<RegisterWrapper> registerComboBox;
|
||||
private FixedBitSizeValueField registerValueField;
|
||||
private JList addressRangeList;
|
||||
private FixedSizeIntegerTextField registerValueField;
|
||||
private JList<String> addressRangeList;
|
||||
private BigInteger registerValue;
|
||||
private Register selectedRegister;
|
||||
private boolean useValueField;
|
||||
@@ -58,7 +61,7 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
if (useValueField) {
|
||||
setFocusComponent(registerValueField.getTextComponent());
|
||||
setFocusComponent(registerValueField.getComponent());
|
||||
}
|
||||
setSelectedRegister(register);
|
||||
setAddressRanges(addrSet);
|
||||
@@ -77,79 +80,72 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private JComponent buildWorkPanel(Register[] registers) {
|
||||
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
||||
registerValueField = new FixedBitSizeValueField(32, true, false);
|
||||
GLabel registerLabel = new GLabel("Register:");
|
||||
GLabel valueLabel = new GLabel("Value:");
|
||||
GLabel addressLabel = new GLabel("Address(es):");
|
||||
registerLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
valueLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
addressLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
addressLabel.setVerticalAlignment(SwingConstants.TOP);
|
||||
|
||||
JPanel panel = new JPanel(new VariableHeightPairLayout(10, 10));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
|
||||
panel.add(registerLabel);
|
||||
panel.add(buildRegisterComboBox(registers));
|
||||
panel.add(valueLabel);
|
||||
panel.add(buildValueField(registers));
|
||||
panel.add(addressLabel);
|
||||
panel.add(buildAddressPanel());
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildValueField(Register[] registers) {
|
||||
registerValueField = new FixedSizeIntegerTextField(16, 16);
|
||||
registerValueField.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
updateOkEnablement();
|
||||
}
|
||||
});
|
||||
return registerValueField.getComponent();
|
||||
}
|
||||
|
||||
private Component buildRegisterComboBox(Register[] registers) {
|
||||
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
||||
registerComboBox.setRenderer(new RegisterComboRenderer());
|
||||
|
||||
registerComboBox.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
registerChanged();
|
||||
updateComboToolTip();
|
||||
}
|
||||
});
|
||||
updateComboToolTip();
|
||||
return registerComboBox;
|
||||
}
|
||||
|
||||
addressRangeList = new JList();
|
||||
private void updateComboToolTip() {
|
||||
RegisterWrapper item = (RegisterWrapper) registerComboBox.getSelectedItem();
|
||||
String tooltip = item == null ? "" : item.getToolTip();
|
||||
registerComboBox.setToolTipText(tooltip);
|
||||
}
|
||||
|
||||
private Component buildAddressPanel() {
|
||||
addressRangeList = new JList<String>();
|
||||
addressRangeList.setEnabled(false);
|
||||
Gui.registerFont(addressRangeList, Fonts.MONOSPACED);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(addressRangeList);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
Dimension d = scrollPane.getPreferredSize();
|
||||
d.height = 120;
|
||||
d.width = 180;
|
||||
scrollPane.setPreferredSize(d);
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.insets = new Insets(5, 5, 1, 5);
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
panel.add(new GLabel("Register:"), gbc);
|
||||
gbc.gridy = 1;
|
||||
if (useValueField) {
|
||||
panel.add(new GLabel("Value:"), gbc);
|
||||
}
|
||||
gbc.gridy = 2;
|
||||
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.insets = new Insets(10, 5, 1, 5);
|
||||
GLabel addressLabel = new GLabel("Address(es):");
|
||||
addressLabel.setVerticalAlignment(SwingConstants.TOP);
|
||||
panel.add(addressLabel, gbc);
|
||||
|
||||
gbc.insets = new Insets(5, 5, 1, 5);
|
||||
gbc.weightx = 1.0;
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 0;
|
||||
panel.add(registerComboBox, gbc);
|
||||
gbc.gridy = 1;
|
||||
if (useValueField) {
|
||||
panel.add(registerValueField, gbc);
|
||||
}
|
||||
|
||||
gbc.gridy = 2;
|
||||
gbc.weighty = 1.0;
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
panel.add(scrollPane, gbc);
|
||||
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
|
||||
|
||||
return panel;
|
||||
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
private void registerChanged() {
|
||||
RegisterWrapper wrapper = (RegisterWrapper) registerComboBox.getSelectedItem();
|
||||
if (wrapper != null) {
|
||||
registerValueField.setBitSize(wrapper.register.getBitLength());
|
||||
int bitLength = wrapper.register.getBitLength();
|
||||
registerValueField.setBitSize(bitLength);
|
||||
updateOkEnablement();
|
||||
}
|
||||
updateValue();
|
||||
@@ -232,6 +228,21 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
return selectedRegister;
|
||||
}
|
||||
|
||||
private static class RegisterComboRenderer extends GListCellRenderer<RegisterWrapper> {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends RegisterWrapper> list,
|
||||
RegisterWrapper value, int index, boolean isSelected, boolean hasFocus) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, hasFocus);
|
||||
String toolTip = value.getToolTip();
|
||||
setToolTipText(toolTip);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getItemText(RegisterWrapper value) {
|
||||
return value == null ? "" : value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterWrapper implements Comparable<RegisterWrapper> {
|
||||
@@ -240,14 +251,24 @@ class RegisterWrapper implements Comparable<RegisterWrapper> {
|
||||
|
||||
RegisterWrapper(Register register) {
|
||||
this.register = register;
|
||||
displayName = register.getName() + " (" + register.getBitLength() + getAliases() + ")";
|
||||
displayName = register.getName() + " (" + register.getBitLength() + ")";
|
||||
}
|
||||
|
||||
private String getAliases() {
|
||||
String getToolTip() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (String alias : register.getAliases()) {
|
||||
buf.append(buf.length() == 0 ? "; " : ", ");
|
||||
buf.append(alias);
|
||||
buf.append(displayName);
|
||||
buf.append(" Aliases: ");
|
||||
Iterator<String> aliases = register.getAliases().iterator();
|
||||
if (!aliases.hasNext()) {
|
||||
buf.append("none");
|
||||
}
|
||||
else {
|
||||
buf.append(aliases.next());
|
||||
buf.append(", ");
|
||||
}
|
||||
while (aliases.hasNext()) {
|
||||
buf.append(", ");
|
||||
buf.append(aliases.next());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -97,7 +97,7 @@ class SelectBlockDialog extends ReusableDialogComponentProvider {
|
||||
numberInputField = new IntegerTextField(10);
|
||||
numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input");
|
||||
numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE));
|
||||
numberInputField.setAllowNegativeValues(false);
|
||||
numberInputField.setMinValue(BigInteger.ZERO);
|
||||
main.add(numberInputField.getComponent());
|
||||
main.getAccessibleContext().setAccessibleName("Block");
|
||||
return main;
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.string;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -331,7 +332,7 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom
|
||||
|
||||
private Component buildOffsetPanel() {
|
||||
offsetField = new IntegerTextField(4, 0L);
|
||||
offsetField.setAllowNegativeValues(false);
|
||||
offsetField.setMinValue(BigInteger.ZERO);
|
||||
offsetField.addChangeListener(e -> updatePreview());
|
||||
|
||||
preview = new JTextField(5);
|
||||
|
||||
@@ -15,22 +15,22 @@
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.table.FocusableEditor;
|
||||
import docking.widgets.textfield.HexDecimalModeTextField;
|
||||
import docking.widgets.textfield.integer.MultiFormatTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.expressions.ExpressionException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@@ -45,7 +45,7 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||
public final static Predicate<AddressSpace> ALL_MEMORY_SPACES = s -> s.isMemorySpace();
|
||||
public final static Predicate<AddressSpace> LOADED_MEMORY_SPACES = s -> s.isLoadedMemorySpace();
|
||||
|
||||
private HexDecimalModeTextField textField;
|
||||
private MultiFormatTextField textField;
|
||||
private AddressSpaceField addressSpaceField;
|
||||
AddressEvaluator addressEvaluator;
|
||||
private Predicate<AddressSpace> addressSpaceFilter = LOADED_MEMORY_SPACES;
|
||||
@@ -170,8 +170,9 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||
* @param hexMode true to assume numbers are hexadecimal.
|
||||
*/
|
||||
public void setAssumeHex(boolean hexMode) {
|
||||
textField.setHexMode(hexMode);
|
||||
hexModeChanged(hexMode);
|
||||
IntegerFormat format = hexMode ? IntegerFormat.HEX : IntegerFormat.DEC;
|
||||
textField.setFormat(format);
|
||||
hexModeChanged(format);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,35 +419,17 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||
|
||||
private void buildComponent() {
|
||||
setLayout(new BorderLayout());
|
||||
textField = new HexDecimalModeTextField(10, b -> hexModeChanged(b));
|
||||
textField.setHexMode(true);
|
||||
textField = new MultiFormatTextField(10, List.of(HEX, DEC), b -> hexModeChanged(b));
|
||||
textField.setName("JTextField");//for JUnits...
|
||||
addressSpaceField = new AddressSpaceField();
|
||||
add(textField, BorderLayout.CENTER);
|
||||
comboAdded = false;
|
||||
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
});
|
||||
|
||||
textField.addTextChangedCallback(this::notifyAddressChanged);
|
||||
}
|
||||
|
||||
private void hexModeChanged(boolean hexMode) {
|
||||
this.assumeHex = hexMode;
|
||||
addressEvaluator.setAssumeHex(hexMode);
|
||||
private void hexModeChanged(IntegerFormat format) {
|
||||
this.assumeHex = format == HEX;
|
||||
addressEvaluator.setAssumeHex(assumeHex);
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package ghidra.app.util.importer.options;
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
@@ -64,7 +65,7 @@ public class HexLongOption extends AbstractOption<HexLong> {
|
||||
setValue(new HexLong(initialState));
|
||||
IntegerTextField field = new IntegerTextField();
|
||||
field.setValue(initialState);
|
||||
field.setHexMode();
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
field.getComponent().setToolTipText(getDescription());
|
||||
field.addChangeListener(e -> {
|
||||
setValue(new HexLong(field.getLongValue()));
|
||||
|
||||
@@ -26,6 +26,7 @@ import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.framework.options.CustomOptionsEditor;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
@@ -101,8 +102,8 @@ public class AddressFieldOptionsPropertyEditor extends PropertyEditorSupport
|
||||
panel.add(label);
|
||||
|
||||
minDigitsField = new IntegerTextField(2);
|
||||
minDigitsField.setAllowNegativeValues(false);
|
||||
minDigitsField.setDecimalMode();
|
||||
minDigitsField.setMinValue(BigInteger.ZERO);
|
||||
minDigitsField.setFormat(IntegerFormat.DEC);
|
||||
minDigitsField.setMaxValue(BigInteger.valueOf(32));
|
||||
minDigitsField.getComponent().setToolTipText(MIN_HEX_DIGITS_TOOLTIP);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -20,6 +20,7 @@ import java.awt.Container;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
@@ -97,7 +98,7 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
||||
Container parent) {
|
||||
|
||||
IntegerTextField textField = new IntegerTextField(10);
|
||||
textField.setAllowNegativeValues(false);
|
||||
textField.setMinValue(BigInteger.ZERO);
|
||||
textField.setEnabled(true);
|
||||
|
||||
JPanel textFieldPanel = new JPanel();
|
||||
@@ -132,7 +133,8 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
||||
}
|
||||
|
||||
private void setLocalValues(ArrayElementWrappedOption namespaceOption) {
|
||||
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox.isSelected()) {
|
||||
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox
|
||||
.isSelected()) {
|
||||
groupElementsCheckBox.setSelected(namespaceOption.showMultipleArrayElementPerLine());
|
||||
}
|
||||
if (namespaceOption.getArrayElementsPerLine() != elementsPerLineField.getIntValue()) {
|
||||
|
||||
@@ -64,7 +64,7 @@ public class ProgramByteSource implements AddressableByteSource {
|
||||
long offset = location.getByteAddress().subtract(location.getProgram().getImageBase());
|
||||
return getProgram().getImageBase().add(offset);
|
||||
}
|
||||
|
||||
|
||||
public Program getProgram() {
|
||||
return memory.getProgram();
|
||||
}
|
||||
|
||||