inital commit кек
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
REM ===========================================================
|
||||
REM CBE Emulator launcher
|
||||
REM Double-click to run with embedded plugins.
|
||||
REM No arguments needed - all plugins are inside cbe-emu.jar
|
||||
REM ===========================================================
|
||||
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
|
||||
set "JAVA_EXE=%~dp0runtime\bin\javaw.exe"
|
||||
set "JAR=%~dp0app\cbe-emu.jar"
|
||||
|
||||
if not exist "%JAVA_EXE%" (
|
||||
echo [ERROR] JRE not found at %JAVA_EXE%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "%JAR%" (
|
||||
echo [ERROR] cbe-emu.jar not found at %JAR%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Launch GUI. Use %* to forward any extra args.
|
||||
"%JAVA_EXE%" -Xmx256m -jar "%JAR%" %*
|
||||
@@ -0,0 +1,40 @@
|
||||
' ===========================================================
|
||||
' CBE Emulator launcher (VBScript - no console window)
|
||||
' Double-click to run with embedded plugins, silently.
|
||||
' ===========================================================
|
||||
|
||||
Option Explicit
|
||||
|
||||
Dim shell, fso, javaExe, jar, baseDir, cmd
|
||||
|
||||
Set shell = CreateObject("WScript.Shell")
|
||||
Set fso = CreateObject("Scripting.FileSystemObject")
|
||||
|
||||
baseDir = fso.GetParentFolderName(WScript.ScriptFullName)
|
||||
javaExe = baseDir & "\runtime\bin\javaw.exe"
|
||||
jar = baseDir & "\app\cbe-emu.jar"
|
||||
|
||||
If Not fso.FileExists(javaExe) Then
|
||||
MsgBox "JRE not found at:" & vbCrLf & javaExe, 16, "CBE Emulator"
|
||||
WScript.Quit 1
|
||||
End If
|
||||
|
||||
If Not fso.FileExists(jar) Then
|
||||
MsgBox "cbe-emu.jar not found at:" & vbCrLf & jar, 16, "CBE Emulator"
|
||||
WScript.Quit 1
|
||||
End If
|
||||
|
||||
' Collect extra args after the .vbs filename
|
||||
Dim extra
|
||||
extra = ""
|
||||
Dim i
|
||||
For i = 0 To WScript.Arguments.Count - 1
|
||||
extra = extra & " """ & WScript.Arguments(i) & """"
|
||||
Next
|
||||
|
||||
' Build command. 0 = hide window, False = don't wait for return
|
||||
cmd = """" & javaExe & """ -Xmx256m -jar """ & jar & """ " & extra
|
||||
shell.Run cmd, 0, False
|
||||
|
||||
Set shell = Nothing
|
||||
Set fso = Nothing
|
||||
@@ -0,0 +1,262 @@
|
||||
package com.cbe.gui;
|
||||
|
||||
import com.cbe.gui.internal.EmulatorWindow;
|
||||
import com.cbe.cbecc.Compiler;
|
||||
import com.cbe.loader.Engine;
|
||||
import com.cbe.loader.ModuleLoadException;
|
||||
import com.cbe.loader.ModuleLoader;
|
||||
import com.cbe.loader.SimpleRegisters;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Simple arg parsing
|
||||
Map<String, String> argMap = new HashMap<String, String>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String a = args[i];
|
||||
if (a.startsWith("--")) {
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
||||
argMap.put(a, args[++i]);
|
||||
} else {
|
||||
argMap.put(a, "true");
|
||||
}
|
||||
} else if ("--nogui".equals(a) || "build".equals(a) || "run".equals(a)) {
|
||||
argMap.put(a, "true");
|
||||
}
|
||||
}
|
||||
|
||||
if (argMap.containsKey("build")) {
|
||||
runBuildCommand(argMap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
// No args: try embedded plugins (from the fat-jar)
|
||||
if (loadEmbeddedPlugins(argMap)) {
|
||||
runGui(argMap);
|
||||
return;
|
||||
}
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// If no --cpu/--ram/--gpu provided, try to fall back to embedded plugins
|
||||
if (!argMap.containsKey("--cpu") && !argMap.containsKey("--ram") && !argMap.containsKey("--gpu")) {
|
||||
loadEmbeddedPlugins(argMap);
|
||||
}
|
||||
|
||||
if (argMap.containsKey("--nogui") && !argMap.containsKey("run")) {
|
||||
runConsole(argMap);
|
||||
return;
|
||||
}
|
||||
|
||||
runGui(argMap);
|
||||
}
|
||||
|
||||
private static boolean loadEmbeddedPlugins(Map<String, String> argMap) {
|
||||
try {
|
||||
String cpu = findEmbedded("embedded/tiny-cpu.cbeplugin");
|
||||
String ram = findEmbedded("embedded/basic-ram.cbeplugin");
|
||||
String gpu = findEmbedded("embedded/vga-display.cbeplugin");
|
||||
String kbd = findEmbedded("embedded/basic-kbd.cbeplugin");
|
||||
String snd = findEmbedded("embedded/basic-snd.cbeplugin");
|
||||
String bios = findEmbedded("embedded/tiny-bios.cbeplugin");
|
||||
if (cpu == null) return false;
|
||||
argMap.put("--cpu", cpu);
|
||||
if (ram != null) argMap.put("--ram", ram);
|
||||
if (gpu != null) argMap.put("--gpu", gpu);
|
||||
if (kbd != null) argMap.put("--kbd", kbd);
|
||||
if (snd != null) argMap.put("--snd", snd);
|
||||
if (bios != null) argMap.put("--bios", bios);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String findEmbedded(String resourcePath) throws java.io.IOException {
|
||||
java.io.InputStream is = Main.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||
if (is == null) return null;
|
||||
// Extract to a temp file so the loaders can use Path-based API
|
||||
java.io.File tmp = java.io.File.createTempFile("cbe-", ".cbeplugin");
|
||||
tmp.deleteOnExit();
|
||||
try (java.io.FileOutputStream out = new java.io.FileOutputStream(tmp)) {
|
||||
byte[] buf = new byte[8192];
|
||||
int n;
|
||||
while ((n = is.read(buf)) > 0) out.write(buf, 0, n);
|
||||
}
|
||||
return tmp.getAbsolutePath();
|
||||
}
|
||||
|
||||
private static void runBuildCommand(Map<String, String> args) {
|
||||
String sourceDir = args.get("--source");
|
||||
String output = args.get("-o");
|
||||
if (sourceDir == null || output == null) {
|
||||
System.err.println("Usage: cbecc build --source <dir> -o <output.cbeplugin>");
|
||||
System.exit(1);
|
||||
}
|
||||
try {
|
||||
new Compiler().compile(Paths.get(sourceDir), Paths.get(output));
|
||||
System.out.println("Compiled: " + sourceDir + " -> " + output);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Build failed: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runConsole(Map<String, String> args) {
|
||||
Engine engine = new Engine();
|
||||
try {
|
||||
attachModules(engine, args);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Module load failed: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
SimpleRegisters regs = engine.createRegisters();
|
||||
regs.write("pc", 0);
|
||||
regs.write("sp", 0x80);
|
||||
long total = 0;
|
||||
long lastReport = 0;
|
||||
try {
|
||||
while (true) {
|
||||
boolean running = engine.step(regs);
|
||||
total++;
|
||||
if (!running) {
|
||||
System.out.println("Halted after " + total + " instructions");
|
||||
return;
|
||||
}
|
||||
if (total - lastReport >= 100_000) {
|
||||
System.out.println("Running... " + total + " instructions" +
|
||||
" | Loop detection: active (infinite loops never halt)");
|
||||
lastReport = total;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
} finally {
|
||||
System.out.println("Total: " + total + " instructions");
|
||||
}
|
||||
}
|
||||
|
||||
private static void runGui(Map<String, String> args) {
|
||||
Engine engine = new Engine();
|
||||
try {
|
||||
attachModules(engine, args);
|
||||
} catch (ModuleLoadException e) {
|
||||
System.err.println("Module load failed: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Write a simple test program to GPU if no program loader mechanism is present
|
||||
if (engine.hasGpu() && args.containsKey("--program")) {
|
||||
String program = args.get("--program");
|
||||
// For now, write directly to GPU. Later, CPU will do this through bus.
|
||||
if (engine.getSourceGpu() != null) {
|
||||
engine.getSourceGpu().writeString(program);
|
||||
} else if (engine.getCompiledGpu() != null) {
|
||||
engine.getCompiledGpu().writeString(program);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
EmulatorWindow window = new EmulatorWindow(engine, args);
|
||||
window.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
private static void attachModules(Engine engine, Map<String, String> args) throws ModuleLoadException {
|
||||
ModuleLoader loader = new ModuleLoader();
|
||||
String cpuPath = args.get("--cpu");
|
||||
String ramPath = args.get("--ram");
|
||||
String gpuPath = args.get("--gpu");
|
||||
String kbdPath = args.get("--kbd");
|
||||
String sndPath = args.get("--snd");
|
||||
String biosPath = args.get("--bios");
|
||||
String diskPath = args.get("--disk");
|
||||
|
||||
if (biosPath != null) {
|
||||
Path p = resolvePath(biosPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledBios(p);
|
||||
else engine.loadBios(p);
|
||||
}
|
||||
if (cpuPath != null) {
|
||||
Path p = resolvePath(cpuPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledCpu(p);
|
||||
else engine.loadCpu(p);
|
||||
}
|
||||
if (ramPath != null) {
|
||||
Path p = resolvePath(ramPath);
|
||||
int base = parseIntOrDefault(args.get("--ram-base"), 0);
|
||||
int size = parseIntOrDefault(args.get("--ram-size"), 256);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledRam(p, base);
|
||||
else engine.loadRam(p, base);
|
||||
}
|
||||
if (gpuPath != null) {
|
||||
Path p = resolvePath(gpuPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledGpu(p);
|
||||
else engine.loadGpu(p);
|
||||
}
|
||||
if (kbdPath != null) {
|
||||
Path p = resolvePath(kbdPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledKbd(p);
|
||||
else engine.loadKbd(p);
|
||||
}
|
||||
if (sndPath != null) {
|
||||
Path p = resolvePath(sndPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledSnd(p);
|
||||
else engine.loadSnd(p);
|
||||
}
|
||||
if (diskPath != null) {
|
||||
Path p = resolvePath(diskPath);
|
||||
if (p.toString().endsWith(".cbeplugin")) engine.loadCompiledDisk(p);
|
||||
else engine.loadDisk(p);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path resolvePath(String path) {
|
||||
Path p = Paths.get(path);
|
||||
if (Files.exists(p)) return p;
|
||||
Path alt = Paths.get("examples").resolve(path);
|
||||
if (Files.exists(alt)) return alt;
|
||||
Path alt2 = Paths.get("build").resolve(path);
|
||||
if (Files.exists(alt2)) return alt2;
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int parseIntOrDefault(String s, int def) {
|
||||
if (s == null) return def;
|
||||
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return def; }
|
||||
}
|
||||
|
||||
private static void printUsage() {
|
||||
System.out.println("CBE Emulator");
|
||||
System.out.println();
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" cbe-emu --cpu <path> [--ram <path> --ram-base <n>] [--gpu <path>] [--program <text>]");
|
||||
System.out.println(" cbecc build --source <dir> -o <output.cbeplugin>");
|
||||
System.out.println();
|
||||
System.out.println("Options:");
|
||||
System.out.println(" --cpu <path> CPU module file (.cbeplugin or source dir)");
|
||||
System.out.println(" --ram <path> RAM module file");
|
||||
System.out.println(" --ram-base <n> Base address for RAM (default 0)");
|
||||
System.out.println(" --ram-size <n> RAM size in bytes (default 256)");
|
||||
System.out.println(" --gpu <path> GPU module file");
|
||||
System.out.println(" --program <text> Initial text to display on GPU");
|
||||
System.out.println(" --nogui Run in console mode (no window)");
|
||||
System.out.println();
|
||||
System.out.println("Examples:");
|
||||
System.out.println(" cbe-emu --cpu build/tiny-cpu.cbeplugin --ram build/basic-ram.cbeplugin --gpu examples/vga-display.gpu --program \"Hello, CBE!\"");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
package com.cbe.gui.internal;
|
||||
|
||||
import com.cbe.core.ModuleInstance;
|
||||
import com.cbe.core.OpcodeResult;
|
||||
import com.cbe.loader.Engine;
|
||||
import com.cbe.loader.SimpleRegisters;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EmulatorWindow extends JFrame {
|
||||
private final Engine engine;
|
||||
private final Map<String, String> args;
|
||||
private final ScreenPanel screenPanel;
|
||||
private final PostCodePanel postCodePanel;
|
||||
private final JLabel systemInfoLabel;
|
||||
private final JTextArea logArea;
|
||||
private final JTextArea regsArea;
|
||||
private final JTextArea vramArea;
|
||||
private JList<String> moduleList;
|
||||
private DefaultListModel<String> moduleListModel;
|
||||
private JLabel statusLabel;
|
||||
private JSpinner stepDelaySpinner;
|
||||
|
||||
private SimpleRegisters regs;
|
||||
private Timer autoRunTimer;
|
||||
private int totalSteps;
|
||||
|
||||
// Dark theme colors
|
||||
private static final Color DARK_BG = new Color(30, 30, 36);
|
||||
private static final Color DARK_BG2 = new Color(38, 38, 46);
|
||||
private static final Color DARK_BORDER = new Color(50, 50, 60);
|
||||
private static final Color DARK_TEXT = new Color(200, 200, 210);
|
||||
private static final Color DARK_TEXT_BRIGHT = new Color(220, 220, 240);
|
||||
private static final Color DARK_ACCENT = new Color(80, 160, 255);
|
||||
private static final Color DARK_GREEN = new Color(80, 220, 120);
|
||||
private static final Color DARK_ORANGE = new Color(255, 160, 60);
|
||||
private static final Color DARK_RED = new Color(220, 80, 80);
|
||||
|
||||
private boolean keyboardCaptureEnabled;
|
||||
private JLabel kbdIndicator;
|
||||
|
||||
public EmulatorWindow(Engine engine, Map<String, String> args) {
|
||||
this.engine = engine;
|
||||
this.args = args;
|
||||
this.totalSteps = 0;
|
||||
this.regs = engine.createRegisters();
|
||||
|
||||
setTitle("CBE Emulator");
|
||||
setSize(1100, 750);
|
||||
setMinimumSize(new Dimension(900, 600));
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
setLayout(new BorderLayout(4, 4));
|
||||
getContentPane().setBackground(DARK_BG);
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
applyDarkUIManager();
|
||||
|
||||
// ======= TOP: Module list (slot board) =======
|
||||
JPanel topPanel = createTopPanel();
|
||||
add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// ======= CENTER: Split pane =======
|
||||
JSplitPane centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
centerSplit.setDividerLocation(720);
|
||||
centerSplit.setBackground(DARK_BG);
|
||||
centerSplit.setBorder(BorderFactory.createLineBorder(DARK_BORDER));
|
||||
|
||||
// LEFT: Screen (like QEMU window)
|
||||
JPanel leftPanel = new JPanel(new BorderLayout(2, 2));
|
||||
leftPanel.setBackground(DARK_BG);
|
||||
leftPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(DARK_BORDER), "Display (QEMU-style)",
|
||||
TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION,
|
||||
new Font(Font.MONOSPACED, Font.BOLD, 11), DARK_ACCENT));
|
||||
screenPanel = new ScreenPanel();
|
||||
leftPanel.add(screenPanel, BorderLayout.CENTER);
|
||||
leftPanel.add(createScreenControls(), BorderLayout.SOUTH);
|
||||
|
||||
// ======= TOP-CENTER: POST code panel (above the screen) =======
|
||||
postCodePanel = new PostCodePanel();
|
||||
engine.addPostListener(postCodePanel);
|
||||
systemInfoLabel = new JLabel(" ");
|
||||
systemInfoLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));
|
||||
systemInfoLabel.setForeground(DARK_GREEN);
|
||||
systemInfoLabel.setBackground(DARK_BG2);
|
||||
systemInfoLabel.setOpaque(true);
|
||||
systemInfoLabel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
||||
JPanel postStack = new JPanel(new BorderLayout());
|
||||
postStack.setBackground(DARK_BG);
|
||||
postStack.add(postCodePanel, BorderLayout.CENTER);
|
||||
postStack.add(systemInfoLabel, BorderLayout.SOUTH);
|
||||
leftPanel.add(postStack, BorderLayout.NORTH);
|
||||
|
||||
// RIGHT: Debug
|
||||
JPanel rightPanel = new JPanel(new GridLayout(3, 1, 2, 2));
|
||||
rightPanel.setBackground(DARK_BG);
|
||||
rightPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(DARK_BORDER), "Debug",
|
||||
TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION,
|
||||
new Font(Font.MONOSPACED, Font.BOLD, 11), DARK_ACCENT));
|
||||
|
||||
regsArea = new JTextArea();
|
||||
regsArea.setEditable(false);
|
||||
styleTextArea(regsArea);
|
||||
JScrollPane regsScroll = new JScrollPane(regsArea);
|
||||
styleScrollPane(regsScroll, "Registers");
|
||||
|
||||
vramArea = new JTextArea();
|
||||
vramArea.setEditable(false);
|
||||
styleTextArea(vramArea);
|
||||
JScrollPane vramScroll = new JScrollPane(vramArea);
|
||||
styleScrollPane(vramScroll, "VRAM (hex)");
|
||||
|
||||
logArea = new JTextArea();
|
||||
logArea.setEditable(false);
|
||||
styleTextArea(logArea);
|
||||
JScrollPane logScroll = new JScrollPane(logArea);
|
||||
styleScrollPane(logScroll, "Log");
|
||||
|
||||
rightPanel.add(regsScroll);
|
||||
rightPanel.add(vramScroll);
|
||||
rightPanel.add(logScroll);
|
||||
|
||||
centerSplit.setLeftComponent(leftPanel);
|
||||
centerSplit.setRightComponent(rightPanel);
|
||||
add(centerSplit, BorderLayout.CENTER);
|
||||
|
||||
// ======= BOTTOM: Status & controls =======
|
||||
JPanel bottomPanel = createBottomPanel();
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Initial population
|
||||
moduleListModel.clear();
|
||||
for (ModuleInstance m : engine.getModules()) {
|
||||
moduleListModel.addElement(formatModule(m));
|
||||
}
|
||||
|
||||
if (engine.getCpu() == null) {
|
||||
appendLog("No CPU loaded. Use --cpu to specify a CPU module.");
|
||||
} else {
|
||||
appendLog("CPU: " + engine.getCpu().getName() + " (" + engine.getCpu().getMetadata().getArch() + ")");
|
||||
}
|
||||
if (engine.getRam() != null) {
|
||||
appendLog("RAM: " + engine.getRam().getName());
|
||||
}
|
||||
if (engine.hasGpu()) {
|
||||
appendLog("GPU: " + (engine.getSourceGpu() != null ? engine.getSourceGpu().getName() : engine.getCompiledGpu().getName()));
|
||||
}
|
||||
|
||||
// Run BIOS POST once before the first draw, so the GPU shows the boot screen
|
||||
if (engine.getCpu() != null) {
|
||||
engine.step(regs); // triggers runDiagnostics() on first step
|
||||
}
|
||||
|
||||
// Periodically refresh screen & debug views
|
||||
Timer refreshTimer = new Timer(80, e -> refreshViews());
|
||||
refreshTimer.start();
|
||||
|
||||
// Forward key events to the engine's KBD module. We use KeyboardFocusManager
|
||||
// so keys are caught regardless of which sub-component has focus.
|
||||
setFocusable(true);
|
||||
requestFocusInWindow();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> {
|
||||
if (keyboardCaptureEnabled && e.getID() == java.awt.event.KeyEvent.KEY_PRESSED && isFocused()) {
|
||||
int k = e.getKeyCode();
|
||||
engine.pushKey(k);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Enable keyboard capture by default only if screenPanel is focused
|
||||
keyboardCaptureEnabled = false;
|
||||
screenPanel.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||
keyboardCaptureEnabled = true;
|
||||
updateKbdIndicator();
|
||||
screenPanel.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent e) {
|
||||
if (keyboardCaptureEnabled) {
|
||||
keyboardCaptureEnabled = false;
|
||||
updateKbdIndicator();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateRegs();
|
||||
updateVram();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
private void applyDarkUIManager() {
|
||||
UIManager.put("Panel.background", DARK_BG);
|
||||
UIManager.put("OptionPane.background", DARK_BG);
|
||||
UIManager.put("OptionPane.messageForeground", DARK_TEXT);
|
||||
UIManager.put("TextField.background", DARK_BG2);
|
||||
UIManager.put("TextField.foreground", DARK_TEXT);
|
||||
UIManager.put("TextField.caretForeground", DARK_TEXT);
|
||||
UIManager.put("TextArea.background", DARK_BG2);
|
||||
UIManager.put("TextArea.foreground", DARK_TEXT);
|
||||
UIManager.put("TextArea.caretForeground", DARK_TEXT);
|
||||
UIManager.put("List.background", DARK_BG2);
|
||||
UIManager.put("List.foreground", DARK_TEXT);
|
||||
UIManager.put("ScrollPane.background", DARK_BG);
|
||||
UIManager.put("Viewport.background", DARK_BG2);
|
||||
UIManager.put("Label.foreground", DARK_TEXT);
|
||||
UIManager.put("Button.background", DARK_BG2);
|
||||
UIManager.put("Button.foreground", DARK_TEXT_BRIGHT);
|
||||
UIManager.put("Button.select", DARK_ACCENT);
|
||||
UIManager.put("TitledBorder.titleColor", DARK_ACCENT);
|
||||
UIManager.put("SplitPane.background", DARK_BG);
|
||||
UIManager.put("Spinner.background", DARK_BG2);
|
||||
UIManager.put("Spinner.foreground", DARK_TEXT);
|
||||
UIManager.put("EditorPane.background", DARK_BG2);
|
||||
UIManager.put("EditorPane.foreground", DARK_TEXT);
|
||||
}
|
||||
|
||||
private void styleTextArea(JTextArea area) {
|
||||
area.setBackground(DARK_BG2);
|
||||
area.setForeground(DARK_GREEN);
|
||||
area.setCaretColor(DARK_TEXT);
|
||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11));
|
||||
area.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
}
|
||||
|
||||
private void styleScrollPane(JScrollPane scroll, String title) {
|
||||
scroll.getViewport().setBackground(DARK_BG2);
|
||||
scroll.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(DARK_BORDER), title,
|
||||
TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION,
|
||||
new Font(Font.MONOSPACED, Font.BOLD, 10), DARK_ACCENT));
|
||||
}
|
||||
|
||||
private JPanel createTopPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout(4, 4));
|
||||
panel.setBackground(DARK_BG);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 0, 4));
|
||||
|
||||
JLabel titleLabel = new JLabel("Motherboard: CBE Emulator \u2014 Loaded Modules");
|
||||
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 12f));
|
||||
titleLabel.setForeground(DARK_TEXT_BRIGHT);
|
||||
panel.add(titleLabel, BorderLayout.NORTH);
|
||||
|
||||
moduleListModel = new DefaultListModel<String>();
|
||||
moduleList = new JList<String>(moduleListModel);
|
||||
moduleList.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11));
|
||||
moduleList.setBackground(DARK_BG2);
|
||||
moduleList.setForeground(DARK_TEXT);
|
||||
JScrollPane scroll = new JScrollPane(moduleList);
|
||||
scroll.setPreferredSize(new Dimension(0, 90));
|
||||
scroll.getViewport().setBackground(DARK_BG2);
|
||||
panel.add(scroll, BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createScreenControls() {
|
||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 2));
|
||||
panel.setBackground(DARK_BG2);
|
||||
|
||||
JButton clearBtn = createStyledButton("Clear GPU");
|
||||
clearBtn.addActionListener(e -> {
|
||||
if (engine.getSourceGpu() != null) engine.getSourceGpu().clear();
|
||||
if (engine.getCompiledGpu() != null) engine.getCompiledGpu().clear();
|
||||
});
|
||||
panel.add(clearBtn);
|
||||
|
||||
JButton helloBtn = createStyledButton("Print Hello");
|
||||
helloBtn.addActionListener(e -> {
|
||||
if (engine.getSourceGpu() != null) engine.getSourceGpu().writeString("Hello, CBE Platform!\n");
|
||||
if (engine.getCompiledGpu() != null) engine.getCompiledGpu().writeString("Hello, CBE Platform!\n");
|
||||
});
|
||||
panel.add(helloBtn);
|
||||
|
||||
JLabel delayLabel = new JLabel("Step delay (ms):");
|
||||
delayLabel.setForeground(DARK_TEXT);
|
||||
panel.add(delayLabel);
|
||||
stepDelaySpinner = new JSpinner(new SpinnerNumberModel(100, 10, 5000, 50));
|
||||
stepDelaySpinner.setBackground(DARK_BG2);
|
||||
stepDelaySpinner.getEditor().getComponent(0).setBackground(DARK_BG2);
|
||||
stepDelaySpinner.getEditor().getComponent(0).setForeground(DARK_TEXT);
|
||||
panel.add(stepDelaySpinner);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JButton createStyledButton(String text) {
|
||||
JButton btn = new JButton(text);
|
||||
btn.setBackground(new Color(50, 50, 65));
|
||||
btn.setForeground(DARK_TEXT_BRIGHT);
|
||||
btn.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(70, 70, 85)),
|
||||
BorderFactory.createEmptyBorder(2, 8, 2, 8)));
|
||||
btn.setFocusPainted(false);
|
||||
return btn;
|
||||
}
|
||||
|
||||
private JPanel createBottomPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout(4, 4));
|
||||
panel.setBackground(DARK_BG);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 4, 4, 4));
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 2));
|
||||
buttonPanel.setBackground(DARK_BG2);
|
||||
|
||||
JButton stepBtn = createStyledButton("Step");
|
||||
stepBtn.addActionListener(e -> doStep());
|
||||
buttonPanel.add(stepBtn);
|
||||
|
||||
JButton runBtn = createStyledButton("Run");
|
||||
runBtn.addActionListener(e -> toggleAutoRun(runBtn));
|
||||
buttonPanel.add(runBtn);
|
||||
|
||||
JButton stopBtn = createStyledButton("Stop");
|
||||
stopBtn.addActionListener(e -> {
|
||||
if (autoRunTimer != null) autoRunTimer.stop();
|
||||
runBtn.setText("Run");
|
||||
});
|
||||
buttonPanel.add(stopBtn);
|
||||
|
||||
JButton resetBtn = createStyledButton("Reset");
|
||||
resetBtn.addActionListener(e -> {
|
||||
totalSteps = 0;
|
||||
regs = engine.createRegisters();
|
||||
engine.reset();
|
||||
appendLog("--- RESET ---");
|
||||
});
|
||||
buttonPanel.add(resetBtn);
|
||||
|
||||
JButton testBtn = createStyledButton("Test ADD a,b");
|
||||
testBtn.addActionListener(e -> runTestAdd());
|
||||
buttonPanel.add(testBtn);
|
||||
|
||||
kbdIndicator = new JLabel("KBD: OFF");
|
||||
kbdIndicator.setForeground(DARK_RED);
|
||||
kbdIndicator.setFont(new Font(Font.MONOSPACED, Font.BOLD, 11));
|
||||
kbdIndicator.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(DARK_RED),
|
||||
BorderFactory.createEmptyBorder(2, 6, 2, 6)));
|
||||
buttonPanel.add(Box.createHorizontalStrut(8));
|
||||
buttonPanel.add(kbdIndicator);
|
||||
|
||||
panel.add(buttonPanel, BorderLayout.WEST);
|
||||
|
||||
statusLabel = new JLabel(" Ready");
|
||||
statusLabel.setForeground(DARK_TEXT);
|
||||
statusLabel.setBackground(DARK_BG2);
|
||||
statusLabel.setOpaque(true);
|
||||
statusLabel.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(DARK_BORDER),
|
||||
BorderFactory.createEmptyBorder(2, 8, 2, 8)));
|
||||
panel.add(statusLabel, BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void toggleAutoRun(JButton runBtn) {
|
||||
if (autoRunTimer != null && autoRunTimer.isRunning()) {
|
||||
autoRunTimer.stop();
|
||||
runBtn.setText("Run");
|
||||
} else {
|
||||
int delay = (Integer) stepDelaySpinner.getValue();
|
||||
autoRunTimer = new Timer(delay, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
doStep();
|
||||
}
|
||||
});
|
||||
autoRunTimer.start();
|
||||
runBtn.setText("Pause");
|
||||
}
|
||||
}
|
||||
|
||||
private void doStep() {
|
||||
if (engine.getCpu() == null) {
|
||||
appendLog("Cannot step: no CPU loaded");
|
||||
return;
|
||||
}
|
||||
boolean running = engine.step(regs);
|
||||
totalSteps++;
|
||||
updateRegs();
|
||||
updateVram();
|
||||
updateStatus();
|
||||
if (!running) {
|
||||
if (autoRunTimer != null) autoRunTimer.stop();
|
||||
appendLog("CPU halted at step " + totalSteps);
|
||||
}
|
||||
}
|
||||
|
||||
private void runTestAdd() {
|
||||
if (engine.getCpu() == null) {
|
||||
appendLog("Cannot run: no CPU loaded");
|
||||
return;
|
||||
}
|
||||
regs.write("a", 3);
|
||||
regs.write("b", 4);
|
||||
OpcodeResult r = engine.getCpu().executeOpcode(0x03, regs, engine.getBus());
|
||||
totalSteps++;
|
||||
appendLog("Test ADD: a(3) + b(4) = " + regs.read("a") + " (cycles=" + r.getCyclesConsumed() + ")");
|
||||
updateRegs();
|
||||
}
|
||||
|
||||
private void refreshViews() {
|
||||
screenPanel.refresh(engine);
|
||||
updateVram();
|
||||
updateStatus();
|
||||
String info = engine.getSystemInfo();
|
||||
if (info != null) systemInfoLabel.setText(info);
|
||||
}
|
||||
|
||||
private void updateRegs() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String name : regs.names()) {
|
||||
int v = regs.read(name) & 0xFF;
|
||||
sb.append(String.format("%-6s = 0x%02X (%3d)%n", name, v, v));
|
||||
}
|
||||
regsArea.setText(sb.toString());
|
||||
}
|
||||
|
||||
private void updateVram() {
|
||||
byte[] vram = null;
|
||||
int rows = 25, cols = 80;
|
||||
if (engine.getSourceGpu() != null) {
|
||||
vram = engine.getSourceGpu().getVram();
|
||||
rows = engine.getSourceGpu().getRows();
|
||||
cols = engine.getSourceGpu().getCols();
|
||||
} else if (engine.getCompiledGpu() != null) {
|
||||
vram = engine.getCompiledGpu().getVram();
|
||||
rows = engine.getCompiledGpu().getRows();
|
||||
cols = engine.getCompiledGpu().getCols();
|
||||
}
|
||||
if (vram == null) {
|
||||
vramArea.setText("(no GPU)\n");
|
||||
return;
|
||||
}
|
||||
if (vram.length == 0) {
|
||||
vramArea.setText("(empty VRAM)\n");
|
||||
return;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("VRAM size: ").append(vram.length).append(" bytes\n");
|
||||
sb.append("Cursor: (").append(getGpuCursorX()).append(",").append(getGpuCursorY()).append(")\n\n");
|
||||
sb.append("Hex dump:\n");
|
||||
for (int row = 0; row < rows; row++) {
|
||||
sb.append(String.format("%04X: ", row * cols));
|
||||
for (int col = 0; col < cols; col++) {
|
||||
sb.append(String.format("%02X ", vram[row * cols + col] & 0xFF));
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
vramArea.setText(sb.toString());
|
||||
}
|
||||
|
||||
private int getGpuCursorX() {
|
||||
if (engine.getSourceGpu() != null) return engine.getSourceGpu().getCursorX();
|
||||
if (engine.getCompiledGpu() != null) return engine.getCompiledGpu().getCursorX();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getGpuCursorY() {
|
||||
if (engine.getSourceGpu() != null) return engine.getSourceGpu().getCursorY();
|
||||
if (engine.getCompiledGpu() != null) return engine.getCompiledGpu().getCursorY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
statusLabel.setText(String.format(" Steps: %d | Bus clock: %d | Modules: %d",
|
||||
totalSteps, engine.getBus().clock(), engine.getModules().size()));
|
||||
}
|
||||
|
||||
private void updateKbdIndicator() {
|
||||
if (keyboardCaptureEnabled) {
|
||||
kbdIndicator.setText("KBD: ON (click outside to disable)");
|
||||
kbdIndicator.setForeground(DARK_GREEN);
|
||||
kbdIndicator.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(DARK_GREEN),
|
||||
BorderFactory.createEmptyBorder(2, 6, 2, 6)));
|
||||
} else {
|
||||
kbdIndicator.setText("KBD: OFF (click VGA screen to enable)");
|
||||
kbdIndicator.setForeground(DARK_RED);
|
||||
kbdIndicator.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(DARK_RED),
|
||||
BorderFactory.createEmptyBorder(2, 6, 2, 6)));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendLog(String line) {
|
||||
logArea.append(line + "\n");
|
||||
logArea.setCaretPosition(logArea.getDocument().getLength());
|
||||
}
|
||||
|
||||
private String formatModule(ModuleInstance m) {
|
||||
return String.format("[%s] %s (%s) @ arch=%s",
|
||||
m.getMetadata().getType(), m.getName(), m.getMetadata().getName(), m.getMetadata().getArch());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.cbe.gui.internal;
|
||||
|
||||
import com.cbe.core.PostCode;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class PostCodePanel extends JPanel implements com.cbe.loader.Engine.PostListener {
|
||||
private final SevenSegmentDigit highDigit;
|
||||
private final SevenSegmentDigit lowDigit;
|
||||
private final JLabel descLabel;
|
||||
private final Led[] leds = new Led[8];
|
||||
private final Timer refreshTimer;
|
||||
|
||||
private static final Color DARK_BG = new Color(22, 22, 28);
|
||||
private static final Color DARK_BORDER = new Color(50, 50, 60);
|
||||
private static final Color DARK_ACCENT = new Color(80, 160, 255);
|
||||
private static final Color DARK_GREEN = new Color(80, 220, 120);
|
||||
|
||||
public PostCodePanel() {
|
||||
setLayout(new BorderLayout(4, 4));
|
||||
setBackground(DARK_BG);
|
||||
setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(DARK_BORDER), "POST (Power-On Self-Test)",
|
||||
TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION,
|
||||
new Font(Font.MONOSPACED, Font.BOLD, 11), DARK_ACCENT));
|
||||
|
||||
JPanel digitsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 4));
|
||||
digitsPanel.setBackground(DARK_BG);
|
||||
highDigit = new SevenSegmentDigit();
|
||||
lowDigit = new SevenSegmentDigit();
|
||||
digitsPanel.add(highDigit);
|
||||
digitsPanel.add(lowDigit);
|
||||
|
||||
descLabel = new JLabel(" ");
|
||||
descLabel.setFont(new Font(Font.MONOSPACED, Font.BOLD, 11));
|
||||
descLabel.setForeground(DARK_GREEN);
|
||||
descLabel.setBackground(DARK_BG);
|
||||
descLabel.setOpaque(true);
|
||||
descLabel.setBorder(BorderFactory.createEmptyBorder(2, 6, 2, 6));
|
||||
descLabel.setPreferredSize(new Dimension(360, 22));
|
||||
|
||||
JPanel leftCol = new JPanel();
|
||||
leftCol.setBackground(DARK_BG);
|
||||
leftCol.setLayout(new BoxLayout(leftCol, BoxLayout.Y_AXIS));
|
||||
leftCol.add(digitsPanel);
|
||||
leftCol.add(descLabel);
|
||||
|
||||
add(leftCol, BorderLayout.CENTER);
|
||||
|
||||
JPanel ledsPanel = new JPanel(new GridLayout(2, 4, 3, 3));
|
||||
ledsPanel.setBackground(DARK_BG);
|
||||
ledsPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
|
||||
String[] labels = {"PWR", "CPU", "MEM", "VID", "KBD", "SND", "DSK", "CLK"};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
leds[i] = new Led(labels[i]);
|
||||
ledsPanel.add(leds[i]);
|
||||
}
|
||||
add(ledsPanel, BorderLayout.EAST);
|
||||
|
||||
updateDisplay(0, 0, "Idle");
|
||||
|
||||
refreshTimer = new Timer(120, e -> repaint());
|
||||
refreshTimer.start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
refreshTimer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostChange(int code, int leds, String description) {
|
||||
updateDisplay(code, leds, description);
|
||||
}
|
||||
|
||||
private void updateDisplay(int code, int ledsMask, String description) {
|
||||
int hi = (code >> 4) & 0x0F;
|
||||
int lo = code & 0x0F;
|
||||
highDigit.setValue(hi);
|
||||
lowDigit.setValue(lo);
|
||||
descLabel.setText(" 0x" + Integer.toHexString(code).toUpperCase() + " " + description);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
leds[i].setOn((ledsMask & (1 << i)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 7-segment digit ======
|
||||
static class SevenSegmentDigit extends JPanel {
|
||||
private int value = 0;
|
||||
private static final Color ON = new Color(255, 100, 20);
|
||||
private static final Color OFF = new Color(50, 20, 0);
|
||||
|
||||
public SevenSegmentDigit() {
|
||||
setPreferredSize(new Dimension(44, 60));
|
||||
setBackground(DARK_BG);
|
||||
}
|
||||
|
||||
public void setValue(int v) { this.value = v & 0x0F; repaint(); }
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
int pad = 4;
|
||||
int segW = (w - 2 * pad) / 2;
|
||||
int segH = (h - 3 * pad) / 4;
|
||||
int cx = w / 2;
|
||||
|
||||
boolean[] segOn = segmentsFor(value);
|
||||
|
||||
drawHSeg(g2, cx, pad, segW, segH / 2, segOn[0]);
|
||||
drawHSeg(g2, cx, h / 2 - segH / 4, segW, segH / 2, segOn[6]);
|
||||
drawHSeg(g2, cx, h - pad - segH / 2, segW, segH / 2, segOn[3]);
|
||||
|
||||
drawVSeg(g2, cx - segW, pad + segH, segW / 2, segH, segOn[5]);
|
||||
drawVSeg(g2, cx + segW / 2, pad + segH, segW / 2, segH, segOn[1]);
|
||||
drawVSeg(g2, cx - segW, h / 2 + segH / 4, segW / 2, segH, segOn[4]);
|
||||
drawVSeg(g2, cx + segW / 2, h / 2 + segH / 4, segW / 2, segH, segOn[2]);
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
private void drawHSeg(Graphics2D g2, int cx, int y, int w, int h, boolean on) {
|
||||
int x = cx - w / 2;
|
||||
Polygon p = new Polygon();
|
||||
p.addPoint(x + 2, y);
|
||||
p.addPoint(x + w - 2, y);
|
||||
p.addPoint(x + w - 4, y + h);
|
||||
p.addPoint(x + 2, y + h);
|
||||
p.addPoint(x + 4, y + h / 2);
|
||||
p.addPoint(x + 4, y + h / 2);
|
||||
g2.setColor(on ? ON : OFF);
|
||||
g2.fillPolygon(p);
|
||||
}
|
||||
|
||||
private void drawVSeg(Graphics2D g2, int x, int y, int w, int h, boolean on) {
|
||||
Polygon p = new Polygon();
|
||||
p.addPoint(x, y);
|
||||
p.addPoint(x + w, y + 1);
|
||||
p.addPoint(x + w, y + h - 2);
|
||||
p.addPoint(x, y + h);
|
||||
p.addPoint(x + w / 2, y + h / 2);
|
||||
g2.setColor(on ? ON : OFF);
|
||||
g2.fillPolygon(p);
|
||||
}
|
||||
|
||||
private boolean[] segmentsFor(int v) {
|
||||
switch (v & 0x0F) {
|
||||
case 0x0: return new boolean[]{true, true, true, true, true, true, false};
|
||||
case 0x1: return new boolean[]{false, true, true, false, false, false, false};
|
||||
case 0x2: return new boolean[]{true, true, false, true, true, false, true};
|
||||
case 0x3: return new boolean[]{true, true, true, true, false, false, true};
|
||||
case 0x4: return new boolean[]{false, true, true, false, false, true, true};
|
||||
case 0x5: return new boolean[]{true, false, true, true, false, true, true};
|
||||
case 0x6: return new boolean[]{true, false, true, true, true, true, true};
|
||||
case 0x7: return new boolean[]{true, true, true, false, false, false, false};
|
||||
case 0x8: return new boolean[]{true, true, true, true, true, true, true};
|
||||
case 0x9: return new boolean[]{true, true, true, true, false, true, true};
|
||||
case 0xA: return new boolean[]{true, true, true, false, true, true, true};
|
||||
case 0xB: return new boolean[]{false, false, true, true, true, true, true};
|
||||
case 0xC: return new boolean[]{true, false, false, true, true, true, false};
|
||||
case 0xD: return new boolean[]{false, true, true, true, true, false, true};
|
||||
case 0xE: return new boolean[]{true, false, false, true, true, true, true};
|
||||
case 0xF: return new boolean[]{true, false, false, false, true, true, true};
|
||||
default: return new boolean[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====== LED ======
|
||||
static class Led extends JPanel {
|
||||
private final String label;
|
||||
private boolean on = false;
|
||||
|
||||
public Led(String label) {
|
||||
this.label = label;
|
||||
setPreferredSize(new Dimension(48, 36));
|
||||
setBackground(DARK_BG);
|
||||
}
|
||||
|
||||
public void setOn(boolean on) {
|
||||
if (this.on != on) {
|
||||
this.on = on;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
int size = Math.min(getWidth(), getHeight()) - 12;
|
||||
int x = (getWidth() - size) / 2;
|
||||
int y = 2;
|
||||
Color c = on ? new Color(60, 255, 80) : new Color(30, 50, 30);
|
||||
g2.setColor(c);
|
||||
g2.fillOval(x, y, size, size);
|
||||
if (on) {
|
||||
g2.setColor(new Color(60, 255, 80, 60));
|
||||
g2.fillOval(x - 2, y - 2, size + 4, size + 4);
|
||||
}
|
||||
g2.setColor(new Color(180, 180, 190));
|
||||
g2.setFont(g2.getFont().deriveFont(9f));
|
||||
FontMetrics fm = g2.getFontMetrics();
|
||||
int tx = (getWidth() - fm.stringWidth(label)) / 2;
|
||||
g2.drawString(label, tx, getHeight() - 2);
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.cbe.gui.internal;
|
||||
|
||||
import com.cbe.loader.CompiledModuleLoader;
|
||||
import com.cbe.loader.Engine;
|
||||
import com.cbe.loader.SourceModuleLoader;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class ScreenPanel extends JPanel {
|
||||
private static final int CHAR_W = 8;
|
||||
private static final int CHAR_H = 14;
|
||||
private static final int COLS = 80;
|
||||
private static final int ROWS = 25;
|
||||
private static final int SCREEN_W = COLS * CHAR_W;
|
||||
private static final int SCREEN_H = ROWS * CHAR_H;
|
||||
private static final int PADDING = 8;
|
||||
|
||||
private BufferedImage screenImage;
|
||||
private Graphics2D screenG2d;
|
||||
private byte[] lastVram;
|
||||
|
||||
private static final Color DARK_BG = new Color(18, 18, 24);
|
||||
private static final Color DARK_FRAME_OUTER = new Color(50, 50, 65);
|
||||
private static final Color DARK_FRAME_INNER = new Color(25, 25, 35);
|
||||
private static final Color DARK_SCREEN_BG = new Color(10, 10, 18);
|
||||
private static final Color DARK_GREEN = new Color(140, 230, 140);
|
||||
private static final Color DARK_CURSOR = new Color(140, 230, 140, 70);
|
||||
private static final Color DARK_SCANLINE = new Color(0, 0, 0, 25);
|
||||
private static final Color DARK_PLACEHOLDER = new Color(130, 130, 140);
|
||||
|
||||
public ScreenPanel() {
|
||||
setBackground(DARK_BG);
|
||||
setDoubleBuffered(true);
|
||||
|
||||
screenImage = new BufferedImage(SCREEN_W, SCREEN_H, BufferedImage.TYPE_INT_RGB);
|
||||
screenG2d = screenImage.createGraphics();
|
||||
screenG2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
screenG2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||
}
|
||||
|
||||
public void refresh(Engine engine) {
|
||||
byte[] vram = null;
|
||||
int cursorX = 0, cursorY = 0;
|
||||
|
||||
if (engine.getSourceGpu() != null) {
|
||||
SourceModuleLoader.GpuModuleInstance g = engine.getSourceGpu();
|
||||
vram = g.getVram();
|
||||
cursorX = g.getCursorX();
|
||||
cursorY = g.getCursorY();
|
||||
} else if (engine.getCompiledGpu() != null) {
|
||||
CompiledModuleLoader.CompiledGpuInstance g = engine.getCompiledGpu();
|
||||
vram = g.getVram();
|
||||
cursorX = g.getCursorX();
|
||||
cursorY = g.getCursorY();
|
||||
}
|
||||
|
||||
if (vram == null) {
|
||||
screenG2d.setColor(DARK_SCREEN_BG);
|
||||
screenG2d.fillRect(0, 0, SCREEN_W, SCREEN_H);
|
||||
screenG2d.setColor(DARK_PLACEHOLDER);
|
||||
screenG2d.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
|
||||
String msg = "(no GPU module loaded)";
|
||||
int w = screenG2d.getFontMetrics().stringWidth(msg);
|
||||
screenG2d.drawString(msg, (SCREEN_W - w) / 2, SCREEN_H / 2);
|
||||
repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (vram == lastVram) {
|
||||
}
|
||||
lastVram = vram;
|
||||
|
||||
screenG2d.setColor(DARK_SCREEN_BG);
|
||||
screenG2d.fillRect(0, 0, SCREEN_W, SCREEN_H);
|
||||
|
||||
screenG2d.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
screenG2d.setColor(DARK_GREEN);
|
||||
for (int row = 0; row < ROWS; row++) {
|
||||
for (int col = 0; col < COLS; col++) {
|
||||
int offset = row * COLS + col;
|
||||
int b = offset < vram.length ? vram[offset] & 0xFF : 0;
|
||||
char c = (char) (b & 0x7F);
|
||||
if (c == 0) c = ' ';
|
||||
int x = col * CHAR_W;
|
||||
int y = row * CHAR_H + CHAR_H - 2;
|
||||
screenG2d.drawString(String.valueOf(c), x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if ((System.currentTimeMillis() / 500) % 2 == 0) {
|
||||
int cx = cursorX * CHAR_W;
|
||||
int cy = cursorY * CHAR_H;
|
||||
screenG2d.setColor(DARK_CURSOR);
|
||||
screenG2d.fillRect(cx, cy, CHAR_W, CHAR_H);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
|
||||
int pw = getWidth();
|
||||
int ph = getHeight();
|
||||
|
||||
g2.setColor(DARK_FRAME_OUTER);
|
||||
g2.fillRoundRect(0, 0, pw, ph, 12, 12);
|
||||
g2.setColor(DARK_FRAME_INNER);
|
||||
g2.fillRoundRect(4, 4, pw - 8, ph - 8, 8, 8);
|
||||
|
||||
double scale = Math.min((double) (pw - 16) / SCREEN_W, (double) (ph - 16) / SCREEN_H);
|
||||
int sw = (int) (SCREEN_W * scale);
|
||||
int sh = (int) (SCREEN_H * scale);
|
||||
int sx = (pw - sw) / 2;
|
||||
int sy = (ph - sh) / 2;
|
||||
g2.drawImage(screenImage, sx, sy, sw, sh, null);
|
||||
|
||||
g2.setColor(DARK_SCANLINE);
|
||||
for (int y = sy; y < sy + sh; y += 2) {
|
||||
g2.drawLine(sx, y, sx + sw, y);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user