Precursor to VIP introduction

This commit is contained in:
2020-12-23 12:26:02 -06:00
parent d636719775
commit 9f5a5233ea
19 changed files with 636 additions and 348 deletions
+2 -2
View File
@@ -1164,7 +1164,7 @@ static vbool cpuFetch(Vue *vue) {
/* First unit */
if (vue->cpu.fetch == 0) {
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;
return VUE_FALSE;
}
@@ -1172,7 +1172,7 @@ static vbool cpuFetch(Vue *vue) {
/* Second unit */
else {
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
vue->cpu.inst.bits |= vue->cpu.access.value;
vue->cpu.fetch = -1;
}
+40 -2
View File
@@ -1,7 +1,9 @@
package app;
// Java imports
import java.awt.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
@@ -16,7 +18,28 @@ public class App {
private ArrayList<MainWindow> windows; // Application windows
// 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
public App(boolean useNative) {
// Instance fields
// Configure instance fields
localizer = new Localizer();
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
setUseNative(useNative);
initLocales();
@@ -83,6 +113,14 @@ public class App {
// 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
private void initLocales() {
+4 -7
View File
@@ -14,7 +14,6 @@ import vue.*;
class BreakpointsWindow extends ChildWindow {
// Private fields
private Font font; // Display font
private ArrayList<Item> items; // List items
private int selectedIndex; // Selected list item
@@ -133,7 +132,7 @@ class BreakpointsWindow extends ChildWindow {
else if (breakpoint.any() && breakpoint.evaluate(inst, acc))
lblStatus.setText("\u2605"); // Star
else lblStatus.setText(" ");
int lineHeight = lblStatus.getPreferredSize().height;
int lineHeight = parent.app.fntDialog.metrics.getHeight();
lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight));
}
@@ -158,8 +157,6 @@ class BreakpointsWindow extends ChildWindow {
super(parent, "breakpoints.title");
// Configure instance fields
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
items = new ArrayList<Item>();
selectedIndex = -1;
@@ -183,7 +180,7 @@ class BreakpointsWindow extends ChildWindow {
lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null));
var scr = new JScrollPane(lstBreakpoints);
client.add(scr, BorderLayout.CENTER);
spacer = new JPanel(null);
spacer = new JPanel();
spacer.setOpaque(false);
lstBreakpoints.add(spacer, SPACER);
@@ -223,7 +220,7 @@ class BreakpointsWindow extends ChildWindow {
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
props.add(buttons, gbc);
var fill = new JPanel(null);
var fill = new JPanel();
fill.setOpaque(false);
fill.addMouseListener(Util.onMouse(e->onDebug(e), null));
gbc = new GridBagConstraints();
@@ -602,7 +599,7 @@ class BreakpointsWindow extends ChildWindow {
var txt = new JTextField();
txt.setEnabled(false);
if (mono)
txt.setFont(font);
txt.setFont(parent.app.fntMono);
txt.addActionListener(e->client.requestFocus());
txt.addFocusListener (Util.onFocus(null, e->handler.run()));
var gbc = new GridBagConstraints();
+1 -123
View File
@@ -13,7 +13,7 @@ import vue.*;
// CPU window
class CPUWindow extends ChildWindow {
// Package fields
// Instance fields
MainWindow parent; // Containing window
// 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 //
///////////////////////////////////////////////////////////////////////////
@@ -94,7 +30,6 @@ class CPUWindow extends ChildWindow {
// Default constructor
CPUWindow(MainWindow parent) {
super(parent, "cpu.title");
instances.add(this);
// Configure instance fields
this.parent = parent;
@@ -117,34 +52,15 @@ class CPUWindow extends ChildWindow {
var client = getContentPane();
client.add(outer);
client.setPreferredSize(new Dimension(480, 300));
configure();
pack();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// The window is closing
public void dispose() {
instances.remove(this);
super.dispose();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
panDasm .configure();
lstProgram.configure();
lstSystem .configure();
}
// Update the display
void refresh(boolean seekToPC) {
panDasm .refresh(seekToPC);
@@ -152,42 +68,4 @@ class CPUWindow extends ChildWindow {
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);
}
}
+317
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;
}
}
+25 -27
View File
@@ -21,8 +21,8 @@ class DisassemblerPane extends JScrollPane {
private int[] widths; // Column widths
// UI components
private JPanel client; // Client area
private ArrayList<Row> rows; // Disassembler output
private JPanel client; // Client area
private ArrayList<Row> rows; // Disassembler output
@@ -53,23 +53,22 @@ class DisassemblerPane extends JScrollPane {
widths = new int[5];
// Configure client area
client = new JPanel(null) {
client = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
onPaint((Graphics2D) g, getWidth(), getHeight());
}
};
client.setBackground(SystemColor.window);
client.setFocusable(true);
client.addFocusListener(
Util.onFocus(e->client.repaint(), e->client.repaint()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.addMouseWheelListener(e->onMouseWheel(e));
client.setBackground(SystemColor.window);
client.setFocusable(true);
// Configure component
setViewportView(client);
configure();
}
@@ -78,17 +77,14 @@ class DisassemblerPane extends JScrollPane {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
client.repaint();
}
// Update the display
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();
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
int address = this.address;
int count = tall(true);
var data = new byte[count * 4];
int offset = 0;
int address = this.address;
int count = tall(true);
var data = new byte[count * 4];
int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
int offset = 0;
// Disassemble from the current address
vue.readBytes(address, data, 0, data.length);
@@ -202,8 +199,8 @@ class DisassemblerPane extends JScrollPane {
}
g.setColor(bg);
g.fillRect(
0, y * CPUWindow.dasmFontHeight,
width, CPUWindow.dasmFontHeight
0, y * lineHeight,
width, lineHeight
);
}
@@ -220,7 +217,7 @@ class DisassemblerPane extends JScrollPane {
// Configure all rows
for (int y = 0; y < rows.size(); y++) {
var row = rows.get(y);
int top = y * CPUWindow.dasmFontHeight;
int top = y * lineHeight;
// Configure all labels
for (int z = 0, x = 0; z < 5; z++) {
@@ -235,17 +232,17 @@ class DisassemblerPane extends JScrollPane {
// Configure the label
label.setLocation(x, top);
label.setSize(label.getPreferredSize());
x += widths[z] + CPUWindow.dasmFontHeight;
x += widths[z] + lineHeight;
}
}
// Update the client's size
var size = client.getPreferredSize();
width = -CPUWindow.dasmFontHeight + 1;
width = -lineHeight + 1;
for (int x = 0; x < 5; x++)
if (widths[x] != 0 && (x != 2 || showBytes))
width += CPUWindow.dasmFontHeight + widths[x];
width += lineHeight + widths[x];
if (width == size.width)
return;
client.setPreferredSize(new Dimension(width, 0));
@@ -268,7 +265,7 @@ class DisassemblerPane extends JScrollPane {
for (int x = 0; x < 5; x++) {
var label = row.labels[x] = new JLabel();
if (x != 4)
label.setFont(CPUWindow.dasmFont);
label.setFont(parent.parent.app.fntMono);
client.add(label);
}
@@ -380,9 +377,10 @@ class DisassemblerPane extends JScrollPane {
// Determine how many rows of output are visible
private int tall(boolean partial) {
int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
return Math.max(1, (client.getHeight() +
(partial ? CPUWindow.dasmFontHeight - 1 : 0)
) / CPUWindow.dasmFontHeight);
(partial ? lineHeight - 1 : 0)
) / lineHeight);
}
// Update a row with its text and measure the column widths
+104 -13
View File
@@ -15,9 +15,11 @@ import vue.*;
class MainWindow extends JFrame {
// Instance fields
App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint
Vue vue; // Emulation core context
App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint
int[][][] palettes; // Raster palettes
byte[] vram; // Snapshot of VIP memory
Vue vue; // Emulation core context
// Private fields
private boolean debugMode; // Window is in debug mode
@@ -28,23 +30,40 @@ class MainWindow extends JFrame {
private File romFile; // Currently loaded ROM file
// UI components
private BreakpointsWindow breakpoints; // Breakpoints window
private JPanel client; // Common client container
private ConsoleWindow console; // Console window
private CPUWindow cpu; // CPU window
private JDesktopPane desktop; // Container for child windows
private MemoryWindow memory; // Memory window
private JMenu mnuDebug; // Debug menu
private JPanel video; // Video output
private JPanel client; // Common client container
private JDesktopPane desktop; // Container for child windows
private JMenu mnuDebug; // Debug menu
private JPanel video; // Video output
private JMenuItem mnuFileDebugMode; // File -> Debug 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 //
///////////////////////////////////////////////////////////////////////////
// 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
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 //
///////////////////////////////////////////////////////////////////////////
@@ -65,13 +95,16 @@ class MainWindow extends JFrame {
// Configure instance fields
this.app = app;
palettes = new int[9][3][4];
pwd = Util.PWD;
vram = new byte[0x40000];
vue = Vue.create(app.getUseNative());
System.out.println("Native: " +
(vue.isNative() ? Vue.getNativeID() : "No"));
//vue.write(0x00078000, Vue.S32, -1);
// Configure video pane
video = new JPanel(null) {
video = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
onPaintVideo((Graphics2D) g, getWidth(), getHeight());
@@ -96,6 +129,7 @@ class MainWindow extends JFrame {
desktop = new JDesktopPane();
desktop.setBackground(SystemColor.controlShadow);
desktop.add(breakpoints = new BreakpointsWindow(this));
desktop.add(characters = new CharactersWindow (this));
desktop.add(console = new ConsoleWindow (this));
desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this));
@@ -160,8 +194,8 @@ class MainWindow extends JFrame {
mnuDebug.add(mnuDebugBackgrounds);
var mnuDebugCharacters = new JMenuItem();
mnuDebugCharacters.setEnabled(false);
loc.add(mnuDebugCharacters, "app.debug.characters");
mnuDebugCharacters.addActionListener(e->characters.setVisible(true));
mnuDebug.add(mnuDebugCharacters);
var mnuDebugFrameBuffers = new JMenuItem();
@@ -226,7 +260,12 @@ class MainWindow extends JFrame {
// Refresh all debug views
void refreshDebug(boolean seekToPC) {
vue.readBytes(0x00000000, vram, 0, vram.length);
refreshPalettes();
breakpoints.refresh();
characters .refresh();
cpu .refresh(seekToPC);
memory .refresh();
}
@@ -364,6 +403,58 @@ class MainWindow extends JFrame {
// 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
private void updateTitle() {
app.localizer.add(this,
+16 -41
View File
@@ -13,10 +13,7 @@ import util.*;
class MemoryWindow extends ChildWindow {
// Private fields
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
private int address; // Address of top row
// UI components
private JPanel client; // Client area
@@ -46,8 +43,6 @@ class MemoryWindow extends ChildWindow {
// Configure instance fields
address = 0x00000000;
font = new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14);
rows = new ArrayList<Row>();
// Configure client area
@@ -64,7 +59,6 @@ class MemoryWindow extends ChildWindow {
content.setBorder(new JScrollPane().getBorder());
content.add(client, BorderLayout.CENTER);
setContentPane(content);
setFont2(font);
pack();
}
@@ -82,9 +76,10 @@ class MemoryWindow extends ChildWindow {
return;
// Configure working variables
int height = client.getHeight();
int count = (height + fontHeight - 1) / fontHeight;
var data = new byte[count * 16];
int height = client.getHeight();
int lineHeight = parent.app.fntMono.metrics.getHeight();
int count = (height + lineHeight - 1) / lineHeight;
var data = new byte[count * 16];
// Retrieve all visible bytes from the emulation context
parent.vue.readBytes(address, data, 0, data.length);
@@ -95,7 +90,7 @@ class MemoryWindow extends ChildWindow {
if (x < rows.size())
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);
update(row, x * lineHeight, address + x * 16, data, x * 16);
setVisible(row, true);
}
@@ -108,30 +103,6 @@ class MemoryWindow extends ChildWindow {
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
private Row createRow() {
var row = new Row();
var font = parent.app.fntMono;
var row = new Row();
// Address label
row.address = new JLabel();
@@ -250,22 +222,25 @@ class MemoryWindow extends ChildWindow {
// Determine how many rows of output are visible
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
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
row.address.setBounds(0, y, 8 * fontWidth, fontHeight);
row.address.setBounds(0, y, 8 * hexDigitWidth, lineHeight);
row.address.setText(String.format("%08X", address));
// 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];
label.setBounds(x, y, 2 * fontWidth, fontHeight);
label.setBounds(x, y, 2 * hexDigitWidth, lineHeight);
label.setText(String.format("%02X", data[offset++] & 0xFF));
x += fontWidth * (z == 7 ? 4 : 3);
x += hexDigitWidth * (z == 7 ? 4 : 3);
}
}
+23 -13
View File
@@ -47,6 +47,10 @@ class Register {
static final int PLAIN = -2;
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);
// Expand button
btnExpand = new JLabel(expandable ? "+" : " ");
btnExpand = new JLabel(expandable ? EXPAND : "");
btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
if (expandable)
btnExpand.addMouseListener(expand);
@@ -124,12 +128,12 @@ class Register {
case Vue.PIR : initPIR (); break;
case Vue.PSW : initPSW (); break;
case Vue.TKCW: initTKCW (); break;
default: return;
default: configure(); return;
}
// Expansion indentation
if (index != Vue.PC) {
indent = new JPanel(null);
indent = new JPanel();
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
indent.setVisible(false);
@@ -152,6 +156,9 @@ class Register {
// Handling for PSW
if (index == Vue.PSW && type == Vue.PSW)
setExpanded(true);
// Apply application settings
configure();
}
@@ -191,7 +198,7 @@ class Register {
for (int x = 0; x < 2; x++) {
// Indentation
indent = new JPanel(null);
indent = new JPanel();
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
@@ -295,26 +302,29 @@ class Register {
lblName.setText(name);
// Expand button
var size = btnExpand.getPreferredSize();
size.width = CPUWindow.regExpandWidth;
var size = btnExpand.getPreferredSize();
var metrics = parent.parent.parent.app.fntDialog.metrics;
size.width = 4 + Math.max(
metrics.stringWidth(EXPAND), metrics.stringWidth(COLLAPSE));
btnExpand.setPreferredSize(size);
// Value text box
var fontSize = CPUWindow.regHexFontSize;
size = new Dimension(8 * fontSize.width + 4, fontSize.height);
txtValue.setFont(CPUWindow.regHexFont);
txtValue.setPreferredSize(size);
var fntMono = parent.parent.parent.app.fntMono;
int hexDigitWidth = parent.parent.parent.app.hexDigitWidth;
txtValue.setFont(fntMono);
txtValue.setPreferredSize(new Dimension(
hexDigitWidth * 8 + 4, fntMono.metrics.getHeight()));
// Expansion controls
for (var ctrl : controls) {
if (!(ctrl instanceof JTextField))
continue;
if (type == Vue.PC || (Boolean) ctrl.getClientProperty("hex"))
((JTextField) ctrl).setFont(CPUWindow.regHexFont);
((JTextField) ctrl).setFont(fntMono);
int digits = type == Vue.PC ? 8 :
(Integer) ctrl.getClientProperty("digits");
size = ctrl.getPreferredSize();
size.width = digits * fontSize.width + 4;
size.width = digits * hexDigitWidth + 4;
ctrl.setPreferredSize(size);
}
}
@@ -397,7 +407,7 @@ class Register {
// Change the display mode of a program register
void setMode(int mode) {
this.mode = mode;
txtValue.setFont(mode == HEX ? CPUWindow.regHexFont : null);
txtValue.setFont(mode!=HEX ? null : parent.parent.parent.app.fntMono);
refresh();
}
+3 -3
View File
@@ -85,7 +85,7 @@ class RegisterList extends JScrollPane {
}
// List terminator
var spacer = new JPanel(null);
var spacer = new JPanel();
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
@@ -144,7 +144,7 @@ class RegisterList extends JScrollPane {
}
// Apply configuration settings
void configure() {
void configure2() {
// Configure registers
for (var reg : registers.values())
@@ -152,7 +152,7 @@ class RegisterList extends JScrollPane {
// Configure component
getVerticalScrollBar().setUnitIncrement(
CPUWindow.regHexFontSize.height);
parent.parent.app.fntMono.metrics.getHeight());
}
// Update the display
+11 -1
View File
@@ -24,7 +24,7 @@ public final class Util {
///////////////////////////////////////////////////////////////////////////
// Types //
// Classes //
///////////////////////////////////////////////////////////////////////////
// Event listener interfaces
@@ -43,6 +43,16 @@ public final class Util {
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);
}
}
///////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -1537,7 +1537,7 @@ public class Breakpoint {
case CODE : sym = vue.getExceptionCode(); break;
case COND : sym = inst.cond ; 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 ID : sym = inst.id ; break;
case OPCODE : sym = inst.opcode ; break;
+2 -2
View File
@@ -443,8 +443,8 @@ class CPU {
// First unit
if (fetch == 0) {
inst.bits = access.value & 0xFFFF;
if (Instruction.size(access.value >> 10 & 0x3F) == 4) {
inst.bits = access.value;
if (Instruction.size(access.value >> 10) == 4) {
fetch = 1;
return false;
}
+12 -4
View File
@@ -114,6 +114,7 @@ public class Instruction {
boolean read = false;
var ret = new Access();
ret.address = vue.getRegister(reg1, false) + disp;
ret.fetch = vue.getFetch();
ret.type = Vue.S32;
// Configure descriptor by ID
@@ -139,10 +140,17 @@ public class Instruction {
if (read)
ret.value = vue.read(ret.address, ret.type);
// Select the value to write to the bus
else ret.value = vue.getRegister(id == Vue.CAXI &&
vue.getRegister(reg2, false) == vue.read(ret.address, Vue.S32) ?
30 : reg2, false);
// Write a register to the bus
else if (id != Vue.CAXI)
vue.getRegister(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;
}
-10
View File
@@ -76,11 +76,6 @@ class JavaVue extends Vue {
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
public int getBreakCode() {
return breakCode;
@@ -91,11 +86,6 @@ class JavaVue extends Vue {
return cpu.exception;
}
// Retrieve a snapshot of the current state's instruction
public Instruction getInstruction() {
return cpu.inst;
}
// Retrieve a register value
public int getRegister(int index, boolean system) {
-46
View File
@@ -141,22 +141,6 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
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
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) {
@@ -171,36 +155,6 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
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
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
-12
View File
@@ -42,12 +42,6 @@ class NativeVue extends Vue {
public int emulate(int 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
private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); }
@@ -58,12 +52,6 @@ class NativeVue extends Vue {
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
private native int getRegister(long handle, int index, boolean system);
public int getRegister(int index, boolean system)