pvbemu/src/desktop/app/Register.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();
}
}