Introducing CPU register lists

This commit is contained in:
Guy Perfect 2020-08-06 16:37:05 -05:00
parent 2e38c1fa49
commit 7d8d33158f
17 changed files with 890 additions and 98 deletions

View File

@ -10,6 +10,7 @@ app {
debug {
(menu) Debug
console Console
cpu CPU
memory Memory
}
@ -36,6 +37,16 @@ console {
title Console
}
# CPU window
cpu {
float Float
hex Hex
last_pc Last PC
signed Signed
title CPU
unsigned Unsigned
}
# Memory window
memory {
title Memory

View File

@ -69,7 +69,7 @@ core:
desktop: clean_desktop
@echo " Compiling Java desktop application"
@javac -sourcepath src/desktop --release 10 -Xlint:unchecked \
-h src/desktop/native -d . src/desktop/Main.java
-h src/desktop/vue -d . src/desktop/Main.java
# Build all native modules
.PHONY: native
@ -103,7 +103,7 @@ clean_desktop:
# Delete everything but the .jar
.PHONY: clean_most
clean_most: clean_desktop
@rm -f src/desktop/native/vue_NativeVUE.h native/*.dll native/*.so
@rm -f src/desktop/vue/vue_NativeVUE.h native/*.dll native/*.so
@ -112,14 +112,14 @@ clean_most: clean_desktop
###############################################################################
# JNI header file
src/desktop/native/vue_NativeVUE.h: src/desktop/vue/NativeVUE.java
@javac -h src/desktop/native -sourcepath src/desktop -d . \
src/desktop/vue/vue_NativeVUE.h: src/desktop/vue/NativeVUE.java
@javac -h src/desktop/vue -sourcepath src/desktop -d . \
src/desktop/vue/NativeVUE.java
@sleep 3
# linux_x86
.PHONY: lin32_pre
lin32_pre: src/desktop/native/vue_NativeVUE.h
lin32_pre: src/desktop/vue/vue_NativeVUE.h
$(eval name = linux_x86)
$(eval prefix = `uname -m`-linux-gnu-)
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
@ -130,7 +130,7 @@ lin32: lin32_pre native_common
# linux_x86-64
.PHONY: lin64_pre
lin64_pre: src/desktop/native/vue_NativeVUE.h
lin64_pre: src/desktop/vue/vue_NativeVUE.h
$(eval name = linux_x86-64)
$(eval prefix = `uname -m`-linux-gnu-)
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
@ -141,7 +141,7 @@ lin64: lin64_pre native_common
# windows_x86
.PHONY: win32_pre
win32_pre: src/desktop/native/vue_NativeVUE.h
win32_pre: src/desktop/vue/vue_NativeVUE.h
$(eval name = windows_x86)
$(eval prefix = i686-w64-mingw32-)
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
@ -151,7 +151,7 @@ win32: win32_pre native_common
# windows_x86-64
.PHONY: win64_pre
win64_pre: src/desktop/native/vue_NativeVUE.h
win64_pre: src/desktop/vue/vue_NativeVUE.h
$(eval name = windows_x86-64)
$(eval prefix = x86_64-w64-mingw32-)
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
@ -165,4 +165,4 @@ native_common:
@echo " Building native module $(name)"
@$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -O2 \
-fno-strict-aliasing -Werror \
-o native/$(name)$(ext) src/desktop/native/native.c src/core/vue.c
-o native/$(name)$(ext) src/desktop/vue/NativeVUE.c src/core/vue.c

View File

@ -69,6 +69,13 @@ static int32_t busReadValue(VUE *vue, uint32_t address, int type) {
return 0; /* Unreachable */
}
/* Perform a system reset */
static void busReset(VUE *vue) {
int x;
for (x = 0; x < 0x10000; x++)
vue->bus.wram[x] = 0;
}
/* Write bytes to host memory */
static void busWriteBytes(uint8_t *dest, uint8_t *src, uint32_t address,
uint32_t size, uint32_t length) {

View File

@ -90,6 +90,13 @@ static int32_t cpuSetSystemRegister(VUE *vue, int index, int32_t value,
return vue->cpu.chcw_ice << 1;
case VUE_ECR:
if (debug) {
vue->cpu.ecr_fecc = value >> 16 & 0xFFFF;
vue->cpu.ecr_eicc = value & 0xFFFF;
}
return vue->cpu.ecr_fecc << 16 | vue->cpu.ecr_eicc;
case VUE_PSW :
vue->cpu.psw_i = value >> 16 & 15;
vue->cpu.psw_np = value >> 15 & 1;
@ -109,16 +116,16 @@ static int32_t cpuSetSystemRegister(VUE *vue, int index, int32_t value,
return value & 0x000FF3FF;
/* Remaining cases to encourage tableswitch */
case 4: case 6: case 7: case 8: case 9: case 10: case 11:
case 12: case 13: case 14: case 15: case 16: case 17: case 18:
case 19: case 20: case 21: case 22: case 23: case 26: case 27:
case 28: case 30:
case 6: case 7: case 8: case 9: case 10: case 11: case 12:
case 13: case 14: case 15: case 16: case 17: case 18: case 19:
case 20: case 21: case 22: case 23: case 26: case 27: case 28:
case 30:
return 0;
}
return 1; /* Unreachable */
}
/* System reset */
/* Perform a system reset */
static void cpuReset(VUE *vue) {
int x;
@ -127,15 +134,17 @@ static void cpuReset(VUE *vue) {
vue->cpu.fetch = 0;
vue->cpu.stage = CPU_FETCH;
/* Reset program counter */
vue->cpu.pc = 0xFFFFFFF0;
/* Clear all registers (hardware only sets ECR, PC and PSW) */
for (x = 0; x < 32; x++) {
vue->cpu.program[x] = 0;
cpuSetSystemRegister(vue, x, 0, VUE_TRUE);
}
/* Configure registers */
vue->cpu.ecr_eicc = 0xFFF0;
vue->cpu.lastPC = 0xFFFFFFF0;
vue->cpu.pc = 0xFFFFFFF0;
vue->cpu.psw_np = 1;
}
/* Test a condition */

View File

@ -43,6 +43,13 @@ extern "C" {
#define VUE_PSW 5
#define VUE_TKCW 7
/* Program register indexes */
#define VUE_GP 4
#define VUE_HP 2
#define VUE_LP 31
#define VUE_SP 3
#define VUE_TP 5
/*****************************************************************************
@ -68,6 +75,7 @@ typedef struct {
struct {
uint32_t cycles; /* Cycles until next stage */
int fetch; /* Fetch unit index */
int32_t lastPC; /* Previous value of PC */
int stage; /* Current processing stage */
/* Program registers */
@ -101,17 +109,17 @@ typedef struct {
int8_t psw_z; /* Zero */
/* Cache Control Word */
int32_t chcw_cec; /* Clear Entry Count */
int32_t chcw_cen; /* Clear Entry Number */
int8_t chcw_icc; /* Instruction Cache Clear */
int8_t chcw_icd; /* Instruction Cache Dump */
int8_t chcw_ice; /* Instruction Cache Enable */
int8_t chcw_icr; /* Instruction Cache Restore */
int32_t chcw_sa; /* Spill-Out Base Address */
uint16_t chcw_cec; /* Clear Entry Count */
uint16_t chcw_cen; /* Clear Entry Number */
int8_t chcw_icc; /* Instruction Cache Clear */
int8_t chcw_icd; /* Instruction Cache Dump */
int8_t chcw_ice; /* Instruction Cache Enable */
int8_t chcw_icr; /* Instruction Cache Restore */
int32_t chcw_sa; /* Spill-Out Base Address */
/* Exception Cause Register */
int8_t ecr_eicc; /* Exception/Interrupt Cause Code */
int8_t ecr_fecc; /* Fatal Error Cause Code */
uint16_t ecr_eicc; /* Exception/Interrupt Cause Code */
uint16_t ecr_fecc; /* Fatal Error Cause Code */
} cpu;
} VUE;
@ -125,6 +133,7 @@ typedef struct {
VUEAPI int32_t vueGetRegister(VUE *vue, int index, vbool system);
VUEAPI void vueInitialize (VUE *vue);
VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length);
VUEAPI void vueReset (VUE *vue);
VUEAPI int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value);
VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size);
VUEAPI vbool vueWrite (VUE *vue, uint32_t address, uint8_t *src, uint32_t length);

View File

@ -2,7 +2,6 @@
#include <vue.h>
/*****************************************************************************
* Constants *
*****************************************************************************/
@ -27,7 +26,7 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/* Retrieve the value of a register */
int32_t vueGetRegister(VUE *vue, int index, vbool system) {
return
return vue == NULL ? 0 :
index == VUE_PC && system ? vue->cpu.pc :
index < 0 || index > 31 ? 0 :
system ? cpuGetSystemRegister(vue, index) :
@ -37,6 +36,8 @@ int32_t vueGetRegister(VUE *vue, int index, vbool system) {
/* Prepare an emulation state context for use */
void vueInitialize(VUE *vue) {
if (vue == NULL)
return;
vue->bus.rom = NULL;
vue->bus.sram = NULL;
}
@ -79,13 +80,21 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) {
return VUE_TRUE;
}
/* Initialize all system components */
void vueReset(VUE *vue) {
if (vue == NULL)
return;
busReset(vue);
cpuReset(vue);
}
/* Specify a value for a register */
int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value) {
return
return vue == NULL ? 0 :
index == VUE_PC && system ? vue->cpu.pc = value & 0xFFFFFFFE :
index < 0 || index > 31 ? 0 :
system ? cpuSetSystemRegister(vue, index, value, VUE_TRUE) :
index == 0 ? 0 : vue->cpu.program[index]
index == 0 ? 0 : (vue->cpu.program[index] = value)
;
}

255
src/desktop/app/CPU.java Normal file
View File

@ -0,0 +1,255 @@
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(640, 480));
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
registers.add(new Register(parent, lst, "PC" , VUE.PC , VUE.PC ));
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 , VUE.PC ));
registers.add(new Register(parent, lst, "EIPSW", VUE.EIPSW, VUE.PSW ));
registers.add(new Register(parent, lst, "FEPC" , VUE.FEPC , VUE.PC ));
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, VUE.PC ));
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, VUE.PC ));
registers.add(new Register(parent, lst, "30" , 30, VUE.PC ));
registers.add(new Register(parent, lst, "31" , 31, VUE.PC ));
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

@ -29,6 +29,7 @@ class MainWindow extends JFrame {
// UI components
private JPanel client; // Common client container
private Console console; // Console window
private CPU cpu; // CPU window
private JDesktopPane desktop; // Container for child windows
private Memory memory; // Memory window
private JPanel video; // Video output
@ -92,9 +93,11 @@ class MainWindow extends JFrame {
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));
// Display window
refreshDebug();
pack();
setLocationRelativeTo(null);
setVisible(true);
@ -131,6 +134,11 @@ class MainWindow extends JFrame {
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
mnuDebug.add(mnuDebugMemory);
var mnuDebugCPU = new JMenuItem();
loc.add(mnuDebugCPU, "app.debug.cpu");
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
mnuDebug.add(mnuDebugCPU);
return mnuDebug;
}
@ -178,6 +186,7 @@ class MainWindow extends JFrame {
// Refresh all debug views
void refreshDebug() {
cpu .refresh();
memory.refresh();
}
@ -283,6 +292,7 @@ class MainWindow extends JFrame {
var bytes = rom.toByteArray();
// Pause emulation
vue.setROM(bytes, 0, bytes.length);
vue.reset();
refreshDebug();
// Resume emulation
}

View File

@ -13,12 +13,13 @@ import util.*;
class Memory extends ChildWindow {
// Private fields
private int address; // Address of top row
private Font font; // Display font
private int address; // Address of top row
private Font font; // Display font
private int fontHeight; // Font line height
private int fontWidth; // Font maximum character width
// UI components
private JPanel client; // Client area
private JLabel fontHeight; // Font height proxy
private JPanel client; // Client area
private ArrayList<Row> rows; // Rows of text
@ -44,23 +45,25 @@ class Memory extends ChildWindow {
super(parent, "memory.title");
// Configure instance fields
address = 0x00000000;
font = new Font(Util.fontFamily(new String[]
address = 0x00000000;
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
fontHeight = new JLabel(".");
rows = new ArrayList<Row>();
rows = new ArrayList<Row>();
// Configure client area
client = new JPanel(null);
client.addComponentListener(Util.onResize(e->onResize()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseWheelListener(e->onWheel(e));
client.setBackground(SystemColor.window);
client.setFocusable(true);
client.setPreferredSize(new Dimension(640, 480));
client.setBackground(SystemColor.window);
// Configure component
setContentPane(client);
var content = new JPanel(new BorderLayout());
content.setBorder(new JScrollPane().getBorder());
content.add(client, BorderLayout.CENTER);
setContentPane(content);
setFont2(font);
pack();
}
@ -79,11 +82,9 @@ class Memory extends ChildWindow {
return;
// Configure working variables
int height = client.getHeight();
int lineHeight = fontHeight.getPreferredSize().height;
int count = (height + lineHeight - 1) / lineHeight;
var data = new byte[count * 16];
var widths = new int[2];
int height = client.getHeight();
int count = (height + fontHeight - 1) / fontHeight;
var data = new byte[count * 16];
// Retrieve all visible bytes from the emulation context
parent.vue.read(address, data, 0, data.length);
@ -91,26 +92,17 @@ class Memory extends ChildWindow {
// Update visible rows
for (int x = 0; x < count; x++) {
Row row;
// Retrieve the row if it exists, make a new one otherwise
if (x < rows.size())
row = rows.get(x);
else row = createRow();
// Configure the row
update(row, address + x * 16, data, x * 16);
row = rows.get(x); // Retrieve row from collection
else row = createRow(); // Produce a new row
update(row, x * fontHeight, address + x * 16, data, x * 16);
setVisible(row, true);
measure(row, widths);
}
// Hide any rows that are not visible
for (int x = count; x < rows.size(); x++)
setVisible(rows.get(x), false);
// Position components
for (int x = 0; x < rows.size(); x++)
arrange(rows.get(x), x * lineHeight, widths, lineHeight);
// Finalize layout
client.revalidate();
client.repaint();
@ -119,12 +111,24 @@ class Memory extends ChildWindow {
// Specify a new font
void setFont2(Font font) {
this.font = font;
fontHeight.setFont(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 rows
for (var row : rows) {
row.address.setFont(font);
for (var label : row.bytes)
label.setFont(font);
}
onResize();
}
@ -192,27 +196,9 @@ class Memory extends ChildWindow {
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Position the elements of a row
private void arrange(Row row, int y, int[] widths, int height) {
int spacing = (height + 1) / 2;
// Size and position the address header
row.address.setSize(row.address.getPreferredSize());
row.address.setLocation(0, y);
// Size and position the byte labels
for (int z = 0, x = widths[0] + height; z < 16; z++) {
var label = row.bytes[z];
label.setBounds(x, y, widths[1], height);
x += widths[1] + (z == 7 ? height : spacing);
}
}
// Add a new row of output
private Row createRow() {
var row = new Row();
row.address =
// Address label
row.address = new JLabel();
@ -260,16 +246,24 @@ class Memory extends ChildWindow {
// Determine how many rows of output are visible
private int tall(boolean partial) {
int lineHeight = fontHeight.getPreferredSize().height;
return lineHeight == 0 ? 0 :
(client.getHeight() + (partial ? lineHeight + 1 : 0)) / lineHeight;
return (client.getHeight() + (partial?fontHeight-1:0)) / fontHeight;
}
// Update the text of a row
private void update(Row row, int address, byte[] data, int offset) {
private void update(Row row, int y, int address, byte[] data, int offset) {
// Update address
row.address.setBounds(0, y, 8 * fontWidth, fontHeight);
row.address.setText(String.format("%08X", address));
for (var label : row.bytes)
// Update bytes
for (int z = 0, x = 10 * fontWidth; z < 16; z++) {
var label = row.bytes[z];
label.setBounds(x, y, 2 * fontWidth, fontHeight);
label.setText(String.format("%02X", data[offset++] & 0xFF));
x += fontWidth * (z == 7 ? 4 : 3);
}
}
}

View File

@ -0,0 +1,419 @@
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 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 lblName; // Register name
private JPanel list; // Containing element
private JPanel spacer; // Expansion area spacer
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 PROGRAM = -2;
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Register(MainWindow parent, JPanel list, String name, int index, int type){
// Configure instance fields
controls = new ArrayList<JComponent>();
this.index = index;
this.list = list;
this.mode = HEX;
this.parent = parent;
this.type = type;
// Click handler for expand and name controls
MouseListener expand = type == VUE.PC ? null : Util.onMouse(
e->{ if (e.getButton() == 1) setExpanded(!expanded); }, null);
// Expand button
btnExpand = new JLabel(expand != null ? "+" : " ");
btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
if (expand != null)
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 (expand != null)
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.PIR : initPIR (); break;
case VUE.PSW : initPSW (); break;
case VUE.TKCW: initTKCW (); break;
default: return;
}
// Expansion spacer
spacer = new JPanel(null);
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
spacer.setVisible(false);
list.add(spacer, new GridBagConstraints());
// Expansion area
expansion.setOpaque(false);
expansion.setVisible(false);
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
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 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))
);
// Expansion controls
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 (type == VUE.PC)
return;
// Update controls
this.expanded = expanded;
btnExpand.setText (expanded ? "-" : "+");
expansion.setVisible(expanded);
spacer .setVisible(expanded);
list.revalidate();
list.repaint();
}
// 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
txtValue.setFont(font);
txtValue.setPreferredSize(
new Dimension(8 * fontWidth + 4, fontHeight));
// 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");
var 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, 0);
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.getLocalizer().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();
}
}

View File

@ -28,12 +28,13 @@ public final class Util {
///////////////////////////////////////////////////////////////////////////
// Event listener interfaces
public interface OnClose { void call(WindowEvent e); }
public interface OnClose2 { void call(InternalFrameEvent e); }
public interface OnFocus { void call(FocusEvent e); }
public interface OnKey { void call(KeyEvent e); }
public interface OnMouse { void call(MouseEvent e); }
public interface OnResize { void call(ComponentEvent e); }
public interface OnClose { void call(WindowEvent e); }
public interface OnClose2 { void call(InternalFrameEvent e); }
public interface OnFocus { void call(FocusEvent e); }
public interface OnKey { void call(KeyEvent e); }
public interface OnMouse { void call(MouseEvent e); }
public interface OnResize { void call(ComponentEvent e); }
public interface OnVisible { void call(AncestorEvent e); }
// Data class for byte-order marks
private static class BOM {
@ -310,6 +311,17 @@ public final class Util {
};
}
// Produce an AncestorListener using functional interfaces
public static AncestorListener onVisible(OnVisible show, OnVisible hide) {
return new AncestorListener() {
public void ancestorMoved (AncestorEvent e) { }
public void ancestorAdded (AncestorEvent e)
{ if (show != null) show.call(e); }
public void ancestorRemoved(AncestorEvent e)
{ if (hide != null) hide.call(e); }
};
}
// Configure the Swing look-and-feel with the system theme
public static boolean setSystemLAF() {
try {

View File

@ -87,6 +87,11 @@ class Bus {
return 0; // Unreachable
}
// Perform a system reset
public void reset() {
Arrays.fill(wram, 0, 0x10000, (byte) 0);
}
// Write bytes to host memory
void writeBytes(byte[] dest,byte[] src,int address,int offset,int length) {

View File

@ -9,6 +9,7 @@ class CPU {
// Package fields
int cycles; // Cycles until next stage
int fetch; // Fetch unit index
int lastPC; // Previous value of PC
int stage; // Current processing stage
// Program registers
@ -113,7 +114,19 @@ class CPU {
// Remaining cases to encourage tableswitch
case 8: case 9: case 10: case 11: case 12: case 13: case 14:
case 15: case 16: case 17: case 18: case 19: case 20: case 21:
case 15: case 16: case 17: case 18: case 19: case 20: case 21: // Configure instance fields
cycles = 0;
fetch = 0;
stage = FETCH;
// Reset program counter
pc = 0xFFFFFFF0;
// Clear all registers (hardware only sets ECR, PC and PSW)
for (int x = 0; x < 32; x++) {
program[x] = 0;
setSystemRegister(x, 0, true);
}
case 22: case 23: case 26: case 27: case 28:
return 0;
}
@ -128,15 +141,17 @@ class CPU {
fetch = 0;
stage = FETCH;
// Reset program counter
pc = 0xFFFFFFF0;
// Clear all registers (hardware only sets ECR, PC and PSW)
for (int x = 0; x < 32; x++) {
program[x] = 0;
setSystemRegister(x, 0, true);
}
// Configure registers
ecr_eicc = 0xFFF0;
lastPC = 0xFFFFFFF0;
pc = 0xFFFFFFF0;
psw_np = 1;
}
// Write a system register
@ -150,6 +165,13 @@ class CPU {
case 29 : return sr29 = value;
case 31 : return sr31 = debug || value >= 0 ? value : -value;
case VUE.ECR:
if (debug) {
ecr_fecc = value >> 16 & 0xFFFF;
ecr_eicc = value & 0xFFFF;
}
return ecr_fecc << 16 | ecr_eicc;
case VUE.CHCW :
chcw_cen = value >> 20 & 0x00000FFF;
chcw_cec = value >> 8 & 0x00000FFF;
@ -181,10 +203,10 @@ class CPU {
return value & 0x000FF3FF;
// Remaining cases to encourage tableswitch
case 4: case 6: case 7: case 8: case 9: case 10: case 11:
case 12: case 13: case 14: case 15: case 16: case 17: case 18:
case 19: case 20: case 21: case 22: case 23: case 26: case 27:
case 28: case 30:
case 6: case 7: case 8: case 9: case 10: case 11: case 12:
case 13: case 14: case 15: case 16: case 17: case 18: case 19:
case 20: case 21: case 22: case 23: case 26: case 27: case 28:
case 30:
return 0;
}
return 1; // Unreachable

View File

@ -26,6 +26,7 @@ class JavaVUE extends VUE {
JavaVUE() {
bus = new Bus(this);
cpu = new CPU(this);
reset();
}
@ -108,13 +109,19 @@ class JavaVUE extends VUE {
return true;
}
// Initialize all system components
public void reset() {
bus.reset();
cpu.reset();
}
// Specify a register value
public int setRegister(int index, boolean system, int value) {
return
index == VUE.PC && system ? cpu.pc = value & 0xFFFFFFFE :
index < 0 || index > 31 ? 0 :
system ? cpu.setSystemRegister(index, value, true) :
index == 0 ? 0 : cpu.program[index]
index == 0 ? 0 : (cpu.program[index] = value)
;
}

View File

@ -1,3 +1,5 @@
// Native-backed emulation core implementation
#include <stdlib.h>
#include <string.h>
#include <jni.h>
@ -21,7 +23,7 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
// Core context state type
typedef struct {
VUE vue;
VUE vue; /* Context into the generic C library */
} CORE;
@ -59,6 +61,7 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_construct
// Produce and initialize s new core context
CORE *core = calloc(sizeof (CORE), 1);
vueInitialize(&core->vue);
vueReset(&core->vue);
// Encode the context handle into a byte array
jbyteArray pointer = (*env)->NewByteArray(env, sizeof (void *));
@ -83,7 +86,7 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose
JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister
(JNIEnv *env, jobject vue, jint index, jboolean system) {
CORE *core = GetCore(env, vue);
return vueGetRegister(&core->vue, index, system);
return vueGetRegister(&core->vue, index, system);;
}
// Retrieve a copy of the ROM data
@ -130,6 +133,13 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read
return JNI_TRUE;
}
// Initialize all system components
JNIEXPORT void JNICALL Java_vue_NativeVUE_reset
(JNIEnv *env, jobject vue) {
CORE *core = GetCore(env, vue);
vueReset(&core->vue);
}
// Specify a register value
JNIEXPORT jint JNICALL Java_vue_NativeVUE_setRegister
(JNIEnv *env, jobject vue, jint index, jboolean system, jint value) {

View File

@ -39,6 +39,9 @@ class NativeVUE extends VUE {
public native boolean read(int address, byte[] dest, int offset,
int length);
// Initialize all system components
public native void reset();
// Specify a register value
public native int setRegister(int index, boolean system, int value);

View File

@ -32,6 +32,13 @@ public abstract class VUE {
public static final int PSW = 5;
public static final int TKCW = 7;
// Program register indexes
public static final int GP = 4;
public static final int HP = 2;
public static final int LP = 31;
public static final int SP = 3;
public static final int TP = 5;
///////////////////////////////////////////////////////////////////////////
@ -81,6 +88,9 @@ public abstract class VUE {
public abstract boolean read(int address, byte[] dest, int offset,
int length);
// Initialize all system components
public abstract void reset();
// Specify a register value
public abstract int setRegister(int index, boolean system, int value);