Implementing disassembler back-end, yet more adjustments to CPU window

This commit is contained in:
Guy Perfect 2020-08-08 18:40:22 -05:00
parent b7c2545ea7
commit 76c95b0523
14 changed files with 952 additions and 431 deletions

View File

@ -1,4 +1,4 @@
Copyright (C) 2019 Planet Virtual Boy
Copyright (C) 2020 Planet Virtual Boy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages

View File

@ -79,7 +79,6 @@ static void cpuDecode(VUE_INST *inst, int32_t bits) {
/* Decode by format */
switch (inst->format) {
case 0: return; /* Illegal opcode */
case 1:
inst->reg2 = bits >> 21 & 31;
inst->reg1 = bits >> 16 & 31;

View File

@ -159,6 +159,7 @@ typedef struct {
/* Emulation state */
typedef struct {
VUE_INST inst; /* Instruction state */
/* Memory bus */
struct {
@ -221,8 +222,6 @@ typedef struct {
uint16_t ecr_fecc; /* Fatal Error Cause Code */
} cpu;
/* Additional fields */
VUE_INST inst; /* Instruction state */
} VUE;

View File

@ -1,257 +0,0 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// CPU window
class CPU extends ChildWindow {
// Instance fields
private int expandWidth; // Width of expand buttons
private Font font; // Display font
private int fontHeight; // Font line height
private int fontWidth; // Font maximum character width
private boolean shown; // Window has been shown
private int systemHeight; // Initial height of system register list
// UI components
private JPanel dasm; // Disassembler
private JPanel system; // System registers
private JPanel program; // Program registers
private ArrayList<Register> registers; // Register controls
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CPU(MainWindow parent) {
super(parent, "cpu.title");
var client = getContentPane();
// Configure instance fields
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
registers = new ArrayList<Register>();
dasm = new JPanel(null);
dasm.setBackground(SystemColor.window);
dasm.setFocusable(true);
var inner = initRegisters();
var outer = Util.splitPane(JSplitPane.HORIZONTAL_SPLIT);
outer.setLeftComponent(new JScrollPane(dasm,
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
outer.setRightComponent(inner);
outer.setResizeWeight(1);
client.add(outer);
client.setPreferredSize(new Dimension(480, 300));
setFont2(font);
pack();
}
///////////////////////////////////////////////////////////////////////////
// Register Constructors //
///////////////////////////////////////////////////////////////////////////
// Initialize program register list
private void initProgram() {
Dimension target = null;
// Program register container
var lst = program = new JPanel(new GridBagLayout()) {
public Dimension getPreferredSize() {
var ret = super.getPreferredSize();
if (!shown)
ret.height = 0;
return ret;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (shown)
return;
shown = true;
system.revalidate();
program.revalidate();
}
};
lst.setBackground(SystemColor.window);
lst.setFocusable(true);
lst.addMouseListener(Util.onMouse(e->lst.requestFocus(),null));
// Produce the list of program registers
for (int x = 0; x < 32; x++) {
String name = "r" + x;
switch (x) {
case VUE.GP: name = "gp"; break;
case VUE.HP: name = "hp"; break;
case VUE.LP: name = "lp"; break;
case VUE.SP: name = "sp"; break;
case VUE.TP: name = "tp"; break;
}
registers.add(new Register(
parent, lst, name, x, Register.PROGRAM));
}
endList(lst);
}
// Initialize register lists
private JSplitPane initRegisters() {
initSystem();
initProgram();
var ret = Util.splitPane(JSplitPane.VERTICAL_SPLIT);
ret.setTopComponent(new JScrollPane(system,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
));
ret.setBottomComponent(new JScrollPane(program,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
));
return ret;
}
// Initialize system register list
private void initSystem() {
// System register container
var lst = system = new JPanel(new GridBagLayout()) {
public Dimension getPreferredSize() {
var ret = super.getPreferredSize();
if (!shown)
ret.height = systemHeight;
return ret;
}
};
system.setBackground(SystemColor.window);
system.setFocusable(true);
system.addMouseListener(Util.onMouse(e->system.requestFocus(), null));
system.putClientProperty("shown", true);
// Add the first two system registers and expand PSW
int plain = Register.PLAIN;
registers.add(new Register(parent, lst, "PC" , VUE.PC , plain ));
Register psw =new Register(parent, lst, "PSW" , VUE.PSW , VUE.PSW );
psw.setExpanded(true);
registers.add(psw);
shown = true;
systemHeight = system.getPreferredSize().height + 6;
shown = false;
// Add the remaining system registers
registers.add(new Register(parent, lst, "EIPC" , VUE.EIPC , plain ));
registers.add(new Register(parent, lst, "EIPSW", VUE.EIPSW, VUE.PSW ));
registers.add(new Register(parent, lst, "FEPC" , VUE.FEPC , plain ));
registers.add(new Register(parent, lst, "FEPSW", VUE.FEPSW, VUE.PSW ));
registers.add(new Register(parent, lst, "ECR" , VUE.ECR , VUE.ECR ));
registers.add(new Register(parent, lst, "ADTRE", VUE.ADTRE, plain ));
registers.add(new Register(parent, lst, "CHCW" , VUE.CHCW , VUE.CHCW));
registers.add(new Register(parent, lst, "PIR" , VUE.PIR , VUE.PIR ));
registers.add(new Register(parent, lst, "TKCW" , VUE.TKCW , VUE.TKCW));
registers.add(new Register(parent, lst, "29" , 29, plain ));
registers.add(new Register(parent, lst, "30" , 30, plain ));
registers.add(new Register(parent, lst, "31" , 31, plain ));
endList(lst);
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Client resize
private void onResize() {
//refreshDasm();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
// The element is not ready
if (registers == null)
return;
// Refresh registers
for (var reg : registers)
reg.refresh();
}
// Specify a new font
void setFont2(Font font) {
this.font = font;
// Configure the maximum font dimensions
var fontMax = new JLabel("!");
fontMax.setFont(font);
fontHeight = Math.max(1, fontMax.getPreferredSize().height);
fontWidth = -1;
for (int x = 0; x < 16; x++) {
fontMax.setText(Integer.toString(x, 16).toUpperCase());
fontWidth = Math.max(fontWidth, fontMax.getPreferredSize().width);
}
// Configure register list scrolling
for (int x = 0; x < 2; x++) {
Component ctrl = x == 0 ? system : program;
while (!(ctrl instanceof JScrollPane))
ctrl = ctrl.getParent();
((JScrollPane) ctrl).getVerticalScrollBar()
.setUnitIncrement(fontHeight);
}
// Determine the width of the register expand buttons
var expand = new JLabel("+");
int width = expand.getPreferredSize().width;
expand.setText("-");
width = Math.max(width, expand.getPreferredSize().width) + 4;
// Configure registers
for (var reg : registers) {
reg.setExpandWidth(width);
reg.setFont(font, fontWidth, fontHeight);
}
onResize();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Terminate a register list
private JPanel endList(JPanel list) {
var spacer = new JPanel(null);
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(1, 1));
var gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
list.add(spacer, gbc);
return spacer;
}
}

View File

@ -0,0 +1,179 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// CPU window
class CPUWindow extends ChildWindow {
// Instance fields
private int expandWidth; // Width of expand buttons
private boolean shown; // Window has been shown
private int systemHeight; // Initial height of system register list
// UI components
private JPanel dasm; // Disassembler
private RegisterList lstProgram; // Program register list
private RegisterList lstSystem; // System register list
///////////////////////////////////////////////////////////////////////////
// Global Settings //
///////////////////////////////////////////////////////////////////////////
static int regExpandWidth; // Width of register list expand button
static Font regHexFont; // Register list hex font
static Dimension regHexFontSize; // Max dimensions
static HashSet<CPUWindow> instances; // Spawned instances
// Static initializer
static {
setDefaults();
instances = new HashSet<CPUWindow>();
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Apply configuration settings to all instances
static void configureAll() {
for (var inst : instances)
inst.configure();
}
// Reset all settings to their default values
static void setDefaults() {
// Register list hex font
setRegHexFont(new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14));
// Width of register list expand button
var label = new JLabel("+");
regExpandWidth = label.getPreferredSize().width;
label.setText("-");
regExpandWidth = Math.max(regExpandWidth,
label.getPreferredSize().width);
}
// Specify a font to use as the register list hex font
static void setRegHexFont(Font font) {
regHexFont = font;
regHexFontSize = measureHex(font);
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CPUWindow(MainWindow parent) {
super(parent, "cpu.title");
instances.add(this);
var client = getContentPane();
dasm = new JPanel(null);
dasm.setBackground(SystemColor.window);
dasm.setFocusable(true);
lstSystem = new RegisterList(this, true);
lstProgram = new RegisterList(this, false);
var inner = Util.splitPane(JSplitPane.VERTICAL_SPLIT);
inner.setTopComponent(lstSystem);
inner.setBottomComponent(lstProgram);
var outer = Util.splitPane(JSplitPane.HORIZONTAL_SPLIT);
outer.setLeftComponent(new JScrollPane(dasm,
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
outer.setRightComponent(inner);
outer.setResizeWeight(1);
client.add(outer);
client.setPreferredSize(new Dimension(480, 300));
configure();
pack();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// The window is closing
public void dispose() {
instances.remove(this);
super.dispose();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
lstProgram.configure();
lstSystem .configure();
}
// Update the display
void refresh() {
lstSystem.refresh();
lstProgram.refresh();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Client resize
private void onResize() {
//refreshDasm();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Determine the maximum character width of a hex character in a font
private static Dimension measureHex(Font font) {
int ret = 0;
Dimension size = null;
// Process all digits
var label = new JLabel();
label.setFont(font);
for (int x = 0; x < 16; x++) {
label.setText(String.format(
"%" + (Disassembler.hexCaps ? "X" : "x"
), x));
size = label.getPreferredSize();
ret = Math.max(ret, size.width);
}
return new Dimension(ret, size.height);
}
}

View File

@ -5,7 +5,7 @@ import java.awt.*;
import javax.swing.*;
// Console window
class Console extends ChildWindow {
class ConsoleWindow extends ChildWindow {
// Instance fields
private boolean shown; // Component has been visible
@ -17,7 +17,7 @@ class Console extends ChildWindow {
///////////////////////////////////////////////////////////////////////////
// Default constructor
Console(MainWindow parent) {
ConsoleWindow(MainWindow parent) {
super(parent, "console.title");
getContentPane().setPreferredSize(new Dimension(384, 224));
pack();

View File

@ -0,0 +1,379 @@
package app;
// Project imports
import vue.*;
// Instruction disassembler
class Disassembler {
private Disassembler() { } // Cannot be instantiated
static { setDefaults(); } // Initialize to default settings
///////////////////////////////////////////////////////////////////////////
// Global Settings //
///////////////////////////////////////////////////////////////////////////
static boolean bcondCombine; // Merge Bcond condition into mnemonic
static boolean bcondNames; // Use symbolic condition names for Bcond
static boolean condCaps; // Uppercase condition mnemonics
static boolean destLast; // Destination register last
static boolean dispDest; // Resolve jump destination addresses
static boolean dispInside; // Load/store displacement inside brackets
static boolean hexCaps; // Upercase hexadecimal
static int hexMode; // Hexadecimal decoration
static boolean immNumber; // Use number sign with immediate values
static boolean jmpBrackets; // Square brackets around JMP
static boolean jumpAddress; // Use jump destination addresses
static boolean lower; // Use L instead of C in conditions
static boolean mnemonicCaps; // Uppercase mnemonics
static boolean programCaps; // Uppercase program register names
static boolean programNames; // Use symbolic program register names
static boolean setfCombine; // Merge SETF condition into mnemonic
static boolean setfNames; // Use symbolic condition names for SETF
static boolean systemCaps; // Uppercase system register names
static boolean systemNames; // Use symbolic system register names
static boolean zero; // Use Z instead of E in conditions
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Hexadecimal decorations
static final int DOLLAR = 0;
static final int H = 1;
static final int ZEROX = 2;
// Condition mneonics
static final String[] CONDITIONS = {
"V" , "C" , "E" , "NH", "N", "T", "LT", "LE",
"NV", "NC", "NE", "H" , "P", "F", "GE", "GT"
};
// Mnemonics
static final String[] MNEMONICS = {
"---" , "ADD" , "ADD" , "ADDF.S", "ADDI" , "AND" ,
"ANDBSU" , "ANDI" , "ANDNBSU", "Bcond" , "CAXI" , "CLI" ,
"CMP" , "CMP" , "CMPF.S" , "CVT.SW", "CVT.WS" , "DIV" ,
"DIVF.S" , "DIVU" , "HALT" , "IN.B" , "IN.H" , "IN.W" ,
"JAL" , "JMP" , "JR" , "LD.B" , "LD.H" , "LD.W" ,
"LDSR" , "MOV" , "MOV" , "MOVBSU", "MOVEA" , "MOVHI" ,
"MPYHW" , "MUL" , "MULF.S" , "MULU" , "NOT" , "NOTBSU" ,
"OR" , "ORBSU" , "ORI" , "ORNBSU", "OUT.B" , "OUT.H" ,
"OUT.W" , "RETI" , "REV" , "SAR" , "SAR" , "SCH0BSD",
"SCH0BSU", "SCH1BSD", "SCH1BSU", "SEI" , "SETF" , "SHL" ,
"SHL" , "SHR" , "SHR" , "ST.B" , "ST.H" , "ST.W" ,
"STSR" , "SUB" , "SUBF.S" , "TRAP" , "TRNC.SW", "XB" ,
"XH" , "XOR" , "XORBSU" , "XORI" , "XORNBSU"
};
// Program register names
private static final String[] PROGRAMS = {
null, null, "hp", "sp", "gp", "tp", null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, "lp"
};
// System register names
private static final String[] SYSTEMS = {
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
null , null , null , null , null , null , null , null ,
null , null , null , null , null , null , null , null ,
"CHCW", "ADTRE", null , null , null , null , null , null
};
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// One row of output
static class Row {
String address; // Bus address
String bytes; // Encoded bytes in bus order
String mnemonic; // Instruction mnemonic
String operands; // Instruction operands, if any
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Reset all settings to their default values
static void setDefaults() {
bcondCombine = true;
bcondNames = true;
condCaps = true;
destLast = true;
dispDest = true;
dispInside = false;
hexCaps = true;
hexMode = ZEROX;
immNumber = false;
jmpBrackets = true;
jumpAddress = true;
lower = true;
mnemonicCaps = true;
programCaps = false;
programNames = true;
setfCombine = false;
setfNames = true;
systemCaps = true;
systemNames = true;
zero = false;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Represent an instruction as text fields
static void disassemble(int address, Instruction inst, Row row) {
// Address
String x = hexCaps ? "X" : "x";
row.address = String.format("%08" + x, address);
// Bytes
x = "%02" + x;
row.bytes = inst.size == 2 ?
String.format(x + " " + x,
inst.bits >> 16 & 0xFF,
inst.bits >> 24 & 0xFF
) : String.format(x + " " + x + " " + x + " " + x,
inst.bits >> 16 & 0xFF,
inst.bits >> 24 & 0xFF,
inst.bits & 0xFF,
inst.bits >> 8 & 0xFF
)
;
// Bcond mnemonic
if (inst.id == VUE.BCOND && bcondCombine) {
row.mnemonic = "B" + CONDITIONS[inst.cond];
if (lower && (inst.cond & 7) == 1)
row.mnemonic = inst.cond == 1 ? "BL" : "BNL";
if (zero && (inst.cond & 7) == 2)
row.mnemonic = inst.cond == 2 ? "BZ" : "BNZ";
if ( (inst.cond & 7) == 5)
row.mnemonic = inst.cond == 5 ? "BR" : "NOP";
}
// SETF mnemonic
else if (inst.id == VUE.SETF && setfCombine)
row.mnemonic = "SETF" + CONDITIONS[inst.imm];
// All other mnemonics
else row.mnemonic = MNEMONICS[inst.id];
// Adjust mnemonic case
if (!mnemonicCaps)
row.mnemonic = row.mnemonic.toLowerCase();
// Operands by format
row.operands = null;
if (inst.id != VUE.ILLEGAL) switch (inst.format) {
case 1: // Fallthrough
case 7: row.operands = formatI_VII(inst ); break;
case 2: row.operands = formatII (inst ); break;
case 3: row.operands = formatIII (inst, address); break;
case 4: row.operands = destination(inst, address); break;
case 5: row.operands = formatV (inst ); break;
case 6: row.operands = formatVI (inst ); break;
}
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Format the destination of a branch or jump
private static String destination(Instruction inst, int address) {
return !dispDest ? toHex(inst.disp, 1, immNumber) :
String.format("%08" + (hexCaps ? "X" : "x"), address + inst.disp);
}
// Operands for Format I and Format VII
private static String formatI_VII(Instruction inst) {
String reg1 = program(inst.reg1);
String reg2 = program(inst.reg2);
// One-operand instructions
switch (inst.id) {
case VUE.JMP:
return String.format(jmpBrackets ? "[%s]" : "%s", reg1);
case VUE.XB: // Fallthrough
case VUE.XH:
return reg2;
}
// Generic
return String.format("%s, %s",
destLast ? reg1 : reg2,
destLast ? reg2 : reg1
);
}
// Operands for Format II
private static String formatII(Instruction inst) {
// Bit string
if (inst.opcode == 0b011111)
return null;
// Zero- or one-operand instructions
switch (inst.id) {
// Zero-operand
case VUE.CLI : // Fallthrough
case VUE.HALT: // Fallthrough
case VUE.RETI: // Fallthrough
case VUE.SEI :
return null;
// One-operand
case VUE.TRAP:
return Integer.toString(inst.imm);
}
// Combined SETF
String reg2 = program(inst.reg2);
if (inst.id == VUE.SETF && setfCombine)
return reg2;
// Two-operand instructions
String imm = String.format("%s%d", immNumber ? "#" : "", inst.imm);
switch (inst.id) {
case VUE.LDSR: // Fallthrough
case VUE.STSR:
if (!systemNames || SYSTEMS[inst.imm] == null)
break;
imm = SYSTEMS[inst.imm];
if (!systemCaps)
imm = imm.toLowerCase();
break;
case VUE.SETF:
if (!setfNames)
break;
imm = CONDITIONS[inst.imm];
if (!condCaps)
imm = imm.toLowerCase();
break;
}
// Generic
return String.format("%s, %s",
destLast ? imm : reg2,
destLast ? reg2 : imm
);
}
// Operands for Format III
private static String formatIII(Instruction inst, int address) {
// NOP
if (bcondCombine && inst.cond == 13)
return null;
// Format destination
String dest = destination(inst, address);
// One-operand notation
if (bcondCombine)
return dest;
// Two-operand notation
String cond = bcondNames ?
CONDITIONS[inst.cond] : Integer.toString(inst.cond);
if (bcondNames && !condCaps)
cond = cond.toLowerCase();
return String.format("%s, %s", cond, dest);
}
// Operands for Format V
private static String formatV(Instruction inst) {
String reg1 = program(inst.reg1);
String reg2 = program(inst.reg2);
String imm = toHex(
inst.id == VUE.MOVEA ? inst.imm & 0xFFFF : inst.imm,
inst.id == VUE.ADDI ? 1 : 4,
immNumber
);
return String.format("%s, %s, %s",
destLast ? imm : reg2,
reg1,
destLast ? reg2 : imm
);
}
// Operands for Format VI
private static String formatVI(Instruction inst) {
String src = program(inst.reg1);
String dest = program(inst.reg2);
// Data operand
src = inst.disp == 0 ? String.format("[%s]", src) :
dispInside ? String.format(
"[%s %s %s]",
src,
inst.disp < 0 ? "-" : "+",
toHex(Math.abs(inst.disp), 1, immNumber)
) : String.format(
"%s[%s]",
toHex(inst.disp, 1, immNumber),
src
)
;
// Write instruction
switch (inst.id) {
case VUE.OUT_B: // Fallthrough
case VUE.OUT_H: // Fallthrough
case VUE.OUT_W: // Fallthrough
case VUE.ST_B : // Fallthrough
case VUE.ST_H : // Fallthrough
case VUE.ST_W :
String temp = src;
src = dest;
dest = temp;
}
// Format operands
return String.format("%s, %s",
destLast ? src : dest,
destLast ? dest : src
);
}
// Format a program register
private static String program(int index) {
String ret = programNames ? PROGRAMS[index] : null;
if (ret == null)
ret = "r" + index;
return programCaps ? ret.toUpperCase() : ret;
}
// Represent an immediate value as hexadecimal
private static String toHex(int value, int digits, boolean number) {
return String.format(
"%s%s%s%0" + digits + (hexCaps ? "X" : "x") + "%s",
number ? "#" : "",
value < 0 ? "-" : "",
hexMode == DOLLAR ? "$" : hexMode == ZEROX ? "0x" : "",
value < 0 ? -value : value,
hexMode == H ? "h" : ""
);
}
}

View File

@ -28,12 +28,12 @@ class MainWindow extends JFrame {
// UI components
private JPanel client; // Common client container
private Console console; // Console window
private CPU cpu; // CPU window
private ConsoleWindow console; // Console window
private CPUWindow cpu; // CPU window
private JDesktopPane desktop; // Container for child windows
private Memory memory; // Memory window
private JPanel video; // Video output
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
@ -92,9 +92,9 @@ class MainWindow extends JFrame {
// Configure child windows
desktop = new JDesktopPane();
desktop.setBackground(SystemColor.controlShadow);
desktop.add(console = new Console(this));
desktop.add(cpu = new CPU (this));
desktop.add(memory = new Memory (this));
desktop.add(console = new ConsoleWindow(this));
desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this));
// Display window
refreshDebug();
@ -207,6 +207,7 @@ class MainWindow extends JFrame {
// Window close, File -> Exit
private void onClose() {
app.removeWindow(this);
cpu.dispose();
dispose();
vue.dispose();
}

View File

@ -10,7 +10,7 @@ import javax.swing.*;
import util.*;
// Memory viewer and hex editor window
class Memory extends ChildWindow {
class MemoryWindow extends ChildWindow {
// Private fields
private int address; // Address of top row
@ -41,7 +41,7 @@ class Memory extends ChildWindow {
///////////////////////////////////////////////////////////////////////////
// Default constructor
Memory(MainWindow parent) {
MemoryWindow(MainWindow parent) {
super(parent, "memory.title");
// Configure instance fields

View File

@ -14,26 +14,22 @@ import vue.*;
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
private boolean expandable; // The expansion area can be shown
private boolean expanded; // The expanded area is being shown
private int index; // Register index
private int mode; // Display mode for program registers
private String name; // Register name
private RegisterList parent; // Containing register list
private int type; // Expansion controls type
private 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
JPanel expansion; // Expansion container
JLabel btnExpand; // Expand button
JPanel indent; // Expansion area indentation
JLabel lblName; // Register name
JTextField txtValue; // Register value
ArrayList<JComponent> controls; // Expansion controls
@ -48,8 +44,8 @@ class Register {
static final int UNSIGNED = 2;
// Types
static final int PLAIN = -1;
static final int PROGRAM = -2;
static final int PLAIN = -2;
static final int PROGRAM = -3;
@ -58,14 +54,15 @@ class Register {
///////////////////////////////////////////////////////////////////////////
// Default constructor
Register(MainWindow parent, JPanel list, String name, int index, int type){
Register(RegisterList parent, String name, int index, int type) {
parent.add(index, this);
// Configure instance fields
controls = new ArrayList<JComponent>();
expandable = type != PLAIN && index != 0 || index == VUE.PC;
expandable = type != PLAIN && index != 0;
this.index = index;
this.list = list;
this.mode = HEX;
mode = HEX;
this.name = name;
this.parent = parent;
this.type = type;
@ -81,17 +78,17 @@ class Register {
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.fill = GridBagConstraints.HORIZONTAL;
list.add(btnExpand, gbc);
parent.add(gbc, btnExpand);
// Name label
lblName = new JLabel(name);
lblName = new JLabel(" ");
if (expandable)
lblName.addMouseListener(expand);
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
list.add(lblName, gbc);
parent.add(gbc, lblName);
// Value text box
txtValue = new JTextField();
@ -101,7 +98,7 @@ class Register {
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
list.add(txtValue, gbc);
parent.add(gbc, txtValue);
// Value changed
txtValue.addActionListener(e->{
@ -129,14 +126,14 @@ class Register {
default: return;
}
// Expansion spacer
// Expansion indentation
if (index != VUE.PC) {
spacer = new JPanel(null);
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
spacer.setVisible(false);
indent = new JPanel(null);
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
indent.setVisible(false);
gbc = new GridBagConstraints();
list.add(spacer, gbc);
parent.add(gbc, indent);
}
// Expansion area
@ -149,7 +146,11 @@ class Register {
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
gbc.weightx = 1;
list.add(expansion, gbc);
parent.add(gbc, expansion);
// Handling for PSW
if (index == VUE.PSW && type == VUE.PSW)
setExpanded(true);
}
@ -161,14 +162,14 @@ class Register {
// Expansion controls for CHCW
private void initCHCW() {
expansion = new JPanel(new GridBagLayout());
addCheckBox("ICE", 1, false); endRow();
addCheckBox("ICE", 1, false, true);
}
// 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();
addTextBox("EICC", 0, 16, false, true);
addTextBox("FECC", 16, 16, false, true);
}
// Expansion controls for program registers
@ -189,38 +190,37 @@ class Register {
for (int x = 0; x < 2; x++) {
// Indentation
var spacer = new JPanel(null);
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
indent = new JPanel(null);
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
gbc.weightx = 1;
expansion.add(spacer, gbc);
expansion.add(indent, gbc);
// Name label
var label = new JLabel();
parent.app.localizer.add(label,
parent.parent.parent.app.localizer.add(label,
x == 0 ? "cpu.jump_from" : "cpu.jump_to" );
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
expansion.add(label, gbc);
controls.add(label);
// Value text box
var txt = new JTextField();
txt.addActionListener(e->{
txt.setText((String) txt.getClientProperty("text"));
list.requestFocus();
parent.requestFocus();
});
txt.putClientProperty("index",
x == 0 ? VUE.JUMP_FROM : VUE.JUMP_TO);
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;
controls.add(txt);
}
}
@ -228,40 +228,40 @@ class Register {
// 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();
addCheckBox("Z" , 0, false, false);
addCheckBox("FRO", 9, false, true );
addCheckBox("S" , 1, false, false);
addCheckBox("FIV", 8, false, true );
addCheckBox("OV" , 2, false, false);
addCheckBox("FZD", 7, false, true );
addCheckBox("CY" , 3, false, false);
addCheckBox("FOV", 6, false, true );
addCheckBox("EP" , 14, false, false);
addCheckBox("FUD", 5, false, true );
addCheckBox("NP" , 15, false, false);
addCheckBox("FPR", 4, false, true );
addCheckBox("AE" , 13, false, true );
addCheckBox("ID" , 12, false, false);
addTextBox ("I" , 16, 4, false, false);
}
// Expansion controls for PIR
private void initPIR() {
expansion = new JPanel(new GridBagLayout());
addTextBox("PT", 0, 16, true, true); endRow();
addTextBox("PT", 0, 16, true, true);
}
// 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();
addCheckBox("OTM", 8, true, false);
addCheckBox("FVT", 5, true, true );
addCheckBox("FIT", 7, true, false);
addCheckBox("FUT", 4, true, true );
addCheckBox("FZT", 6, true, false);
addCheckBox("FPT", 3, true, true );
addCheckBox("RDI", 2, true, false);
addTextBox ("RD", 0, 2, true, false);
}
@ -270,11 +270,61 @@ class Register {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Refresh controls
void refresh() {
// Apply configuration settings
void configure() {
String name = null;
// System register
if (type != PROGRAM) {
name = this.name;
if (!Disassembler.systemCaps)
name = name.toLowerCase();
}
// Program register
else {
if (Disassembler.programNames)
name = this.name;
if (name == null)
name = "r" + index;
if (Disassembler.programCaps)
name = name.toUpperCase();
}
// Name label
lblName.setText(name);
// Expand button
var size = btnExpand.getPreferredSize();
size.width = CPUWindow.regExpandWidth;
btnExpand.setPreferredSize(size);
// Value text box
value = parent.vue.getRegister(index, type != PROGRAM);
var fontSize = CPUWindow.regHexFontSize;
size = new Dimension(8 * fontSize.width + 4, fontSize.height);
txtValue.setFont(CPUWindow.regHexFont);
txtValue.setPreferredSize(size);
// Expansion controls
for (var ctrl : controls) {
if (!(ctrl instanceof JTextField))
continue;
if (type == VUE.PC || (Boolean) ctrl.getClientProperty("hex"))
((JTextField) ctrl).setFont(CPUWindow.regHexFont);
int digits = type == VUE.PC ? 8 :
(Integer) ctrl.getClientProperty("digits");
size = ctrl.getPreferredSize();
size.width = digits * fontSize.width + 4;
ctrl.setPreferredSize(size);
}
}
// Refresh controls
void refresh() {
var vue = parent.parent.parent.vue;
// Value text box
value = vue.getRegister(index, type != PROGRAM);
txtValue.setText(
type != PROGRAM || mode == HEX ?
String.format("%08X", value) :
@ -283,18 +333,8 @@ class Register {
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) {
// Expansion controls
for (var control : controls) {
// Check box
if (control instanceof JCheckBox) {
@ -306,13 +346,32 @@ class Register {
// Text box
if (control instanceof JTextField) {
var ctrl = (JTextField) control;
int digits; // Maximum digits that can be displayed
boolean hex; // The value is hexadecimal
int val; // The value to be displayed
// Jump history
if (type == VUE.PC) {
digits = 8;
hex = true;
val = vue.getRegister((Integer)
ctrl.getClientProperty("index"), true);
}
// All other values
else {
int bit = (Integer) ctrl.getClientProperty("bit" );
int digits = (Integer) ctrl.getClientProperty("digits");
boolean hex = (Boolean) ctrl.getClientProperty("hex");
digits = (Integer) ctrl.getClientProperty("digits");
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));
val = value >> bit & (1 << width) - 1;
}
// Update the text
String text = !hex ? Integer.toString(val) : String.format(
"%0" + digits + (Disassembler.hexCaps ? "X" : "x"), val);
ctrl.putClientProperty("text", text);
ctrl.setText(text);
}
}
@ -329,52 +388,16 @@ class Register {
// Update controls
this.expanded = expanded;
btnExpand.setText(expanded ? "-" : "+");
if (index != VUE.PC)
spacer.setVisible(expanded);
if (type != VUE.PC)
indent.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);
}
parent.revalidate();
}
// Change the display mode of a program register
void setMode(int mode) {
this.mode = mode;
txtValue.setFont(mode == HEX ? font : null);
txtValue.setFont(mode == HEX ? CPUWindow.regHexFont : null);
refresh();
}
@ -385,7 +408,8 @@ class Register {
///////////////////////////////////////////////////////////////////////////
// Add a check box to the expansion area
private void addCheckBox(String name, int bit, boolean readOnly) {
private void addCheckBox(String name, int bit, boolean readOnly,
boolean last) {
int mask = 1 << bit;
// Configure control
@ -406,7 +430,7 @@ class Register {
// Configure expansion area
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = 2;
gbc.gridwidth = last ? GridBagConstraints.REMAINDER : 2;
gbc.insets = new Insets(0, 4, 0, 4);
expansion.add(ctrl, gbc);
}
@ -416,7 +440,7 @@ class Register {
// Configure control
var ctrl = new JRadioButton();
parent.app.localizer.add(ctrl, key);
parent.parent.parent.app.localizer.add(ctrl, key);
ctrl.setBorder(null);
ctrl.setFocusable(false);
ctrl.setOpaque(false);
@ -469,23 +493,16 @@ class Register {
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 = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
expansion.add(spacer, gbc);
gbc.insets = new Insets(0, 4, 0, 4);
expansion.add(ctrl , gbc);
}
// Update the register value
private void setValue(int value) {
list.requestFocus();
parent.vue.setRegister(index, type != PROGRAM, value);
parent.requestFocus();
parent.parent.parent.vue.setRegister(index, type != PROGRAM, value);
refresh();
}

View File

@ -0,0 +1,201 @@
package app;
// Java imports
import java.awt.*;
import java.util.*;
import javax.swing.*;
// Project imports
import vue.*;
// List of CPU registers
class RegisterList extends JScrollPane {
// Package fields
CPUWindow parent; // Containing CPU window
// Private fields
private boolean shown; // Component has been shown
private HashMap<Integer, Register> registers; // Register items
// UI components
private JPanel client; // Client area
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
RegisterList(CPUWindow parent, boolean system) {
super(VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Configure instance fields
this.parent = parent;
registers = new HashMap<Integer, Register>();
shown = true;
// Configure client area
client = new JPanel(new GridBagLayout()) {
public Dimension getPreferredSize() {
var ret = super.getPreferredSize();
if (!shown) ret.height = system ? getInitialHeight() : 0;
return ret;
}
public void paintComponent(Graphics g) {
shown = true;
super.paintComponent(g);
}
};
client.setBackground(SystemColor.window);
client.setFocusable(true);
// Initialize system registers
if (system) {
new Register(this, "PC" , VUE.PC , VUE.PC );
new Register(this, "PSW" , VUE.PSW , VUE.PSW );
new Register(this, "EIPC" , VUE.EIPC , Register.PLAIN);
new Register(this, "EIPSW", VUE.EIPSW, VUE.PSW );
new Register(this, "FEPC" , VUE.FEPC , Register.PLAIN);
new Register(this, "FEPSW", VUE.FEPSW, VUE.PSW );
new Register(this, "ECR" , VUE.ECR , VUE.ECR );
new Register(this, "ADTRE", VUE.ADTRE, Register.PLAIN);
new Register(this, "CHCW" , VUE.CHCW , VUE.CHCW );
new Register(this, "PIR" , VUE.PIR , VUE.PIR );
new Register(this, "TKCW" , VUE.TKCW , VUE.TKCW );
new Register(this, "29" , 29, Register.PLAIN);
new Register(this, "30" , 30, Register.PLAIN);
new Register(this, "31" , 31, Register.PLAIN);
}
// Initialize program registers
else for (int x = 0; x < 32; x++) {
String name = null;
switch (x) {
case VUE.GP: name = "gp"; break;
case VUE.HP: name = "hp"; break;
case VUE.LP: name = "lp"; break;
case VUE.SP: name = "sp"; break;
case VUE.TP: name = "tp"; break;
}
new Register(this, name, x, Register.PROGRAM);
}
// List terminator
var spacer = new JPanel(null);
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
client.add(spacer, gbc);
// Configure component
shown = false;
setViewportView(client);
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Draw the component
public void paintComponent(Graphics g) {
if (!shown) {
shown = true;
revalidate();
}
super.paintComponent(g);
}
// Transfer focus to the client area
public void requestFocus() {
client.requestFocus();
}
// Recalculate layout
public void revalidate() {
if (client != null)
client.revalidate();
super.revalidate();
repaint();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Add a register component to the client area
void add(GridBagConstraints gbc, JComponent comp) {
client.add(comp, gbc);
}
// Associate a register with its index
void add(int index, Register register) {
registers.put(index, register);
}
// Apply configuration settings
void configure() {
// Configure registers
for (var reg : registers.values())
reg.configure();
// Configure component
getVerticalScrollBar().setUnitIncrement(
CPUWindow.regHexFontSize.height);
}
// Update the display
void refresh() {
for (var reg : registers.values())
reg.refresh();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Determine the initial height upon first show
private int getInitialHeight() {
int height = 0;
var layout = (GridBagLayout) client.getLayout();
int ret = 0;
int row = 0;
// Process controls until the target row is found
for (var control : client.getComponents()) {
var ctrl = (JComponent) control;
// Track the tallest control on the row
if (ctrl.isVisible())
height = Math.max(height, ctrl.getPreferredSize().height);
// This is not the last control on the row
if (layout.getConstraints(control).gridwidth !=
GridBagConstraints.REMAINDER)
continue;
// Advance to the next row
ret += height;
height = 0;
row++;
// The target row has been reached
if (row == 4)
break;
}
return ret;
}
}

View File

@ -104,7 +104,6 @@ public class Instruction {
// Decode by format
switch (format) {
case 0: return; // Illegal opcode
case 1:
reg2 = bits >> 21 & 31;
reg1 = bits >> 16 & 31;

View File

@ -3,6 +3,9 @@ package vue;
// Java emulation core implementation
class JavaVUE extends VUE {
// Instance fields
Instruction inst; // Instruction state
// Components
Bus bus; // Memory bus
CPU cpu; // Processor
@ -26,6 +29,7 @@ class JavaVUE extends VUE {
JavaVUE() {
bus = new Bus(this);
cpu = new CPU(this);
inst = new Instruction();
reset();
}