492 lines
17 KiB
Java
492 lines
17 KiB
Java
package app;
|
|
|
|
// Java imports
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import javax.swing.*;
|
|
|
|
// Project imports
|
|
import util.*;
|
|
import vue.*;
|
|
|
|
// Register list item
|
|
class Register {
|
|
|
|
// Instance fields
|
|
boolean expandable; // The expansion area can be shown
|
|
boolean expanded; // The expanded area is being shown
|
|
Font font; // Hexadecimal font
|
|
int index; // Register index
|
|
int mode; // Display mode for program registers
|
|
MainWindow parent; // Containing window
|
|
int type; // Expansion controls type
|
|
int value; // Current register value
|
|
|
|
// UI components
|
|
private JPanel expansion; // Expansion area
|
|
private JLabel btnExpand; // Expand button
|
|
private JLabel lblLastPC; // Last PC name
|
|
private JLabel lblName; // Register name
|
|
private JPanel list; // Containing element
|
|
private JPanel spacer; // Expansion area spacer
|
|
private JTextField txtJumpFrom; // Jump-from value
|
|
private JTextField txtJumpTo; // Jump-to value
|
|
private JTextField txtValue; // Register value
|
|
private ArrayList<JComponent> controls; // Expansion controls
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Constants //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Modes
|
|
static final int FLOAT = 3;
|
|
static final int HEX = 0;
|
|
static final int SIGNED = 1;
|
|
static final int UNSIGNED = 2;
|
|
|
|
// Types
|
|
static final int PLAIN = -1;
|
|
static final int PROGRAM = -2;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Constructors //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Default constructor
|
|
Register(MainWindow parent, JPanel list, String name, int index, int type){
|
|
|
|
// Configure instance fields
|
|
controls = new ArrayList<JComponent>();
|
|
expandable = type != PLAIN && index != 0 || index == VUE.PC;
|
|
this.index = index;
|
|
this.list = list;
|
|
this.mode = HEX;
|
|
this.parent = parent;
|
|
this.type = type;
|
|
|
|
// Click handler for expand and name controls
|
|
MouseListener expand = !expandable ? null : Util.onMouse(e->{
|
|
if (e.getButton() == 1) setExpanded(!expanded); }, null);
|
|
|
|
// Expand button
|
|
btnExpand = new JLabel(expandable ? "+" : " ");
|
|
btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
|
|
if (expandable)
|
|
btnExpand.addMouseListener(expand);
|
|
var gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTH;
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
list.add(btnExpand, gbc);
|
|
|
|
// Name label
|
|
lblName = new JLabel(name);
|
|
if (expandable)
|
|
lblName.addMouseListener(expand);
|
|
gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTH;
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
gbc.weightx = 1;
|
|
list.add(lblName, gbc);
|
|
|
|
// Value text box
|
|
txtValue = new JTextField();
|
|
txtValue.setBorder(null);
|
|
txtValue.setOpaque(false);
|
|
txtValue.setText("00000000");
|
|
gbc = new GridBagConstraints();
|
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
gbc.insets = new Insets(0, 4, 0, 0);
|
|
list.add(txtValue, gbc);
|
|
|
|
// Value changed
|
|
txtValue.addActionListener(e->{
|
|
String text = txtValue.getText();
|
|
int val = value;
|
|
try { switch (mode) {
|
|
case HEX : val = (int) Long.parseLong(text, 16); break;
|
|
case SIGNED : // Fallthrough
|
|
case UNSIGNED: val = (int) Long.parseLong(text, 10); break;
|
|
case FLOAT : val =
|
|
Float.floatToIntBits(Float.parseFloat(text) ); break;
|
|
}} catch (Exception x) { }
|
|
setValue(val);
|
|
});
|
|
|
|
// Expansion controls
|
|
switch (type) {
|
|
case PROGRAM : initProgram(); break;
|
|
case VUE.CHCW: initCHCW (); break;
|
|
case VUE.ECR : initECR (); break;
|
|
case VUE.PC : initPC (); break;
|
|
case VUE.PIR : initPIR (); break;
|
|
case VUE.PSW : initPSW (); break;
|
|
case VUE.TKCW: initTKCW (); break;
|
|
default: return;
|
|
}
|
|
|
|
// Expansion spacer
|
|
if (index != VUE.PC) {
|
|
spacer = new JPanel(null);
|
|
spacer.setOpaque(false);
|
|
spacer.setPreferredSize(new Dimension(0, 0));
|
|
spacer.setVisible(false);
|
|
gbc = new GridBagConstraints();
|
|
list.add(spacer, gbc);
|
|
}
|
|
|
|
// Expansion area
|
|
expansion.setOpaque(false);
|
|
expansion.setVisible(false);
|
|
gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.WEST;
|
|
if (index == VUE.PC)
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
gbc.insets = new Insets(0, 4, 0, 0);
|
|
gbc.weightx = 1;
|
|
list.add(expansion, gbc);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Expansion Constructors //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Expansion controls for CHCW
|
|
private void initCHCW() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
addCheckBox("ICE", 1, false); endRow();
|
|
}
|
|
|
|
// Expansion controls for ECR
|
|
private void initECR() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
addTextBox("EICC", 0, 16, false, true); endRow();
|
|
addTextBox("FECC", 16, 16, false, true); endRow();
|
|
}
|
|
|
|
// Expansion controls for program registers
|
|
private void initProgram() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
var group = new ButtonGroup();
|
|
group.add(addRadioButton("cpu.hex" , HEX ));
|
|
group.add(addRadioButton("cpu.signed" , SIGNED ));
|
|
group.add(addRadioButton("cpu.unsigned", UNSIGNED));
|
|
group.add(addRadioButton("cpu.float" , FLOAT ));
|
|
}
|
|
|
|
// Expansion controls for PC
|
|
private void initPC() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
|
|
// Configure controls
|
|
for (int x = 0; x < 2; x++) {
|
|
|
|
// Indentation
|
|
var spacer = new JPanel(null);
|
|
spacer.setOpaque(false);
|
|
spacer.setPreferredSize(new Dimension(0, 0));
|
|
var gbc = new GridBagConstraints();
|
|
gbc.weightx = 1;
|
|
expansion.add(spacer, gbc);
|
|
|
|
// Name label
|
|
var label = new JLabel();
|
|
parent.app.localizer.add(label,
|
|
x == 0 ? "cpu.jump_from" : "cpu.jump_to" );
|
|
gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
expansion.add(label, gbc);
|
|
|
|
// Value text box
|
|
var txt = new JTextField();
|
|
txt.addActionListener(e->{
|
|
txt.setText((String) txt.getClientProperty("text"));
|
|
list.requestFocus();
|
|
});
|
|
txt.setBorder(null);
|
|
txt.setOpaque(false);
|
|
gbc = new GridBagConstraints();
|
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
gbc.insets = new Insets(0, 4, 0, 0);
|
|
expansion.add(txt, gbc);
|
|
|
|
// Initialize the corresponding component field
|
|
if (x == 0)
|
|
txtJumpFrom = txt;
|
|
else txtJumpTo = txt;
|
|
}
|
|
|
|
}
|
|
|
|
// Expansion controls for PSW
|
|
private void initPSW() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
addCheckBox("Z" , 0, false);
|
|
addCheckBox("FRO", 9, false); endRow();
|
|
addCheckBox("S" , 1, false);
|
|
addCheckBox("FIV", 8, false); endRow();
|
|
addCheckBox("OV" , 2, false);
|
|
addCheckBox("FZD", 7, false); endRow();
|
|
addCheckBox("CY" , 3, false);
|
|
addCheckBox("FOV", 6, false); endRow();
|
|
addCheckBox("EP" , 14, false);
|
|
addCheckBox("FUD", 5, false); endRow();
|
|
addCheckBox("NP" , 15, false);
|
|
addCheckBox("FPR", 4, false); endRow();
|
|
addCheckBox("AE" , 13, false); endRow();
|
|
addCheckBox("ID" , 12, false);
|
|
addTextBox ("I" , 16, 4, false, false); endRow();
|
|
}
|
|
|
|
// Expansion controls for PIR
|
|
private void initPIR() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
addTextBox("PT", 0, 16, true, true); endRow();
|
|
}
|
|
|
|
// Expansion controls for TKCW
|
|
private void initTKCW() {
|
|
expansion = new JPanel(new GridBagLayout());
|
|
addCheckBox("OTM", 8, true);
|
|
addCheckBox("FVT", 5, true); endRow();
|
|
addCheckBox("FIT", 7, true);
|
|
addCheckBox("FUT", 4, true); endRow();
|
|
addCheckBox("FZT", 6, true);
|
|
addCheckBox("FPT", 3, true); endRow();
|
|
addCheckBox("RDI", 2, true);
|
|
addTextBox ("RD", 0, 2, true, false); endRow();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Package Methods //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Refresh controls
|
|
void refresh() {
|
|
|
|
// Value text box
|
|
value = parent.vue.getRegister(index, type != PROGRAM);
|
|
txtValue.setText(
|
|
type != PROGRAM || mode == HEX ?
|
|
String.format("%08X", value) :
|
|
mode == SIGNED ? Integer.toString(value) :
|
|
mode == UNSIGNED ? Long.toString(value & 0xFFFFFFFFL) :
|
|
Float.toString(Float.intBitsToFloat(value))
|
|
);
|
|
|
|
// PC expansion controls
|
|
if (index == VUE.PC) for (int x = 0; x < 2; x++) {
|
|
JTextField txt = x == 0 ? txtJumpFrom : txtJumpTo;
|
|
int index = x == 0 ? VUE.JUMP_FROM : VUE.JUMP_TO;
|
|
int value = parent.vue.getRegister(index, true);
|
|
String text = String.format("%08X", value);
|
|
txt.putClientProperty("text", text);
|
|
txt.setText(text);
|
|
}
|
|
|
|
// Other expansion controls
|
|
else for (var control : controls) {
|
|
|
|
// Check box
|
|
if (control instanceof JCheckBox) {
|
|
var ctrl = (JCheckBox) control;
|
|
int bit = (Integer) ctrl.getClientProperty("bit");
|
|
ctrl.setSelected((value & 1 << bit) != 0);
|
|
}
|
|
|
|
// Text box
|
|
if (control instanceof JTextField) {
|
|
var ctrl = (JTextField) control;
|
|
int bit = (Integer) ctrl.getClientProperty("bit" );
|
|
int digits = (Integer) ctrl.getClientProperty("digits");
|
|
boolean hex = (Boolean) ctrl.getClientProperty("hex");
|
|
int width = (Integer) ctrl.getClientProperty("width");
|
|
int val = value >> bit & (1 << width) - 1;
|
|
ctrl.setText(!hex ? Integer.toString(val) :
|
|
String.format("%0" + digits + "X", val));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Specify whether the expansion area is expanded
|
|
void setExpanded(boolean expanded) {
|
|
|
|
// Error checking
|
|
if (!expandable)
|
|
return;
|
|
|
|
// Update controls
|
|
this.expanded = expanded;
|
|
btnExpand.setText(expanded ? "-" : "+");
|
|
if (index != VUE.PC)
|
|
spacer.setVisible(expanded);
|
|
expansion.setVisible(expanded);
|
|
list.revalidate();
|
|
}
|
|
|
|
// Specify the width of the expand button
|
|
void setExpandWidth(int width) {
|
|
var size = btnExpand.getPreferredSize();
|
|
size.width = width;
|
|
btnExpand.setPreferredSize(size);
|
|
}
|
|
|
|
// Specify a new font
|
|
void setFont(Font font, int fontWidth, int fontHeight) {
|
|
this.font = font;
|
|
|
|
// Value text box
|
|
var size = new Dimension(8 * fontWidth + 4, fontHeight);
|
|
txtValue.setFont(font);
|
|
txtValue.setPreferredSize(size);
|
|
if (index == VUE.PC) {
|
|
txtJumpFrom.setFont(font);
|
|
txtJumpFrom.setPreferredSize(size);
|
|
txtJumpTo .setFont(font);
|
|
txtJumpTo .setPreferredSize(size);
|
|
}
|
|
|
|
// Expansion controls
|
|
for (var ctrl : controls) {
|
|
if (!(ctrl instanceof JTextField))
|
|
continue;
|
|
if ((Boolean) ctrl.getClientProperty("hex"))
|
|
((JTextField) ctrl).setFont(font);
|
|
int digits = (Integer) ctrl.getClientProperty("digits");
|
|
size = ctrl.getPreferredSize();
|
|
size.width = digits * fontWidth + 4;
|
|
ctrl.setPreferredSize(size);
|
|
}
|
|
|
|
}
|
|
|
|
// Change the display mode of a program register
|
|
void setMode(int mode) {
|
|
this.mode = mode;
|
|
txtValue.setFont(mode == HEX ? font : null);
|
|
refresh();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Private Methods //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Add a check box to the expansion area
|
|
private void addCheckBox(String name, int bit, boolean readOnly) {
|
|
int mask = 1 << bit;
|
|
|
|
// Configure control
|
|
var ctrl = new JCheckBox(name);
|
|
ctrl.putClientProperty("bit", bit);
|
|
ctrl.setBorder(null);
|
|
ctrl.setEnabled(!readOnly);
|
|
ctrl.setFocusable(false);
|
|
ctrl.setOpaque(false);
|
|
controls.add(ctrl);
|
|
|
|
// Event handler
|
|
ctrl.addItemListener(e->setValue(
|
|
e.getStateChange() == ItemEvent.SELECTED ?
|
|
value | mask : value & ~mask
|
|
));
|
|
|
|
// Configure expansion area
|
|
var gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
gbc.gridwidth = 2;
|
|
gbc.insets = new Insets(0, 4, 0, 4);
|
|
expansion.add(ctrl, gbc);
|
|
}
|
|
|
|
// Add a radio button to the expansion area
|
|
private JRadioButton addRadioButton(String key, int mode) {
|
|
|
|
// Configure control
|
|
var ctrl = new JRadioButton();
|
|
parent.app.localizer.add(ctrl, key);
|
|
ctrl.setBorder(null);
|
|
ctrl.setFocusable(false);
|
|
ctrl.setOpaque(false);
|
|
ctrl.setSelected(mode == HEX);
|
|
controls.add(ctrl);
|
|
|
|
// Event handler
|
|
ctrl.addItemListener(e->{
|
|
if (e.getStateChange() == ItemEvent.SELECTED) setMode(mode); });
|
|
|
|
// Configure expansion area
|
|
var gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
gbc.insets = new Insets(0, 4, 0, 0);
|
|
expansion.add(ctrl, gbc);
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
// Add a text box to the expansion area
|
|
private void addTextBox(String name, int bit, int width, boolean readOnly,
|
|
boolean hex) {
|
|
int mask = (1 << width) - 1;
|
|
|
|
// Configure control
|
|
var ctrl = new JTextField();
|
|
ctrl.putClientProperty("bit", bit);
|
|
ctrl.putClientProperty("digits",
|
|
Integer.toString(mask, hex ? 16 : 10).length());
|
|
ctrl.putClientProperty("hex", hex);
|
|
ctrl.putClientProperty("width", width);
|
|
ctrl.setBorder(null);
|
|
ctrl.setEnabled(!readOnly);
|
|
ctrl.setOpaque(false);
|
|
controls.add(ctrl);
|
|
|
|
// Event handlers
|
|
ctrl.addActionListener(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);
|
|
label.setEnabled(!readOnly);
|
|
var gbc = new GridBagConstraints();
|
|
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
gbc.insets = new Insets(0, 4, 0, 4);
|
|
expansion.add(label, gbc);
|
|
expansion.add(ctrl , gbc);
|
|
}
|
|
|
|
// Terminate a row of expansion controls
|
|
private void endRow() {
|
|
var spacer = new JPanel(null);
|
|
spacer.setOpaque(false);
|
|
spacer.setPreferredSize(new Dimension(0, 0));
|
|
var gbc = new GridBagConstraints();
|
|
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
|
expansion.add(spacer, gbc);
|
|
}
|
|
|
|
// Update the register value
|
|
private void setValue(int value) {
|
|
list.requestFocus();
|
|
parent.vue.setRegister(index, type != PROGRAM, value);
|
|
refresh();
|
|
}
|
|
|
|
} |