Precursor to VIP introduction

This commit is contained in:
Guy Perfect 2020-12-23 12:26:02 -06:00
parent d636719775
commit 9f5a5233ea
19 changed files with 636 additions and 348 deletions

View File

@ -135,7 +135,7 @@
border-width: 0 1px 1px 0; border-width: 0 1px 1px 0;
} }
.bordered tr > :first-child { .bordered tr > :first-child, .op1 {
padding: 1px 12px 1px 12px; padding: 1px 12px 1px 12px;
text-align: center; text-align: center;
} }
@ -245,12 +245,12 @@
</p> </p>
<p> <p>
Floats, like words, are represented as sequences of character digits. They Floats, like words, are represented as sequences of character digits. They
are differentiated from words by the presence of a dot <code>.</code> are distinguished from words by the presence of a dot <code>.</code>
character somewhere within the sequence. Only one dot may be present in a character somewhere within the sequence. Only one dot may be present in a
float literal, and dots cannot be used in hexadecimal literals. If the given float literal, and dots cannot be used in hexadecimal literals. If the given
value cannot be represented in the float data type, a parsing error occurs. value cannot be represented in the float data type, a parsing error occurs.
Float values in the expression evaluator are subjected to the same Float values in the expression evaluator are subjected to the following
restrictions as on the Virtual Boy's CPU: if the result of any float restrictions: if the value of any float literal or the result of any float
operation is NaN, an infinity, a denormal number or negative zero, it will be operation is NaN, an infinity, a denormal number or negative zero, it will be
changed to positive zero. changed to positive zero.
</p> </p>
@ -259,7 +259,7 @@
slightly different. When used in a numeric operation, a boolean will use the slightly different. When used in a numeric operation, a boolean will use the
value <code>0</code> if false or the value <code>1</code> if true. Boolean value <code>0</code> if false or the value <code>1</code> if true. Boolean
literals are specified with the named values <code>true</code> and literals are specified with the named values <code>true</code> and
<code>false</code> within the expression. In a boolean operation, any result <code>false</code> within the expression. In a boolean operation, any value
that is zero is considered false, and any non-zero value is considered true. that is zero is considered false, and any non-zero value is considered true.
</p> </p>
<p> <p>
@ -298,13 +298,13 @@
<td class="mono">disp</td> <td class="mono">disp</td>
<td>Displacement offset for jumps and memory accesses.</td> <td>Displacement offset for jumps and memory accesses.</td>
</tr> </tr>
<tr> <!--tr>
<td class="mono">fetch</td> <td class="mono">fetch</td>
<td> <td>
Data unit index during a fetch operation, or <code>-1</code> if the read Data unit index during a fetch operation, or <code>-1</code> if the read
operation is not a fetch. operation is not a fetch.
</td> </td>
</tr> </tr-->
<tr> <tr>
<td class="mono">format</td> <td class="mono">format</td>
<td>Current instruction's encoding format.</td> <td>Current instruction's encoding format.</td>
@ -354,7 +354,7 @@
</tr> </tr>
<tr> <tr>
<td class="mono">value</td> <td class="mono">value</td>
<td>Value read by the current memory access.</td> <td>Value read or to be written by the current memory access.</td>
</tr> </tr>
<tr> <tr>
<td class="mono">vector</td> <td class="mono">vector</td>
@ -434,15 +434,14 @@
<p> <p>
Operators may apply to one (unary) or two (binary) values. All unary Operators may apply to one (unary) or two (binary) values. All unary
operators appear to the left of the value they modify (or another unary operators appear to the left of the value they modify (or another unary
operator). Binary operators appear between the values they modify. Each operator). Binary operators appear between the values they modify.
operator considers the types of its operands in order to produce a new value
of the appropriate type.
</p> </p>
<p> <p>
If the operands of a binary operator have different types, one of the values Each operator considers the types of its operands in order to produce a new
will be converted to the other type before performing the operation. The value of the appropriate type. If the operands of a binary operator have
conversion depends on the "greater" of the two types, in the following order different types, one of the values will be converted to the other type before
(higher is "greater"): performing the operation. The conversion selects the "greater" of the two
types, in the following order (higher is "greater"):
</p> </p>
<table class="indent"> <table class="indent">
<tr><td class="center narrow">&uarr;</td><td>Float</td></tr> <tr><td class="center narrow">&uarr;</td><td>Float</td></tr>
@ -452,7 +451,7 @@
</table> </table>
<p> <p>
For example, if an operation contains both a signed word and a float, the For example, if an operation contains both a signed word and a float, the
signed word value is first converted to float. signed word value is converted to float before performing the operation.
</p> </p>
<p> <p>
Operators have assigned precedence that specifies the order of operations. Operators have assigned precedence that specifies the order of operations.
@ -467,12 +466,14 @@
<p> <p>
The following operators may be used in expressions. Groups listed higher have The following operators may be used in expressions. Groups listed higher have
higher precedence and happen before groups listed lower. Operators within higher precedence and happen before groups listed lower. Operators within
groups have the same precedence and are processed in the order they appear in groups have the same precedence and are processed according to the order in
the expression from left to right. which they appear in the expression: right-to-left for unary operators,
left-to-right for binary operators.
</p> </p>
<table class="indent bordered"> <table class="indent bordered">
<tr> <tr>
<td class="mono open">~</td> <td rowSpan="18" class="middle">Unary</td>
<td class="mono open op1">~</td>
<td class="open">Not Bitwise</td> <td class="open">Not Bitwise</td>
<td class="open">Cannot be used with a float value.</td> <td class="open">Cannot be used with a float value.</td>
</tr> </tr>
@ -562,7 +563,8 @@
<td>The binary value is not modified.</td> <td>The binary value is not modified.</td>
</tr> </tr>
<tr> <tr>
<td class="mono open">/</td> <td rowSpan="20" class="middle">Binary</td>
<td class="mono open op1">/</td>
<td class="open">Divide</td> <td class="open">Divide</td>
<td class="open">Zero divisor yields zero as result.</td> <td class="open">Zero divisor yields zero as result.</td>
</tr> </tr>
@ -589,17 +591,17 @@
<tr> <tr>
<td class="mono open">&lt;&lt;</td> <td class="mono open">&lt;&lt;</td>
<td class="open">Shift Left</td> <td class="open">Shift Left</td>
<td class="open">Cannot be used with a float value.</td> <td class="open">Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono open">&gt;&gt;</td> <td class="mono open">&gt;&gt;</td>
<td class="open">Shift Right Arithmetic</td> <td class="open">Shift Right Arithmetic</td>
<td class="open">Cannot be used with a float value.</td> <td class="open">Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono">&gt;&gt;&gt;</td> <td class="mono">&gt;&gt;&gt;</td>
<td>Shift Right Logical</td> <td>Shift Right Logical</td>
<td>Cannot be used with a float value.</td> <td>Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono open">&gt;</td> <td class="mono open">&gt;</td>
@ -634,17 +636,17 @@
<tr> <tr>
<td class="mono">&amp;</td> <td class="mono">&amp;</td>
<td>And Bitwise</td> <td>And Bitwise</td>
<td>Cannot be used with a float value.</td> <td>Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono">^</td> <td class="mono">^</td>
<td>Exclusive Or Bitwise</td> <td>Exclusive Or Bitwise</td>
<td>Cannot be used with a float value.</td> <td>Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono">|</td> <td class="mono">|</td>
<td>Or Bitwise</td> <td>Or Bitwise</td>
<td>Cannot be used with a float value.</td> <td>Cannot be used with float values.</td>
</tr> </tr>
<tr> <tr>
<td class="mono">&amp;&amp;</td> <td class="mono">&amp;&amp;</td>
@ -691,6 +693,13 @@
<td>Performs a halfword read of the specified signedness.</td> <td>Performs a halfword read of the specified signedness.</td>
</tr> </tr>
</table> </table>
<p>
Reads are subjected to the alginment restrictions of the Virtual Boy's CPU:
the lowest bit of the address is ignored for halfword reads, and the lowest
two bits of the address are ignored for word reads.
</p>
<h1>&nbsp;</h1>
</body> </body>
</html> </html>

View File

@ -68,11 +68,33 @@ breakpoints {
} }
# Characters window
characters {
address Address
grid Grid
index Index
mirror Mirror
Palette Palette
scale Scale
title Characters
wide Wide
}
# Console window # Console window
console { console {
title Console title Console
} }
# Emulation core
core {
java Java
linux-x86 Linux (32-bit)
linux-x86_64 Linux (64-bit)
native Native
windows-x86 Windows (32-bit)
windows-x86_64 Windows (64-bit)
}
# CPU window # CPU window
cpu { cpu {
float Float float Float
@ -84,21 +106,6 @@ cpu {
unsigned Unsigned unsigned Unsigned
} }
# Memory window
memory {
title Memory
}
# Emulation core
core {
java Java
linux-x86 Linux (32-bit)
linux-x86_64 Linux (64-bit)
native Native
windows-x86 Windows (32-bit)
windows-x86_64 Windows (64-bit)
}
# File dialog # File dialog
dialog { dialog {
ext_isx ISX modules (*.isx) ext_isx ISX modules (*.isx)
@ -108,3 +115,21 @@ dialog {
load_rom_error Unable to load the selected ROM file. load_rom_error Unable to load the selected ROM file.
load_rom_notvb The selected file does not appear to be a Virtual Boy ROM. load_rom_notvb The selected file does not appear to be a Virtual Boy ROM.
} }
# Memory window
memory {
title Memory
}
# Palettes
palette {
Generic Generic
gplt0 BG 0
gplt1 BG 1
gplt2 BG 2
gplt3 BG 3
jplt0 OBJ 0
jplt1 OBJ 1
jplt2 OBJ 2
jplt3 OBJ 3
}

View File

@ -1164,7 +1164,7 @@ static vbool cpuFetch(Vue *vue) {
/* First unit */ /* First unit */
if (vue->cpu.fetch == 0) { if (vue->cpu.fetch == 0) {
vue->cpu.inst.bits = vue->cpu.access.value << 16; vue->cpu.inst.bits = vue->cpu.access.value << 16;
if (cpuSize(vue->cpu.access.value >> 10 & 0x3F) == 4) { if (cpuSize(vue->cpu.access.value >> 10) == 4) {
vue->cpu.fetch = 1; vue->cpu.fetch = 1;
return VUE_FALSE; return VUE_FALSE;
} }
@ -1172,7 +1172,7 @@ static vbool cpuFetch(Vue *vue) {
/* Second unit */ /* Second unit */
else { else {
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF; vue->cpu.inst.bits |= vue->cpu.access.value;
vue->cpu.fetch = -1; vue->cpu.fetch = -1;
} }

View File

@ -1,7 +1,9 @@
package app; package app;
// Java imports // Java imports
import java.awt.*;
import java.util.*; import java.util.*;
import javax.swing.*;
// Project imports // Project imports
import util.*; import util.*;
@ -16,7 +18,28 @@ public class App {
private ArrayList<MainWindow> windows; // Application windows private ArrayList<MainWindow> windows; // Application windows
// Configuration fields // Configuration fields
Localizer localizer; // UI localization manager Util.Font fntDialog; // Dialog font
Util.Font fntMono; // Monospaced font
int hexDigitWidth; // Width in pixels of one hex digit
Localizer localizer; // UI localization manager
int[][] rgbBase; // Base anaglyph filter colors
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Anaglyph presets
static final int[] RED = { 0xFF, 0x00, 0x00 };
static final int[] CYAN = { 0x00, 0xC6, 0xF0 };
static final int[] GREEN = { 0x00, 0xB4, 0x00 };
static final int[] MAGENTA = { 0xC8, 0x00, 0xFF };
// Eyes
static final int LEFT = 0;
static final int RIGHT = 1;
static final int CHR = 2;
@ -27,10 +50,17 @@ public class App {
// Default constructor // Default constructor
public App(boolean useNative) { public App(boolean useNative) {
// Instance fields // Configure instance fields
localizer = new Localizer(); localizer = new Localizer();
windows = new ArrayList<MainWindow>(); windows = new ArrayList<MainWindow>();
// Configure config fields
fntDialog = new Util.Font(new JLabel().getFont());
fntMono = new Util.Font(new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14));
hexDigitWidth = hexDigitWidth();
rgbBase = new int[][] { GREEN, MAGENTA, RED };
// Additional processing // Additional processing
setUseNative(useNative); setUseNative(useNative);
initLocales(); initLocales();
@ -83,6 +113,14 @@ public class App {
// Private Methods // // Private Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Calculate the maximum hexadecimal digit width
private int hexDigitWidth() {
int width = 0;
for (var digit : "0123456789ABCDEFabcdef".toCharArray())
width = Math.max(width, fntMono.metrics.charWidth(digit));
return width;
}
// Load and parse all locale translations // Load and parse all locale translations
private void initLocales() { private void initLocales() {

View File

@ -14,7 +14,6 @@ import vue.*;
class BreakpointsWindow extends ChildWindow { class BreakpointsWindow extends ChildWindow {
// Private fields // Private fields
private Font font; // Display font
private ArrayList<Item> items; // List items private ArrayList<Item> items; // List items
private int selectedIndex; // Selected list item private int selectedIndex; // Selected list item
@ -133,7 +132,7 @@ class BreakpointsWindow extends ChildWindow {
else if (breakpoint.any() && breakpoint.evaluate(inst, acc)) else if (breakpoint.any() && breakpoint.evaluate(inst, acc))
lblStatus.setText("\u2605"); // Star lblStatus.setText("\u2605"); // Star
else lblStatus.setText(" "); else lblStatus.setText(" ");
int lineHeight = lblStatus.getPreferredSize().height; int lineHeight = parent.app.fntDialog.metrics.getHeight();
lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight)); lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight));
} }
@ -158,8 +157,6 @@ class BreakpointsWindow extends ChildWindow {
super(parent, "breakpoints.title"); super(parent, "breakpoints.title");
// Configure instance fields // Configure instance fields
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
items = new ArrayList<Item>(); items = new ArrayList<Item>();
selectedIndex = -1; selectedIndex = -1;
@ -183,7 +180,7 @@ class BreakpointsWindow extends ChildWindow {
lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null)); lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null));
var scr = new JScrollPane(lstBreakpoints); var scr = new JScrollPane(lstBreakpoints);
client.add(scr, BorderLayout.CENTER); client.add(scr, BorderLayout.CENTER);
spacer = new JPanel(null); spacer = new JPanel();
spacer.setOpaque(false); spacer.setOpaque(false);
lstBreakpoints.add(spacer, SPACER); lstBreakpoints.add(spacer, SPACER);
@ -223,7 +220,7 @@ class BreakpointsWindow extends ChildWindow {
gbc.fill = GridBagConstraints.HORIZONTAL; gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.gridwidth = GridBagConstraints.REMAINDER;
props.add(buttons, gbc); props.add(buttons, gbc);
var fill = new JPanel(null); var fill = new JPanel();
fill.setOpaque(false); fill.setOpaque(false);
fill.addMouseListener(Util.onMouse(e->onDebug(e), null)); fill.addMouseListener(Util.onMouse(e->onDebug(e), null));
gbc = new GridBagConstraints(); gbc = new GridBagConstraints();
@ -602,7 +599,7 @@ class BreakpointsWindow extends ChildWindow {
var txt = new JTextField(); var txt = new JTextField();
txt.setEnabled(false); txt.setEnabled(false);
if (mono) if (mono)
txt.setFont(font); txt.setFont(parent.app.fntMono);
txt.addActionListener(e->client.requestFocus()); txt.addActionListener(e->client.requestFocus());
txt.addFocusListener (Util.onFocus(null, e->handler.run())); txt.addFocusListener (Util.onFocus(null, e->handler.run()));
var gbc = new GridBagConstraints(); var gbc = new GridBagConstraints();

View File

@ -13,7 +13,7 @@ import vue.*;
// CPU window // CPU window
class CPUWindow extends ChildWindow { class CPUWindow extends ChildWindow {
// Package fields // Instance fields
MainWindow parent; // Containing window MainWindow parent; // Containing window
// UI components // UI components
@ -23,70 +23,6 @@ class CPUWindow extends ChildWindow {
///////////////////////////////////////////////////////////////////////////
// Global Settings //
///////////////////////////////////////////////////////////////////////////
static Font dasmFont; // Disassembler font
static int dasmFontHeight; // Disassembler font line height
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) + 4;
// Disassembler font
setDasmFont(new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14));
}
// Specify a font to use as the disassembler font
static void setDasmFont(Font font) {
dasmFont = font;
var label = new JLabel("!");
label.setFont(font);
dasmFontHeight = label.getPreferredSize().height;
}
// Specify a font to use as the register list hex font
static void setRegHexFont(Font font) {
regHexFont = font;
regHexFontSize = measureHex(font);
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Constructors // // Constructors //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -94,7 +30,6 @@ class CPUWindow extends ChildWindow {
// Default constructor // Default constructor
CPUWindow(MainWindow parent) { CPUWindow(MainWindow parent) {
super(parent, "cpu.title"); super(parent, "cpu.title");
instances.add(this);
// Configure instance fields // Configure instance fields
this.parent = parent; this.parent = parent;
@ -117,34 +52,15 @@ class CPUWindow extends ChildWindow {
var client = getContentPane(); var client = getContentPane();
client.add(outer); client.add(outer);
client.setPreferredSize(new Dimension(480, 300)); client.setPreferredSize(new Dimension(480, 300));
configure();
pack(); pack();
} }
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// The window is closing
public void dispose() {
instances.remove(this);
super.dispose();
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
panDasm .configure();
lstProgram.configure();
lstSystem .configure();
}
// Update the display // Update the display
void refresh(boolean seekToPC) { void refresh(boolean seekToPC) {
panDasm .refresh(seekToPC); panDasm .refresh(seekToPC);
@ -152,42 +68,4 @@ class CPUWindow extends ChildWindow {
lstProgram.refresh(); lstProgram.refresh();
} }
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Client resize
private void onResize() {
//refreshDasm();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Determine the maximum 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

@ -0,0 +1,317 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
// VIP characters window
class CharactersWindow extends ChildWindow {
// Instance fields
private int color; // Selected color index
// UI components
private JPanel client; // Client area
private JPanel panCharacters; // Characters panel
private JPanel panPalette; // Palette panel
private JPanel panPattern; // Pattern panel
private JScrollPane scrControls; // Controls panel
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CharactersWindow(MainWindow parent) {
super(parent, "characters.title");
// Configure instance fields
color = 0;
// Configure client area
client = new JPanel(new BorderLayout());
client.setBackground(SystemColor.control);
client.setFocusable(true);
client.setPreferredSize(new Dimension(480, 360));
client.addComponentListener(Util.onResize(e->onResize()));
// Configure controls panel
var ctrls = new JPanel(new GridBagLayout());
ctrls.setBackground(SystemColor.control);
ctrls.addMouseListener(
Util.onMouse(e->client.requestFocus(), null));
scrControls = new JScrollPane(ctrls,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrControls.setBorder(null);
scrControls.getVerticalScrollBar().setUnitIncrement(20);
client.add(scrControls, BorderLayout.WEST);
label(ctrls, "characters.index", true);
spinner(ctrls, 0, 2047, 0, true);
label(ctrls, "characters.address", false);
textBox(ctrls);
label(ctrls, "characters.mirror", false);
textBox(ctrls);
// Pattern panel
var panPattern = new JPanel();
panPattern.setBackground(Color.black);
panPattern.setPreferredSize(new Dimension(96, 96));
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 2, 2, 2);
ctrls.add(panPattern, gbc);
// Palette panel
panPalette = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
onPaintPalette((Graphics2D) g, getWidth(), getHeight());
}
};
panPalette.setOpaque(false);
panPalette.setPreferredSize(new Dimension(0, 20 + 6));
panPalette.addMouseListener(
Util.onMouse(e->onMouseDownPalette(e), null));
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 2, 4, 2);
ctrls.add(panPalette, gbc);
// Fill the extra space above the view controls
var spacer = new JPanel();
spacer.setOpaque(false);
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
ctrls.add(spacer, gbc);
label(ctrls, "characters.grid", false);
checkBox(ctrls).setSelected(true);
label(ctrls, "characters.wide", false);
spinner(ctrls, 0, 2048, 0, false);
label(ctrls, "characters.palette", false);
select(ctrls, new String[] { "palette.generic",
"palette.gplt0", "palette.gplt1", "palette.gplt2", "palette.gplt3",
"palette.jplt0", "palette.jplt1", "palette.jplt2", "palette.jplt3"
});
label(ctrls, "characters.scale", false);
slider(ctrls, 1, 10, 1);
// Terminate the list of controls
spacer = new JPanel();
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
ctrls.add(spacer, gbc);
// Configure characters panel
panCharacters = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
onPaintCharacters((Graphics2D) g, getWidth(), getHeight());
}
};
panCharacters.setBackground(SystemColor.control);
panCharacters.addMouseListener(
Util.onMouse(e->client.requestFocus(), null));
var scr = new JScrollPane(panCharacters);
client.add(scr, BorderLayout.CENTER);
// Configure component
setContentPane(client);
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Palette mouse button press
private void onMouseDownPalette(MouseEvent e) {
// Common processing
client.requestFocus();
// Only consider left clicks
if (e.getButton() != MouseEvent.BUTTON1)
return;
// Working variables
int height = panPalette.getHeight();
int width = panPalette.getWidth();
int size = Math.max(1, Math.min((width - 18) / 4, height - 6));
int left = (width - size * 4 - 18) / 2;
int top = (height - size - 6) / 2;
int x = e.getX() - left - 3;
int y = e.getY() - top - 3;
// The click was not on top of a color
if (
x < 0 || x >= (size + 4) * 4 ||
x % (size + 4) >= size ||
y < 0 || y >= size
) return;
// Select the clicked color
color = x / (size + 4);
panPalette.repaint();
}
// Characters paint
private void onPaintCharacters(Graphics2D g, int width, int height) {
g.setColor(new Color(0x001830));
g.fillRect(0, 0, 256, 512);
}
// Palette paint
private void onPaintPalette(Graphics2D g, int width, int height) {
int size = Math.max(1, Math.min((width - 18) / 4, height - 6));
int left = (width - size * 4 - 18) / 2;
int top = (height - size - 6) / 2;
// Draw the color picker
for (int x = 0; x < 4; x++, left += size + 4) {
// The current color is selected
if (x == color) {
g.setColor(SystemColor.textHighlight);
g.fillRect(left , top , size + 6, size + 6);
g.setColor(SystemColor.control);
g.fillRect(left + 2, top + 2, size + 2, size + 2);
}
// Draw the color area
g.setColor(Color.black);
g.fillRect(left + 3, top + 3, size , size );
}
}
// Window resize
private void onResize() {
var viewport = scrControls.getViewport();
int inner = viewport.getView().getPreferredSize().width;
int outer = viewport.getExtentSize().width;
// The controls container does not need to be resized
if (inner == outer)
return;
// Size the controls container to match the inner component
scrControls.setPreferredSize(new Dimension(
scrControls.getPreferredSize().width + inner - outer, 0));
scrControls.revalidate();
scrControls.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a check box to the controls panel
private JCheckBox checkBox(JPanel panel) {
var chk = new JCheckBox();
chk.setBorder(null);
chk.setFocusable(false);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(chk, gbc);
return chk;
}
// Add a label to the controls panel
private void label(JPanel panel, String key, boolean top) {
var lbl = new JLabel();
parent.app.localizer.add(lbl, key);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(top ? 2 : 0, 2, 2, 2);
panel.add(lbl, gbc);
}
// Add a combo box to the controls panel
private JComboBox<String> select(JPanel panel, String[] options) {
var cmb = new JComboBox<String>();
parent.app.localizer.add(cmb, options);
cmb.setSelectedIndex(0);
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 1, 2);
panel.add(cmb, gbc);
return cmb;
}
// Add a slider to the controls panel
private JSlider slider(JPanel panel, int min, int max, int value) {
var sld = new JSlider(min, max, value);
sld.setFocusable(false);
sld.setPreferredSize(new Dimension(0, sld.getPreferredSize().height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 1, 2);
panel.add(sld, gbc);
return sld;
}
// Add a spinner to the controls panel
private JSpinner spinner(JPanel panel, int min, int max, int value,
boolean top) {
var spn = new JSpinner(new SpinnerNumberModel(value, min, max, 1));
spn.setEditor(new JSpinner.NumberEditor(spn, "#"));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(top ? 2 : 0, 0, 2, 2);
gbc.weightx = 1;
panel.add(spn, gbc);
return spn;
}
// Add a text box to the controls panel
private JTextField textBox(JPanel panel) {
var txt = new JTextField();
txt.setFont(parent.app.fntMono);
var size = txt.getPreferredSize();
txt.setPreferredSize(new Dimension(
parent.app.hexDigitWidth * 8 + 2 + size.width, size.height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(txt, gbc);
return txt;
}
}

View File

@ -21,8 +21,8 @@ class DisassemblerPane extends JScrollPane {
private int[] widths; // Column widths private int[] widths; // Column widths
// UI components // UI components
private JPanel client; // Client area private JPanel client; // Client area
private ArrayList<Row> rows; // Disassembler output private ArrayList<Row> rows; // Disassembler output
@ -53,23 +53,22 @@ class DisassemblerPane extends JScrollPane {
widths = new int[5]; widths = new int[5];
// Configure client area // Configure client area
client = new JPanel(null) { client = new JPanel() {
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
onPaint((Graphics2D) g, getWidth(), getHeight()); onPaint((Graphics2D) g, getWidth(), getHeight());
} }
}; };
client.setBackground(SystemColor.window);
client.setFocusable(true);
client.addFocusListener( client.addFocusListener(
Util.onFocus(e->client.repaint(), e->client.repaint())); Util.onFocus(e->client.repaint(), e->client.repaint()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null)); client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null)); client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.addMouseWheelListener(e->onMouseWheel(e)); client.addMouseWheelListener(e->onMouseWheel(e));
client.setBackground(SystemColor.window);
client.setFocusable(true);
// Configure component // Configure component
setViewportView(client); setViewportView(client);
configure();
} }
@ -78,17 +77,14 @@ class DisassemblerPane extends JScrollPane {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
client.repaint();
}
// Update the display // Update the display
void refresh(boolean seekToPC) { void refresh(boolean seekToPC) {
if (seekToPC) {
int pc = parent.parent.vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, tall(false) / 3);
}
client.repaint(); client.repaint();
int pc = parent.parent.vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, tall(false) / 3);
} }
@ -176,10 +172,11 @@ class DisassemblerPane extends JScrollPane {
} }
// Configure working variables // Configure working variables
int address = this.address; int address = this.address;
int count = tall(true); int count = tall(true);
var data = new byte[count * 4]; var data = new byte[count * 4];
int offset = 0; int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
int offset = 0;
// Disassemble from the current address // Disassemble from the current address
vue.readBytes(address, data, 0, data.length); vue.readBytes(address, data, 0, data.length);
@ -202,8 +199,8 @@ class DisassemblerPane extends JScrollPane {
} }
g.setColor(bg); g.setColor(bg);
g.fillRect( g.fillRect(
0, y * CPUWindow.dasmFontHeight, 0, y * lineHeight,
width, CPUWindow.dasmFontHeight width, lineHeight
); );
} }
@ -220,7 +217,7 @@ class DisassemblerPane extends JScrollPane {
// Configure all rows // Configure all rows
for (int y = 0; y < rows.size(); y++) { for (int y = 0; y < rows.size(); y++) {
var row = rows.get(y); var row = rows.get(y);
int top = y * CPUWindow.dasmFontHeight; int top = y * lineHeight;
// Configure all labels // Configure all labels
for (int z = 0, x = 0; z < 5; z++) { for (int z = 0, x = 0; z < 5; z++) {
@ -235,17 +232,17 @@ class DisassemblerPane extends JScrollPane {
// Configure the label // Configure the label
label.setLocation(x, top); label.setLocation(x, top);
label.setSize(label.getPreferredSize()); label.setSize(label.getPreferredSize());
x += widths[z] + CPUWindow.dasmFontHeight; x += widths[z] + lineHeight;
} }
} }
// Update the client's size // Update the client's size
var size = client.getPreferredSize(); var size = client.getPreferredSize();
width = -CPUWindow.dasmFontHeight + 1; width = -lineHeight + 1;
for (int x = 0; x < 5; x++) for (int x = 0; x < 5; x++)
if (widths[x] != 0 && (x != 2 || showBytes)) if (widths[x] != 0 && (x != 2 || showBytes))
width += CPUWindow.dasmFontHeight + widths[x]; width += lineHeight + widths[x];
if (width == size.width) if (width == size.width)
return; return;
client.setPreferredSize(new Dimension(width, 0)); client.setPreferredSize(new Dimension(width, 0));
@ -268,7 +265,7 @@ class DisassemblerPane extends JScrollPane {
for (int x = 0; x < 5; x++) { for (int x = 0; x < 5; x++) {
var label = row.labels[x] = new JLabel(); var label = row.labels[x] = new JLabel();
if (x != 4) if (x != 4)
label.setFont(CPUWindow.dasmFont); label.setFont(parent.parent.app.fntMono);
client.add(label); client.add(label);
} }
@ -380,9 +377,10 @@ class DisassemblerPane extends JScrollPane {
// Determine how many rows of output are visible // Determine how many rows of output are visible
private int tall(boolean partial) { private int tall(boolean partial) {
int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
return Math.max(1, (client.getHeight() + return Math.max(1, (client.getHeight() +
(partial ? CPUWindow.dasmFontHeight - 1 : 0) (partial ? lineHeight - 1 : 0)
) / CPUWindow.dasmFontHeight); ) / lineHeight);
} }
// Update a row with its text and measure the column widths // Update a row with its text and measure the column widths

View File

@ -15,9 +15,11 @@ import vue.*;
class MainWindow extends JFrame { class MainWindow extends JFrame {
// Instance fields // Instance fields
App app; // Containing application App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint Breakpoint brkStep; // Single step internal breakpoint
Vue vue; // Emulation core context int[][][] palettes; // Raster palettes
byte[] vram; // Snapshot of VIP memory
Vue vue; // Emulation core context
// Private fields // Private fields
private boolean debugMode; // Window is in debug mode private boolean debugMode; // Window is in debug mode
@ -28,23 +30,40 @@ class MainWindow extends JFrame {
private File romFile; // Currently loaded ROM file private File romFile; // Currently loaded ROM file
// UI components // UI components
private BreakpointsWindow breakpoints; // Breakpoints window private JPanel client; // Common client container
private JPanel client; // Common client container private JDesktopPane desktop; // Container for child windows
private ConsoleWindow console; // Console window private JMenu mnuDebug; // Debug menu
private CPUWindow cpu; // CPU window private JPanel video; // Video output
private JDesktopPane desktop; // Container for child windows
private MemoryWindow memory; // Memory window
private JMenu mnuDebug; // Debug menu
private JPanel video; // Video output
private JMenuItem mnuFileDebugMode; // File -> Debug mode private JMenuItem mnuFileDebugMode; // File -> Debug mode
private JMenuItem mnuFileGameMode; // File -> Game mode private JMenuItem mnuFileGameMode; // File -> Game mode
// Child windows
private BreakpointsWindow breakpoints;
private CharactersWindow characters;
private ConsoleWindow console;
private CPUWindow cpu;
private MemoryWindow memory;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Constants // // Constants //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Palette indexes
static final int GENERIC = 0;
static final int GPLT0 = 1;
static final int GPLT1 = 2;
static final int GPLT2 = 3;
static final int GPLT3 = 4;
static final int JPLT0 = 5;
static final int JPLT1 = 6;
static final int JPLT2 = 7;
static final int JPLT3 = 8;
static final int LEFT = 0;
static final int RIGHT = 1;
static final int RED = 2;
// Application icon // Application icon
private static final BufferedImage APPICON; private static final BufferedImage APPICON;
@ -55,6 +74,17 @@ class MainWindow extends JFrame {
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Calculate the address of a character by index
static int chrAddress(int index) {
return index >> 9 << 15 | (index & 511) << 4;
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Constructors // // Constructors //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -65,13 +95,16 @@ class MainWindow extends JFrame {
// Configure instance fields // Configure instance fields
this.app = app; this.app = app;
palettes = new int[9][3][4];
pwd = Util.PWD; pwd = Util.PWD;
vram = new byte[0x40000];
vue = Vue.create(app.getUseNative()); vue = Vue.create(app.getUseNative());
System.out.println("Native: " + System.out.println("Native: " +
(vue.isNative() ? Vue.getNativeID() : "No")); (vue.isNative() ? Vue.getNativeID() : "No"));
//vue.write(0x00078000, Vue.S32, -1);
// Configure video pane // Configure video pane
video = new JPanel(null) { video = new JPanel() {
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
onPaintVideo((Graphics2D) g, getWidth(), getHeight()); onPaintVideo((Graphics2D) g, getWidth(), getHeight());
@ -96,6 +129,7 @@ class MainWindow extends JFrame {
desktop = new JDesktopPane(); desktop = new JDesktopPane();
desktop.setBackground(SystemColor.controlShadow); desktop.setBackground(SystemColor.controlShadow);
desktop.add(breakpoints = new BreakpointsWindow(this)); desktop.add(breakpoints = new BreakpointsWindow(this));
desktop.add(characters = new CharactersWindow (this));
desktop.add(console = new ConsoleWindow (this)); desktop.add(console = new ConsoleWindow (this));
desktop.add(cpu = new CPUWindow (this)); desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this)); desktop.add(memory = new MemoryWindow (this));
@ -160,8 +194,8 @@ class MainWindow extends JFrame {
mnuDebug.add(mnuDebugBackgrounds); mnuDebug.add(mnuDebugBackgrounds);
var mnuDebugCharacters = new JMenuItem(); var mnuDebugCharacters = new JMenuItem();
mnuDebugCharacters.setEnabled(false);
loc.add(mnuDebugCharacters, "app.debug.characters"); loc.add(mnuDebugCharacters, "app.debug.characters");
mnuDebugCharacters.addActionListener(e->characters.setVisible(true));
mnuDebug.add(mnuDebugCharacters); mnuDebug.add(mnuDebugCharacters);
var mnuDebugFrameBuffers = new JMenuItem(); var mnuDebugFrameBuffers = new JMenuItem();
@ -226,7 +260,12 @@ class MainWindow extends JFrame {
// Refresh all debug views // Refresh all debug views
void refreshDebug(boolean seekToPC) { void refreshDebug(boolean seekToPC) {
vue.readBytes(0x00000000, vram, 0, vram.length);
refreshPalettes();
breakpoints.refresh(); breakpoints.refresh();
characters .refresh();
cpu .refresh(seekToPC); cpu .refresh(seekToPC);
memory .refresh(); memory .refresh();
} }
@ -364,6 +403,58 @@ class MainWindow extends JFrame {
// Private Methods // // Private Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Update the palette composites
private void refreshPalettes() {
// Process brightness levels
int[] brt = { 0,
vue.read(0x0005F824, Vue.U8),
vue.read(0x0005F826, Vue.U8),
vue.read(0x0005F828, Vue.U8)
};
brt[3] += brt[0] + brt[1];
for (int x = 1; x < 4; x++)
brt[x] = (Math.min(127, brt[x]) * 510 + 127) / 254 << 1;
// Process all palettes
var pal = new int[4];
for (int x = 0; x < 9; x++) {
// Generic palette
if (x == GENERIC) {
pal[1] = 0x55 << 1;
pal[2] = 0xAA << 1;
pal[3] = 0xFF << 1;
}
// Palette from emulation state
else {
int bits = vue.read(0x0005F860 + (x - 1 << 1), Vue.U8);
pal[1] = brt[bits >> 2 & 3];
pal[2] = brt[bits >> 4 & 3];
pal[3] = brt[bits >> 6 ];
}
// Process colors
for (int y = 0; y < 3; y++) {
var base = app.rgbBase[y];
var dest = palettes[x][y];
for (int z = 1; z < 4; z++) {
dest[z] = 0xFF000000;
for (int w = 0, bits = 16; w < 3; w++, bits -= 8)
dest[z] |= (pal[z] * base[w] + 255) / 510 << bits;
}
}
}
}
// Separate the RGB components of a color
private static int[] split(int rgb) {
return new int[] { rgb >> 16 & 0xFF, rgb >> 8 & 0xFF, rgb & 0xFF };
}
// Update the window title // Update the window title
private void updateTitle() { private void updateTitle() {
app.localizer.add(this, app.localizer.add(this,

View File

@ -13,10 +13,7 @@ import util.*;
class MemoryWindow extends ChildWindow { class MemoryWindow extends ChildWindow {
// Private fields // Private fields
private int address; // Address of top row 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 // UI components
private JPanel client; // Client area private JPanel client; // Client area
@ -46,8 +43,6 @@ class MemoryWindow extends ChildWindow {
// Configure instance fields // Configure instance fields
address = 0x00000000; address = 0x00000000;
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
rows = new ArrayList<Row>(); rows = new ArrayList<Row>();
// Configure client area // Configure client area
@ -64,7 +59,6 @@ class MemoryWindow extends ChildWindow {
content.setBorder(new JScrollPane().getBorder()); content.setBorder(new JScrollPane().getBorder());
content.add(client, BorderLayout.CENTER); content.add(client, BorderLayout.CENTER);
setContentPane(content); setContentPane(content);
setFont2(font);
pack(); pack();
} }
@ -82,9 +76,10 @@ class MemoryWindow extends ChildWindow {
return; return;
// Configure working variables // Configure working variables
int height = client.getHeight(); int height = client.getHeight();
int count = (height + fontHeight - 1) / fontHeight; int lineHeight = parent.app.fntMono.metrics.getHeight();
var data = new byte[count * 16]; int count = (height + lineHeight - 1) / lineHeight;
var data = new byte[count * 16];
// Retrieve all visible bytes from the emulation context // Retrieve all visible bytes from the emulation context
parent.vue.readBytes(address, data, 0, data.length); parent.vue.readBytes(address, data, 0, data.length);
@ -95,7 +90,7 @@ class MemoryWindow extends ChildWindow {
if (x < rows.size()) if (x < rows.size())
row = rows.get(x); // Retrieve row from collection row = rows.get(x); // Retrieve row from collection
else row = createRow(); // Produce a new row else row = createRow(); // Produce a new row
update(row, x * fontHeight, address + x * 16, data, x * 16); update(row, x * lineHeight, address + x * 16, data, x * 16);
setVisible(row, true); setVisible(row, true);
} }
@ -108,30 +103,6 @@ class MemoryWindow extends ChildWindow {
client.repaint(); client.repaint();
} }
// 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 rows
for (var row : rows) {
row.address.setFont(font);
for (var label : row.bytes)
label.setFont(font);
}
onResize();
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -202,7 +173,8 @@ class MemoryWindow extends ChildWindow {
// Add a new row of output // Add a new row of output
private Row createRow() { private Row createRow() {
var row = new Row(); var font = parent.app.fntMono;
var row = new Row();
// Address label // Address label
row.address = new JLabel(); row.address = new JLabel();
@ -250,22 +222,25 @@ class MemoryWindow extends ChildWindow {
// Determine how many rows of output are visible // Determine how many rows of output are visible
private int tall(boolean partial) { private int tall(boolean partial) {
return (client.getHeight() + (partial?fontHeight-1:0)) / fontHeight; int lineHeight = parent.app.fntMono.metrics.getHeight();
return (client.getHeight() + (partial?lineHeight-1:0)) / lineHeight;
} }
// Update the text of a row // Update the text of a row
private void update(Row row, int y, int address, byte[] data, int offset) { private void update(Row row, int y, int address, byte[] data, int offset) {
int hexDigitWidth = parent.app.hexDigitWidth;
int lineHeight = parent.app.fntMono.metrics.getHeight();
// Update address // Update address
row.address.setBounds(0, y, 8 * fontWidth, fontHeight); row.address.setBounds(0, y, 8 * hexDigitWidth, lineHeight);
row.address.setText(String.format("%08X", address)); row.address.setText(String.format("%08X", address));
// Update bytes // Update bytes
for (int z = 0, x = 10 * fontWidth; z < 16; z++) { for (int z = 0, x = 10 * hexDigitWidth; z < 16; z++) {
var label = row.bytes[z]; var label = row.bytes[z];
label.setBounds(x, y, 2 * fontWidth, fontHeight); label.setBounds(x, y, 2 * hexDigitWidth, lineHeight);
label.setText(String.format("%02X", data[offset++] & 0xFF)); label.setText(String.format("%02X", data[offset++] & 0xFF));
x += fontWidth * (z == 7 ? 4 : 3); x += hexDigitWidth * (z == 7 ? 4 : 3);
} }
} }

View File

@ -47,6 +47,10 @@ class Register {
static final int PLAIN = -2; static final int PLAIN = -2;
static final int PROGRAM = -3; static final int PROGRAM = -3;
// Expand button labels
static final String COLLAPSE = "-";
static final String EXPAND = "+";
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -71,7 +75,7 @@ class Register {
if (e.getButton() == 1) setExpanded(!expanded); }, null); if (e.getButton() == 1) setExpanded(!expanded); }, null);
// Expand button // Expand button
btnExpand = new JLabel(expandable ? "+" : " "); btnExpand = new JLabel(expandable ? EXPAND : "");
btnExpand.setHorizontalAlignment(SwingConstants.CENTER); btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
if (expandable) if (expandable)
btnExpand.addMouseListener(expand); btnExpand.addMouseListener(expand);
@ -124,12 +128,12 @@ class Register {
case Vue.PIR : initPIR (); break; case Vue.PIR : initPIR (); break;
case Vue.PSW : initPSW (); break; case Vue.PSW : initPSW (); break;
case Vue.TKCW: initTKCW (); break; case Vue.TKCW: initTKCW (); break;
default: return; default: configure(); return;
} }
// Expansion indentation // Expansion indentation
if (index != Vue.PC) { if (index != Vue.PC) {
indent = new JPanel(null); indent = new JPanel();
indent.setOpaque(false); indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0)); indent.setPreferredSize(new Dimension(0, 0));
indent.setVisible(false); indent.setVisible(false);
@ -152,6 +156,9 @@ class Register {
// Handling for PSW // Handling for PSW
if (index == Vue.PSW && type == Vue.PSW) if (index == Vue.PSW && type == Vue.PSW)
setExpanded(true); setExpanded(true);
// Apply application settings
configure();
} }
@ -191,7 +198,7 @@ class Register {
for (int x = 0; x < 2; x++) { for (int x = 0; x < 2; x++) {
// Indentation // Indentation
indent = new JPanel(null); indent = new JPanel();
indent.setOpaque(false); indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0)); indent.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints(); var gbc = new GridBagConstraints();
@ -295,26 +302,29 @@ class Register {
lblName.setText(name); lblName.setText(name);
// Expand button // Expand button
var size = btnExpand.getPreferredSize(); var size = btnExpand.getPreferredSize();
size.width = CPUWindow.regExpandWidth; var metrics = parent.parent.parent.app.fntDialog.metrics;
size.width = 4 + Math.max(
metrics.stringWidth(EXPAND), metrics.stringWidth(COLLAPSE));
btnExpand.setPreferredSize(size); btnExpand.setPreferredSize(size);
// Value text box // Value text box
var fontSize = CPUWindow.regHexFontSize; var fntMono = parent.parent.parent.app.fntMono;
size = new Dimension(8 * fontSize.width + 4, fontSize.height); int hexDigitWidth = parent.parent.parent.app.hexDigitWidth;
txtValue.setFont(CPUWindow.regHexFont); txtValue.setFont(fntMono);
txtValue.setPreferredSize(size); txtValue.setPreferredSize(new Dimension(
hexDigitWidth * 8 + 4, fntMono.metrics.getHeight()));
// Expansion controls // Expansion controls
for (var ctrl : controls) { for (var ctrl : controls) {
if (!(ctrl instanceof JTextField)) if (!(ctrl instanceof JTextField))
continue; continue;
if (type == Vue.PC || (Boolean) ctrl.getClientProperty("hex")) if (type == Vue.PC || (Boolean) ctrl.getClientProperty("hex"))
((JTextField) ctrl).setFont(CPUWindow.regHexFont); ((JTextField) ctrl).setFont(fntMono);
int digits = type == Vue.PC ? 8 : int digits = type == Vue.PC ? 8 :
(Integer) ctrl.getClientProperty("digits"); (Integer) ctrl.getClientProperty("digits");
size = ctrl.getPreferredSize(); size = ctrl.getPreferredSize();
size.width = digits * fontSize.width + 4; size.width = digits * hexDigitWidth + 4;
ctrl.setPreferredSize(size); ctrl.setPreferredSize(size);
} }
} }
@ -397,7 +407,7 @@ class Register {
// Change the display mode of a program register // Change the display mode of a program register
void setMode(int mode) { void setMode(int mode) {
this.mode = mode; this.mode = mode;
txtValue.setFont(mode == HEX ? CPUWindow.regHexFont : null); txtValue.setFont(mode!=HEX ? null : parent.parent.parent.app.fntMono);
refresh(); refresh();
} }

View File

@ -85,7 +85,7 @@ class RegisterList extends JScrollPane {
} }
// List terminator // List terminator
var spacer = new JPanel(null); var spacer = new JPanel();
spacer.setOpaque(false); spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0)); spacer.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints(); var gbc = new GridBagConstraints();
@ -144,7 +144,7 @@ class RegisterList extends JScrollPane {
} }
// Apply configuration settings // Apply configuration settings
void configure() { void configure2() {
// Configure registers // Configure registers
for (var reg : registers.values()) for (var reg : registers.values())
@ -152,7 +152,7 @@ class RegisterList extends JScrollPane {
// Configure component // Configure component
getVerticalScrollBar().setUnitIncrement( getVerticalScrollBar().setUnitIncrement(
CPUWindow.regHexFontSize.height); parent.parent.app.fntMono.metrics.getHeight());
} }
// Update the display // Update the display

View File

@ -24,7 +24,7 @@ public final class Util {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Types // // Classes //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Event listener interfaces // Event listener interfaces
@ -43,6 +43,16 @@ public final class Util {
BOM(byte[] m, Charset s) { mark = m; set = s; } BOM(byte[] m, Charset s) { mark = m; set = s; }
} }
// Font metrics wrapper
public static class Font extends java.awt.Font {
public final FontMetrics metrics;
public Font(java.awt.Font font) {
super(font);
metrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
.getGraphics().getFontMetrics(this);
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@ -1537,7 +1537,7 @@ public class Breakpoint {
case CODE : sym = vue.getExceptionCode(); break; case CODE : sym = vue.getExceptionCode(); break;
case COND : sym = inst.cond ; break; case COND : sym = inst.cond ; break;
case DISP : sym = inst.disp ; break; case DISP : sym = inst.disp ; break;
case FETCH : sym = vue.getFetch (); break; case FETCH : sym = acc.fetch ; break;
case FORMAT : sym = inst.format ; break; case FORMAT : sym = inst.format ; break;
case ID : sym = inst.id ; break; case ID : sym = inst.id ; break;
case OPCODE : sym = inst.opcode ; break; case OPCODE : sym = inst.opcode ; break;

View File

@ -443,8 +443,8 @@ class CPU {
// First unit // First unit
if (fetch == 0) { if (fetch == 0) {
inst.bits = access.value & 0xFFFF; inst.bits = access.value;
if (Instruction.size(access.value >> 10 & 0x3F) == 4) { if (Instruction.size(access.value >> 10) == 4) {
fetch = 1; fetch = 1;
return false; return false;
} }

View File

@ -114,6 +114,7 @@ public class Instruction {
boolean read = false; boolean read = false;
var ret = new Access(); var ret = new Access();
ret.address = vue.getRegister(reg1, false) + disp; ret.address = vue.getRegister(reg1, false) + disp;
ret.fetch = vue.getFetch();
ret.type = Vue.S32; ret.type = Vue.S32;
// Configure descriptor by ID // Configure descriptor by ID
@ -139,10 +140,17 @@ public class Instruction {
if (read) if (read)
ret.value = vue.read(ret.address, ret.type); ret.value = vue.read(ret.address, ret.type);
// Select the value to write to the bus // Write a register to the bus
else ret.value = vue.getRegister(id == Vue.CAXI && else if (id != Vue.CAXI)
vue.getRegister(reg2, false) == vue.read(ret.address, Vue.S32) ? vue.getRegister(reg2, false);
30 : reg2, false);
// CAXI processing
else {
int value = vue.read(ret.address, Vue.S32);
int compare = vue.getRegister(reg2, false);
int exchange = vue.getRegister( 30, false);
ret.value = value == compare ? value : exchange;
}
return ret; return ret;
} }

View File

@ -76,11 +76,6 @@ class JavaVue extends Vue {
return maxCycles; return maxCycles;
} }
// Retrieve a snapshot of the current state's memory access
public Access getAccess() {
return cpu.access;
}
// Retrieve the most recent applicaiton break code // Retrieve the most recent applicaiton break code
public int getBreakCode() { public int getBreakCode() {
return breakCode; return breakCode;
@ -91,11 +86,6 @@ class JavaVue extends Vue {
return cpu.exception; return cpu.exception;
} }
// Retrieve a snapshot of the current state's instruction
public Instruction getInstruction() {
return cpu.inst;
}
// Retrieve a register value // Retrieve a register value
public int getRegister(int index, boolean system) { public int getRegister(int index, boolean system) {

View File

@ -141,22 +141,6 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
return vueEmulate(&core->vue, maxCycles); return vueEmulate(&core->vue, maxCycles);
} }
// Retrieve a snapshot of the current state's memory access
JNIEXPORT jobject JNICALL Java_vue_NativeVue_getAccess
(JNIEnv *env, jobject vue, jlong handle, jobject acc) {
Core *core = *(Core **)&handle;
jclass cls = (*env)->GetObjectClass(env, acc);
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "address", "I"),
core->vue.cpu.access.address);
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "fetch", "I"),
core->vue.cpu.fetch);
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "type", "I"),
core->vue.cpu.access.type);
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "value", "I"),
core->vue.cpu.access.value);
return acc;
}
// Retrieve the most recent exception code // Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) { (JNIEnv *env, jobject vue, jlong handle) {
@ -171,36 +155,6 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
return vueGetExceptionCode(&core->vue); return vueGetExceptionCode(&core->vue);
} }
// Retrieve a snapshot of the current state's instruction
JNIEXPORT jobject JNICALL Java_vue_NativeVue_getInstruction
(JNIEnv *env, jobject vue, jlong handle, jobject inst) {
Core *core = *(Core **)&handle;
jclass cls = (*env)->GetObjectClass(env, inst);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "bits", "I"),
core->vue.cpu.inst.bits);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "cond", "I"),
core->vue.cpu.inst.cond);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "disp", "I"),
core->vue.cpu.inst.disp);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "format", "I"),
core->vue.cpu.inst.format);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "id", "I"),
core->vue.cpu.inst.id);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "imm", "I"),
core->vue.cpu.inst.imm);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "opcode", "I"),
core->vue.cpu.inst.opcode);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "reg1", "I"),
core->vue.cpu.inst.reg1);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "reg2", "I"),
core->vue.cpu.inst.reg2);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "size", "I"),
core->vue.cpu.inst.size);
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env,cls,"subopcode","I"),
core->vue.cpu.inst.subopcode);
return inst;
}
// Retrieve a register value // Retrieve a register value
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) { (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {

View File

@ -42,12 +42,6 @@ class NativeVue extends Vue {
public int emulate(int maxCycles) public int emulate(int maxCycles)
{ return emulate(handle, maxCycles); } { return emulate(handle, maxCycles); }
// Retrieve a snapshot of the current state's memory access
private native Access getAccess(long handle, Access access);
public Access getAccess() {
return getAccess(handle, new Access());
}
// Retrieve the most recent application break code // Retrieve the most recent application break code
private native int getBreakCode(long handle); private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); } public int getBreakCode() { return getBreakCode(handle); }
@ -58,12 +52,6 @@ class NativeVue extends Vue {
return getExceptionCode(handle); return getExceptionCode(handle);
} }
// Retrieve a snapshot of the current state's instruction
private native Instruction getInstruction(long handle, Instruction inst);
public Instruction getInstruction() {
return getInstruction(handle, new Instruction());
}
// Retrieve a register value // Retrieve a register value
private native int getRegister(long handle, int index, boolean system); private native int getRegister(long handle, int index, boolean system);
public int getRegister(int index, boolean system) public int getRegister(int index, boolean system)