Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d31c55391 | ||
|
|
3cf006ba13 | ||
|
|
fccb799a9c | ||
|
|
a7985bad6e | ||
|
|
b33a6c3055 | ||
|
|
89feaaa3e9 | ||
|
|
573f72e46f | ||
|
|
94486ecf02 | ||
|
|
70f0f2a318 | ||
|
|
85cc0f5754 | ||
|
|
af5fc0c4fc | ||
|
|
ff07e1907f | ||
|
|
3dc8b93f94 | ||
|
|
5a02e7e52d | ||
|
|
b2adf1f9dc | ||
|
|
f753f9f59b | ||
|
|
dd066e0bbb | ||
|
|
0927a42107 | ||
|
|
4b3852a1d2 |
@@ -1,11 +1,12 @@
|
||||
* text=auto
|
||||
|
||||
*.c text diff=c
|
||||
*.h text diff=c
|
||||
*.html text diff=html
|
||||
*.java text diff=java
|
||||
*.sample text
|
||||
*.txt text
|
||||
*.c text eol=lf diff=c
|
||||
*.css text eol=lf diff=css
|
||||
*.h text eol=lf diff=c
|
||||
*.html text eol=lf diff=html
|
||||
*.java text eol=lf diff=java
|
||||
*.js text eol=lf diff=js
|
||||
*.txt text eol=lf
|
||||
|
||||
*.class binary
|
||||
*.dll binary
|
||||
*.wasm binary
|
||||
*.woff2 binary
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.nio.charset.*;
|
||||
import java.util.*;
|
||||
import javax.imageio.*;
|
||||
|
||||
public class Bundle {
|
||||
|
||||
// File loaded from disk
|
||||
static class File2 implements Comparable<File2> {
|
||||
|
||||
// Instance fields
|
||||
byte[] data; // Contents
|
||||
String filename; // Full path relative to root
|
||||
|
||||
// Comparator
|
||||
public int compareTo(File2 o) {
|
||||
if (filename.equals("app/_boot.js"))
|
||||
return -1;
|
||||
if (o.filename.equals("app/_boot.js"))
|
||||
return 1;
|
||||
return filename.compareTo(o.filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load all files in directory tree into memory
|
||||
static HashMap<String, File2> readFiles(String bundleTitle) {
|
||||
var dirs = new ArrayDeque<File>();
|
||||
var root = new File(".");
|
||||
var subs = new ArrayDeque<String>();
|
||||
var files = new HashMap<String, File2>();
|
||||
|
||||
// Process all subdirectories
|
||||
dirs.add(root);
|
||||
while (!dirs.isEmpty()) {
|
||||
var dir = dirs.remove();
|
||||
|
||||
// Add all subdirectories
|
||||
for (var sub : dir.listFiles(f->f.isDirectory())) {
|
||||
|
||||
// Exclusions
|
||||
if (dir == root && sub.getName().equals(".git"))
|
||||
continue;
|
||||
|
||||
// Add the directory for bundling
|
||||
dirs.add(sub);
|
||||
}
|
||||
|
||||
// Add all files
|
||||
for (var file : dir.listFiles(f->f.isFile())) {
|
||||
var file2 = new File2();
|
||||
|
||||
// Read the file into memory
|
||||
try {
|
||||
var stream = new FileInputStream(file);
|
||||
file2.data = stream.readAllBytes();
|
||||
stream.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
// Determine the file's full pathname
|
||||
subs.clear();
|
||||
subs.addFirst(file.getName());
|
||||
for (;;) {
|
||||
file = file.getParentFile();
|
||||
if (file.equals(root))
|
||||
break;
|
||||
subs.addFirst(file.getName());
|
||||
}
|
||||
file2.filename = String.join("/", subs);
|
||||
|
||||
// Exclusions
|
||||
if (
|
||||
file2.filename.startsWith(".git" ) ||
|
||||
file2.filename.startsWith(bundleTitle + "_") &&
|
||||
file2.filename.endsWith (".html" )
|
||||
) continue;
|
||||
|
||||
// Add the file to the output
|
||||
files.put(file2.filename, file2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
// Prepend manifest object to _boot.js
|
||||
static void manifest(HashMap<String, File2> files, String bundleName) {
|
||||
|
||||
// Produce a sorted list of files
|
||||
var values = files.values().toArray(new File2[files.size()]);
|
||||
Arrays.sort(values);
|
||||
|
||||
// Build a file manifest
|
||||
var manifest = new StringBuilder();
|
||||
manifest.append("\"use strict\";\nlet manifest=[");
|
||||
for (var file : values) {
|
||||
manifest.append(
|
||||
"[\"" + file.filename + "\"," + file.data.length + "]");
|
||||
if (file != values[values.length - 1])
|
||||
manifest.append(",");
|
||||
}
|
||||
manifest.append("],bundleName=\"" + bundleName + "\";");
|
||||
|
||||
// Prepend the manifest to _boot.js
|
||||
var boot = files.get("app/_boot.js");
|
||||
boot.data = (
|
||||
manifest.toString() +
|
||||
new String(boot.data, StandardCharsets.UTF_8) +
|
||||
"\u0000"
|
||||
).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
// Construct bundled blob
|
||||
static byte[] blob(HashMap<String, File2> files) {
|
||||
|
||||
// Produce a sorted list of files
|
||||
var values = files.values().toArray(new File2[files.size()]);
|
||||
Arrays.sort(values);
|
||||
|
||||
// Build the blob
|
||||
var blob = new ByteArrayOutputStream();
|
||||
for (var file : values) try {
|
||||
blob.write(file.data);
|
||||
} catch (Exception e) { }
|
||||
|
||||
return blob.toByteArray();
|
||||
}
|
||||
|
||||
// Encode bundled blob as a .png
|
||||
static byte[] png(byte[] blob) {
|
||||
|
||||
// Calculate the dimensions of the image
|
||||
int width = (int) Math.ceil(Math.sqrt(blob.length));
|
||||
int height = (int) Math.ceil((double) blob.length / width);
|
||||
|
||||
// Prepare the pixel data
|
||||
var pixels = new int[width * height];
|
||||
for (int x = 0; x < blob.length; x++) {
|
||||
int l = blob[x] & 0xFF;
|
||||
pixels[x] = 0xFF000000 | l << 16 | l << 8 | l;
|
||||
}
|
||||
|
||||
// Produce a BufferedImage containing the pixels
|
||||
var img = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_BYTE_GRAY);
|
||||
img.getRaster().setPixels(0, 0, width, height, pixels);
|
||||
|
||||
// Encode the image as a PNG byte array
|
||||
var png = new ByteArrayOutputStream();
|
||||
try { ImageIO.write(img, "png", png); }
|
||||
catch (Exception e) { }
|
||||
return png.toByteArray();
|
||||
}
|
||||
|
||||
// Embed bundle .png into template.html as a data URL
|
||||
static void template(byte[] png, String bundleName) {
|
||||
|
||||
// Encode the PNG as a data URL
|
||||
String url = "data:image/png;base64," +
|
||||
Base64.getMimeEncoder().encodeToString(png)
|
||||
.replaceAll("\\r\\n", "");
|
||||
|
||||
try {
|
||||
|
||||
// Read template.html into memory
|
||||
var inStream = new FileInputStream("app/template.html");
|
||||
String template =
|
||||
new String(inStream.readAllBytes(), StandardCharsets.UTF_8)
|
||||
.replace("src=\"\"", "src=\"" + url + "\"")
|
||||
;
|
||||
inStream.close();
|
||||
|
||||
// Write the output HTML file
|
||||
var outStream = new FileOutputStream(bundleName + ".html");
|
||||
outStream.write(template.getBytes(StandardCharsets.UTF_8));
|
||||
outStream.close();
|
||||
} catch (Exception e) { throw new RuntimeException(e.getMessage()); }
|
||||
|
||||
}
|
||||
|
||||
// Determine the filename of the bundle
|
||||
static String bundleName(String name) {
|
||||
var calendar = Calendar.getInstance();
|
||||
return String.format("%s_%04d%02d%02d",
|
||||
name,
|
||||
calendar.get(Calendar.YEAR),
|
||||
calendar.get(Calendar.MONTH) + 1,
|
||||
calendar.get(Calendar.DAY_OF_MONTH)
|
||||
);
|
||||
}
|
||||
|
||||
// Program entry point
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Error checking
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: Bundle <name>");
|
||||
return;
|
||||
}
|
||||
|
||||
// Program tasks
|
||||
String bundleName = bundleName(args[0]);
|
||||
var files = readFiles(args[0]);
|
||||
manifest(files, bundleName);
|
||||
var blob = blob(files);
|
||||
var png = png(blob);
|
||||
template(png, bundleName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
The Bundle.java utility prepends a file manifest to this script before
|
||||
execution is started. This script runs within the context of an async
|
||||
function.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Bundle //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Bundled file manager
|
||||
let Bundle = globalThis.Bundle = new class Bundle extends Array {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Configure instance fields
|
||||
this.debug =
|
||||
location.protocol != "file:" && location.hash == "#debug";
|
||||
this.decoder = new TextDecoder();
|
||||
this.encoder = new TextEncoder();
|
||||
this.moduleCall = (... a)=>this.module (... a);
|
||||
this.resourceCall = (... a)=>this.resource(... a);
|
||||
|
||||
// Generate the CRC32 lookup table
|
||||
this.crcLookup = new Uint32Array(256);
|
||||
for (let x = 0; x <= 255; x++) {
|
||||
let l = x;
|
||||
for (let j = 7; j >= 0; j--)
|
||||
l = ((l >>> 1) ^ (0xEDB88320 & -(l & 1)));
|
||||
this.crcLookup[x] = l;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Add a file to the bundle
|
||||
add(name, data) {
|
||||
this.push(this[name] = new BundledFile(name, data));
|
||||
}
|
||||
|
||||
// Export all bundled resources to a .ZIP file
|
||||
save() {
|
||||
let centrals = new Array(this.length);
|
||||
let locals = new Array(this.length);
|
||||
let offset = 0;
|
||||
let size = 0;
|
||||
|
||||
// Encode file and directory entries
|
||||
for (let x = 0; x < this.length; x++) {
|
||||
let file = this[x];
|
||||
let sum = this.crc32(file.data);
|
||||
locals [x] = file.toZipHeader(sum);
|
||||
centrals[x] = file.toZipHeader(sum, offset);
|
||||
offset += locals [x].length;
|
||||
size += centrals[x].length;
|
||||
}
|
||||
|
||||
// Encode end of central directory
|
||||
let end = [];
|
||||
this.writeInt(end, 4, 0x06054B50); // Signature
|
||||
this.writeInt(end, 2, 0); // Disk number
|
||||
this.writeInt(end, 2, 0); // Central dir start disk
|
||||
this.writeInt(end, 2, this.length); // # central dir this disk
|
||||
this.writeInt(end, 2, this.length); // # central dir total
|
||||
this.writeInt(end, 4, size); // Size of central dir
|
||||
this.writeInt(end, 4, offset); // Offset of central dir
|
||||
this.writeInt(end, 2, 0); // .ZIP comment length
|
||||
|
||||
// Prompt the user to save the resulting file
|
||||
let a = document.createElement("a");
|
||||
a.download = bundleName + ".zip";
|
||||
a.href = URL.createObjectURL(new Blob(
|
||||
locals.concat(centrals).concat([Uint8Array.from(end)]),
|
||||
{ type: "application/zip" }
|
||||
));
|
||||
a.style.visibility = "hidden";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Write a byte array into an output buffer
|
||||
writeBytes(data, bytes) {
|
||||
//data.push(... bytes);
|
||||
for (let b of bytes)
|
||||
data.push(b);
|
||||
}
|
||||
|
||||
// Write an integer into an output buffer
|
||||
writeInt(data, size, value) {
|
||||
for (; size > 0; size--, value >>= 8)
|
||||
data.push(value & 0xFF);
|
||||
}
|
||||
|
||||
// Write a string of text as bytes into an output buffer
|
||||
writeString(data, text) {
|
||||
data.push(... this.encoder.encode(text));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Calculate the CRC32 checksum for a byte array
|
||||
crc32(data) {
|
||||
let c = 0xFFFFFFFF;
|
||||
for (let x = 0; x < data.length; x++)
|
||||
c = ((c >>> 8) ^ this.crcLookup[(c ^ data[x]) & 0xFF]);
|
||||
return ~c & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
}();
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BundledFile //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Individual bundled file
|
||||
class BundledFile {
|
||||
|
||||
constructor(name, data) {
|
||||
|
||||
// Configure instance fields
|
||||
this.data = data;
|
||||
this.name = name;
|
||||
|
||||
// Resolve the MIME type
|
||||
this.mime =
|
||||
name.endsWith(".css" ) ? "text/css;charset=UTF-8" :
|
||||
name.endsWith(".frag" ) ? "text/plain;charset=UTF-8" :
|
||||
name.endsWith(".js" ) ? "text/javascript;charset=UTF-8" :
|
||||
name.endsWith(".json" ) ? "application/json;charset=UTF-8" :
|
||||
name.endsWith(".png" ) ? "image/png" :
|
||||
name.endsWith(".svg" ) ? "image/svg+xml;charset=UTF-8" :
|
||||
name.endsWith(".txt" ) ? "text/plain;charset=UTF-8" :
|
||||
name.endsWith(".vert" ) ? "text/plain;charset=UTF-8" :
|
||||
name.endsWith(".wasm" ) ? "application/wasm" :
|
||||
name.endsWith(".woff2") ? "font/woff2" :
|
||||
"application/octet-stream"
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Install a font from a bundled resource
|
||||
async installFont(name) {
|
||||
if (name === undefined) {
|
||||
name = "/" + this.name;
|
||||
name = name.substring(name.lastIndexOf("/") + 1);
|
||||
}
|
||||
let ret = new FontFace(name, "url('"+
|
||||
(Bundle.debug ? this.name : this.toDataURL()) + "'");
|
||||
await ret.load();
|
||||
document.fonts.add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Install an image as a CSS icon from a bundled resource
|
||||
installImage(name, filename) {
|
||||
document.documentElement.style.setProperty("--" +
|
||||
name || this.name.replaceAll(/\/\./, "_"),
|
||||
"url('" + (Bundle.debug ?
|
||||
filename || this.name : this.toDataURL()) + "')");
|
||||
return name;
|
||||
}
|
||||
|
||||
// Install a stylesheet from a bundled resource
|
||||
installStylesheet(enabled) {
|
||||
let ret = document.createElement("link");
|
||||
ret.href = Bundle.debug ? this.name : this.toDataURL();
|
||||
ret.rel = "stylesheet";
|
||||
ret.type = "text/css";
|
||||
ret.setEnabled = enabled=>{
|
||||
if (enabled)
|
||||
ret.removeAttribute("disabled");
|
||||
else ret.setAttribute("disabled", "");
|
||||
};
|
||||
ret.setEnabled(!!enabled);
|
||||
document.head.appendChild(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Encode the file data as a data URL
|
||||
toDataURL() {
|
||||
return "data:" + this.mime + ";base64," +
|
||||
btoa(String.fromCharCode(...this.data));
|
||||
}
|
||||
|
||||
// Interpret the file's contents as bundled script source data URL
|
||||
toScript() {
|
||||
|
||||
// Process all URL strings prefixed with /**/
|
||||
let parts = this.toString().split("/**/");
|
||||
let src = parts.shift();
|
||||
for (let part of parts) {
|
||||
let quote = part.indexOf("\"", 1);
|
||||
|
||||
// Begin with the path of the current file
|
||||
let path = this.name.split("/");
|
||||
path.pop();
|
||||
|
||||
// Navigate to the path of the target file
|
||||
let file = part.substring(1, quote).split("/");
|
||||
while (file.length > 0) {
|
||||
let sub = file.shift();
|
||||
switch (sub) {
|
||||
case "..": path.pop(); // Fallthrough
|
||||
case "." : break;
|
||||
default : path.push(sub);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the file as a data URL
|
||||
file = Bundle[path.join("/")];
|
||||
src += "\"" + file[
|
||||
file.mime.startsWith("text/javascript") ?
|
||||
"toScript" : "toDataURL"
|
||||
]() + "\"" + part.substring(quote + 1);
|
||||
}
|
||||
|
||||
// Encode the transformed source as a data URL
|
||||
return "data:" + this.mime + ";base64," + btoa(src);
|
||||
}
|
||||
|
||||
// Decode the file data as a UTF-8 string
|
||||
toString() {
|
||||
return Bundle.decoder.decode(this.data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Produce a .ZIP header for export
|
||||
toZipHeader(crc32, offset) {
|
||||
let central = offset !== undefined;
|
||||
let ret = [];
|
||||
if (central) {
|
||||
Bundle.writeInt (ret, 4, 0x02014B50); // Signature
|
||||
Bundle.writeInt (ret, 2, 20); // Version created by
|
||||
} else
|
||||
Bundle.writeInt (ret, 4, 0x04034B50); // Signature
|
||||
Bundle.writeInt (ret, 2, 20); // Version required
|
||||
Bundle.writeInt (ret, 2, 0); // Bit flags
|
||||
Bundle.writeInt (ret, 2, 0); // Compression method
|
||||
Bundle.writeInt (ret, 2, 0); // Modified time
|
||||
Bundle.writeInt (ret, 2, 0); // Modified date
|
||||
Bundle.writeInt (ret, 4, crc32); // Checksum
|
||||
Bundle.writeInt (ret, 4, this.data.length); // Compressed size
|
||||
Bundle.writeInt (ret, 4, this.data.length); // Uncompressed size
|
||||
Bundle.writeInt (ret, 2, this.name.length); // Filename length
|
||||
Bundle.writeInt (ret, 2, 0); // Extra field length
|
||||
if (central) {
|
||||
Bundle.writeInt (ret, 2, 0); // File comment length
|
||||
Bundle.writeInt (ret, 2, 0); // Disk number start
|
||||
Bundle.writeInt (ret, 2, 0); // Internal attributes
|
||||
Bundle.writeInt (ret, 4, 0); // External attributes
|
||||
Bundle.writeInt (ret, 4, offset); // Relative offset
|
||||
}
|
||||
Bundle.writeString (ret, this.name); // Filename
|
||||
if (!central)
|
||||
Bundle.writeBytes(ret, this.data); // File data
|
||||
return Uint8Array.from(ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Boot Program //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// De-register the boot function
|
||||
delete globalThis.a;
|
||||
|
||||
// Remove the bundle image element from the document
|
||||
Bundle.src = arguments[0].src;
|
||||
arguments[0].remove();
|
||||
|
||||
// Convert the file manifest into BundledFile objects
|
||||
let buffer = arguments[1];
|
||||
let offset = arguments[2] - manifest[0][1];
|
||||
for (let entry of manifest) {
|
||||
Bundle.add(entry[0], buffer.subarray(offset, offset + entry[1]));
|
||||
offset += entry[1];
|
||||
if (Bundle.length == 1)
|
||||
offset++; // Skip null delimiter
|
||||
}
|
||||
|
||||
// Begin program operations
|
||||
import(Bundle.debug ? "./app/main.js" : Bundle["app/main.js"].toScript());
|
||||
@@ -0,0 +1,448 @@
|
||||
import { Debugger } from /**/"./Debugger.js";
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// App //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Web-based emulator application
|
||||
class App extends Toolkit {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(options) {
|
||||
super({
|
||||
className: "tk tk-app",
|
||||
label : "app.title",
|
||||
role : "application",
|
||||
tagName : "div",
|
||||
style : {
|
||||
display : "flex",
|
||||
flexDirection: "column"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
options = options || {};
|
||||
this.debugMode = true;
|
||||
this.dualSims = false;
|
||||
this.core = options.core;
|
||||
this.linkSims = true;
|
||||
this.locales = {};
|
||||
this.themes = {};
|
||||
this.Toolkit = Toolkit;
|
||||
|
||||
// Configure themes
|
||||
if ("themes" in options)
|
||||
for (let theme of Object.entries(options.themes))
|
||||
this.addTheme(theme[0], theme[1]);
|
||||
if ("theme" in options)
|
||||
this.setTheme(options.theme);
|
||||
|
||||
// Configure locales
|
||||
if ("locales" in options)
|
||||
for (let locale of options.locales)
|
||||
this.addLocale(locale);
|
||||
if ("locale" in options)
|
||||
this.setLocale(options.locale);
|
||||
|
||||
// Configure widget
|
||||
this.localize(this);
|
||||
|
||||
// Not presenting a standalone application
|
||||
if (!options.standalone)
|
||||
return;
|
||||
|
||||
// Set up standalone widgets
|
||||
this.initMenuBar();
|
||||
this.desktop = new Toolkit.Desktop(this,
|
||||
{ style: { flexGrow: 1 } });
|
||||
this.add(this.desktop);
|
||||
|
||||
// Configure document for presentation
|
||||
document.body.className = "tk tk-body";
|
||||
window.addEventListener("resize", e=>
|
||||
this.element.style.height = window.innerHeight + "px");
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
document.body.appendChild(this.element);
|
||||
|
||||
// Configure debugger components
|
||||
this[0] = new Debugger(this, 0, this.core[0]);
|
||||
this[1] = new Debugger(this, 1, this.core[1]);
|
||||
|
||||
// Configure subscription handling
|
||||
this.subscriptions = {
|
||||
[this.core[0].sim]: this[0],
|
||||
[this.core[1].sim]: this[1]
|
||||
};
|
||||
this.core.onsubscriptions = e=>this.onSubscriptions(e);
|
||||
|
||||
// Temporary config debugging
|
||||
console.log("Memory keyboard commands:");
|
||||
console.log(" Ctrl+G: Goto");
|
||||
console.log("Disassembler keyboard commands:");
|
||||
console.log(" Ctrl+B: Toggle bytes column");
|
||||
console.log(" Ctrl+F: Fit columns");
|
||||
console.log(" Ctrl+G: Goto");
|
||||
console.log(" F10: Run to next");
|
||||
console.log(" F11: Single step");
|
||||
console.log("Call dasm(\"key\", value) in the console " +
|
||||
"to configure the disassembler:");
|
||||
console.log(this[0].getDasmConfig());
|
||||
window.dasm = (key, value)=>{
|
||||
let config = this[0].getDasmConfig();
|
||||
if (!key in config || typeof value != typeof config[key])
|
||||
return;
|
||||
if (typeof value == "number" && value != 1 && value != 0)
|
||||
return;
|
||||
config[key] = value;
|
||||
this[0].setDasmConfig(config);
|
||||
this[1].setDasmConfig(config);
|
||||
return this[0].getDasmConfig();
|
||||
};
|
||||
}
|
||||
|
||||
// Configure file menu
|
||||
initFileMenu(menuBar) {
|
||||
let menu, item;
|
||||
|
||||
// Menu
|
||||
menuBar.add(menu = menuBar.file = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.file._" }));
|
||||
|
||||
// Load ROM
|
||||
menu.add(item = menu.loadROM0 = new Toolkit.MenuItem(this, {
|
||||
text: "app.menu.file.loadROM"
|
||||
}));
|
||||
item.setSubstitution("sim", "");
|
||||
item.addEventListener("action",
|
||||
()=>this.promptFile(f=>this.loadROM(0, f)));
|
||||
menu.add(item = menu.loadROM1 = new Toolkit.MenuItem(this, {
|
||||
text : "app.menu.file.loadROM",
|
||||
visible: false
|
||||
}));
|
||||
item.setSubstitution("sim", " 2");
|
||||
item.addEventListener("action",
|
||||
()=>this.promptFile(f=>this.loadROM(1, f)));
|
||||
|
||||
// Debug Mode
|
||||
menu.add(item = menu.debugMode = new Toolkit.MenuItem(this, {
|
||||
checked: this.debugMode,
|
||||
enabled: false,
|
||||
text : "app.menu.file.debugMode",
|
||||
type : "check"
|
||||
}));
|
||||
item.addEventListener("action", e=>e.component.setChecked(true));
|
||||
}
|
||||
|
||||
// Configure Emulation menu
|
||||
initEmulationMenu(menuBar) {
|
||||
let menu, item;
|
||||
|
||||
menuBar.add(menu = menuBar.emulation = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.emulation._" }));
|
||||
|
||||
menu.add(item = menu.runPause = new Toolkit.MenuItem(this, {
|
||||
enabled: false,
|
||||
text : "app.menu.emulation.run"
|
||||
}));
|
||||
|
||||
menu.add(item = menu.reset = new Toolkit.MenuItem(this, {
|
||||
enabled: false,
|
||||
text : "app.menu.emulation.reset"
|
||||
}));
|
||||
|
||||
menu.add(item = menu.dualSims = new Toolkit.MenuItem(this, {
|
||||
checked: this.dualSims,
|
||||
text : "app.menu.emulation.dualSims",
|
||||
type : "check"
|
||||
}));
|
||||
item.addEventListener("action",
|
||||
e=>this.setDualSims(e.component.isChecked));
|
||||
|
||||
menu.add(item = menu.linkSims = new Toolkit.MenuItem(this, {
|
||||
checked: this.linkSims,
|
||||
text : "app.menu.emulation.linkSims",
|
||||
type : "check",
|
||||
visible: this.dualSims
|
||||
}));
|
||||
item.addEventListener("action",
|
||||
e=>this.setLinkSims(e.component.isChecked));
|
||||
}
|
||||
|
||||
// Configure Debug menus
|
||||
initDebugMenu(menuBar, sim) {
|
||||
let menu, item;
|
||||
|
||||
menuBar.add(menu = menuBar["debug" + sim] =
|
||||
new Toolkit.MenuItem(this, {
|
||||
text : "app.menu.debug._",
|
||||
visible: sim == 0 || this.dualSims
|
||||
}));
|
||||
menu.setSubstitution("sim",
|
||||
sim == 1 || this.dualSims ? " " + (sim + 1) : "");
|
||||
|
||||
menu.add(item = menu.console = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.console", enabled: false }));
|
||||
|
||||
menu.add(item = menu.memory = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.memory" }));
|
||||
item.addEventListener("action",
|
||||
()=>this.showWindow(this[sim].memoryWindow));
|
||||
|
||||
menu.add(item = menu.cpu = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.cpu" }));
|
||||
item.addEventListener("action",
|
||||
()=>this.showWindow(this[sim].cpuWindow));
|
||||
|
||||
menu.add(item = menu.breakpoints = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.breakpoints", enabled: false }));
|
||||
menu.addSeparator();
|
||||
menu.add(item = menu.palettes = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.palettes", enabled: false }));
|
||||
menu.add(item = menu.characters = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.characters", enabled: false }));
|
||||
menu.add(item = menu.bgMaps = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.bgMaps", enabled: false }));
|
||||
menu.add(item = menu.objects = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.objects", enabled: false }));
|
||||
menu.add(item = menu.worlds = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.worlds", enabled: false }));
|
||||
menu.add(item = menu.frameBuffers = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.debug.frameBuffers", enabled: false }));
|
||||
|
||||
}
|
||||
|
||||
// Configure Theme menu
|
||||
initThemeMenu(menuBar) {
|
||||
let menu, item;
|
||||
|
||||
menuBar.add(menu = menuBar.theme = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.theme._" }));
|
||||
|
||||
menu.add(item = menu.light = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.theme.light" }));
|
||||
item.addEventListener("action", e=>this.setTheme("light"));
|
||||
|
||||
menu.add(item = menu.dark = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.theme.dark" }));
|
||||
item.addEventListener("action", e=>this.setTheme("dark"));
|
||||
|
||||
menu.add(item = menu.virtual = new Toolkit.MenuItem(this,
|
||||
{ text: "app.menu.theme.virtual" }));
|
||||
item.addEventListener("action", e=>this.setTheme("virtual"));
|
||||
}
|
||||
|
||||
// Set up the menu bar
|
||||
initMenuBar() {
|
||||
let menuBar = this.menuBar = new Toolkit.MenuBar(this,
|
||||
{ label: "app.menu._" });
|
||||
this.initFileMenu (menuBar);
|
||||
this.initEmulationMenu(menuBar);
|
||||
this.initDebugMenu (menuBar, 0);
|
||||
this.initDebugMenu (menuBar, 1);
|
||||
this.initThemeMenu (menuBar);
|
||||
this.add(menuBar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Subscriptions arrived from the core thread
|
||||
onSubscriptions(subscriptions) {
|
||||
for (let sim of Object.entries(subscriptions)) {
|
||||
let dbg = this.subscriptions[sim[0]];
|
||||
for (let sub of Object.entries(sim[1])) switch (sub[0]) {
|
||||
case "proregs": dbg.programRegisters.refresh(sub[1]); break;
|
||||
case "sysregs": dbg.systemRegisters .refresh(sub[1]); break;
|
||||
case "dasm" : dbg.disassembler .refresh(sub[1]); break;
|
||||
case "memory" : dbg.memory .refresh(sub[1]); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Register a locale JSON
|
||||
addLocale(locale) {
|
||||
if (!("id" in locale))
|
||||
throw "No id field in locale";
|
||||
this.locales[locale.id] = Toolkit.flatten(locale);
|
||||
}
|
||||
|
||||
// Register a theme stylesheet
|
||||
addTheme(id, stylesheet) {
|
||||
this.themes[id] = stylesheet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Specify the language for localization management
|
||||
setLocale(id) {
|
||||
if (!(id in this.locales)) {
|
||||
let lang = id.substring(0, 2);
|
||||
id = "en-US";
|
||||
for (let key of Object.keys(this.locales)) {
|
||||
if (key.substring(0, 2) == lang) {
|
||||
id = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setLocale(this.locales[id]);
|
||||
}
|
||||
|
||||
// Specify the active color theme
|
||||
setTheme(key) {
|
||||
if (!(key in this.themes))
|
||||
return;
|
||||
for (let tkey of Object.keys(this.themes))
|
||||
this.themes[tkey].setEnabled(tkey == key);
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
if (arguments.length != 0)
|
||||
return super.translate.apply(this, arguments);
|
||||
document.title = super.translate("app.title", this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Load a ROM for a simulation
|
||||
async loadROM(index, file) {
|
||||
|
||||
// No file was given
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
// Load the file into memory
|
||||
try { file = new Uint8Array(await file.arrayBuffer()); }
|
||||
catch {
|
||||
alert(this.translate("error.fileRead"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size
|
||||
if (
|
||||
file.length < 1024 ||
|
||||
file.length > 0x1000000 ||
|
||||
(file.length - 1 & file.length) != 0
|
||||
) {
|
||||
alert(this.translate("error.romNotVB"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the ROM into the simulation
|
||||
if (!(await this[index].sim.setROM(file, { refresh: true }))) {
|
||||
alert(this.translate("error.romNotVB"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Seek the disassembler to PC
|
||||
this[index].disassembler.seek(0xFFFFFFF0, true);
|
||||
}
|
||||
|
||||
// Prompt the user to select a file
|
||||
promptFile(then) {
|
||||
let file = document.createElement("input");
|
||||
file.type = "file";
|
||||
file.addEventListener("input",
|
||||
e=>file.files[0] && then(file.files[0]));
|
||||
file.click();
|
||||
}
|
||||
|
||||
// Attempt to run until the next instruction
|
||||
async runNext(index) {
|
||||
let two = this.dualSims && this.linkSims;
|
||||
|
||||
// Perform the operation
|
||||
let data = await this.core.runNext(
|
||||
this[index].sim.sim,
|
||||
two ? this[index ^ 1].sim.sim : 0, {
|
||||
refresh: true
|
||||
});
|
||||
|
||||
// Update the disassemblers
|
||||
this[index].disassembler.pc = data.pc[0];
|
||||
this[index].disassembler.seek(data.pc[0]);
|
||||
if (two) {
|
||||
this[index ^ 1].disassembler.pc = data.pc[1];
|
||||
this[index ^ 1].disassembler.seek(data.pc[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Specify whether dual sims mode is active
|
||||
setDualSims(dualSims) {
|
||||
let sub = dualSims ? " 1" : "";
|
||||
|
||||
// Configure instance fields
|
||||
this.dualSims = dualSims = !!dualSims;
|
||||
|
||||
// Configure menus
|
||||
this.menuBar.emulation.dualSims.setChecked(dualSims);
|
||||
this.menuBar.emulation.linkSims.setVisible(dualSims);
|
||||
this.menuBar.file.loadROM0.setSubstitution("sim", sub);
|
||||
this.menuBar.file.loadROM1.setVisible(dualSims);
|
||||
this.menuBar.debug0.setSubstitution("sim", sub);
|
||||
this.menuBar.debug1.setVisible(dualSims);
|
||||
|
||||
// Configure debuggers
|
||||
this[0].setDualSims(dualSims);
|
||||
this[1].setDualSims(dualSims);
|
||||
this.core.connect(this[0].sim.sim,
|
||||
dualSims && this.linkSims ? this[1].sim.sim : 0);
|
||||
}
|
||||
|
||||
// Specify whether the sims are connected for communicatinos
|
||||
setLinkSims(linked) {
|
||||
linked = !!linked;
|
||||
|
||||
// State is not changing
|
||||
if (linked == this.linkSims)
|
||||
return;
|
||||
|
||||
// Link or un-link the sims
|
||||
if (this.dualSims)
|
||||
this.core.connect(this[0].sim.sim, linked ? this[1].sim.sim : 0);
|
||||
}
|
||||
|
||||
// Display a window
|
||||
showWindow(wnd) {
|
||||
wnd.setVisible(true);
|
||||
wnd.focus()
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
async singleStep(index) {
|
||||
let two = this.dualSims && this.linkSims;
|
||||
|
||||
// Perform the operation
|
||||
let data = await this.core.singleStep(
|
||||
this[index].sim.sim,
|
||||
two ? this[index ^ 1].sim.sim : 0, {
|
||||
refresh: true
|
||||
});
|
||||
|
||||
// Update the disassemblers
|
||||
this[index].disassembler.pc = data.pc[0];
|
||||
this[index].disassembler.seek(data.pc[0]);
|
||||
if (two) {
|
||||
this[index ^ 1].disassembler.pc = data.pc[1];
|
||||
this[index ^ 1].disassembler.seek(data.pc[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { App };
|
||||
@@ -0,0 +1,106 @@
|
||||
import { Disassembler } from /**/"./Disassembler.js";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CPU //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// CPU register editor and disassembler
|
||||
class CPU extends Toolkit.SplitPane {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(app, sim) {
|
||||
super(app, {
|
||||
className: "tk tk-splitpane tk-cpu",
|
||||
edge : Toolkit.SplitPane.RIGHT,
|
||||
style : {
|
||||
position: "relative"
|
||||
}
|
||||
});
|
||||
|
||||
this.app = app;
|
||||
this.sim = sim;
|
||||
this.initDasm();
|
||||
|
||||
this.metrics = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-mono",
|
||||
tagName : "div",
|
||||
style : {
|
||||
position : "absolute",
|
||||
visibility: "hidden"
|
||||
}
|
||||
});
|
||||
let text = "";
|
||||
for (let x = 0; x < 16; x++) {
|
||||
if (x) text += "\n";
|
||||
let digit = x.toString(16);
|
||||
text += digit + "\n" + digit.toUpperCase();
|
||||
}
|
||||
this.metrics.element.innerText = text;
|
||||
this.splitter.append(this.metrics.element);
|
||||
|
||||
this.setView(1, this.regs = new Toolkit.SplitPane(app, {
|
||||
className: "tk tk-splitpane",
|
||||
edge : Toolkit.SplitPane.TOP
|
||||
}));
|
||||
|
||||
this.regs.setView(0, this.sysregs = new RegisterList(this, true ));
|
||||
this.regs.setView(1, this.proregs = new RegisterList(this, false));
|
||||
|
||||
// Adjust split panes to the initial size of the System Registers pane
|
||||
let resize;
|
||||
let preshow = e=>this.onPreshow(resize);
|
||||
resize = new ResizeObserver(preshow);
|
||||
resize.observe(this.sysregs.viewport);
|
||||
resize.observe(this.metrics.element);
|
||||
|
||||
this.metrics.addEventListener("resize", e=>this.metricsResize());
|
||||
}
|
||||
|
||||
initDasm() {
|
||||
this.dasm = new Disassembler(this.app, this.sim);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
|
||||
|
||||
// Resize handler prior to first visibility
|
||||
onPreshow(resize) {
|
||||
this.metricsResize();
|
||||
|
||||
// Once the list of registers is visible, stop listening
|
||||
if (this.isVisible()) {
|
||||
resize.disconnect();
|
||||
this.sysregs.view.element.style.display = "grid";
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the split panes
|
||||
let sys = this.sysregs.view.element;
|
||||
let pro = this.proregs.view.element;
|
||||
this.setValue(
|
||||
Math.max(sys.scrollWidth, pro.scrollWidth) +
|
||||
this.sysregs.vertical.getBounds().width
|
||||
);
|
||||
this.regs.setValue(
|
||||
this.sysregs[PSW].expansion.getBounds().bottom -
|
||||
sys.getBoundingClientRect().top
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { CPU };
|
||||
@@ -0,0 +1,187 @@
|
||||
import { Disassembler } from /**/"./Disassembler.js";
|
||||
import { Memory } from /**/"./Memory.js";
|
||||
import { RegisterList } from /**/"./RegisterList.js";
|
||||
|
||||
|
||||
|
||||
class Debugger {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(app, index, sim) {
|
||||
|
||||
// Configure instance fields
|
||||
this.app = app;
|
||||
this.index = index;
|
||||
this.sim = sim;
|
||||
|
||||
// Configure components
|
||||
this.disassembler = new Disassembler(this);
|
||||
this.memory = new Memory (this);
|
||||
this.programRegisters = new RegisterList(this, false);
|
||||
this.systemRegisters = new RegisterList(this, true );
|
||||
|
||||
// Configure windows
|
||||
this.initCPUWindow ();
|
||||
this.initMemoryWindow();
|
||||
}
|
||||
|
||||
// Set up the CPU window
|
||||
initCPUWindow() {
|
||||
let app = this.app;
|
||||
|
||||
// Produce the window
|
||||
let wnd = this.cpuWindow = new app.Toolkit.Window(app, {
|
||||
width : 400,
|
||||
height : 300,
|
||||
className : "tk tk-window tk-cpu" + (this.index==0?"":" two"),
|
||||
closeToolTip: "common.close",
|
||||
title : "cpu._",
|
||||
visible : false
|
||||
});
|
||||
wnd.setSubstitution("sim", this.index == 1 ? " 2" : "");
|
||||
wnd.addEventListener("close", ()=>wnd.setVisible(false));
|
||||
app.desktop.add(wnd);
|
||||
|
||||
// Visibility override
|
||||
let that = this;
|
||||
let setVisible = wnd.setVisible;
|
||||
wnd.setVisible = function(visible) {
|
||||
that.disassembler .setSubscribed(visible);
|
||||
that.systemRegisters .setSubscribed(visible);
|
||||
that.programRegisters.setSubscribed(visible);
|
||||
setVisible.apply(wnd, arguments);
|
||||
};
|
||||
|
||||
// Auto-seek the initial view
|
||||
let onSeek = ()=>this.disassembler.seek(this.disassembler.pc, true);
|
||||
Toolkit.addResizeListener(this.disassembler.element, onSeek);
|
||||
wnd.addEventListener("firstshow", ()=>{
|
||||
app.desktop.center(wnd);
|
||||
Toolkit.removeResizeListener(this.disassembler.element, onSeek);
|
||||
});
|
||||
|
||||
// Window splitters
|
||||
let sptMain = new Toolkit.SplitPane(this.app, {
|
||||
className: "tk tk-splitpane tk-main",
|
||||
edge : Toolkit.SplitPane.RIGHT
|
||||
});
|
||||
let sptRegs = new Toolkit.SplitPane(this.app, {
|
||||
className: "tk tk-splitpane tk-registers",
|
||||
edge : Toolkit.SplitPane.TOP
|
||||
});
|
||||
|
||||
// Configure window splitter initial size
|
||||
let resize = new ResizeObserver(()=>{
|
||||
|
||||
// Measure register lists
|
||||
let sys = this.systemRegisters .getPreferredSize();
|
||||
let pro = this.programRegisters.getPreferredSize();
|
||||
let height = Math.ceil(Math.max(sys.height, pro.height));
|
||||
let width = Math.ceil(Math.max(sys.width , pro.width )) +
|
||||
this.systemRegisters.vertical.getBounds().width;
|
||||
|
||||
// Configure splitters
|
||||
if (sptMain.getValue() != width)
|
||||
sptMain.setValue(width);
|
||||
if (sptRegs.getValue() != height)
|
||||
sptRegs.setValue(height);
|
||||
});
|
||||
resize.observe(this.programRegisters.view.element);
|
||||
resize.observe(this.systemRegisters .view.element);
|
||||
|
||||
// Stop monitoring splitter size when something receives focus
|
||||
let onFocus = e=>{
|
||||
resize.disconnect();
|
||||
wnd.removeEventListener("focus", onFocus, true);
|
||||
};
|
||||
sptMain.addEventListener("focus", onFocus, true);
|
||||
|
||||
// Configure window layout
|
||||
sptMain.setView(0, this.disassembler);
|
||||
sptMain.setView(1, sptRegs);
|
||||
sptRegs.setView(0, this.systemRegisters);
|
||||
sptRegs.setView(1, this.programRegisters);
|
||||
wnd.append(sptMain);
|
||||
}
|
||||
|
||||
// Set up the Memory window
|
||||
initMemoryWindow() {
|
||||
let app = this.app;
|
||||
|
||||
// Produce the window
|
||||
let wnd = this.memoryWindow = new app.Toolkit.Window(app, {
|
||||
width : 400,
|
||||
height : 300,
|
||||
className : "tk tk-window" + (this.index == 0 ? "" : " two"),
|
||||
closeToolTip: "common.close",
|
||||
title : "memory._",
|
||||
visible : false
|
||||
});
|
||||
wnd.setSubstitution("sim", this.index == 1 ? " 2" : "");
|
||||
wnd.addEventListener("close" , ()=>wnd.setVisible(false));
|
||||
wnd.addEventListener("firstshow", ()=>app.desktop.center(wnd));
|
||||
app.desktop.add(wnd);
|
||||
|
||||
// Visibility override
|
||||
let that = this;
|
||||
let setVisible = wnd.setVisible;
|
||||
wnd.setVisible = function(visible) {
|
||||
that.memory.setSubscribed(visible);
|
||||
setVisible.apply(wnd, arguments);
|
||||
};
|
||||
|
||||
// Configure window layout
|
||||
wnd.append(this.memory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Retrieve the disassembler configuraiton
|
||||
getDasmConfig() {
|
||||
return this.disassembler.getConfig();
|
||||
}
|
||||
|
||||
// Attempt to run until the next instruction
|
||||
runNext() {
|
||||
this.app.runNext(this.index);
|
||||
}
|
||||
|
||||
// Update the disassembler configuration
|
||||
setDasmConfig(config) {
|
||||
this.disassembler .setConfig(config);
|
||||
this.memory .dasmChanged();
|
||||
this.programRegisters.dasmChanged();
|
||||
this.systemRegisters .dasmChanged();
|
||||
}
|
||||
|
||||
// Specify whether dual sims mode is active
|
||||
setDualSims(dualSims) {
|
||||
|
||||
// Update substitutions for sim 1
|
||||
if (this.index == 0) {
|
||||
let sub = dualSims ? " 1" : "";
|
||||
this.cpuWindow .setSubstitution("sim", sub);
|
||||
this.memoryWindow.setSubstitution("sim", sub);
|
||||
}
|
||||
|
||||
// Hide windows for sim 2
|
||||
else if (!dualSims) {
|
||||
this.cpuWindow .close(false);
|
||||
this.memoryWindow.close(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
singleStep() {
|
||||
this.app.singleStep(this.index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { Debugger };
|
||||
@@ -0,0 +1,958 @@
|
||||
import { Util } from /**/"../app/Util.js";
|
||||
|
||||
|
||||
|
||||
// Opcode definition
|
||||
class Opdef {
|
||||
constructor(format, mnemonic, signExtend) {
|
||||
this.format = format;
|
||||
this.mnemonic = mnemonic;
|
||||
this.signExtend = !!signExtend;
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level opcode definition lookup table by opcode
|
||||
let OPDEFS = [
|
||||
new Opdef(1, "MOV" ), new Opdef(1, "ADD" ), new Opdef(1, "SUB" ),
|
||||
new Opdef(1, "CMP" ), new Opdef(1, "SHL" ), new Opdef(1, "SHR" ),
|
||||
new Opdef(1, "JMP" ), new Opdef(1, "SAR" ), new Opdef(1, "MUL" ),
|
||||
new Opdef(1, "DIV" ), new Opdef(1, "MULU" ), new Opdef(1, "DIVU" ),
|
||||
new Opdef(1, "OR" ), new Opdef(1, "AND" ), new Opdef(1, "XOR" ),
|
||||
new Opdef(1, "NOT" ), new Opdef(2, "MOV" ,1), new Opdef(2, "ADD",1),
|
||||
new Opdef(2, "SETF" ), new Opdef(2, "CMP" ,1), new Opdef(2, "SHL" ),
|
||||
new Opdef(2, "SHR" ), new Opdef(2, "CLI" ), new Opdef(2, "SAR" ),
|
||||
new Opdef(2, "TRAP" ), new Opdef(2, "RETI" ), new Opdef(2, "HALT" ),
|
||||
new Opdef(0, null ), new Opdef(2, "LDSR" ), new Opdef(2, "STSR" ),
|
||||
new Opdef(2, "SEI" ), new Opdef(2, null ), new Opdef(3, "Bcond"),
|
||||
new Opdef(3, "Bcond"), new Opdef(3, "Bcond" ), new Opdef(3, "Bcond"),
|
||||
new Opdef(3, "Bcond"), new Opdef(3, "Bcond" ), new Opdef(3, "Bcond"),
|
||||
new Opdef(3, "Bcond"), new Opdef(5,"MOVEA",1), new Opdef(5,"ADDI",1),
|
||||
new Opdef(4, "JR" ), new Opdef(4, "JAL" ), new Opdef(5, "ORI" ),
|
||||
new Opdef(5, "ANDI" ), new Opdef(5, "XORI" ), new Opdef(5, "MOVHI"),
|
||||
new Opdef(6, "LD.B" ), new Opdef(6, "LD.H" ), new Opdef(0, null ),
|
||||
new Opdef(6, "LD.W" ), new Opdef(6, "ST.B" ), new Opdef(6, "ST.H" ),
|
||||
new Opdef(0, null ), new Opdef(6, "ST.W" ), new Opdef(6, "IN.B" ),
|
||||
new Opdef(6, "IN.H" ), new Opdef(6, "CAXI" ), new Opdef(6, "IN.W" ),
|
||||
new Opdef(6, "OUT.B"), new Opdef(6, "OUT.H" ), new Opdef(7, null ),
|
||||
new Opdef(6, "OUT.W")
|
||||
];
|
||||
|
||||
// Bit string mnemonic lookup table by sub-opcode
|
||||
let BITSTRINGS = [
|
||||
"SCH0BSU", "SCH0BSD", "SCH1BSU", "SCH1BSD",
|
||||
null , null , null , null ,
|
||||
"ORBSU" , "ANDBSU" , "XORBSU" , "MOVBSU" ,
|
||||
"ORNBSU" , "ANDNBSU", "XORNBSU", "NOTBSU"
|
||||
];
|
||||
|
||||
// Floating-point/Nintendo mnemonic lookup table by sub-opcode
|
||||
let FLOATENDOS = [
|
||||
"CMPF.S", null , "CVT.WS", "CVT.SW" ,
|
||||
"ADDF.S", "SUBF.S", "MULF.S", "DIVF.S" ,
|
||||
"XB" , "XH" , "REV" , "TRNC.SW",
|
||||
"MPYHW"
|
||||
];
|
||||
|
||||
// Program register names
|
||||
let PROREGS = { 2: "hp", 3: "sp", 4: "gp", 5: "tp", 31: "lp" };
|
||||
|
||||
// System register names
|
||||
let SYSREGS = [
|
||||
"EIPC", "EIPSW", "FEPC", "FEPSW",
|
||||
"ECR" , "PSW" , "PIR" , "TKCW" ,
|
||||
null , null , null , null ,
|
||||
null , null , null , null ,
|
||||
null , null , null , null ,
|
||||
null , null , null , null ,
|
||||
"CHCW", "ADTRE", null , null ,
|
||||
null , null , null , null
|
||||
];
|
||||
|
||||
// Condition mnemonics
|
||||
let CONDS = [
|
||||
"V" , ["C" , "L" ], ["E" , "Z" ], "NH",
|
||||
"N" , "T" , "LT" , "LE",
|
||||
"NV", ["NC", "NL"], ["NE", "NZ"], "H" ,
|
||||
"P" , "F" , "GE" , "GT"
|
||||
];
|
||||
|
||||
// Output setting keys
|
||||
const SETTINGS = [
|
||||
"bcondMerged", "branchAddress", "condCase", "condCL", "condEZ",
|
||||
"condNames", "hexCaps", "hexDollar", "hexSuffix", "imm5OtherHex",
|
||||
"imm5ShiftHex", "imm5TrapHex", "imm16AddiLargeHex", "imm16AddiSmallHex",
|
||||
"imm16MoveHex", "imm16OtherHex", "jmpBrackets", "memoryLargeHex",
|
||||
"memorySmallHex", "memoryInside", "mnemonicCaps", "operandReverse",
|
||||
"proregCaps", "proregNames", "setfMerged", "sysregCaps", "sysregNames"
|
||||
];
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Line //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One line of output
|
||||
class Line {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(parent, first) {
|
||||
|
||||
// Configure instance fields
|
||||
this.first = first;
|
||||
this.parent = parent;
|
||||
|
||||
// Configure labels
|
||||
this.lblAddress = this.label("tk-address" , first);
|
||||
this.lblBytes = this.label("tk-bytes" , first);
|
||||
this.lblMnemonic = this.label("tk-mnemonic", first);
|
||||
this.lblOperands = this.label("tk-operands", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the elements' display
|
||||
refresh(row, isPC) {
|
||||
|
||||
// The row is not available
|
||||
if (!row) {
|
||||
this.lblAddress .innerText = "--------";
|
||||
this.lblBytes .innerText = "";
|
||||
this.lblMnemonic.innerText = "---";
|
||||
this.lblOperands.innerText = "";
|
||||
}
|
||||
|
||||
// Update labels with the disassembled row's contents
|
||||
else {
|
||||
this.lblAddress .innerText = row.address;
|
||||
this.lblBytes .innerText = row.bytes;
|
||||
this.lblMnemonic.innerText = row.mnemonic;
|
||||
this.lblOperands.innerText = row.operands;
|
||||
}
|
||||
|
||||
// Update style according to selection
|
||||
let method = row && isPC ? "add" : "remove";
|
||||
this.lblAddress .classList[method]("tk-selected");
|
||||
this.lblBytes .classList[method]("tk-selected");
|
||||
this.lblMnemonic.classList[method]("tk-selected");
|
||||
this.lblOperands.classList[method]("tk-selected");
|
||||
}
|
||||
|
||||
// Specify whether the elements on this line are visible
|
||||
setVisible(visible) {
|
||||
|
||||
// Column elements
|
||||
let columns = [
|
||||
this.lblAddress,
|
||||
this.lblBytes,
|
||||
this.lblMnemonic,
|
||||
this.lblOperands
|
||||
];
|
||||
|
||||
// Column elements on the first row
|
||||
if (this.first) {
|
||||
columns[0] = columns[0].parentNode; // Address
|
||||
columns[1] = columns[1].parentNode; // Bytes
|
||||
columns[2] = columns[2].parentNode; // Mnemonic
|
||||
}
|
||||
|
||||
// Column visibility
|
||||
visible = [
|
||||
visible, // Address
|
||||
visible && this.parent.hasBytes, // Bytes
|
||||
visible, // Mnemonic
|
||||
visible // Operands
|
||||
];
|
||||
|
||||
// Configure elements
|
||||
for (let x = 0; x < 4; x++)
|
||||
columns[x].style.display = visible[x] ? "block" : "none";
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Create a display label
|
||||
label(className, first) {
|
||||
|
||||
// Create the label element
|
||||
let label = document.createElement("div");
|
||||
label.className = "tk " + className;
|
||||
|
||||
// The label is part of the first row of output
|
||||
let element = label;
|
||||
if (first) {
|
||||
|
||||
// Create a container element
|
||||
element = document.createElement("div");
|
||||
element.append(label);
|
||||
element.max = 0;
|
||||
|
||||
// Ensure the container can always fit the column contents
|
||||
Toolkit.addResizeListener(element, ()=>{
|
||||
let width = Math.ceil(label.getBoundingClientRect().width);
|
||||
if (width <= element.max)
|
||||
return;
|
||||
element.max = width;
|
||||
element.style.minWidth = width + "px";
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Configure elements
|
||||
this.parent.view.append(element);
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Disassembler //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Text disassembler for NVC
|
||||
class Disassembler extends Toolkit.ScrollPane {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(debug) {
|
||||
super(debug.app, {
|
||||
className : "tk tk-scrollpane tk-disassembler",
|
||||
horizontal: Toolkit.ScrollPane.AS_NEEDED,
|
||||
focusable : true,
|
||||
tabStop : true,
|
||||
tagName : "div",
|
||||
vertical : Toolkit.ScrollPane.NEVER
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.address = Util.u32(0xFFFFFFF0);
|
||||
this.app = debug.app;
|
||||
this.columns = [ 0, 0, 0, 0 ];
|
||||
this.data = [];
|
||||
this.debug = debug;
|
||||
this.hasBytes = true;
|
||||
this.isSubscribed = false;
|
||||
this.lines = null;
|
||||
this.pc = this.address;
|
||||
this.pending = [];
|
||||
this.rows = [];
|
||||
this.scroll = 0;
|
||||
this.sim = debug.sim;
|
||||
|
||||
// Default output settings
|
||||
this.setConfig({
|
||||
bcondMerged : true,
|
||||
branchAddress : true,
|
||||
condCase : false,
|
||||
condCL : 1,
|
||||
condEZ : 1,
|
||||
condNames : true,
|
||||
hexCaps : true,
|
||||
hexDollar : false,
|
||||
hexSuffix : false,
|
||||
imm5OtherHex : false,
|
||||
imm5ShiftHex : false,
|
||||
imm5TrapHex : false,
|
||||
imm16AddiLargeHex: true,
|
||||
imm16AddiSmallHex: false,
|
||||
imm16MoveHex : true,
|
||||
imm16OtherHex : true,
|
||||
jmpBrackets : true,
|
||||
memoryLargeHex : true,
|
||||
memorySmallHex : false,
|
||||
memoryInside : false,
|
||||
mnemonicCaps : true,
|
||||
operandReverse : false,
|
||||
proregCaps : false,
|
||||
proregNames : true,
|
||||
setfMerged : false,
|
||||
sysregCaps : true,
|
||||
sysregNames : true
|
||||
});
|
||||
|
||||
// Configure viewport
|
||||
this.viewport.classList.add("tk-mono");
|
||||
|
||||
// Configure view
|
||||
let view = document.createElement("div");
|
||||
view.className = "tk tk-view";
|
||||
Object.assign(view.style, {
|
||||
display : "grid",
|
||||
gridTemplateColumns: "repeat(3, max-content) auto"
|
||||
});
|
||||
this.setView(view);
|
||||
|
||||
// Font-measuring element
|
||||
this.metrics = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-metrics tk-mono",
|
||||
tagName : "div",
|
||||
style : {
|
||||
position : "absolute",
|
||||
visibility: "hidden"
|
||||
}
|
||||
});
|
||||
this.metrics.element.innerText = "X";
|
||||
this.append(this.metrics.element);
|
||||
|
||||
// First row always exists
|
||||
this.lines = [ new Line(this, true) ];
|
||||
|
||||
// Configure event handlers
|
||||
Toolkit.addResizeListener(this.viewport, e=>this.onResize(e));
|
||||
this.addEventListener("keydown", e=>this.onKeyDown (e));
|
||||
this.addEventListener("wheel" , e=>this.onMouseWheel(e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
let tall = this.tall(false);
|
||||
|
||||
|
||||
// Ctrl key is pressed
|
||||
if (e.ctrlKey) switch (e.key) {
|
||||
|
||||
// Toggle bytes column
|
||||
case "b": case "B":
|
||||
this.showBytes(!this.hasBytes);
|
||||
break;
|
||||
|
||||
// Fit columns
|
||||
case "f": case "F":
|
||||
this.fitColumns();
|
||||
break;
|
||||
|
||||
// Goto
|
||||
case "g": case "G":
|
||||
this.promptGoto();
|
||||
break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Ctrl key is not pressed
|
||||
else switch (e.key) {
|
||||
|
||||
// Navigation
|
||||
case "ArrowDown" : this.fetch(+1 , true); break;
|
||||
case "ArrowUp" : this.fetch(-1 , true); break;
|
||||
case "PageDown" : this.fetch(+tall, true); break;
|
||||
case "PageUp" : this.fetch(-tall, true); break;
|
||||
|
||||
// View control
|
||||
case "ArrowLeft" : this.horizontal.setValue(
|
||||
this.horizontal.value - this.horizontal.increment); break;
|
||||
case "ArrowRight": this.horizontal.setValue(
|
||||
this.horizontal.value + this.horizontal.increment); break;
|
||||
|
||||
// Single step
|
||||
case "F10":
|
||||
this.debug.runNext();
|
||||
break;
|
||||
|
||||
// Single step
|
||||
case "F11":
|
||||
this.debug.singleStep();
|
||||
break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
onMouseWheel(e) {
|
||||
|
||||
// User agent scaling action
|
||||
if (e.ctrlKey)
|
||||
return;
|
||||
|
||||
// No rotation has occurred
|
||||
let offset = Math.sign(e.deltaY) * 3;
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
// Update the display address
|
||||
this.fetch(offset, true);
|
||||
}
|
||||
|
||||
// Viewport resized
|
||||
onResize(e) {
|
||||
let fetch = false;
|
||||
let tall = this.tall(true);
|
||||
|
||||
// Add additional lines to the output
|
||||
for (let x = 0; x < tall; x++) {
|
||||
if (x >= this.lines.length) {
|
||||
fetch = true;
|
||||
this.lines.push(new Line(this));
|
||||
}
|
||||
this.lines[x].setVisible(true);
|
||||
}
|
||||
|
||||
// Remove extra lines from the output
|
||||
for (let x = tall; x < this.lines.length; x++)
|
||||
this.lines[x].setVisible(false);
|
||||
|
||||
// Configure horizontal scroll bar
|
||||
if (this.metrics)
|
||||
this.horizontal.setIncrement(this.metrics.getBounds().width);
|
||||
|
||||
// Update the display
|
||||
if (fetch)
|
||||
this.fetch(0, true);
|
||||
else this.refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Produce disassembly text
|
||||
disassemble(rows) {
|
||||
|
||||
// Produce a deep copy of the input list
|
||||
let copy = new Array(rows.length);
|
||||
for (let x = 0; x < rows.length; x++) {
|
||||
copy[x] = {};
|
||||
Object.assign(copy[x], rows[x]);
|
||||
}
|
||||
rows = copy;
|
||||
|
||||
// Process all rows
|
||||
for (let row of rows) {
|
||||
row.operands = [];
|
||||
|
||||
// Read instruction bits from the bus
|
||||
let bits0 = row.bytes[1] << 8 | row.bytes[0];
|
||||
let bits1;
|
||||
if (row.bytes.length == 4)
|
||||
bits1 = row.bytes[3] << 8 | row.bytes[2];
|
||||
|
||||
// Working variables
|
||||
let opcode = bits0 >> 10;
|
||||
let opdef = OPDEFS[opcode];
|
||||
|
||||
// Sub-opcode mnemonics
|
||||
if (row.opcode == 0b011111)
|
||||
row.mnemonic = BITSTRINGS[bits0 & 31] || "---";
|
||||
else if (row.opcode == 0b111110)
|
||||
row.mnemonic = FLOATENDOS[bits1 >> 10 & 63] || "---";
|
||||
else row.mnemonic = opdef.mnemonic;
|
||||
|
||||
// Processing by format
|
||||
switch (opdef.format) {
|
||||
case 1: this.formatI (row, bits0 ); break;
|
||||
case 3: this.formatIII(row, bits0 ); break;
|
||||
case 4: this.formatIV (row, bits0, bits1); break;
|
||||
case 6: this.formatVI (row, bits0, bits1); break;
|
||||
case 7: this.formatVII(row, bits0 ); break;
|
||||
case 2:
|
||||
this.formatII(row, bits0, opdef.signExtend); break;
|
||||
case 5:
|
||||
this.formatV (row, bits0, bits1, opdef.signExtend);
|
||||
}
|
||||
|
||||
// Format bytes
|
||||
let text = [];
|
||||
for (let x = 0; x < row.bytes.length; x++)
|
||||
text.push(row.bytes[x].toString(16).padStart(2, "0"));
|
||||
row.bytes = text.join(" ");
|
||||
|
||||
// Post-processing
|
||||
row.address = row.address.toString(16).padStart(8, "0");
|
||||
if (this.hexCaps) {
|
||||
row.address = row.address.toUpperCase();
|
||||
row.bytes = row.bytes .toUpperCase();
|
||||
}
|
||||
if (!this.mnemonicCaps)
|
||||
row.mnemonic = row.mnemonic.toLowerCase();
|
||||
if (this.operandReverse)
|
||||
row.operands.reverse();
|
||||
row.operands = row.operands.join(", ");
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Retrieve all output settings in an object
|
||||
getConfig() {
|
||||
let ret = {};
|
||||
for (let key of SETTINGS)
|
||||
ret[key] = this[key];
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Update with disassembly state from the core
|
||||
refresh(data = 0) {
|
||||
let bias;
|
||||
|
||||
// Scrolling prefresh
|
||||
if (typeof data == "number")
|
||||
bias = 16 + data;
|
||||
|
||||
// Received data from the core thread
|
||||
else {
|
||||
this.data = data.rows;
|
||||
this.pc = data.pc;
|
||||
if (this.data.length == 0)
|
||||
return;
|
||||
this.address = this.data[0].address;
|
||||
this.rows = this.disassemble(this.data);
|
||||
bias = 16 +
|
||||
(data.scroll === null ? 0 : this.scroll - data.scroll);
|
||||
}
|
||||
|
||||
// Update elements
|
||||
let count = Math.min(this.tall(true), this.data.length);
|
||||
for (let y = 0; y < count; y++) {
|
||||
let index = bias + y;
|
||||
let line = this.data[index];
|
||||
let row = this.rows[index];
|
||||
this.lines[y].refresh(row, line && line.address == this.pc);
|
||||
}
|
||||
|
||||
// Refesh scroll pane
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Bring an address into view
|
||||
seek(address, force) {
|
||||
|
||||
// Check if the address is already in the view
|
||||
if (!force) {
|
||||
let bias = 16;
|
||||
let tall = this.tall(false);
|
||||
let count = Math.min(tall, this.data.length);
|
||||
|
||||
// The address is currently visible in the output
|
||||
for (let y = 0; y < count; y++) {
|
||||
let row = this.data[bias + y];
|
||||
if (!row || Util.u32(address - row.address) >= row.size)
|
||||
continue;
|
||||
|
||||
// The address is on this row
|
||||
this.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Place the address at a particular position in the view
|
||||
this.address = address;
|
||||
this.fetch(null);
|
||||
}
|
||||
|
||||
// Update output settings
|
||||
setConfig(config) {
|
||||
|
||||
// Update settings
|
||||
for (let key of SETTINGS)
|
||||
if (key in config)
|
||||
this[key] = config[key];
|
||||
|
||||
// Regenerate output
|
||||
this.refresh({
|
||||
pc : this.pc,
|
||||
rows : this.data,
|
||||
scroll: null
|
||||
});
|
||||
}
|
||||
|
||||
// Subscribe to or unsubscribe from core updates
|
||||
setSubscribed(subscribed) {
|
||||
subscribed = !!subscribed;
|
||||
|
||||
// Nothing to change
|
||||
if (subscribed == this.isSubscribed)
|
||||
return;
|
||||
|
||||
// Configure instance fields
|
||||
this.isSubscribed = subscribed;
|
||||
|
||||
// Subscribe to core updates
|
||||
if (subscribed)
|
||||
this.fetch(0);
|
||||
|
||||
// Unsubscribe from core updates
|
||||
else this.sim.unsubscribe("dasm");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Select a condition's name
|
||||
cond(cond) {
|
||||
let ret = CONDS[cond];
|
||||
switch (cond) {
|
||||
case 1: case 9: return CONDS[cond][this.condCL];
|
||||
case 2: case 10: return CONDS[cond][this.condEZ];
|
||||
}
|
||||
return CONDS[cond];
|
||||
}
|
||||
|
||||
// Retrieve disassembly data from the core
|
||||
async fetch(scroll, prefresh) {
|
||||
let row;
|
||||
|
||||
// Scrolling relative to the current view
|
||||
if (scroll) {
|
||||
if (prefresh)
|
||||
this.refresh(scroll);
|
||||
this.scroll = Util.s32(this.scroll + scroll);
|
||||
row = -scroll;
|
||||
}
|
||||
|
||||
// Jumping to an address directly
|
||||
else row = scroll === null ? Math.floor(this.tall(false) / 3) + 16 : 0;
|
||||
|
||||
// Retrieve data from the core
|
||||
this.refresh(
|
||||
await this.sim.disassemble(
|
||||
this.address,
|
||||
row,
|
||||
this.tall(true) + 32,
|
||||
scroll === null ? null : this.scroll, {
|
||||
subscribe: this.isSubscribed && "dasm"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Shrink all columns to their minimum size
|
||||
fitColumns() {
|
||||
let line = this.lines[0];
|
||||
for (let column of [ "lblAddress", "lblBytes", "lblMnemonic" ] ) {
|
||||
let element = line[column].parentNode;
|
||||
element.max = 0;
|
||||
element.style.removeProperty("min-width");
|
||||
}
|
||||
}
|
||||
|
||||
// Represent a hexadecimal value
|
||||
hex(value, digits) {
|
||||
let sign = Util.s32(value) < 0 ? "-" : "";
|
||||
let ret = Math.abs(Util.u32(value)).toString(16).padStart(digits,"0");
|
||||
if (this.hexCaps)
|
||||
ret = ret.toUpperCase();
|
||||
if (this.hexSuffix)
|
||||
ret = ("abcdefABCDEF".indexOf(ret[0]) == -1 ? "" : "0") +
|
||||
ret + "h";
|
||||
else ret = (this.hexDollar ? "$" : "0x") + ret;
|
||||
return sign + ret;
|
||||
}
|
||||
|
||||
// Prompt the user to specify a new address
|
||||
promptGoto() {
|
||||
|
||||
// Receive input from the user
|
||||
let address = prompt(this.app.translate("common.gotoPrompt"));
|
||||
if (address == null)
|
||||
return;
|
||||
|
||||
// Process the input as an address in hexadecimal
|
||||
address = parseInt(address, 16);
|
||||
if (isNaN(address))
|
||||
return;
|
||||
|
||||
// Move the selection and refresh the display
|
||||
this.seek(Util.u32(address));
|
||||
}
|
||||
|
||||
// Select a program register name
|
||||
proreg(index) {
|
||||
let ret = this.proregNames && PROREGS[index] || "r" + index;
|
||||
return this.proregCaps ? ret.toUpperCase() : ret;
|
||||
}
|
||||
|
||||
// Specify whether or not to show the bytes column
|
||||
showBytes(show) {
|
||||
let tall = this.tall(true);
|
||||
|
||||
// Configure instance fields
|
||||
this.hasBytes = show;
|
||||
|
||||
// Configure elements
|
||||
this.view.style.gridTemplateColumns =
|
||||
"repeat(" + (show ? 3 : 2) + ", max-content) auto";
|
||||
for (let x = 0; x < tall; x++)
|
||||
this.lines[x].setVisible(true);
|
||||
|
||||
// Measure scroll pane
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Measure how many rows of output are visible
|
||||
tall(partial) {
|
||||
let lineHeight = !this.metrics ? 0 :
|
||||
Math.ceil(this.metrics.getBounds().height);
|
||||
return lineHeight <= 0 ? 1 : Math.max(1, Math[partial?"ceil":"floor"](
|
||||
this.getBounds().height / lineHeight));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////// Decoding Methods /////////////////////////////
|
||||
|
||||
// Disassemble a Format I instruction
|
||||
formatI(row, bits0) {
|
||||
let reg1 = this.proreg(bits0 & 31);
|
||||
|
||||
// JMP
|
||||
if (row.mnemonic == "JMP") {
|
||||
if (this.jmpBrackets)
|
||||
reg1 = "[" + reg1 + "]";
|
||||
row.operands.push(reg1);
|
||||
}
|
||||
|
||||
// Other instructions
|
||||
else {
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
row.operands.push(reg1, reg2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Disassemble a Format II instruction
|
||||
formatII(row, bits0, signExtend) {
|
||||
|
||||
// Bit-string instructions are zero-operand
|
||||
if (bits0 >> 10 == 0b011111)
|
||||
return;
|
||||
|
||||
// Processing by mnemonic
|
||||
switch (row.mnemonic) {
|
||||
|
||||
// Zero-operand
|
||||
case "---" : // Fallthrough
|
||||
case "CLI" : // Fallthrough
|
||||
case "HALT": // Fallthrough
|
||||
case "RETI": // Fallthrough
|
||||
case "SEI" : return;
|
||||
|
||||
// Distinct notation
|
||||
case "LDSR": return this.ldstsr(row, bits0, true );
|
||||
case "SETF": return this.setf (row, bits0 );
|
||||
case "STSR": return this.ldstsr(row, bits0, false);
|
||||
}
|
||||
|
||||
// Retrieve immediate operand
|
||||
let imm = bits0 & 31;
|
||||
if (signExtend)
|
||||
imm = Util.signExtend(bits0, 5);
|
||||
|
||||
// TRAP instruction is one-operand
|
||||
if (row.mnemonic == "TRAP") {
|
||||
row.operands.push(this.trapHex ?
|
||||
this.hex(imm, 1) : imm.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Processing by mnemonic
|
||||
let hex = this.imm5OtherHex;
|
||||
switch (row.mnemonic) {
|
||||
case "SAR": // Fallthrough
|
||||
case "SHL": // Fallthrough
|
||||
case "SHR": hex = this.imm5ShiftHex;
|
||||
}
|
||||
imm = hex ? this.hex(imm, 1) : imm.toString();
|
||||
|
||||
// Two-operand instruction
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
row.operands.push(imm, reg2);
|
||||
}
|
||||
|
||||
// Disassemble a Format III instruction
|
||||
formatIII(row, bits0) {
|
||||
let cond = this.cond(bits0 >> 9 & 15);
|
||||
let disp = Util.signExtend(bits0 & 0x1FF, 9);
|
||||
|
||||
// Condition merged with mnemonic
|
||||
if (this.bcondMerged) {
|
||||
switch (cond) {
|
||||
case "F": row.mnemonic = "NOP"; return;
|
||||
case "T": row.mnemonic = "BR" ; break;
|
||||
default : row.mnemonic = "B" + cond;
|
||||
}
|
||||
}
|
||||
|
||||
// Condition as operand
|
||||
else {
|
||||
if (!this.condCaps)
|
||||
cond = cond.toLowerCase();
|
||||
row.operands.push(cond);
|
||||
}
|
||||
|
||||
// Operand as destination address
|
||||
if (this.branchAddress) {
|
||||
disp = Util.u32(row.address + disp & 0xFFFFFFFE)
|
||||
.toString(16).padStart(8, "0");
|
||||
if (this.hexCaps)
|
||||
disp = disp.toUpperCase();
|
||||
row.operands.push(disp);
|
||||
}
|
||||
|
||||
// Operand as displacement
|
||||
else {
|
||||
let sign = disp < 0 ? "-" : disp > 0 ? "+" : "";
|
||||
let rel = this.hex(Math.abs(disp), 1);
|
||||
row.operands.push(sign + rel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Disassemble a Format IV instruction
|
||||
formatIV(row, bits0, bits1) {
|
||||
let disp = Util.signExtend(bits0 << 16 | bits1, 26);
|
||||
|
||||
// Operand as destination address
|
||||
if (this.branchAddress) {
|
||||
disp = Util.u32(row.address + disp & 0xFFFFFFFE)
|
||||
.toString(16).padStart(8, "0");
|
||||
if (this.hexCaps)
|
||||
disp = disp.toUpperCase();
|
||||
row.operands.push(disp);
|
||||
}
|
||||
|
||||
// Operand as displacement
|
||||
else {
|
||||
let sign = disp < 0 ? "-" : disp > 0 ? "+" : "";
|
||||
let rel = this.hex(Math.abs(disp), 1);
|
||||
row.operands.push(sign + rel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Disassemble a Format V instruction
|
||||
formatV(row, bits0, bits1, signExtend) {
|
||||
let imm = signExtend ? Util.signExtend(bits1) : bits1;
|
||||
let reg1 = this.proreg(bits0 & 31);
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
|
||||
if (
|
||||
row.mnemonic == "ADDI" ?
|
||||
Math.abs(imm) <= 256 ?
|
||||
this.imm16AddiSmallHex :
|
||||
this.imm16AddiLargeHex
|
||||
: row.mnemonic == "MOVEA" || row.mnemonic == "MOVHI" ?
|
||||
this.imm16MoveHex
|
||||
:
|
||||
this.imm16OtherHex
|
||||
) imm = this.hex(imm, 4);
|
||||
|
||||
row.operands.push(imm, reg1, reg2);
|
||||
}
|
||||
|
||||
// Disassemble a Format VI instruction
|
||||
formatVI(row, bits0, bits1) {
|
||||
let disp = Util.signExtend(bits1);
|
||||
let reg1 = this.proreg(bits0 & 31);
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
let sign =
|
||||
disp < 0 ? "-" :
|
||||
disp == 0 || !this.memoryInside ? "" :
|
||||
"+";
|
||||
|
||||
// Displacement is hexadecimal
|
||||
disp = Math.abs(disp);
|
||||
if (disp == 0)
|
||||
disp = ""
|
||||
else if (disp <= 256 ? this.memorySmallHex : this.memoryLargeHex)
|
||||
disp = this.hex(disp, 1);
|
||||
|
||||
// Format the displacement figure according to its presentation
|
||||
disp = this.memoryInside ?
|
||||
sign == "" ? "" : " " + sign + " " + disp :
|
||||
sign + disp
|
||||
;
|
||||
|
||||
// Apply operands
|
||||
row.operands.push(this.memoryInside ?
|
||||
"[" + reg1 + disp + "]" :
|
||||
disp + "[" + reg1 + "]",
|
||||
reg2);
|
||||
|
||||
// Swap operands for output and store instructions
|
||||
switch (row.mnemonic) {
|
||||
case "OUT.B": case "OUT.H": case "OUT.W":
|
||||
case "ST.B" : case "ST.H" : case "ST.W" :
|
||||
row.operands.reverse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Disassemble a Format VII instruction
|
||||
formatVII(row, bits0) {
|
||||
let reg1 = this.proreg(bits0 & 31);
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
|
||||
// Invalid sub-opcode is zero-operand
|
||||
if (row.mnemonic == "---")
|
||||
return;
|
||||
|
||||
// Processing by mnemonic
|
||||
switch (row.mnemonic) {
|
||||
case "XB": // Fallthrough
|
||||
case "XH": break;
|
||||
default : row.operands.push(reg1);
|
||||
}
|
||||
|
||||
row.operands.push(reg2);
|
||||
}
|
||||
|
||||
// Format an LDSR or STSR instruction
|
||||
ldstsr(row, bits0, reverse) {
|
||||
|
||||
// System register
|
||||
let sysreg = bits0 & 31;
|
||||
sysreg = this.sysregNames && SYSREGS[sysreg] || sysreg.toString();
|
||||
if (!this.sysregCaps)
|
||||
sysreg = sysreg.toLowerCase();
|
||||
|
||||
// Program register
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
|
||||
// Operands
|
||||
row.operands.push(sysreg, reg2);
|
||||
if (reverse)
|
||||
row.operands.reverse();
|
||||
}
|
||||
|
||||
// Format a SETF instruction
|
||||
setf(row, bits0) {
|
||||
let cond = this.cond (bits0 & 15);
|
||||
let reg2 = this.proreg(bits0 >> 5 & 31);
|
||||
|
||||
// Condition merged with mnemonic
|
||||
if (!this.bcondMerged) {
|
||||
row.mnemonic += cond;
|
||||
}
|
||||
|
||||
// Condition as operand
|
||||
else {
|
||||
if (!this.condCaps)
|
||||
cond = cond.toLowerCase();
|
||||
row.operands.push(cond);
|
||||
}
|
||||
|
||||
row.operands.push(reg2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { Disassembler };
|
||||
@@ -0,0 +1,517 @@
|
||||
import { Util } from /**/"./Util.js";
|
||||
|
||||
|
||||
|
||||
// Bus indexes
|
||||
const MEMORY = 0;
|
||||
|
||||
// Text to hex digit conversion
|
||||
const DIGITS = {
|
||||
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7,
|
||||
"8": 8, "9": 9, "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Line //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One line of output
|
||||
class Line {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(parent, index) {
|
||||
|
||||
// Configure instance fields
|
||||
this.index = index;
|
||||
this.parent = parent;
|
||||
|
||||
// Address label
|
||||
this.lblAddress = document.createElement("div");
|
||||
this.lblAddress.className = "tk tk-address";
|
||||
parent.view.appendChild(this.lblAddress);
|
||||
|
||||
// Byte labels
|
||||
this.lblBytes = new Array(16);
|
||||
for (let x = 0; x < 16; x++) {
|
||||
let lbl = this.lblBytes[x] = document.createElement("div");
|
||||
lbl.className = "tk tk-byte tk-" + x.toString(16);
|
||||
parent.view.appendChild(lbl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the elements' display
|
||||
refresh() {
|
||||
let bus = this.parent[this.parent.bus];
|
||||
let address = this.parent.mask(bus.address + this.index * 16);
|
||||
let data = bus.data;
|
||||
let dataAddress = bus.dataAddress;
|
||||
let hexCaps = this.parent.dasm.hexCaps;
|
||||
let offset =
|
||||
(this.parent.row(address) - this.parent.row(dataAddress)) * 16;
|
||||
|
||||
// Format the line's address
|
||||
let text = address.toString(16).padStart(8, "0");
|
||||
if (hexCaps)
|
||||
text = text.toUpperCase();
|
||||
this.lblAddress.innerText = text;
|
||||
|
||||
// The line's data is not available
|
||||
if (offset < 0 || offset >= data.length) {
|
||||
for (let lbl of this.lblBytes) {
|
||||
lbl.innerText = "--";
|
||||
lbl.classList.remove("tk-selected");
|
||||
}
|
||||
}
|
||||
|
||||
// The line's data is available
|
||||
else for (let x = 0; x < 16; x++, offset++) {
|
||||
let lbl = this.lblBytes[x];
|
||||
text = data[offset].toString(16).padStart(2, "0");
|
||||
|
||||
// The byte is the current selection
|
||||
if (Util.u32(address + x) == bus.selection) {
|
||||
lbl.classList.add("tk-selected");
|
||||
if (this.parent.digit !== null)
|
||||
text = this.parent.digit.toString(16);
|
||||
}
|
||||
|
||||
// The byte is not the current selection
|
||||
else lbl.classList.remove("tk-selected");
|
||||
|
||||
// Update the label's text
|
||||
if (hexCaps)
|
||||
text = text.toUpperCase();
|
||||
lbl.innerText = text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Specify whether the elements on this line are visible
|
||||
setVisible(visible) {
|
||||
visible = visible ? "block" : "none";
|
||||
this.lblAddress.style.display = visible;
|
||||
for (let lbl of this.lblBytes)
|
||||
lbl.style.display = visible;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Memory //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Memory hex editor
|
||||
class Memory extends Toolkit.Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(debug) {
|
||||
super(debug.app, {
|
||||
className : "tk tk-memory",
|
||||
tagName : "div",
|
||||
style : {
|
||||
alignItems : "stretch",
|
||||
display : "grid",
|
||||
gridTemplateRows: "auto",
|
||||
position : "relative"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.app = debug.app;
|
||||
this.bus = MEMORY;
|
||||
this.dasm = debug.disassembler;
|
||||
this.debug = debug;
|
||||
this.digit = null;
|
||||
this.isSubscribed = false;
|
||||
this.lines = [];
|
||||
this.sim = debug.sim;
|
||||
|
||||
// Initialize bus
|
||||
this[MEMORY] = {
|
||||
address : 0x05000000,
|
||||
data : [],
|
||||
dataAddress: 0x05000000,
|
||||
selection : 0x05000000
|
||||
};
|
||||
|
||||
// Configure editor pane
|
||||
this.editor = new Toolkit.ScrollPane(this.app, {
|
||||
className : "tk tk-scrollpane tk-editor",
|
||||
horizontal: Toolkit.ScrollPane.AS_NEEDED,
|
||||
focusable : true,
|
||||
tabStop : true,
|
||||
tagName : "div",
|
||||
vertical : Toolkit.ScrollPane.NEVER
|
||||
});
|
||||
this.append(this.editor);
|
||||
|
||||
// Configure view
|
||||
this.view = document.createElement("div");
|
||||
this.view.className = "tk tk-view";
|
||||
Object.assign(this.view.style, {
|
||||
display : "grid",
|
||||
gridTemplateColumns: "repeat(17, max-content)"
|
||||
});
|
||||
this.editor.setView(this.view);
|
||||
|
||||
// Font-measuring element
|
||||
this.metrics = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-metrics tk-mono",
|
||||
tagName : "div",
|
||||
style : {
|
||||
position : "absolute",
|
||||
visibility: "hidden"
|
||||
}
|
||||
});
|
||||
this.metrics.element.innerText = "X";
|
||||
this.append(this.metrics.element);
|
||||
|
||||
// Configure event handlers
|
||||
Toolkit.addResizeListener(this.editor.viewport, e=>this.onResize(e));
|
||||
this.addEventListener("keydown" , e=>this.onKeyDown (e));
|
||||
this.addEventListener("pointerdown", e=>this.onPointerDown(e));
|
||||
this.addEventListener("wheel" , e=>this.onMouseWheel (e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Typed a digit
|
||||
onDigit(digit) {
|
||||
let bus = this[this.bus];
|
||||
|
||||
// Begin an edit
|
||||
if (this.digit === null) {
|
||||
this.digit = digit;
|
||||
this.setSelection(bus.selection, true);
|
||||
}
|
||||
|
||||
// Complete an edit
|
||||
else {
|
||||
this.digit = this.digit << 4 | digit;
|
||||
this.setSelection(bus.selection + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
let bus = this[this.bus];
|
||||
let key = e.key;
|
||||
|
||||
// A hex digit was entered
|
||||
if (key.toUpperCase() in DIGITS) {
|
||||
this.onDigit(DIGITS[key.toUpperCase()]);
|
||||
key = "digit";
|
||||
}
|
||||
|
||||
// Ctrl key is pressed
|
||||
if (e.ctrlKey) switch (key) {
|
||||
|
||||
// Goto
|
||||
case "g": case "G":
|
||||
this.promptGoto();
|
||||
break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Ctrl key is not pressed
|
||||
else switch (key) {
|
||||
|
||||
// Arrow key navigation
|
||||
case "ArrowDown" : this.setSelection(bus.selection + 16); break;
|
||||
case "ArrowLeft" : this.setSelection(bus.selection - 1); break;
|
||||
case "ArrowRight": this.setSelection(bus.selection + 1); break;
|
||||
case "ArrowUp" : this.setSelection(bus.selection - 16); break;
|
||||
|
||||
// Commit current edit
|
||||
case "Enter":
|
||||
case " ":
|
||||
if (this.digit !== null)
|
||||
this.setSelection(bus.selection);
|
||||
break;
|
||||
|
||||
// Page key navigation
|
||||
case "PageDown":
|
||||
this.setSelection(bus.selection + this.tall(false) * 16);
|
||||
break;
|
||||
case "PageUp":
|
||||
this.setSelection(bus.selection - this.tall(false) * 16);
|
||||
break;
|
||||
|
||||
// Hex digit: already processed
|
||||
case "digit": break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
onMouseWheel(e) {
|
||||
|
||||
// User agent scaling action
|
||||
if (e.ctrlKey)
|
||||
return;
|
||||
|
||||
// No rotation has occurred
|
||||
let offset = Math.sign(e.deltaY) * 48;
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
// Update the display address
|
||||
this.fetch(this[this.bus].address + offset, true);
|
||||
}
|
||||
|
||||
// Pointer down
|
||||
onPointerDown(e) {
|
||||
|
||||
// Common handling
|
||||
this.editor.focus();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Not a click action
|
||||
if (e.button != 0)
|
||||
return;
|
||||
|
||||
// Determine the row that was clicked on
|
||||
let lineHeight = !this.metrics ? 0 :
|
||||
Math.max(0, Math.ceil(this.metrics.getBounds().height));
|
||||
if (lineHeight == 0)
|
||||
return;
|
||||
let y = Math.floor(
|
||||
(e.y - this.view.getBoundingClientRect().top) / lineHeight);
|
||||
|
||||
// Determine the column that was clicked on
|
||||
let columns = this.lines[0].lblBytes;
|
||||
let bndCur = columns[0].getBoundingClientRect();
|
||||
if (e.x >= bndCur.left) for (let x = 0; x < 16; x++) {
|
||||
let bndNext = x == 15 ? null :
|
||||
columns[x + 1].getBoundingClientRect();
|
||||
|
||||
// The current column was clicked: update the selection
|
||||
if (e.x < (x == 15 ? bndCur.right :
|
||||
bndCur.right + (bndNext.left - bndCur.right) / 2)) {
|
||||
this.setSelection(this[this.bus].address + y * 16 + x);
|
||||
return;
|
||||
}
|
||||
|
||||
// Advance to the next column
|
||||
bndCur = bndNext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Viewport resized
|
||||
onResize(e) {
|
||||
let fetch = false;
|
||||
let tall = this.tall(true);
|
||||
|
||||
// Add additional lines to the output
|
||||
for (let x = 0; x < tall; x++) {
|
||||
if (x >= this.lines.length) {
|
||||
fetch = true;
|
||||
this.lines.push(new Line(this, x));
|
||||
}
|
||||
this.lines[x].setVisible(true);
|
||||
}
|
||||
|
||||
// Remove extra lines from the output
|
||||
for (let x = tall; x < this.lines.length; x++)
|
||||
this.lines[x].setVisible(false);
|
||||
|
||||
// Configure horizontal scroll bar
|
||||
if (this.metrics) this.editor.horizontal
|
||||
.setIncrement(this.metrics.getBounds().width);
|
||||
|
||||
// Update the display
|
||||
if (fetch)
|
||||
this.fetch(this[this.bus].address, true);
|
||||
else this.refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Update with memory state from the core
|
||||
refresh(data) {
|
||||
let bus = this[this.bus];
|
||||
|
||||
// Update with data from the core thread
|
||||
if (data) {
|
||||
bus.data = data.bytes;
|
||||
bus.dataAddress = data.address;
|
||||
}
|
||||
|
||||
// Update elements
|
||||
for (let y = 0, tall = this.tall(true); y < tall; y++)
|
||||
this.lines[y].refresh();
|
||||
}
|
||||
|
||||
// Subscribe to or unsubscribe from core updates
|
||||
setSubscribed(subscribed) {
|
||||
subscribed = !!subscribed;
|
||||
|
||||
// Nothing to change
|
||||
if (subscribed == this.isSubscribed)
|
||||
return;
|
||||
|
||||
// Configure instance fields
|
||||
this.isSubscribed = subscribed;
|
||||
|
||||
// Subscribe to core updates
|
||||
if (subscribed)
|
||||
this.fetch(this[this.bus].address);
|
||||
|
||||
// Unsubscribe from core updates
|
||||
else this.sim.unsubscribe("memory");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// The disassembler configuration has changed
|
||||
dasmChanged() {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Retrieve memory data from the core
|
||||
async fetch(address, prefresh) {
|
||||
|
||||
// Configure instance fields
|
||||
this[this.bus].address = address = this.mask(address);
|
||||
|
||||
// Update the view immediately
|
||||
if (prefresh)
|
||||
this.refresh();
|
||||
|
||||
// Retrieve data from the core
|
||||
this.refresh(
|
||||
await this.sim.read(
|
||||
address - 16 * 16,
|
||||
(this.tall(true) + 32) * 16, {
|
||||
subscribe: this.isSubscribed && "memory"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Mask an address according to the current bus
|
||||
mask(address) {
|
||||
return Util.u32(address);
|
||||
}
|
||||
|
||||
// Prompt the user to specify a new address
|
||||
promptGoto() {
|
||||
|
||||
// Receive input from the user
|
||||
let address = prompt(this.app.translate("common.gotoPrompt"));
|
||||
if (address == null)
|
||||
return;
|
||||
|
||||
// Process the input as an address in hexadecimal
|
||||
address = parseInt(address, 16);
|
||||
if (isNaN(address))
|
||||
return;
|
||||
|
||||
// The address is not currently visible in the output
|
||||
let tall = this.tall(false);
|
||||
if (Util.u32(address - this.address) >= tall * 16)
|
||||
this.fetch((address & 0xFFFFFFF0) - Math.floor(tall / 3) * 16);
|
||||
|
||||
// Move the selection and refresh the display
|
||||
this.setSelection(address);
|
||||
}
|
||||
|
||||
// Determine which row relative to top the selection is on
|
||||
row(address) {
|
||||
let row = address - this[this.bus].address & 0xFFFFFFF0;
|
||||
row = Util.s32(row);
|
||||
return row / 16;
|
||||
}
|
||||
|
||||
// Specify which byte is selected
|
||||
setSelection(address, noCommit) {
|
||||
let bus = this[this.bus];
|
||||
let fetch = false;
|
||||
|
||||
// Commit a pending data entry
|
||||
if (!noCommit && this.digit !== null) {
|
||||
this.write(this.digit);
|
||||
this.digit = null;
|
||||
fetch = true;
|
||||
}
|
||||
|
||||
// Configure instance fields
|
||||
bus.selection = address = this.mask(address);
|
||||
|
||||
// Working variables
|
||||
let row = this.row(address);
|
||||
|
||||
// The new address is above the top line of output
|
||||
if (row < 0) {
|
||||
this.fetch(bus.address + row * 16 & 0xFFFFFFF0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// The new address is below the bottom line of output
|
||||
let tall = this.tall(false);
|
||||
if (row >= tall) {
|
||||
this.fetch(address - tall * 16 + 16 & 0xFFFFFFF0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the display
|
||||
if (fetch)
|
||||
this.fetch(bus.address, true);
|
||||
else this.refresh();
|
||||
}
|
||||
|
||||
// Measure how many rows of output are visible
|
||||
tall(partial) {
|
||||
let lineHeight = !this.metrics ? 0 :
|
||||
Math.ceil(this.metrics.getBounds().height);
|
||||
return lineHeight <= 0 ? 1 : Math.max(1, Math[partial?"ceil":"floor"](
|
||||
this.editor.getBounds().height / lineHeight));
|
||||
}
|
||||
|
||||
// Write a value to the core thread
|
||||
write(value) {
|
||||
let bus = this[this.bus];
|
||||
let offset = (this.row(bus.selection) + 16) * 16;
|
||||
if (offset < bus.data.length)
|
||||
bus.data[offset | bus.selection & 15] = value;
|
||||
this.sim.write(
|
||||
bus.selection,
|
||||
Uint8Array.from([ value ]), {
|
||||
refresh: true
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { Memory };
|
||||
@@ -0,0 +1,889 @@
|
||||
import { Util } from /**/"./Util.js";
|
||||
|
||||
|
||||
|
||||
// Value types
|
||||
const HEX = 0;
|
||||
const SIGNED = 1;
|
||||
const UNSIGNED = 2;
|
||||
const FLOAT = 3;
|
||||
|
||||
// System register indexes
|
||||
const ADTRE = 25;
|
||||
const CHCW = 24;
|
||||
const ECR = 4;
|
||||
const EIPC = 0;
|
||||
const EIPSW = 1;
|
||||
const FEPC = 2;
|
||||
const FEPSW = 3;
|
||||
const PC = -1;
|
||||
const PIR = 6;
|
||||
const PSW = 5;
|
||||
const TKCW = 7;
|
||||
|
||||
// Program register names
|
||||
const PROREGS = {
|
||||
[ 2]: "hp",
|
||||
[ 3]: "sp",
|
||||
[ 4]: "gp",
|
||||
[ 5]: "tp",
|
||||
[31]: "lp"
|
||||
};
|
||||
|
||||
// System register names
|
||||
const SYSREGS = {
|
||||
[ADTRE]: "ADTRE",
|
||||
[CHCW ]: "CHCW",
|
||||
[ECR ]: "ECR",
|
||||
[EIPC ]: "EIPC",
|
||||
[EIPSW]: "EIPSW",
|
||||
[FEPC ]: "FEPC",
|
||||
[FEPSW]: "FEPSW",
|
||||
[PC ]: "PC",
|
||||
[PIR ]: "PIR",
|
||||
[PSW ]: "PSW",
|
||||
[TKCW ]: "TKCW",
|
||||
[29 ]: "29",
|
||||
[30 ]: "30",
|
||||
[31 ]: "31"
|
||||
};
|
||||
|
||||
// Expansion control types
|
||||
const BIT = 0;
|
||||
const INT = 1;
|
||||
|
||||
// Produce a template object for register expansion controls
|
||||
function ctrl(name, shift, size, disabled) {
|
||||
return {
|
||||
disabled: !!disabled,
|
||||
name : name,
|
||||
shift : shift,
|
||||
size : size
|
||||
};
|
||||
}
|
||||
|
||||
// Program register epansion controls
|
||||
const EXP_PROGRAM = [
|
||||
ctrl("cpu.hex" , true , HEX ),
|
||||
ctrl("cpu.signed" , false, SIGNED ),
|
||||
ctrl("cpu.unsigned", false, UNSIGNED),
|
||||
ctrl("cpu.float" , false, FLOAT )
|
||||
];
|
||||
|
||||
// CHCW expansion controls
|
||||
const EXP_CHCW = [
|
||||
ctrl("ICE", 1, 1)
|
||||
];
|
||||
|
||||
// ECR expansion controls
|
||||
const EXP_ECR = [
|
||||
ctrl("FECC", 16, 16),
|
||||
ctrl("EICC", 0, 16)
|
||||
];
|
||||
|
||||
// PIR expansion controls
|
||||
const EXP_PIR = [
|
||||
ctrl("PT", 0, 16, true)
|
||||
];
|
||||
|
||||
// PSW expansion controls
|
||||
const EXP_PSW = [
|
||||
ctrl("CY", 3, 1), ctrl("FRO", 9, 1),
|
||||
ctrl("OV", 2, 1), ctrl("FIV", 8, 1),
|
||||
ctrl("S" , 1, 1), ctrl("FZD", 7, 1),
|
||||
ctrl("Z" , 0, 1), ctrl("FOV", 6, 1),
|
||||
ctrl("NP", 15, 1), ctrl("FUD", 5, 1),
|
||||
ctrl("EP", 14, 1), ctrl("FPR", 4, 1),
|
||||
ctrl("ID", 12, 1), ctrl("I" , 16, 4),
|
||||
ctrl("AE", 13, 1)
|
||||
];
|
||||
|
||||
// TKCW expansion controls
|
||||
const EXP_TKCW = [
|
||||
ctrl("FIT", 7, 1, true), ctrl("FUT", 4, 1, true),
|
||||
ctrl("FZT", 6, 1, true), ctrl("FPT", 3, 1, true),
|
||||
ctrl("FVT", 5, 1, true), ctrl("OTM", 8, 1, true),
|
||||
ctrl("RDI", 2, 1, true), ctrl("RD" , 0, 2, true)
|
||||
];
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Register //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One register within a register list
|
||||
class Register {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(list, index, andMask, orMask) {
|
||||
|
||||
// Configure instance fields
|
||||
this.andMask = andMask;
|
||||
this.app = list.app;
|
||||
this.controls = [];
|
||||
this.dasm = list.dasm;
|
||||
this.format = HEX;
|
||||
this.index = index;
|
||||
this.isExpanded = null;
|
||||
this.list = list;
|
||||
this.metrics = { width: 0, height: 0 };
|
||||
this.orMask = orMask;
|
||||
this.sim = list.sim;
|
||||
this.system = list.system;
|
||||
this.value = 0x00000000;
|
||||
|
||||
// Establish elements
|
||||
let row = document.createElement("tr");
|
||||
let cell;
|
||||
list.view.append(row);
|
||||
|
||||
// Processing by type
|
||||
this[this.system ? "initSystem" : "initProgram"]();
|
||||
|
||||
// Expansion button
|
||||
this.btnExpand = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-expand tk-mono",
|
||||
tagName : "div"
|
||||
});
|
||||
row .append(cell = document.createElement("td"));
|
||||
cell.className = "tk";
|
||||
cell.style.width = "1px";
|
||||
cell.append(this.btnExpand.element);
|
||||
|
||||
// Name label
|
||||
this.lblName = document.createElement("div");
|
||||
Object.assign(this.lblName, {
|
||||
className: "tk tk-name",
|
||||
id : Toolkit.id(),
|
||||
innerText: this.dasm.sysregCaps?this.name:this.name.toLowerCase()
|
||||
});
|
||||
this.lblName.style.userSelect = "none";
|
||||
row .append(cell = document.createElement("td"));
|
||||
cell.className = "tk";
|
||||
cell.append(this.lblName);
|
||||
|
||||
// Value text box
|
||||
this.txtValue = new Toolkit.TextBox(this.app, {
|
||||
className: "tk tk-textbox tk-mono",
|
||||
maxLength: 8
|
||||
});
|
||||
this.txtValue.setAttribute("aria-labelledby", this.lblName.id);
|
||||
this.txtValue.setAttribute("digits", "8");
|
||||
this.txtValue.addEventListener("action", e=>this.onValue());
|
||||
row .append(cell = document.createElement("td"));
|
||||
Object.assign(cell.style, {
|
||||
textAlign: "right",
|
||||
width : "1px"
|
||||
});
|
||||
cell.className = "tk";
|
||||
cell.append(this.txtValue.element);
|
||||
|
||||
// Expansion area
|
||||
if (this.expansion != null)
|
||||
this.list.view.append(this.expansion);
|
||||
|
||||
// Enable expansion function
|
||||
if (this.expansion != null) {
|
||||
let key = e=>this.expandKeyDown (e);
|
||||
let pointer = e=>this.expandPointerDown(e);
|
||||
this.btnExpand.setAttribute("aria-controls", this.expansion.id);
|
||||
this.btnExpand.setAttribute("aria-labelledby", this.lblName.id);
|
||||
this.btnExpand.setAttribute("role", "button");
|
||||
this.btnExpand.setAttribute("tabindex", "0");
|
||||
this.btnExpand.addEventListener("keydown" , key );
|
||||
this.btnExpand.addEventListener("pointerdown", pointer);
|
||||
this.lblName .addEventListener("pointerdown", pointer);
|
||||
this.setExpanded(this.system && this.index == PSW);
|
||||
}
|
||||
|
||||
// Expansion function is unavailable
|
||||
else this.btnExpand.setAttribute("aria-hidden", "true");
|
||||
|
||||
}
|
||||
|
||||
// Set up a program register
|
||||
initProgram() {
|
||||
this.name = PROREGS[this.index] || "r" + this.index.toString();
|
||||
this.initExpansion(EXP_PROGRAM);
|
||||
}
|
||||
|
||||
// Set up a system register
|
||||
initSystem() {
|
||||
this.name = SYSREGS[this.index] || this.index.toString();
|
||||
|
||||
switch (this.index) {
|
||||
case CHCW :
|
||||
this.initExpansion(EXP_CHCW); break;
|
||||
case ECR :
|
||||
this.initExpansion(EXP_ECR ); break;
|
||||
case EIPSW: case FEPSW: case PSW:
|
||||
this.initExpansion(EXP_PSW ); break;
|
||||
case PIR :
|
||||
this.initExpansion(EXP_PIR ); break;
|
||||
case TKCW :
|
||||
this.initExpansion(EXP_TKCW); break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Initialize expansion controls
|
||||
initExpansion(controls) {
|
||||
let two = this.index == ECR || this.index == PIR;
|
||||
|
||||
// Establish expansion element
|
||||
let exp = this.expansion = document.createElement("tr");
|
||||
exp.contents = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-expansion",
|
||||
id : Toolkit.id(),
|
||||
tagName : "div",
|
||||
style : {
|
||||
display : "grid",
|
||||
gridTemplateColumns:
|
||||
this.system ? "repeat(2, max-content)" : "max-content"
|
||||
}
|
||||
});
|
||||
let cell = document.createElement("td");
|
||||
cell.className = "tk";
|
||||
cell.colSpan = "3";
|
||||
cell.append(exp.contents.element);
|
||||
exp.append(cell);
|
||||
exp = exp.contents;
|
||||
|
||||
// Produce program register controls
|
||||
if (!this.system) {
|
||||
let group = new Toolkit.Group();
|
||||
exp.append(group);
|
||||
|
||||
// Process all controls
|
||||
for (let template of controls) {
|
||||
|
||||
// Create control
|
||||
let ctrl = new Toolkit.Radio(this.app, {
|
||||
group : group,
|
||||
selected: template.shift,
|
||||
text : template.name
|
||||
});
|
||||
ctrl.format = template.size;
|
||||
|
||||
// Configure event handler
|
||||
ctrl.addEventListener("action",
|
||||
e=>this.setFormat(e.component.format));
|
||||
|
||||
// Add the control to the element
|
||||
let box = document.createElement("div");
|
||||
box.append(ctrl.element);
|
||||
exp.append(box);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Process all control templates
|
||||
for (let template of controls) {
|
||||
let box, ctrl;
|
||||
|
||||
// Not using an inner two-column layout
|
||||
if (!two)
|
||||
exp.append(box = document.createElement("div"));
|
||||
|
||||
// Bit check box
|
||||
if (template.size == 1) {
|
||||
box.classList.add("tk-bit");
|
||||
|
||||
// Create control
|
||||
ctrl = new Toolkit.CheckBox(this.app, {
|
||||
text : "name",
|
||||
substitutions: { name: template.name }
|
||||
});
|
||||
ctrl.mask = 1 << template.shift;
|
||||
box.append(ctrl.element);
|
||||
|
||||
// Disable control
|
||||
if (template.disabled)
|
||||
ctrl.setEnabled(false);
|
||||
|
||||
// Configure event handler
|
||||
ctrl.addEventListener("action", e=>this.onBit(e.component));
|
||||
}
|
||||
|
||||
// Number text box
|
||||
else {
|
||||
if (!two)
|
||||
box.classList.add("tk-number");
|
||||
|
||||
// Create label
|
||||
let label = document.createElement("label");
|
||||
Object.assign(label, {
|
||||
className: "tk tk-label",
|
||||
innerText: template.name,
|
||||
});
|
||||
if (!two) Object.assign(box.style, {
|
||||
columnGap : "2px",
|
||||
display : "grid",
|
||||
gridTemplateColumns: "max-content auto"
|
||||
});
|
||||
(two ? exp : box).append(label);
|
||||
|
||||
// Create control
|
||||
ctrl = new Toolkit.TextBox(this.app, {
|
||||
id : Toolkit.id(),
|
||||
style: { height: "1em" }
|
||||
});
|
||||
label.htmlFor = ctrl.id;
|
||||
(two ? exp : box).append(ctrl.element);
|
||||
|
||||
// Control is a hex field
|
||||
if (template.size == 16) {
|
||||
ctrl.element.classList.add("tk-mono");
|
||||
ctrl.setAttribute("digits", 4);
|
||||
ctrl.setMaxLength(4);
|
||||
}
|
||||
|
||||
// Disable control
|
||||
if (template.disabled) {
|
||||
ctrl.setEnabled(false);
|
||||
(two ? label : box).setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
// Configure event handler
|
||||
ctrl.addEventListener("action", e=>this.onNumber(e.component));
|
||||
}
|
||||
|
||||
Object.assign(ctrl, template);
|
||||
this.controls.push(ctrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Expand button key press
|
||||
expandKeyDown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "Enter":
|
||||
case " ":
|
||||
this.setExpanded(!this.isExpanded);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Expand button pointer down
|
||||
expandPointerDown(e) {
|
||||
|
||||
// Focus management
|
||||
this.btnExpand.focus();
|
||||
|
||||
// Error checking
|
||||
if (e.button != 0)
|
||||
return;
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Configure expansion area
|
||||
this.setExpanded(!this.isExpanded);
|
||||
}
|
||||
|
||||
// Expansion bit check box
|
||||
onBit(ctrl) {
|
||||
this.setValue(ctrl.isSelected ?
|
||||
this.value | ctrl.mask :
|
||||
this.value & Util.u32(~ctrl.mask)
|
||||
);
|
||||
}
|
||||
|
||||
// Expansion number text box
|
||||
onNumber(ctrl) {
|
||||
let mask = (1 << ctrl.size) - 1 << ctrl.shift;
|
||||
let value = parseInt(ctrl.getText(), ctrl.size == 16 ? 16 : 10);
|
||||
this.setValue(isNaN(value) ? this.value :
|
||||
this.value & Util.u32(~mask) | value << ctrl.shift & mask);
|
||||
}
|
||||
|
||||
// Register value
|
||||
onValue() {
|
||||
let text = this.txtValue.getText();
|
||||
let value;
|
||||
|
||||
// Processing by type
|
||||
switch (this.format) {
|
||||
|
||||
// Unsigned hexadecimal
|
||||
case HEX:
|
||||
value = parseInt(text, 16);
|
||||
break;
|
||||
|
||||
// Decimal
|
||||
case SIGNED:
|
||||
case UNSIGNED:
|
||||
value = parseInt(text);
|
||||
break;
|
||||
|
||||
// Float
|
||||
case FLOAT:
|
||||
value = parseFloat(text);
|
||||
if (isNaN(value))
|
||||
break;
|
||||
value = Util.fromF32(value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Assign the new value
|
||||
this.setValue(isNaN(value) ? this.value : value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Disassembler settings have been updated
|
||||
dasmChanged() {
|
||||
let dasm = this.list.dasm;
|
||||
let name = this.name;
|
||||
|
||||
// Program register name
|
||||
if (!this.system) {
|
||||
if (!dasm.proregNames)
|
||||
name = "r" + this.index.toString();
|
||||
if (dasm.proregCaps)
|
||||
name = name.toUpperCase();
|
||||
}
|
||||
|
||||
// System register name
|
||||
else {
|
||||
if (!dasm.sysregCaps)
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
|
||||
// Common processing
|
||||
this.lblName.innerText = name;
|
||||
this.refresh(this.value);
|
||||
}
|
||||
|
||||
// Update the value returned from the core
|
||||
refresh(value) {
|
||||
let text;
|
||||
|
||||
// Configure instance fields
|
||||
this.value = value = Util.u32(value);
|
||||
|
||||
// Value text box
|
||||
switch (this.format) {
|
||||
|
||||
// Unsigned hexadecimal
|
||||
case HEX:
|
||||
text = value.toString(16).padStart(8, "0");
|
||||
if (this.dasm.hexCaps)
|
||||
text = text.toUpperCase();
|
||||
break;
|
||||
|
||||
// Signed decimal
|
||||
case SIGNED:
|
||||
text = Util.s32(value).toString();
|
||||
break;
|
||||
|
||||
// Unsigned decial
|
||||
case UNSIGNED:
|
||||
text = Util.u32(value).toString();
|
||||
break;
|
||||
|
||||
// Float
|
||||
case FLOAT:
|
||||
if ((value & 0x7F800000) != 0x7F800000) {
|
||||
text = Util.toF32(value).toFixed(5).replace(/0+$/, "");
|
||||
if (text.endsWith("."))
|
||||
text += "0";
|
||||
} else text = "NaN";
|
||||
break;
|
||||
}
|
||||
this.txtValue.setText(text);
|
||||
|
||||
// No further processing for program registers
|
||||
if (!this.system)
|
||||
return;
|
||||
|
||||
// Process all expansion controls
|
||||
for (let ctrl of this.controls) {
|
||||
|
||||
// Bit check box
|
||||
if (ctrl.size == 1) {
|
||||
ctrl.setSelected(value & ctrl.mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Integer text box
|
||||
text = value >> ctrl.shift & (1 << ctrl.size) - 1;
|
||||
text = ctrl.size != 16 ? text.toString() :
|
||||
text.toString(16).padStart(4, "0");
|
||||
if (this.dasm.hexCaps)
|
||||
text = text.toUpperCase();
|
||||
ctrl.setText(text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Specify whether the expansion area is visible
|
||||
setExpanded(expanded) {
|
||||
expanded = !!expanded;
|
||||
|
||||
// Error checking
|
||||
if (this.expansion == null || expanded === this.isExpanded)
|
||||
return;
|
||||
|
||||
// Configure instance fields
|
||||
this.isExpanded = expanded;
|
||||
|
||||
// Configure elements
|
||||
let key = expanded ? "common.collapse" : "common.expand";
|
||||
this.btnExpand.setAttribute("aria-expanded", expanded);
|
||||
this.btnExpand.setToolTip(key);
|
||||
this.expansion.style.display =
|
||||
expanded ? "table-row" : "none";
|
||||
}
|
||||
|
||||
// Specify the font metrics
|
||||
setMetrics(width, height) {
|
||||
|
||||
// Configure instance fields
|
||||
this.metrics = { width: width, height: height };
|
||||
|
||||
// Height
|
||||
height += "px";
|
||||
this.txtValue.element.style.height = height;
|
||||
for (let ctrl of this.controls.filter(c=>c.size > 1))
|
||||
ctrl.element.style.height = height;
|
||||
|
||||
// Hexadecimal formatting
|
||||
if (this.format == HEX) {
|
||||
this.txtValue.element.style.width = (width * 8) + "px";
|
||||
this.txtValue.setMaxLength(8);
|
||||
}
|
||||
|
||||
// Decimal formatting
|
||||
else {
|
||||
this.txtValue.element.style.removeProperty("width");
|
||||
this.txtValue.setMaxLength(null);
|
||||
}
|
||||
|
||||
// Expansion text boxes
|
||||
for (let box of this.controls.filter(c=>c.size > 1)) {
|
||||
box.element.style.height = height;
|
||||
if (box.size == 16)
|
||||
box.element.style.width = (width * 4) + "px";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Specify the formatting type of the register value
|
||||
setFormat(format) {
|
||||
if (format == this.format)
|
||||
return;
|
||||
this.format = format;
|
||||
this.txtValue.element
|
||||
.classList[format == HEX ? "add" : "remove"]("tk-mono");
|
||||
this.setMetrics(this.metrics.width, this.metrics.height);
|
||||
this.refresh(this.value);
|
||||
}
|
||||
|
||||
// Specify a new value for the register
|
||||
async setValue(value) {
|
||||
|
||||
// Update the display with the new value immediately
|
||||
value = Util.u32(value & this.andMask | this.orMask);
|
||||
let matched = value == this.value;
|
||||
this.refresh(value);
|
||||
if (matched)
|
||||
return;
|
||||
|
||||
// Update the new value in the core
|
||||
let options = { refresh: true };
|
||||
this.refresh(await (
|
||||
!this.system ?
|
||||
this.sim.setProgramRegister(this.index, value, options) :
|
||||
this.index == PC ?
|
||||
this.sim.setProgramCounter ( value, options) :
|
||||
this.sim.setSystemRegister (this.index, value, options)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RegisterList //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Scrolling list of registers
|
||||
class RegisterList extends Toolkit.ScrollPane {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(debug, system) {
|
||||
super(debug.app, {
|
||||
className: "tk tk-scrollpane tk-reglist " +
|
||||
(system ? "tk-system" : "tk-program"),
|
||||
vertical : Toolkit.ScrollPane.ALWAYS
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.app = debug.app;
|
||||
this.dasm = debug.disassembler;
|
||||
this.method = system?"getSystemRegisters":"getProgramRegisters";
|
||||
this.registers = [];
|
||||
this.sim = debug.sim;
|
||||
this.subscription = system ? "sysregs" : "proregs";
|
||||
this.system = system;
|
||||
|
||||
// Configure view element
|
||||
this.setView(new Toolkit.Component(debug.app, {
|
||||
className: "tk tk-list",
|
||||
tagName : "table",
|
||||
style : {
|
||||
width: "100%"
|
||||
}
|
||||
}));
|
||||
|
||||
// Font-measuring element
|
||||
let text = "";
|
||||
for (let x = 0; x < 16; x++) {
|
||||
if (x != 0) text += "\n";
|
||||
let digit = x.toString(16);
|
||||
text += digit + "\n" + digit.toUpperCase();
|
||||
}
|
||||
this.metrics = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-mono",
|
||||
tagName : "div",
|
||||
style : {
|
||||
position : "absolute",
|
||||
visibility: "hidden"
|
||||
}
|
||||
});
|
||||
this.metrics.element.innerText = text;
|
||||
this.metrics.addEventListener("resize", e=>this.onMetrics());
|
||||
this.viewport.append(this.metrics.element);
|
||||
|
||||
// Processing by type
|
||||
this[system ? "initSystem" : "initProgram"]();
|
||||
|
||||
// Configure component
|
||||
this.addEventListener("keydown", e=>this.onKeyDown (e));
|
||||
this.addEventListener("wheel" , e=>this.onMouseWheel(e));
|
||||
}
|
||||
|
||||
// Initialize a list of program registers
|
||||
initProgram() {
|
||||
this.add(new Register(this, 0, 0x00000000, 0x00000000));
|
||||
for (let x = 1; x < 32; x++)
|
||||
this.add(new Register(this, x, 0xFFFFFFFF, 0x00000000));
|
||||
}
|
||||
|
||||
// Initialie a list of system registers
|
||||
initSystem() {
|
||||
this.add(new Register(this, PC , 0xFFFFFFFE, 0x00000000));
|
||||
this.add(new Register(this, PSW , 0x000FF3FF, 0x00000000));
|
||||
this.add(new Register(this, ADTRE, 0xFFFFFFFE, 0x00000000));
|
||||
this.add(new Register(this, CHCW , 0x00000002, 0x00000000));
|
||||
this.add(new Register(this, ECR , 0xFFFFFFFF, 0x00000000));
|
||||
this.add(new Register(this, EIPC , 0xFFFFFFFE, 0x00000000));
|
||||
this.add(new Register(this, EIPSW, 0x000FF3FF, 0x00000000));
|
||||
this.add(new Register(this, FEPC , 0xFFFFFFFE, 0x00000000));
|
||||
this.add(new Register(this, FEPSW, 0x000FF3FF, 0x00000000));
|
||||
this.add(new Register(this, PIR , 0x00000000, 0x00005346));
|
||||
this.add(new Register(this, TKCW , 0x00000000, 0x000000E0));
|
||||
this.add(new Register(this, 29 , 0xFFFFFFFF, 0x00000000));
|
||||
this.add(new Register(this, 30 , 0x00000000, 0x00000004));
|
||||
this.add(new Register(this, 31 , 0xFFFFFFFF, 0x00000000));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "ArrowDown":
|
||||
this.vertical.setValue(this.vertical.value +
|
||||
this.vertical.increment);
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
this.horizontal.setValue(this.horizontal.value -
|
||||
this.horizontal.increment);
|
||||
break;
|
||||
case "ArrowRight":
|
||||
this.horizontal.setValue(this.horizontal.value +
|
||||
this.horizontal.increment);
|
||||
break;
|
||||
case "ArrowUp":
|
||||
this.vertical.setValue(this.vertical.value -
|
||||
this.vertical.increment);
|
||||
break;
|
||||
case "PageDown":
|
||||
this.vertical.setValue(this.vertical.value +
|
||||
this.vertical.extent);
|
||||
break;
|
||||
case "PageUp":
|
||||
this.vertical.setValue(this.vertical.value -
|
||||
this.vertical.extent);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Metrics element resized
|
||||
onMetrics() {
|
||||
|
||||
// Error checking
|
||||
if (!this.metrics)
|
||||
return;
|
||||
|
||||
// Measure the dimensions of one hex character
|
||||
let bounds = this.metrics.getBounds();
|
||||
if (bounds.height <= 0)
|
||||
return;
|
||||
let width = Math.ceil(bounds.width);
|
||||
let height = Math.ceil(bounds.height / 32);
|
||||
|
||||
// Resize all text boxes
|
||||
for (let reg of this.registers)
|
||||
reg.setMetrics(width, height);
|
||||
|
||||
// Update scroll bars
|
||||
this.horizontal.setIncrement(height);
|
||||
this.vertical .setIncrement(height);
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
onMouseWheel(e) {
|
||||
|
||||
// User agent scaling action
|
||||
if (e.ctrlKey)
|
||||
return;
|
||||
|
||||
// No rotation has occurred
|
||||
let offset = Math.sign(e.deltaY) * 3;
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
// Update the display address
|
||||
this.vertical.setValue(this.vertical.value +
|
||||
this.vertical.increment * offset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Update with CPU state from the core
|
||||
refresh(registers) {
|
||||
|
||||
// System registers
|
||||
if (this.system) {
|
||||
for (let reg of Object.entries(SYSREGS))
|
||||
this[reg[0]].refresh(registers[reg[1].toLowerCase()]);
|
||||
}
|
||||
|
||||
// Program registers
|
||||
else for (let x = 0; x < 32; x++)
|
||||
this[x].refresh(registers[x]);
|
||||
}
|
||||
|
||||
// Subscribe to or unsubscribe from core updates
|
||||
setSubscribed(subscribed) {
|
||||
subscribed = !!subscribed;
|
||||
|
||||
// Nothing to change
|
||||
if (subscribed == this.isSubscribed)
|
||||
return;
|
||||
|
||||
// Configure instance fields
|
||||
this.isSubscribed = subscribed;
|
||||
|
||||
// Subscribe to core updates
|
||||
if (subscribed)
|
||||
this.fetch();
|
||||
|
||||
// Unsubscribe from core updates
|
||||
else this.sim.unsubscribe(this.subscription);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Disassembler settings have been updated
|
||||
dasmChanged() {
|
||||
for (let reg of this.registers)
|
||||
reg.dasmChanged();
|
||||
}
|
||||
|
||||
// Determine the initial size of the register list
|
||||
getPreferredSize() {
|
||||
let ret = {
|
||||
height: 0,
|
||||
width : 0
|
||||
};
|
||||
|
||||
// Error checking
|
||||
if (!this.view)
|
||||
return ret;
|
||||
|
||||
// Measure the view element
|
||||
ret.width = this.view.element.scrollWidth;
|
||||
|
||||
// Locate the bottom of PSW
|
||||
if (this.system && this[PSW].expansion) {
|
||||
ret.height = this[PSW].expansion.getBoundingClientRect().bottom -
|
||||
this.view.getBounds().top;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Add a register to the list
|
||||
add(reg) {
|
||||
this[reg.index] = reg;
|
||||
this.registers.push(reg);
|
||||
}
|
||||
|
||||
// Retrieve CPU state from the core
|
||||
async fetch() {
|
||||
this.refresh(
|
||||
await this.sim[this.method]({
|
||||
subscribe: this.isSubscribed && this.subscription
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { RegisterList };
|
||||
@@ -0,0 +1,44 @@
|
||||
let F32 = new Float32Array( 1);
|
||||
let S32 = new Int32Array (F32.buffer, 0, 1);
|
||||
let U32 = new Uint32Array (F32.buffer, 0, 1);
|
||||
|
||||
// Interpret a floating short as a 32-bit integer
|
||||
function fromF32(x) {
|
||||
F32[0] = x;
|
||||
return S32[0];
|
||||
}
|
||||
|
||||
// Interpret a 32-bit integer as a floating short
|
||||
function toF32(x) {
|
||||
S32[0] = x;
|
||||
return F32[0];
|
||||
}
|
||||
|
||||
// Represent a value as a signed 32-bit integer
|
||||
function s32(x) {
|
||||
S32[0] = x;
|
||||
return S32[0];
|
||||
}
|
||||
|
||||
// Sign-extend a value with a given number of bits
|
||||
function signExtend(value, bits) {
|
||||
bits = 32 - bits;
|
||||
S32[0] = value << bits;
|
||||
return S32[0] >> bits;
|
||||
}
|
||||
|
||||
// Represent a value as an unsigned 32-bit integer
|
||||
function u32(x) {
|
||||
U32[0] = x;
|
||||
return U32[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
export let Util = {
|
||||
fromF32 : fromF32,
|
||||
toF32 : toF32,
|
||||
s32 : s32,
|
||||
signExtend: signExtend,
|
||||
u32 : u32
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
import { Sim } from /**/"./Sim.js";
|
||||
|
||||
let url = u=>u.startsWith("data:")?u:new URL(u,import.meta.url).toString();
|
||||
|
||||
let RESTRICT = {};
|
||||
let WASM_URL = url(/**/"./core.wasm" );
|
||||
let WORKER_URL = url(/**/"./CoreWorker.js");
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Core //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Environment manager for simulated Virtual Boys
|
||||
class Core {
|
||||
|
||||
//////////////////////////////// Constants ////////////////////////////////
|
||||
|
||||
// States
|
||||
static IDLE = 0;
|
||||
static RUNNING = 1;
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Static Methods //////////////////////////////
|
||||
|
||||
// Create a new instance of Core
|
||||
static create(options) {
|
||||
return new Core(RESTRICT).init(options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
// Stub constructor
|
||||
constructor(restrict) {
|
||||
if (restrict != RESTRICT) {
|
||||
throw "Cannot instantiate Core directly. " +
|
||||
"Use Core.create() instead.";
|
||||
}
|
||||
}
|
||||
|
||||
// Substitute constructor
|
||||
async init(options = {}) {
|
||||
|
||||
// Configure instance fields
|
||||
this.length = 0;
|
||||
this.onsubscriptions = null;
|
||||
this.resolutions = [];
|
||||
this.state = Core.IDLE;
|
||||
this.worker = new Worker(WORKER_URL);
|
||||
this.worker.onmessage = e=>this.onMessage(e.data);
|
||||
|
||||
// Issue a create command
|
||||
if ("sims" in options)
|
||||
await this.create(options.sims, WASM_URL);
|
||||
|
||||
// Only initialize the WebAssembly module
|
||||
else this.send("init", false, { wasm: WASM_URL });
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Worker message received
|
||||
onMessage(data) {
|
||||
|
||||
// Process a promised response
|
||||
if ("response" in data)
|
||||
this.resolutions.shift()(data.response);
|
||||
|
||||
// Process subscriptions
|
||||
if (this.onsubscriptions && data.subscriptions)
|
||||
this.onsubscriptions(data.subscriptions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Associate two simulations as peers, or remove an association
|
||||
connect(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "connect",
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Create and initialize new simulations
|
||||
async create(sims, wasm) {
|
||||
let numSims = sims===undefined ? 1 : Math.max(0, parseInt(sims) || 0);
|
||||
|
||||
// Execute the command in the core thread
|
||||
let response = await this.send({
|
||||
command: "create",
|
||||
sims : numSims,
|
||||
wasm : wasm
|
||||
});
|
||||
|
||||
// Process the core thread's response
|
||||
let ret = [];
|
||||
for (let x = 0; x < numSims; x++, this.length++)
|
||||
ret.push(this[this.length] =
|
||||
new Sim(this, response[x], this.length));
|
||||
return sims === undefined ? ret[0] : ret;
|
||||
}
|
||||
|
||||
// Delete a simulation
|
||||
destroy(sim, options = {}) {
|
||||
|
||||
// Configure simulation
|
||||
sim = this[sim] || sim;
|
||||
if (sim.core != this)
|
||||
return;
|
||||
let ptr = sim.destroy();
|
||||
|
||||
// State management
|
||||
for (let x = sim.index + 1; x < this.length; x++)
|
||||
(this[x - 1] = this[x]).index--;
|
||||
delete this[--this.length];
|
||||
|
||||
// Execute the command on the core thread
|
||||
return this.send({
|
||||
command: "destroy",
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sim : ptr
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to run until the next instruction
|
||||
runNext(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "runNext",
|
||||
refresh: !!options.refresh,
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
singleStep(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "singleStep",
|
||||
refresh: !!options.refresh,
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Unsubscribe from frame data
|
||||
unsubscribe(key, sim = 0) {
|
||||
this.send({
|
||||
command: "unsubscribe",
|
||||
key : key,
|
||||
respond: false,
|
||||
sim : sim
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Send a message to the Worker
|
||||
send(data = {}, transfers = []) {
|
||||
|
||||
// Create the message object
|
||||
Object.assign(data, {
|
||||
respond: !("respond" in data) || !!data.respond,
|
||||
run : !("run" in data) || !!data.run
|
||||
});
|
||||
|
||||
// Do not wait on a response
|
||||
if (!data.respond)
|
||||
this.worker.postMessage(data, transfers);
|
||||
|
||||
// Wait for the response to come back
|
||||
else return new Promise((resolve, reject)=>{
|
||||
this.resolutions.push(response=>resolve(response));
|
||||
this.worker.postMessage(data, transfers);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export { Core };
|
||||
@@ -0,0 +1,377 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
|
||||
// Un-sign a 32-bit integer
|
||||
// Emscripten is sign-extending uint32_t and Firefox can't import in Workers
|
||||
let u32 = (()=>{
|
||||
let U32 = new Uint32Array(1);
|
||||
return x=>{ U32[0] = x; return U32[0]; };
|
||||
})();
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CoreWorker //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Thread manager for Core commands
|
||||
new class CoreWorker {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
// Stub constructor
|
||||
constructor() {
|
||||
onmessage = async e=>{
|
||||
await this.init(e.data.wasm);
|
||||
onmessage = e=>this.onCommand(e.data, false);
|
||||
onmessage(e);
|
||||
};
|
||||
}
|
||||
|
||||
// Substitute constructor
|
||||
async init(wasm) {
|
||||
|
||||
// Load the WebAssembly module
|
||||
let imports = {
|
||||
env: { emscripten_notify_memory_growth: ()=>this.onMemory() }
|
||||
};
|
||||
this.wasm = await (typeof wasm == "string" ?
|
||||
WebAssembly.instantiateStreaming(fetch(wasm), imports) :
|
||||
WebAssembly.instantiate ( wasm , imports)
|
||||
);
|
||||
|
||||
// Configure instance fields
|
||||
this.api = this.wasm.instance.exports;
|
||||
this.frameData = null;
|
||||
this.isRunning = false;
|
||||
this.memory = this.api.memory.buffer;
|
||||
this.ptrSize = this.api.PointerSize();
|
||||
this.ptrType = this.ptrSize == 4 ? Uint32Array : Uint64Array;
|
||||
this.subscriptions = {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Message from audio thread
|
||||
onAudio(frames) {
|
||||
}
|
||||
|
||||
// Message from main thread
|
||||
onCommand(data) {
|
||||
|
||||
// Subscribe to the command
|
||||
if (data.subscribe) {
|
||||
let sub = data.sim || 0;
|
||||
sub = this.subscriptions[sub] || (this.subscriptions[sub] = {});
|
||||
sub = sub[data.subscribe] = {};
|
||||
Object.assign(sub, data);
|
||||
delete sub.promised;
|
||||
delete sub.run;
|
||||
delete sub.subscribe;
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
if (data.run)
|
||||
this[data.command](data);
|
||||
|
||||
// Process all subscriptions to refresh any debugging interfaces
|
||||
if (data.refresh)
|
||||
this.doSubscriptions(data.sim ? [ data.sim ] : data.sims);
|
||||
|
||||
// Reply to the main thread
|
||||
if (data.respond) {
|
||||
postMessage({
|
||||
response: data.response
|
||||
}, data.transfers);
|
||||
}
|
||||
}
|
||||
|
||||
// Memory growth
|
||||
onMemory() {
|
||||
this.memory = this.api.memory.buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////// Commands /////////////////////////////////
|
||||
|
||||
// Associate two simulations as peers, or remove an association
|
||||
connect(data) {
|
||||
this.api.vbConnect(data.sims[0], data.sims[1]);
|
||||
}
|
||||
|
||||
// Allocate and initialize a new simulation
|
||||
create(data) {
|
||||
let ptr = this.api.Create(data.sims);
|
||||
data.response = new this.ptrType(this.memory, ptr, data.sims).slice();
|
||||
data.transfers = [ data.response.buffer ];
|
||||
this.api.Free(ptr);
|
||||
}
|
||||
|
||||
// Delete a simulation
|
||||
destroy(data) {
|
||||
this.api.Destroy(data.sim);
|
||||
}
|
||||
|
||||
// Locate instructions for disassembly
|
||||
disassemble(data) {
|
||||
let decode; // Address of next row
|
||||
let index; // Index in list of next row
|
||||
let rows = new Array(data.rows);
|
||||
let pc = u32(this.api.vbGetProgramCounter(data.sim));
|
||||
let row; // Located output row
|
||||
|
||||
// The target address is before or on the first row of output
|
||||
if (data.row <= 0) {
|
||||
decode = u32(data.target - 4 * Math.max(0, data.row + 10));
|
||||
|
||||
// Locate the target row
|
||||
for (;;) {
|
||||
row = this.dasmRow(data.sim, decode, pc);
|
||||
if (u32(data.target - decode) < row.size)
|
||||
break;
|
||||
decode = u32(decode + row.size);
|
||||
}
|
||||
|
||||
// Locate the first row of output
|
||||
for (index = data.row; index < 0; index++) {
|
||||
decode = u32(decode + row.size);
|
||||
row = this.dasmRow(data.sim, decode, pc);
|
||||
}
|
||||
|
||||
// Prepare to process remaining rows
|
||||
decode = u32(decode + row.size);
|
||||
rows[0] = row;
|
||||
index = 1;
|
||||
}
|
||||
|
||||
// The target address is after the first row of output
|
||||
else {
|
||||
let circle = new Array(data.row + 1);
|
||||
let count = Math.min(data.row + 1, data.rows);
|
||||
let src = 0;
|
||||
decode = u32(data.target - 4 * (data.row + 10));
|
||||
|
||||
// Locate the target row
|
||||
for (;;) {
|
||||
row = circle[src] = this.dasmRow(data.sim, decode, pc);
|
||||
decode = u32(decode + row.size);
|
||||
if (u32(data.target - row.address) < row.size)
|
||||
break;
|
||||
src = (src + 1) % circle.length;
|
||||
}
|
||||
|
||||
// Copy entries from the circular buffer to the output list
|
||||
for (index = 0; index < count; index++) {
|
||||
src = (src + 1) % circle.length;
|
||||
rows[index] = circle[src];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Locate any remaining rows
|
||||
for (; index < data.rows; index++) {
|
||||
let row = rows[index] = this.dasmRow(data.sim, decode, pc);
|
||||
decode = u32(decode + row.size);
|
||||
}
|
||||
|
||||
// Respond to main thread
|
||||
data.response = {
|
||||
pc : pc,
|
||||
rows : rows,
|
||||
scroll: data.scroll
|
||||
};
|
||||
}
|
||||
|
||||
// Retrieve all CPU program registers
|
||||
getProgramRegisters(data) {
|
||||
let ret = data.response = new Uint32Array(32);
|
||||
for (let x = 0; x < 32; x++)
|
||||
ret[x] = this.api.vbGetProgramRegister(data.sim, x);
|
||||
data.transfers = [ ret.buffer ];
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(data) {
|
||||
data.response = u32(this.api.vbGetSystemRegister(data.sim, data.id));
|
||||
}
|
||||
|
||||
// Retrieve all CPU system registers (including PC)
|
||||
getSystemRegisters(data) {
|
||||
data.response = {
|
||||
adtre: u32(this.api.vbGetSystemRegister(data.sim, 25)),
|
||||
chcw : u32(this.api.vbGetSystemRegister(data.sim, 24)),
|
||||
ecr : u32(this.api.vbGetSystemRegister(data.sim, 4)),
|
||||
eipc : u32(this.api.vbGetSystemRegister(data.sim, 0)),
|
||||
eipsw: u32(this.api.vbGetSystemRegister(data.sim, 1)),
|
||||
fepc : u32(this.api.vbGetSystemRegister(data.sim, 2)),
|
||||
fepsw: u32(this.api.vbGetSystemRegister(data.sim, 3)),
|
||||
pc : u32(this.api.vbGetProgramCounter(data.sim )),
|
||||
pir : u32(this.api.vbGetSystemRegister(data.sim, 6)),
|
||||
psw : u32(this.api.vbGetSystemRegister(data.sim, 5)),
|
||||
tkcw : u32(this.api.vbGetSystemRegister(data.sim, 7)),
|
||||
[29] : u32(this.api.vbGetSystemRegister(data.sim, 29)),
|
||||
[30] : u32(this.api.vbGetSystemRegister(data.sim, 30)),
|
||||
[31] : u32(this.api.vbGetSystemRegister(data.sim, 31))
|
||||
};
|
||||
}
|
||||
|
||||
// Read bytes from the simulation
|
||||
read(data) {
|
||||
let ptr = this.api.Malloc(data.length);
|
||||
this.api.ReadBuffer(data.sim, ptr, data.address, data.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.length).slice();
|
||||
this.api.Free(ptr);
|
||||
data.response = {
|
||||
address: data.address,
|
||||
bytes : buffer
|
||||
};
|
||||
data.transfers = [ buffer.buffer ];
|
||||
}
|
||||
|
||||
// Attempt to execute until the following instruction
|
||||
runNext(data) {
|
||||
this.api.RunNext(data.sims[0], data.sims[1]);
|
||||
let pc = [ u32(this.api.vbGetProgramCounter(data.sims[0])) ];
|
||||
if (data.sims[1])
|
||||
pc.push(u32(this.api.vbGetProgramCounter(data.sims[1])));
|
||||
data.response = {
|
||||
pc: pc
|
||||
}
|
||||
}
|
||||
|
||||
// Specify a new value for PC
|
||||
setProgramCounter(data) {
|
||||
data.response =
|
||||
u32(this.api.vbSetProgramCounter(data.sim, data.value));
|
||||
}
|
||||
|
||||
// Specify a new value for a program register
|
||||
setProgramRegister(data) {
|
||||
data.response = this.api.vbSetProgramRegister
|
||||
(data.sim, data.id, data.value);
|
||||
}
|
||||
|
||||
// Specify a ROM buffer
|
||||
setROM(data) {
|
||||
let ptr = this.api.Malloc(data.rom.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.rom.length);
|
||||
for (let x = 0; x < data.rom.length; x++)
|
||||
buffer[x] = data.rom[x];
|
||||
data.response = !!this.api.SetROM(data.sim, ptr, data.rom.length);
|
||||
}
|
||||
|
||||
// Specify a new value for a system register
|
||||
setSystemRegister(data) {
|
||||
data.response = u32(this.api.vbSetSystemRegister
|
||||
(data.sim, data.id, data.value));
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
singleStep(data) {
|
||||
this.api.SingleStep(data.sims[0], data.sims[1]);
|
||||
let pc = [ u32(this.api.vbGetProgramCounter(data.sims[0])) ];
|
||||
if (data.sims[1])
|
||||
pc.push(u32(this.api.vbGetProgramCounter(data.sims[1])));
|
||||
data.response = {
|
||||
pc: pc
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe from frame data
|
||||
unsubscribe(data) {
|
||||
let sim = data.sim || 0;
|
||||
if (sim in this.subscriptions) {
|
||||
let subs = this.subscriptions[sim];
|
||||
delete subs[data.key];
|
||||
if (Object.keys(subs).length == 0)
|
||||
delete this.subscriptions[sim];
|
||||
}
|
||||
}
|
||||
|
||||
// Write bytes to the simulation
|
||||
write(data) {
|
||||
let ptr = this.api.Malloc(data.bytes.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.bytes.length);
|
||||
for (let x = 0; x < data.bytes.length; x++)
|
||||
buffer[x] = data.bytes[x];
|
||||
this.api.WriteBuffer(data.sim, ptr, data.address, data.bytes.length);
|
||||
this.api.Free(ptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Retrieve basic information for a row of disassembly
|
||||
dasmRow(sim, address, pc) {
|
||||
let bits = this.api.vbRead(sim, address, 3 /* VB_U16 */);
|
||||
let opcode = bits >> 10 & 63;
|
||||
let size = (
|
||||
opcode < 0b101000 || // Formats I through III
|
||||
opcode == 0b110010 || // Illegal
|
||||
opcode == 0b110110 // Illegal
|
||||
) ? 2 : 4;
|
||||
|
||||
// Establish row information
|
||||
let row = {
|
||||
address: address,
|
||||
bytes : [ bits & 0xFF, bits >> 8 ],
|
||||
size : u32(address + 2) == pc ? 2 : size
|
||||
};
|
||||
|
||||
// Read additional bytes
|
||||
if (size == 4) {
|
||||
bits = this.api.vbRead(sim, address + 2, 3 /* VB_U16 */);
|
||||
row.bytes.push(bits & 0xFF, bits >> 8);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
// Process subscriptions and send a message to the main thread
|
||||
doSubscriptions(sims) {
|
||||
let message = { subscriptions: {} };
|
||||
let transfers = [];
|
||||
|
||||
// Process all simulations
|
||||
for (let sim of sims) {
|
||||
|
||||
// There are no subscriptions for this sim
|
||||
if (!(sim in this.subscriptions))
|
||||
continue;
|
||||
|
||||
// Working variables
|
||||
let subs = message.subscriptions[sim] = {};
|
||||
|
||||
// Process all subscriptions
|
||||
for (let sub of Object.entries(this.subscriptions[sim])) {
|
||||
|
||||
// Run the command
|
||||
this[sub[1].command](sub[1]);
|
||||
|
||||
// Add the response to the message
|
||||
if (sub[1].response) {
|
||||
subs[sub[0]] = sub[1].response;
|
||||
delete sub[1].response;
|
||||
}
|
||||
|
||||
// Add the transferable objects to the message
|
||||
if (sub[1].transfers) {
|
||||
transfers.push(... sub[1].transfers);
|
||||
delete sub[1].transfers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Send the message to the main thread
|
||||
if (Object.keys(message).length != 0)
|
||||
postMessage(message, transfers);
|
||||
}
|
||||
|
||||
}();
|
||||
@@ -0,0 +1,151 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sim //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One simulated Virtual Boy
|
||||
class Sim {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(core, sim, index) {
|
||||
this.core = core;
|
||||
this.index = index;
|
||||
this.sim = sim;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Locate CPU instructions
|
||||
disassemble(target, row, rows, scroll, options = {}) {
|
||||
return this.core.send({
|
||||
command : "disassemble",
|
||||
row : row,
|
||||
rows : rows,
|
||||
scroll : scroll,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe,
|
||||
target : target
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all CPU program registers
|
||||
getProgramRegisters(options = {}) {
|
||||
return this.core.send({
|
||||
command : "getProgramRegisters",
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(id, options = {}) {
|
||||
return this.core.send({
|
||||
command : "getSystemRegister",
|
||||
id : id,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all CPU system registers (including PC)
|
||||
getSystemRegisters(options = {}) {
|
||||
return this.core.send({
|
||||
command : "getSystemRegisters",
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Read multiple bytes from the bus
|
||||
read(address, length, options = {}) {
|
||||
return this.core.send({
|
||||
address : address,
|
||||
command : "read",
|
||||
debug : !("debug" in options) || !!options.debug,
|
||||
length : length,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Specify a new value for PC
|
||||
setProgramCounter(value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setProgramCounter",
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Specify a new value for a program register
|
||||
setProgramRegister(id, value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setProgramRegister",
|
||||
id : id,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Specify the current ROM buffer
|
||||
setROM(rom, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setROM",
|
||||
rom : rom,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim
|
||||
}, [ rom.buffer ]);
|
||||
}
|
||||
|
||||
// Specify a new value for a system register
|
||||
setSystemRegister(id, value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setSystemRegister",
|
||||
id : id,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Ubsubscribe from frame data
|
||||
unsubscribe(key) {
|
||||
return this.core.unsubscribe(key, this.sim);
|
||||
}
|
||||
|
||||
// Write multiple bytes to the bus
|
||||
write(address, bytes, options = {}) {
|
||||
return this.core.send({
|
||||
address : address,
|
||||
command : "write",
|
||||
bytes : bytes,
|
||||
debug : !("debug" in options) || !!options.debug,
|
||||
refresh : !!options.refresh,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
},
|
||||
bytes instanceof ArrayBuffer ? [ bytes ] :
|
||||
bytes.buffer instanceof ArrayBuffer ? [ bytes.buffer ] :
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// The simulation has been destroyed
|
||||
destroy() {
|
||||
let sim = this.sim;
|
||||
this.core = null;
|
||||
this.sim = 0;
|
||||
return sim;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Sim };
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"id": "en-US",
|
||||
|
||||
"common": {
|
||||
"close" : "Close",
|
||||
"collapse" : "Collapse",
|
||||
"expand" : "Expand",
|
||||
"gotoPrompt": "Enter the address to go to:"
|
||||
},
|
||||
|
||||
"error": {
|
||||
"fileRead": "An error occurred when reading the file.",
|
||||
"romNotVB": "The selected file is not a Virtual Boy ROM."
|
||||
},
|
||||
|
||||
"app": {
|
||||
"title": "Virtual Boy Emulator",
|
||||
|
||||
"menu": {
|
||||
"_": "Application menu bar",
|
||||
|
||||
"file": {
|
||||
"_" : "File",
|
||||
"loadROM" : "Load ROM{sim}...",
|
||||
"debugMode": "Debug mode"
|
||||
},
|
||||
|
||||
"emulation": {
|
||||
"_" : "Emulation",
|
||||
"run" : "Run",
|
||||
"reset" : "Reset",
|
||||
"dualSims": "Dual sims",
|
||||
"linkSims": "Link sims"
|
||||
},
|
||||
|
||||
"debug": {
|
||||
"_" : "Debug{sim}",
|
||||
"console" : "Console",
|
||||
"memory" : "Memory",
|
||||
"cpu" : "CPU",
|
||||
"breakpoints" : "Breakpoints",
|
||||
"palettes" : "Palettes",
|
||||
"characters" : "Characters",
|
||||
"bgMaps" : "BG maps",
|
||||
"objects" : "Objects",
|
||||
"worlds" : "Worlds",
|
||||
"frameBuffers": "Frame buffers"
|
||||
},
|
||||
|
||||
"theme": {
|
||||
"_" : "Theme",
|
||||
"light" : "Light",
|
||||
"dark" : "Dark",
|
||||
"virtual": "Virtual"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"cpu": {
|
||||
"_" : "CPU{sim}",
|
||||
"float" : "Float",
|
||||
"hex" : "Hex",
|
||||
"signed" : "Signed",
|
||||
"splitHorizontal": "Program and system registers splitter",
|
||||
"splitVertical" : "Disassembler and registers splitter",
|
||||
"unsigned" : "Unsigned"
|
||||
},
|
||||
|
||||
"memory": {
|
||||
"_": "Memory{sim}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Global theme assets
|
||||
Bundle["app/theme/kiosk.css"].installStylesheet(true);
|
||||
await Bundle["app/theme/inconsolata.woff2"].installFont(
|
||||
"Inconsolata SemiExpanded Medium");
|
||||
await Bundle["app/theme/roboto.woff2"].installFont("Roboto");
|
||||
Bundle["app/theme/check.svg" ].installImage("tk-check" , "check.svg" );
|
||||
Bundle["app/theme/close.svg" ].installImage("tk-close" , "close.svg" );
|
||||
Bundle["app/theme/collapse.svg"].installImage("tk-collapse", "collapse.svg");
|
||||
Bundle["app/theme/expand.svg" ].installImage("tk-expand" , "expand.svg" );
|
||||
Bundle["app/theme/radio.svg" ].installImage("tk-radio" , "radio.svg" );
|
||||
Bundle["app/theme/scroll.svg" ].installImage("tk-scroll" , "scroll.svg" );
|
||||
|
||||
// Module imports
|
||||
import { Core } from /**/"./core/Core.js";
|
||||
import { Toolkit } from /**/"./toolkit/Toolkit.js";
|
||||
import { App } from /**/"./app/App.js";
|
||||
|
||||
// Begin application
|
||||
let dark = matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
let url = u=>u.startsWith("data:")?u:new URL(u,import.meta.url).toString();
|
||||
new App({
|
||||
core : await Core.create({ sims: 2 }),
|
||||
locale : navigator.language,
|
||||
standalone: true,
|
||||
theme : dark ? "dark" : "light",
|
||||
locales : [
|
||||
await (await fetch(url(/**/"./locale/en-US.json"))).json()
|
||||
],
|
||||
themes : {
|
||||
dark : Bundle["app/theme/dark.css" ].installStylesheet( dark),
|
||||
light : Bundle["app/theme/light.css" ].installStylesheet(!dark),
|
||||
virtual: Bundle["app/theme/virtual.css"].installStylesheet(false)
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Virtual Boy Emulator</title>
|
||||
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
|
||||
<script>window.a=async(b,c,d,e)=>{e=document.createElement('canvas');e.width=c;e.height=d;e=e.getContext('2d');e.drawImage(b,0,0);c=e.getImageData(0,0,c,d).data.filter((z,y)=>!(y&3));d=c.indexOf(0);Object.getPrototypeOf(a).constructor(String.fromCharCode(...c.slice(0,d)))(b,c,d)}</script>
|
||||
</head>
|
||||
<body>
|
||||
<img alt="" style="display: none;" onload="a(this,width,height)" src="">
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458 2.6458" version="1.1">
|
||||
<g>
|
||||
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 1.05832,1.653625 1.3229,-1.3229 v 0.66145 l -1.3229,1.3229 -0.79374,-0.79374 v -0.66145 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 459 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104167" version="1.1">
|
||||
<g>
|
||||
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 0.52916666,0.52916665 0.396875,0 0.52916664,0.52916665 0.5291667,-0.52916665 0.396875,0 0,0.396875 L 1.8520833,1.4552083 2.38125,1.984375 l 0,0.396875 -0.396875,0 L 1.4552083,1.8520834 0.92604166,2.38125 l -0.396875,0 0,-0.396875 L 1.0583333,1.4552083 0.52916666,0.92604165 Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 652 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168">
|
||||
<g>
|
||||
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="0.79375005" y="1.3229167" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 367 B |
@@ -0,0 +1,26 @@
|
||||
:root {
|
||||
--tk-control : #333333;
|
||||
--tk-control-active : #555555;
|
||||
--tk-control-border : #cccccc;
|
||||
--tk-control-highlight : #444444;
|
||||
--tk-control-shadow : #9b9b9b;
|
||||
--tk-control-text : #cccccc;
|
||||
--tk-desktop : #111111;
|
||||
--tk-selected : #008542;
|
||||
--tk-selected-blur : #325342;
|
||||
--tk-selected-blur-text : #ffffff;
|
||||
--tk-selected-text : #ffffff;
|
||||
--tk-splitter-focus : #ffffff99;
|
||||
--tk-window : #222222;
|
||||
--tk-window-blur-close : #d9aeae;
|
||||
--tk-window-blur-close-text: #eeeeee;
|
||||
--tk-window-blur-title : #9fafb9;
|
||||
--tk-window-blur-title2 : #c0b2ab;
|
||||
--tk-window-blur-title-text: #444444;
|
||||
--tk-window-close : #ee9999;
|
||||
--tk-window-close-text : #ffffff;
|
||||
--tk-window-text : #cccccc;
|
||||
--tk-window-title : #80ccff;
|
||||
--tk-window-title2 : #ffb894;
|
||||
--tk-window-title-text : #000000;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168">
|
||||
<g>
|
||||
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="1.3229167" y="0.79375005" />
|
||||
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="0.79375005" y="1.3229167" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 522 B |
@@ -0,0 +1,761 @@
|
||||
:root {
|
||||
--tk-font-dialog: "Roboto", sans-serif;
|
||||
--tk-font-mono : "Inconsolata SemiExpanded Medium", monospace;
|
||||
--tk-font-size : 12px;
|
||||
}
|
||||
|
||||
.tk {
|
||||
font-family: var(--tk-font-dialog);
|
||||
font-size : var(--tk-font-size);
|
||||
line-height: 1em;
|
||||
margin : 0;
|
||||
outline : none;
|
||||
padding : 0;
|
||||
}
|
||||
|
||||
table.tk {
|
||||
border : none;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.tk-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tk-app {
|
||||
/* Height managed through resize listener */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tk-mono {
|
||||
font-family: var(--tk-font-mono);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************** Button ***********************************/
|
||||
|
||||
.tk-button > * {
|
||||
background: var(--tk-control);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
box-shadow: 1px 1px 0 0 var(--tk-control-shadow);
|
||||
color : var(--tk-control-text);
|
||||
margin : 0 1px 1px 0;
|
||||
padding : 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tk-button:focus > * {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-button.active > * {
|
||||
box-shadow: none;
|
||||
margin : 1px 0 0 1px;
|
||||
}
|
||||
|
||||
.tk-button[aria-disabled="true"] > * {
|
||||
color: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************* Check Box *********************************/
|
||||
|
||||
.tk-checkbox {
|
||||
align-items: center;
|
||||
column-gap : 2px;
|
||||
}
|
||||
|
||||
.tk-checkbox .tk-icon {
|
||||
align-items: center;
|
||||
background : var(--tk-window);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
box-sizing : border-box;
|
||||
display : flex;
|
||||
height : 12px;
|
||||
width : 12px;
|
||||
}
|
||||
|
||||
.tk-checkbox .tk-icon:before {
|
||||
background : transparent;
|
||||
content : "";
|
||||
height : 100%;
|
||||
display : block;
|
||||
mask-image : var(--tk-check);
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : contain;
|
||||
width : 100%;
|
||||
-webkit-mask-image : var(--tk-check);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : contain;
|
||||
}
|
||||
|
||||
.tk-checkbox[aria-checked="true"] .tk-icon:before {
|
||||
background: var(--tk-window-text);
|
||||
}
|
||||
|
||||
.tk-checkbox:focus .tk-icon {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-checkbox[aria-checked="true"]:focus .tk-icon:before {
|
||||
background: var(--tk-control-text);
|
||||
}
|
||||
|
||||
.tk-checkbox.active:focus .tk-icon:before {
|
||||
background: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/****************************** Drop-Down List *******************************/
|
||||
|
||||
.tk-dropdown {
|
||||
background : var(--tk-window);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
border-radius: 0;
|
||||
color : var(--tk-window-text);
|
||||
margin : 0;
|
||||
padding : 1px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************* Menu Bar **********************************/
|
||||
|
||||
.tk-menu-bar {
|
||||
background : var(--tk-control);
|
||||
border-bottom: 1px solid var(--tk-control-border);
|
||||
color : var(--tk-control-text);
|
||||
column-gap : 1px;
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
padding : 2px;
|
||||
user-select : none;
|
||||
white-space : nowrap;
|
||||
}
|
||||
|
||||
.tk-menu {
|
||||
background : var(--tk-control);
|
||||
border : 1px solid var(--tk-control-border);
|
||||
box-shadow : 1px 1px 0 0 var(--tk-control-border);
|
||||
display : flex;
|
||||
flex-direction: column;
|
||||
padding : 3px;
|
||||
row-gap : 1px;
|
||||
}
|
||||
|
||||
.tk-menu-item > * {
|
||||
background: var(--tk-control);
|
||||
border : 1px solid transparent;
|
||||
column-gap: 4px;
|
||||
display : flex;
|
||||
margin : 0 1px 1px 0;
|
||||
padding : 3px;
|
||||
}
|
||||
|
||||
.tk-menu-item > * > .tk-icon {
|
||||
box-sizing: border-box;
|
||||
display : none;
|
||||
height : 1em;
|
||||
width : 1em;
|
||||
}
|
||||
|
||||
.tk-menu-item > * > .tk-text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.tk-menu-item[aria-disabled="true"] > * > .tk-text {
|
||||
color: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-menu-item:not(.active, [aria-disabled="true"]):hover > *,
|
||||
.tk-menu-item:not(.active):focus > * {
|
||||
border-color: var(--tk-control-shadow);
|
||||
box-shadow : 1px 1px 0 0 var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-menu.icons > .tk-menu-item > * > .tk-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tk-menu-item[role="menuitemcheckbox"] > * > .tk-icon {
|
||||
border: 1px solid var(--tk-control-border);
|
||||
}
|
||||
|
||||
.tk-menu-item[role="menuitemcheckbox"] > * > .tk-icon:before {
|
||||
background : transparent;
|
||||
content : "";
|
||||
height : 100%;
|
||||
display : block;
|
||||
mask-image : var(--tk-check);
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : contain;
|
||||
width : 100%;
|
||||
-webkit-mask-image : var(--tk-check);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : contain;
|
||||
}
|
||||
|
||||
.tk-menu-item[role="menuitemcheckbox"][aria-checked="true"]
|
||||
> * > .tk-icon:before {
|
||||
background: var(--tk-control-text);
|
||||
}
|
||||
|
||||
.tk-menu-item[role="menuitemcheckbox"][aria-disabled="true"]
|
||||
> * > .tk-icon {
|
||||
border: 1px solid var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-menu-item[role="menuitemcheckbox"][aria-disabled="true"]
|
||||
> * > .tk-icon:before {
|
||||
background: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-menu-item:not(.active):focus > * {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-menu-item.active > * {
|
||||
background : var(--tk-control-active);
|
||||
border-color: var(--tk-control-shadow);
|
||||
margin : 1px 0 0 1px;
|
||||
}
|
||||
|
||||
.tk-menu-separator {
|
||||
border : 0 solid var(--tk-control-shadow);
|
||||
border-width : 1px 0 0 0;
|
||||
height : 0;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*********************************** Radio ***********************************/
|
||||
|
||||
.tk-radio {
|
||||
align-items: center;
|
||||
column-gap : 2px;
|
||||
}
|
||||
|
||||
.tk-radio .tk-icon {
|
||||
align-items : center;
|
||||
background : var(--tk-window);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
border-radius: 50%;
|
||||
box-sizing : border-box;
|
||||
display : flex;
|
||||
height : 10px;
|
||||
width : 10px;
|
||||
}
|
||||
|
||||
.tk-radio .tk-icon:before {
|
||||
background : transparent;
|
||||
content : "";
|
||||
height : 100%;
|
||||
display : block;
|
||||
mask-image : var(--tk-radio);
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : contain;
|
||||
width : 100%;
|
||||
-webkit-mask-image : var(--tk-radio);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : contain;
|
||||
}
|
||||
|
||||
.tk-radio[aria-checked="true"] .tk-icon:before {
|
||||
background: var(--tk-window-text);
|
||||
}
|
||||
|
||||
.tk-radio:focus .tk-icon {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-radio[aria-checked="true"]:focus .tk-icon:before {
|
||||
background: var(--tk-control-text);
|
||||
}
|
||||
|
||||
.tk-radio.active[aria-checked="false"]:focus .tk-icon:before {
|
||||
background: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************** Scroll Bar *********************************/
|
||||
|
||||
.tk-scrollbar {
|
||||
background: var(--tk-control-highlight);
|
||||
box-shadow: 0 0 0 1px var(--tk-control-shadow) inset;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-thumb,
|
||||
.tk-scrollbar .tk-unit-down,
|
||||
.tk-scrollbar .tk-unit-up {
|
||||
background: var(--tk-control);
|
||||
border : 1px solid var(--tk-control-border);
|
||||
box-sizing: border-box;
|
||||
color : var(--tk-control-text);
|
||||
}
|
||||
|
||||
.tk-scrollbar:focus .tk-thumb,
|
||||
.tk-scrollbar:focus .tk-unit-down,
|
||||
.tk-scrollbar:focus .tk-unit-up {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-unit-down,
|
||||
.tk-scrollbar .tk-unit-up {
|
||||
height: 13px;
|
||||
width : 13px;
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-unit-down:before,
|
||||
.tk-scrollbar .tk-unit-up:before {
|
||||
background : currentColor;
|
||||
content : "";
|
||||
display : block;
|
||||
height : 100%;
|
||||
mask-image : var(--tk-scroll);
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : 100%;
|
||||
width : 100%;
|
||||
-webkit-mask-image : var(--tk-scroll);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : 100%;
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-orientation="horizontal"] .tk-unit-down:before {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-orientation="horizontal"] .tk-unit-up:before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-orientation="vertical"] .tk-unit-down:before {
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-orientation="vertical"] .tk-unit-up:before {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-unit-down.tk-active:before,
|
||||
.tk-scrollbar .tk-unit-up.tk-active:before {
|
||||
mask-size: calc(100% - 2px);
|
||||
-webkit-mask-size: calc(100% - 2px);
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-unit-down,
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-unit-up,
|
||||
.tk-scrollbar.tk-full .tk-unit-down,
|
||||
.tk-scrollbar.tk-full .tk-unit-up {
|
||||
background: var(--tk-control);
|
||||
border-color: var(--tk-control-shadow);
|
||||
color : var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-block-down,
|
||||
.tk-scrollbar .tk-block-up {
|
||||
background : var(--tk-control-highlight);
|
||||
border-color: var(--tk-control-shadow);
|
||||
border-style: solid;
|
||||
border-width: 0 1px;
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-orientation="horizontal"] .tk-block-down,
|
||||
.tk-scrollbar[aria-orientation="horizontal"] .tk-block-up {
|
||||
border-width: 1px 0;
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-block-down.tk-active,
|
||||
.tk-scrollbar .tk-block-up.tk-active {
|
||||
background: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-thumb,
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-block-down,
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-block-up,
|
||||
.tk-scrollbar.tk-full .tk-thumb,
|
||||
.tk-scrollbar.tk-full .tk-block-down,
|
||||
.tk-scrollbar.tk-full .tk-block-up {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************** Scroll Pane ********************************/
|
||||
|
||||
.tk-scrollpane {
|
||||
background: var(--tk-control);
|
||||
}
|
||||
|
||||
.tk-scrollpane > .tk-scrollbar {
|
||||
border: 0 solid var(--tk-control);
|
||||
}
|
||||
|
||||
.tk-scrollpane > .tk-scrollbar[aria-orientation="horizontal"] {
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
|
||||
.tk-scrollpane > .tk-scrollbar[aria-orientation="vertical"] {
|
||||
border-width: 0 0 0 1px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************** Split Pane *********************************/
|
||||
|
||||
.tk-splitpane > [role="separator"][aria-orientation="horizontal"] {
|
||||
cursor: ns-resize;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.tk-splitpane > [role="separator"][aria-orientation="vertical"] {
|
||||
cursor: ew-resize;
|
||||
width : 3px;
|
||||
}
|
||||
|
||||
.tk-splitpane > [role="separator"]:focus {
|
||||
background: var(--tk-splitter-focus);
|
||||
z-index : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************* Text Box **********************************/
|
||||
|
||||
.tk-textbox {
|
||||
background: var(--tk-window);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
color : var(--tk-window-text);
|
||||
margin : 0;
|
||||
padding : 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************** Windows **********************************/
|
||||
|
||||
.tk-desktop {
|
||||
background: var(--tk-desktop);
|
||||
}
|
||||
|
||||
.tk-window > * {
|
||||
border : 1px solid var(--tk-control-border);
|
||||
box-shadow: 1px 1px 0 0 var(--tk-control-border);
|
||||
margin : 0 1px 1px 0;
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-nw {left : -1px; top : -1px; height: 8px; width : 8px; }
|
||||
.tk-window > * > .tk-n {left : 7px; top : -1px; right : 8px; height: 3px; }
|
||||
.tk-window > * > .tk-ne {right: 0px; top : -1px; height: 8px; width : 8px; }
|
||||
.tk-window > * > .tk-w {left : -1px; top : 7px; width : 3px; bottom: 8px; }
|
||||
.tk-window > * > .tk-e {right: 0px; top : 7px; width : 3px; bottom: 8px; }
|
||||
.tk-window > * > .tk-sw {left : -1px; bottom: 0px; height: 8px; width : 8px; }
|
||||
.tk-window > * > .tk-s {left : 7px; bottom: 0px; right : 8px; height: 3px; }
|
||||
.tk-window > * > .tk-se {right: 0px; bottom: 0px; height: 8px; width : 8px; }
|
||||
|
||||
.tk-window > * > .tk-title {
|
||||
align-items : center;
|
||||
background : var(--tk-window-blur-title);
|
||||
border-bottom: 1px solid var(--tk-control-shadow);
|
||||
box-sizing : border-box;
|
||||
color : var(--tk-window-blur-title-text);
|
||||
overflow : hidden;
|
||||
padding : 1px;
|
||||
position : relative;
|
||||
}
|
||||
|
||||
.tk-window.two > * > .tk-title {
|
||||
background: var(--tk-window-blur-title2);
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-title .tk-text {
|
||||
cursor : default;
|
||||
flex-basis : 0;
|
||||
font-weight : bold;
|
||||
min-width : 0;
|
||||
overflow : hidden;
|
||||
padding : 1px 1px 1px calc(1em + 3px);
|
||||
text-align : center;
|
||||
text-overflow: ellipsis;
|
||||
user-select : none;
|
||||
white-space : nowrap;
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-title .tk-close {
|
||||
background: var(--tk-window-blur-close);
|
||||
border : 1px solid var(--tk-control-shadow);
|
||||
color : var(--tk-window-blur-close-text);
|
||||
height : calc(1em - 1px);
|
||||
margin : 1px 1px 1px 0;
|
||||
overflow : none;
|
||||
width : calc(1em - 1px);
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-title .tk-close:before {
|
||||
background : currentColor;
|
||||
content : "";
|
||||
display : block;
|
||||
height : 100%;
|
||||
width : 100%;
|
||||
mask-image : var(--tk-close);
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : 100%;
|
||||
-webkit-mask-image : var(--tk-close);
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : 100%;
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-title .tk-close.active:before {
|
||||
mask-size: calc(100% - 2px);
|
||||
-webkit-mask-size: calc(100% - 2px);
|
||||
}
|
||||
|
||||
.tk-window:focus-within > * > .tk-title {
|
||||
background: var(--tk-window-title);
|
||||
color : var(--tk-window-title-text);
|
||||
}
|
||||
|
||||
.tk-window.two:focus-within > * > .tk-title {
|
||||
background: var(--tk-window-title2);
|
||||
}
|
||||
|
||||
.tk-window:focus-within > * > .tk-title .tk-close {
|
||||
background: var(--tk-window-close);
|
||||
color : var(--tk-window-close-text);
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-client {
|
||||
background: var(--tk-control);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************ CPU ************************************/
|
||||
|
||||
.tk-cpu .tk-main {
|
||||
height: 100%;
|
||||
width : 100%;
|
||||
}
|
||||
|
||||
.tk-cpu .tk-main > .tk-a,
|
||||
.tk-cpu .tk-registers > .tk-a,
|
||||
.tk-cpu .tk-registers > .tk-b {
|
||||
box-shadow: 0 0 0 1px var(--tk-control),0 0 0 2px var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-cpu .tk-main > .tk-a { margin : 3px; }
|
||||
.tk-cpu .tk-main > [role="separator"] { margin : 1px -2px; }
|
||||
.tk-cpu .tk-main > .tk-b { margin : 3px; }
|
||||
.tk-cpu .tk-registers > .tk-a { margin-bottom: 3px; }
|
||||
.tk-cpu .tk-registers > [role="separator"] { margin : -2px; }
|
||||
.tk-cpu .tk-registers > .tk-b { margin-top : 3px; }
|
||||
|
||||
|
||||
|
||||
.tk-disassembler .tk-viewport {
|
||||
background: var(--tk-window);
|
||||
color : var(--tk-window-text);
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-metrics {
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.tk-disassembler .tk {
|
||||
cursor : default;
|
||||
font-family: var(--tk-font-mono);
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-bytes,
|
||||
.tk-disassembler .tk-mnemonic,
|
||||
.tk-disassembler .tk-operands {
|
||||
padding: 0 0 1px calc(1.2em - 1px);
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-address {
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-operands {
|
||||
padding-right: 1px;
|
||||
}
|
||||
|
||||
.tk-disassembler .tk-selected {
|
||||
background: var(--tk-selected-blur);
|
||||
color : var(--tk-selected-blur-text);
|
||||
}
|
||||
|
||||
.tk-disassembler:focus-within .tk-selected {
|
||||
background: var(--tk-selected);
|
||||
color : var(--tk-selected-text);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.tk-reglist .tk-viewport {
|
||||
background: var(--tk-window);
|
||||
color : var(--tk-window-text);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-list {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand {
|
||||
align-items : center;
|
||||
border-radius : 2px;
|
||||
display : flex;
|
||||
height : 11px;
|
||||
justify-content: center;
|
||||
width : 11px;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand:before {
|
||||
content : "";
|
||||
height : 100%;
|
||||
display : block;
|
||||
mask-position: center;
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : contain;
|
||||
width : 100%;
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : contain;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand:focus {
|
||||
background: var(--tk-control-active);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand[aria-expanded]:before {
|
||||
background: var(--tk-window-text);
|
||||
}
|
||||
.tk-reglist .tk-expand[aria-expanded]:focus:before {
|
||||
background: var(--tk-control-text);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand[aria-expanded="false"]:before {
|
||||
mask-image: var(--tk-expand);
|
||||
-webkit-mask-image: var(--tk-expand);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expand[aria-expanded="true"]:before {
|
||||
mask-image: var(--tk-collapse);
|
||||
-webkit-mask-image: var(--tk-collapse);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-name {
|
||||
padding: 0 0.5em 0 1px;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-textbox {
|
||||
background: transparent;
|
||||
border : none;
|
||||
padding : 0;
|
||||
width : 1.5em;
|
||||
}
|
||||
|
||||
.tk-reglist.tk-program .tk-textbox:not(.tk-mono) {
|
||||
text-align: right;
|
||||
width : 6em;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion {
|
||||
align-items : center;
|
||||
column-gap : 0.8em;
|
||||
margin-bottom: 2px;
|
||||
padding : 2px 0 0 1.5em;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-number .tk-label {
|
||||
align-items : center;
|
||||
display : flex;
|
||||
justify-content: center;
|
||||
min-width : 12px;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-checkbox[aria-disabled="true"][aria-checked="true"]
|
||||
.tk-icon:before {
|
||||
background: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-checkbox[aria-disabled="true"] .tk-contents,
|
||||
.tk-reglist .tk-expansion .tk-number[disabled] *,
|
||||
.tk-reglist .tk-expansion .tk-label[disabled],
|
||||
.tk-reglist .tk-expansion .tk-textbox[disabled] {
|
||||
color: var(--tk-control-shadow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************************** Memory ***********************************/
|
||||
|
||||
.tk-window .tk-memory {
|
||||
height: 100%;
|
||||
width : 100%;
|
||||
}
|
||||
|
||||
.tk-memory .tk-editor {
|
||||
box-shadow: 0 0 0 1px var(--tk-control),0 0 0 2px var(--tk-control-shadow);
|
||||
height : calc(100% - 6px);
|
||||
margin : 3px;
|
||||
width : calc(100% - 6px);
|
||||
}
|
||||
|
||||
.tk-memory .tk-viewport {
|
||||
background: var(--tk-window);
|
||||
color : var(--tk-window-text);
|
||||
}
|
||||
|
||||
.tk-memory .tk-metrics,
|
||||
.tk-memory .tk-view * {
|
||||
padding-bottom: 1px;
|
||||
cursor : default;
|
||||
font-family : var(--tk-font-mono);
|
||||
user-select : none;
|
||||
}
|
||||
|
||||
.tk-memory .tk-byte {
|
||||
border : 0 solid transparent;
|
||||
padding : 0 1px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tk-memory .tk-byte:not(.tk-15) {
|
||||
margin-right: calc(0.6em - 1px);
|
||||
}
|
||||
|
||||
.tk-memory .tk-address,
|
||||
.tk-memory .tk-byte.tk-7 {
|
||||
margin-right: calc(1.2em - 1px);
|
||||
}
|
||||
|
||||
.tk-memory .tk-byte.tk-selected {
|
||||
background: var(--tk-selected-blur);
|
||||
color : var(--tk-selected-blur-text);
|
||||
}
|
||||
|
||||
.tk-memory .tk-editor:focus-within .tk-byte.tk-selected {
|
||||
background: var(--tk-selected);
|
||||
color : var(--tk-selected-text);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
:root {
|
||||
--tk-control : #eeeeee;
|
||||
--tk-control-active : #cccccc;
|
||||
--tk-control-border : #000000;
|
||||
--tk-control-highlight : #f8f8f8;
|
||||
--tk-control-shadow : #6c6c6c;
|
||||
--tk-control-text : #000000;
|
||||
--tk-desktop : #cccccc;
|
||||
--tk-selected : #008542;
|
||||
--tk-selected-blur : #325342;
|
||||
--tk-selected-blur-text : #ffffff;
|
||||
--tk-selected-text : #ffffff;
|
||||
--tk-splitter-focus : #00000080;
|
||||
--tk-window : #ffffff;
|
||||
--tk-window-blur-close : #d9aeae;
|
||||
--tk-window-blur-close-text: #eeeeee;
|
||||
--tk-window-blur-title : #aac4d5;
|
||||
--tk-window-blur-title2 : #dbc4b8;
|
||||
--tk-window-blur-title-text: #444444;
|
||||
--tk-window-close : #ee9999;
|
||||
--tk-window-close-text : #ffffff;
|
||||
--tk-window-text : #000000;
|
||||
--tk-window-title : #80ccff;
|
||||
--tk-window-title2 : #ffb894;
|
||||
--tk-window-title-text : #000000;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458332 2.6458332" version="1.1">
|
||||
<g>
|
||||
<circle style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" cx="1.3229166" cy="1.3229166" r="0.66145831" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 361 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104167" version="1.1">
|
||||
<g>
|
||||
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 1.4552083,0.66145833 0.52916666,1.5874999 V 2.2489583 L 1.4552083,1.3229166 2.38125,2.2489583 V 1.5874999 Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 484 B |
@@ -0,0 +1,70 @@
|
||||
:root {
|
||||
--tk-control : #000000;
|
||||
--tk-control-active : #550000;
|
||||
--tk-control-border : #ff0000;
|
||||
--tk-control-highlight : #550000;
|
||||
--tk-control-shadow : #aa0000;
|
||||
--tk-control-text : #ff0000;
|
||||
--tk-desktop : #000000;
|
||||
--tk-selected : #550000;
|
||||
--tk-selected-blur : #550000;
|
||||
--tk-selected-blur-text : #ff0000;
|
||||
--tk-selected-text : #ff0000;
|
||||
--tk-splitter-focus : #ff000099;
|
||||
--tk-window : #000000;
|
||||
--tk-window-blur-close : #000000;
|
||||
--tk-window-blur-close-text: #aa0000;
|
||||
--tk-window-blur-title : #000000;
|
||||
--tk-window-blur-title2 : #000000;
|
||||
--tk-window-blur-title-text: #aa0000;
|
||||
--tk-window-close : #550000;
|
||||
--tk-window-close-text : #ff0000;
|
||||
--tk-window-text : #ff0000;
|
||||
--tk-window-title : #550000;
|
||||
--tk-window-title2 : #550000;
|
||||
--tk-window-title-text : #ff0000;
|
||||
}
|
||||
|
||||
input {
|
||||
filter: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxmaWx0ZXIgaWQ9InYiPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VHcmFwaGljIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMSAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMSAwIiAvPjwvZmlsdGVyPjwvc3ZnPg==#v");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************** Scroll Bar *********************************/
|
||||
|
||||
.tk-scrollbar .tk-thumb,
|
||||
.tk-scrollbar .tk-unit-down,
|
||||
.tk-scrollbar .tk-unit-up {
|
||||
background : #aa0000;
|
||||
border-color: #550000;
|
||||
color : #000000;
|
||||
}
|
||||
|
||||
.tk-scrollbar:focus .tk-thumb,
|
||||
.tk-scrollbar:focus .tk-unit-down,
|
||||
.tk-scrollbar:focus .tk-unit-up {
|
||||
background : #ff0000;
|
||||
border-color: #aa0000;
|
||||
}
|
||||
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-thumb,
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-unit-down,
|
||||
.tk-scrollbar[aria-disabled="true"] .tk-unit-up,
|
||||
.tk-scrollbar.tk-full .tk-thumb,
|
||||
.tk-scrollbar.tk-full .tk-unit-down,
|
||||
.tk-scrollbar.tk-full .tk-unit-up {
|
||||
background : #550000;
|
||||
border-color: #aa0000;
|
||||
color : #aa0000;
|
||||
}
|
||||
|
||||
.tk-scrollbar .tk-block-down,
|
||||
.tk-scrollbar .tk-block-up {
|
||||
background : #550000;
|
||||
border-color: #aa0000;
|
||||
}
|
||||
|
||||
.tk-window > * > .tk-client > .tk-memory {
|
||||
box-shadow: 0 0 0 1px #000000, 0 0 0 2px #ff0000;
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Button //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Push, toggle or radio button
|
||||
class Button extends Component {
|
||||
static Component = Component;
|
||||
|
||||
//////////////////////////////// Constants ////////////////////////////////
|
||||
|
||||
// Types
|
||||
static BUTTON = 0;
|
||||
static RADIO = 1;
|
||||
static TOGGLE = 2;
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-button",
|
||||
focusable: true,
|
||||
role : "button",
|
||||
tagName : "div",
|
||||
style : {
|
||||
display : "inline-block",
|
||||
userSelect: "none"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
options = options || {};
|
||||
this.attribute = options.attribute || "aria-pressed";
|
||||
this.group = null;
|
||||
this.isEnabled = null;
|
||||
this.isSelected = false;
|
||||
this.text = null;
|
||||
this.type = Button.BUTTON;
|
||||
|
||||
// Configure contents
|
||||
this.contents = document.createElement("div");
|
||||
this.append(this.contents);
|
||||
|
||||
// Configure component
|
||||
this.setEnabled(!("enabled" in options) || options.enabled);
|
||||
if ("group" in options)
|
||||
options.group.add(this);
|
||||
this.setText (options.text);
|
||||
this.setType (options.type);
|
||||
if ("selected" in options)
|
||||
this.setSelected(options.selected);
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("keydown" , e=>this.onKeyDown (e));
|
||||
this.addEventListener("pointerdown", e=>this.onPointerDown(e));
|
||||
this.addEventListener("pointermove", e=>this.onPointerMove(e));
|
||||
this.addEventListener("pointerup" , e=>this.onPointerUp (e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "Enter": // Fallthrough
|
||||
case " " :
|
||||
this.click();
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Pointer down
|
||||
onPointerDown(e) {
|
||||
this.focus();
|
||||
|
||||
// Error checking
|
||||
if (
|
||||
!this.isEnabled ||
|
||||
this.element.hasPointerCapture(e.pointerId) ||
|
||||
e.button != 0
|
||||
) return;
|
||||
|
||||
// Configure event
|
||||
this.element.setPointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Configure component
|
||||
this.element.classList.add("active");
|
||||
}
|
||||
|
||||
// Pointer move
|
||||
onPointerMove(e) {
|
||||
|
||||
// Error checking
|
||||
if (!this.element.hasPointerCapture(e.pointerId))
|
||||
return;
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Configure component
|
||||
this.element.classList[
|
||||
Toolkit.isInside(this.element, e) ? "add" : "remove"]("active");
|
||||
}
|
||||
|
||||
// Pointer up
|
||||
onPointerUp(e) {
|
||||
|
||||
// Error checking
|
||||
if (
|
||||
!this.isEnabled ||
|
||||
e.button != 0 ||
|
||||
!this.element.hasPointerCapture(e.pointerId)
|
||||
) return;
|
||||
|
||||
// Configure event
|
||||
this.element.releasePointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Configure component
|
||||
this.element.classList.remove("active");
|
||||
|
||||
// Item is an action
|
||||
let bounds = this.getBounds();
|
||||
if (this.menu == null && Toolkit.isInside(this.element, e))
|
||||
this.click();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Programmatically activate the button
|
||||
click() {
|
||||
if (this instanceof Toolkit.CheckBox)
|
||||
this.setSelected(this instanceof Toolkit.Radio||!this.isSelected);
|
||||
this.event("action");
|
||||
}
|
||||
|
||||
// Specify whether the button can be activated
|
||||
setEnabled(enabled) {
|
||||
this.isEnabled = enabled = !!enabled;
|
||||
this.setAttribute("aria-disabled", enabled ? null : "true");
|
||||
}
|
||||
|
||||
// Specify whether the toggle or radio button is selected
|
||||
setSelected(selected) {
|
||||
selected = !!selected;
|
||||
|
||||
// Take no action
|
||||
if (selected == this.isSelected)
|
||||
return;
|
||||
|
||||
// Processing by button type
|
||||
switch (this.type) {
|
||||
case Button.RADIO :
|
||||
if (selected && this.group != null)
|
||||
this.group.deselect();
|
||||
// Fallthrough
|
||||
case Button.TOGGLE:
|
||||
this.isSelected = selected;
|
||||
this.setAttribute(this.attribute, selected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Specify the widget's display text
|
||||
setText(text) {
|
||||
this.text = (text || "").toString().trim();
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify what kind of button this is
|
||||
setType(type) {
|
||||
switch (type) {
|
||||
case Button.BUTTON:
|
||||
this.type = type;
|
||||
this.setAttribute(this.attribute, null);
|
||||
this.setSelected(false);
|
||||
break;
|
||||
case Button.RADIO : // Fallthrough
|
||||
case Button.TOGGLE:
|
||||
this.type = type;
|
||||
this.setAttribute(this.attribute, this.isSelected);
|
||||
this.setSelected(this.isSelected);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
super.translate();
|
||||
if (this.contents != null)
|
||||
this.contents.innerText = this.gui.translate(this.text, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CheckBox //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// On/off toggle box
|
||||
class CheckBox extends Button {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(app, options) {
|
||||
|
||||
// Default options override
|
||||
let uptions = {};
|
||||
Object.assign(uptions, options || {});
|
||||
for (let entry of Object.entries({
|
||||
attribute: "aria-checked",
|
||||
className: "tk tk-checkbox",
|
||||
role : "checkbox",
|
||||
style : {},
|
||||
type : Button.TOGGLE
|
||||
})) if (!(entry[0] in uptions))
|
||||
uptions[entry[0]] = entry[1];
|
||||
|
||||
// Default styles override
|
||||
for (let entry of Object.entries({
|
||||
display : "inline-grid",
|
||||
gridTemplateColumns: "max-content auto"
|
||||
})) if (!(entry[0] in uptions.style))
|
||||
uptions.style[entry[0]] = entry[1];
|
||||
|
||||
// Component overrides
|
||||
super(app, uptions);
|
||||
this.contents.classList.add("tk-contents");
|
||||
|
||||
// Configure icon
|
||||
this.icon = document.createElement("div");
|
||||
this.icon.className = "tk tk-icon";
|
||||
this.icon.setAttribute("aria-hidden", "true");
|
||||
this.prepend(this.icon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Radio //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Single selection box
|
||||
class Radio extends CheckBox {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(app, options) {
|
||||
|
||||
// Default options override
|
||||
let uptions = {};
|
||||
Object.assign(uptions, options || {});
|
||||
for (let entry of Object.entries({
|
||||
className: "tk tk-radio",
|
||||
role : "radio",
|
||||
type : Button.RADIO
|
||||
})) if (!(entry[0] in uptions))
|
||||
uptions[entry[0]] = entry[1];
|
||||
|
||||
// Component overrides
|
||||
super(app, uptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Group //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Radio button or menu item group
|
||||
class Group extends Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(app) {
|
||||
super(app, {
|
||||
tagName: "div",
|
||||
style : {
|
||||
height : "0",
|
||||
position: "absolute",
|
||||
width : "0"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Add an item
|
||||
add(item) {
|
||||
|
||||
// Error checking
|
||||
if (!Toolkit.isComponent(item) || this.items.indexOf(item) != -1)
|
||||
return item;
|
||||
|
||||
// Configure component
|
||||
this.setAttribute("role",
|
||||
item instanceof Toolkit.Radio ? "radiogroup" : "group");
|
||||
|
||||
// Configure item
|
||||
if (item.group != null)
|
||||
item.group.remove(item);
|
||||
item.group = this;
|
||||
|
||||
// Add the item to the collection
|
||||
item.id = item.id || Toolkit.id();
|
||||
this.items.push(item);
|
||||
this.setAttribute("aria-owns", this.items.map(i=>i.id).join(" "));
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// Remove all items
|
||||
clear() {
|
||||
this.items.splice();
|
||||
this.setAttribute("aria-owns", "");
|
||||
}
|
||||
|
||||
// Un-check all items in the group
|
||||
deselect() {
|
||||
for (let item of this.items)
|
||||
if (item.isSelected && "setSelected" in item)
|
||||
item.setSelected(false);
|
||||
}
|
||||
|
||||
// Remove an item
|
||||
remove(item) {
|
||||
|
||||
// Error checking
|
||||
let index = this.items.indexOf(item);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
// Remove the item from the collection
|
||||
this.items.splice(index, 1);
|
||||
this.setAttribute("aria-owns", this.items.map(i=>i.id).join(" "));
|
||||
item.group = null;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { Button, CheckBox, Group, Radio };
|
||||
@@ -0,0 +1,312 @@
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Component //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Abstract class representing a distinct UI element
|
||||
class Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options, defaults) {
|
||||
|
||||
// Configure instance fields
|
||||
this.children = [];
|
||||
this.gui = gui || this;
|
||||
this.label = null;
|
||||
this.resizeObserver = null;
|
||||
this.substitutions = {};
|
||||
this.toolTip = null;
|
||||
|
||||
// Configure default options
|
||||
let uptions = options || {};
|
||||
options = {};
|
||||
Object.assign(options, uptions);
|
||||
options.style = options.style || {};
|
||||
defaults = defaults || {};
|
||||
defaults.style = defaults.style || {};
|
||||
for (let key of Object.keys(defaults))
|
||||
if (!(key in options))
|
||||
options[key] = defaults[key];
|
||||
for (let key of Object.keys(defaults.style))
|
||||
if (!(key in options.style))
|
||||
options.style[key] = defaults.style[key];
|
||||
this.visibility = !!options.visibility;
|
||||
|
||||
// Configure element
|
||||
this.element = document.createElement(
|
||||
("tagName" in options ? options.tagName : null) || "div");
|
||||
if (Object.keys(options.style).length != 0)
|
||||
Object.assign(this.element.style, options.style);
|
||||
if ("className" in options && options.className)
|
||||
this.element.className = options.className;
|
||||
if ("focusable" in options)
|
||||
this.setFocusable(options.focusable, options.tabStop);
|
||||
if ("id" in options)
|
||||
this.setId(options.id);
|
||||
if ("role" in options && options.role )
|
||||
this.element.setAttribute("role", options.role);
|
||||
if ("visible" in options)
|
||||
this.setVisible(options.visible);
|
||||
|
||||
// Configure component
|
||||
this.setAttribute("name", options.name || "");
|
||||
this.setLabel (options.label || "");
|
||||
this.setToolTip (options.toolTip || "");
|
||||
|
||||
// Configure substitutions
|
||||
if ("substitutions" in options) {
|
||||
for (let sub of Object.entries(options.substitutions))
|
||||
this.setSubstitution(sub[0], sub[1], true);
|
||||
this.translate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Add a child component
|
||||
add(component) {
|
||||
|
||||
// The component is already a child of this component
|
||||
let index = this.children.indexOf(component);
|
||||
if (index != -1)
|
||||
return index;
|
||||
|
||||
// The component has a different parent already
|
||||
if (component.parent != null)
|
||||
component.parent.remove(component);
|
||||
|
||||
// Add the child component to this component
|
||||
component.parent = this;
|
||||
this.children.push(component);
|
||||
if ("addHook" in this)
|
||||
this.addHook(component);
|
||||
else this.append(component);
|
||||
if ("addedHook" in component)
|
||||
component.addedHook(this);
|
||||
return this.children.length - 1;
|
||||
}
|
||||
|
||||
// Listen for events
|
||||
addEventListener(type, listener, useCapture) {
|
||||
let callback = e=>{
|
||||
e.component = this;
|
||||
return listener(e);
|
||||
};
|
||||
|
||||
// Register the listener for the event type
|
||||
this.element.addEventListener(type, callback, useCapture);
|
||||
|
||||
// Listen for resize events on the element
|
||||
if (type == "resize" && this.resizeObserver == null) {
|
||||
this.resizeObserver = new ResizeObserver(
|
||||
()=>this.event("resize"));
|
||||
this.resizeObserver.observe(this.element);
|
||||
}
|
||||
|
||||
return callback;
|
||||
}
|
||||
|
||||
// Add a DOM element as a sibling after this component
|
||||
after(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.element.after(element);
|
||||
}
|
||||
|
||||
// Add a DOM element to the end of this component's children
|
||||
append(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.element.append(element);
|
||||
}
|
||||
|
||||
// Add a DOM element as a sibling before this component
|
||||
before(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.element.before(element);
|
||||
}
|
||||
|
||||
// Request non-focus on this component
|
||||
blur() {
|
||||
this.element.blur();
|
||||
}
|
||||
|
||||
// Determine whether this component contains another or an element
|
||||
contains(child) {
|
||||
|
||||
// Child is an element
|
||||
if (child instanceof Element)
|
||||
return this.element.contains(child);
|
||||
|
||||
// Child is a component
|
||||
for (let component = child; component; component = component.parent)
|
||||
if (component == this)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Request focus on the component
|
||||
focus() {
|
||||
this.element.focus();
|
||||
}
|
||||
|
||||
// Retrieve the current DOM position of the element
|
||||
getBounds() {
|
||||
return this.element.getBoundingClientRect();
|
||||
}
|
||||
|
||||
// Determine whether this component currently has focus
|
||||
hasFocus() {
|
||||
return document.activeElement == this.element;
|
||||
}
|
||||
|
||||
// Determine whether the component is visible
|
||||
isVisible() {
|
||||
|
||||
// Common visibility test
|
||||
if (
|
||||
!document.contains(this.element) ||
|
||||
this.parent && !this.parent.isVisible()
|
||||
) return false;
|
||||
|
||||
// Overridden visibility test
|
||||
if ("visibleHook" in this) {
|
||||
if (!this.visibleHook())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default visibility test
|
||||
else {
|
||||
let style = getComputedStyle(this.element);
|
||||
if (style.display == "none" || style.visibility == "hidden")
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a DOM element to the beginning of this component's children
|
||||
prepend(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.element.prepend(element);
|
||||
}
|
||||
|
||||
// Remove a child component
|
||||
remove(component) {
|
||||
let index = this.children.indexOf(component);
|
||||
|
||||
// The component does not belong to this component
|
||||
if (index == -1)
|
||||
return -1;
|
||||
|
||||
// Remove the child component from this component
|
||||
this.children.splice(index, 1);
|
||||
if ("removeHook" in this)
|
||||
this.removeHook(component);
|
||||
else component.element.remove();
|
||||
if ("removedHook" in component)
|
||||
component.removedHook(this);
|
||||
return index;
|
||||
}
|
||||
|
||||
// Remove an event listener
|
||||
removeEventListener(type, listener, useCapture) {
|
||||
this.element.removeEventListener(type, listener, useCapture);
|
||||
}
|
||||
|
||||
// Specify an HTML attribute's value
|
||||
setAttribute(name, value) {
|
||||
value =
|
||||
value === false ? false :
|
||||
value === null || value === undefined ? "" :
|
||||
value.toString().trim()
|
||||
;
|
||||
if (value === "")
|
||||
this.element.removeAttribute(name);
|
||||
else this.element.setAttribute(name, value);
|
||||
}
|
||||
|
||||
// Specify whether or not the element is focusable
|
||||
setFocusable(focusable, tabStop) {
|
||||
if (!focusable)
|
||||
this.element.removeAttribute("tabindex");
|
||||
else this.element.setAttribute("tabindex",
|
||||
tabStop || tabStop === undefined ? "0" : "-1");
|
||||
}
|
||||
|
||||
// Specify a localization key for the accessible name label
|
||||
setLabel(key) {
|
||||
this.label = key;
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify the DOM Id for this element
|
||||
setId(id) {
|
||||
this.id = id = id || null;
|
||||
this.setAttribute("id", id);
|
||||
}
|
||||
|
||||
// Specify text to substitute within localized contexts
|
||||
setSubstitution(key, text, noTranslate) {
|
||||
let ret = this.substitutions[key] || null;
|
||||
|
||||
// Providing new text
|
||||
if (text !== null)
|
||||
this.substitutions[key] = text.toString();
|
||||
|
||||
// Removing an association
|
||||
else if (key in this.substitutions)
|
||||
delete this.substitutions[key];
|
||||
|
||||
// Update display text
|
||||
if (!noTranslate)
|
||||
this.translate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Specify a localization key for the tool tip text
|
||||
setToolTip(key) {
|
||||
this.toolTip = key;
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify whether the component is visible
|
||||
setVisible(visible) {
|
||||
let prop = this.visibility ? "visibility" : "display";
|
||||
if (!!visible)
|
||||
this.element.style.removeProperty(prop);
|
||||
else this.element.style[prop] = this.visibility ? "hidden" : "none";
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Dispatch an event
|
||||
event(type, fields) {
|
||||
this.element.dispatchEvent(Toolkit.event(type, this, fields));
|
||||
}
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
if (this.label)
|
||||
this.setAttribute("aria-label", this.gui.translate(this.label, this));
|
||||
if (this.toolTip)
|
||||
this.setAttribute("title", this.gui.translate(this.toolTip, this));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
export { Component };
|
||||
@@ -0,0 +1,125 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DropDown //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Text entry field
|
||||
class DropDown extends Component {
|
||||
static Component = Component;
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-dropdown",
|
||||
tagName : "select"
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.isEnabled = null;
|
||||
this.options = [];
|
||||
|
||||
// Configure component
|
||||
options = options || {};
|
||||
this.setEnabled(!("enabled" in options) || options.enabled);
|
||||
if ("options" in options)
|
||||
this.setOptions(options.options);
|
||||
this.setSelectedIndex(
|
||||
("selectedIndex" in options ? options : this).selectedIndex);
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("keydown" , e=>e.stopPropagation());
|
||||
this.addEventListener("pointerdown", e=>e.stopPropagation());
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Programmatically change the selection
|
||||
change() {
|
||||
this.element.dispatchEvent(this.event("input"));
|
||||
}
|
||||
|
||||
// Retrieve the current selection index
|
||||
getSelectedIndex() {
|
||||
return this.element.selectedIndex;
|
||||
}
|
||||
|
||||
// Specify whether the button can be activated
|
||||
setEnabled(enabled) {
|
||||
this.isEnabled = enabled = !!enabled;
|
||||
this.setAttribute("disabled", enabled ? null : "true");
|
||||
}
|
||||
|
||||
// Specify the list contents
|
||||
setOptions(options) {
|
||||
|
||||
// Error checking
|
||||
if (!Array.isArray(options))
|
||||
return;
|
||||
|
||||
// Erase the list of options
|
||||
this.options.splice(0);
|
||||
this.element.replaceChildren();
|
||||
|
||||
// Add options from the input
|
||||
for (let option of options) {
|
||||
if (typeof option != "string")
|
||||
continue;
|
||||
this.options.push(option);
|
||||
this.element.add(document.createElement("option"));
|
||||
}
|
||||
|
||||
// Update the display text
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify the current selection
|
||||
setSelectedIndex(index) {
|
||||
|
||||
// Error checking
|
||||
if (typeof index != "number" || isNaN(index))
|
||||
return this.element.selectedIndex;
|
||||
index = Math.round(index);
|
||||
if (index < -1 || index >= this.options.length)
|
||||
return this.element.selectedIndex;
|
||||
|
||||
// Configure element and instance fields
|
||||
return this.element.selectedIndex = index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
super.translate();
|
||||
|
||||
// Error checking
|
||||
if (!this.options)
|
||||
return;
|
||||
|
||||
// Update the list items
|
||||
for (let x = 0; x < this.options.length; x++) {
|
||||
this.element.item(x).innerText =
|
||||
this.gui.translate(this.options[x], this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { DropDown };
|
||||
@@ -0,0 +1,748 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Menu //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Pop-up menu container, child of MenuItem
|
||||
class Menu extends Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className : "tk tk-menu",
|
||||
role : "menu",
|
||||
tagName : "div",
|
||||
visibility: true,
|
||||
visible : false,
|
||||
style : {
|
||||
position: "absolute",
|
||||
}
|
||||
});
|
||||
|
||||
// Trap pointer events
|
||||
this.addEventListener("pointerdown", e=>{
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Replacement behavior for parent.add()
|
||||
addedHook(parent) {
|
||||
this.setAttribute("aria-labelledby", parent.id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MenuSeparator //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Separator between groups of menu items
|
||||
class MenuSeparator extends Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-menu-separator",
|
||||
role : "separator",
|
||||
tagName : "div"
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MenuItem //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Individual menu selection
|
||||
class MenuItem extends Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-menu-item",
|
||||
focusable: true,
|
||||
tabStop : false,
|
||||
tagName : "div"
|
||||
});
|
||||
options = options || {};
|
||||
|
||||
// Configure instance fields
|
||||
this.isEnabled = null;
|
||||
this.isExpanded = false;
|
||||
this.menu = null;
|
||||
this.menuBar = null;
|
||||
this.text = null;
|
||||
this.type = null;
|
||||
|
||||
// Configure element
|
||||
this.contents = document.createElement("div");
|
||||
this.append(this.contents);
|
||||
this.eicon = document.createElement("div");
|
||||
this.eicon.className = "tk tk-icon";
|
||||
this.contents.append(this.eicon);
|
||||
this.etext = document.createElement("div");
|
||||
this.etext.className = "tk tk-text";
|
||||
this.contents.append(this.etext);
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("blur" , e=>this.onBlur (e));
|
||||
this.addEventListener("keydown" , e=>this.onKeyDown (e));
|
||||
this.addEventListener("pointerdown", e=>this.onPointerDown(e));
|
||||
this.addEventListener("pointermove", e=>this.onPointerMove(e));
|
||||
this.addEventListener("pointerup" , e=>this.onPointerUp (e));
|
||||
|
||||
// Configure widget
|
||||
this.gui.localize(this);
|
||||
this.setEnabled("enabled" in options ? !!options.enabled : true);
|
||||
this.setId (Toolkit.id());
|
||||
this.setText (options.text);
|
||||
this.setType (options.type, options.checked);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Focus lost
|
||||
onBlur(e) {
|
||||
|
||||
// An item in a different menu is receiving focus
|
||||
if (this.menu != null) {
|
||||
if (
|
||||
!this .contains(e.relatedTarget) &&
|
||||
!this.menu.contains(e.relatedTarget)
|
||||
) this.setExpanded(false);
|
||||
}
|
||||
|
||||
// Item is an action
|
||||
else if (e.component == this)
|
||||
this.element.classList.remove("active");
|
||||
|
||||
// Simulate a bubbling event sequence
|
||||
if (this.parent)
|
||||
this.parent.onBlur(e);
|
||||
}
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
|
||||
case "ArrowDown":
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Top-level: open the menu and focus its first item
|
||||
if (this.parent == this.menuBar) {
|
||||
if (this.menu == null)
|
||||
return;
|
||||
this.setExpanded(true);
|
||||
this.listItems()[0].focus();
|
||||
}
|
||||
|
||||
// Sub-menu: cycle to the next sibling
|
||||
else {
|
||||
let items = this.parent.listItems();
|
||||
items[(items.indexOf(this) + 1) % items.length].focus();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "ArrowLeft":
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Sub-menu: close and focus parent
|
||||
if (
|
||||
this.parent != this.menuBar &&
|
||||
this.parent.parent != this.menuBar
|
||||
) {
|
||||
this.parent.setExpanded(false);
|
||||
this.parent.focus();
|
||||
}
|
||||
|
||||
// Top-level: cycle to previous sibling
|
||||
else {
|
||||
let menu = this.parent == this.menuBar ?
|
||||
this : this.parent;
|
||||
let items = this.menuBar.listItems();
|
||||
let prev = items[(items.indexOf(menu) +
|
||||
items.length - 1) % items.length];
|
||||
if (menu.isExpanded)
|
||||
prev.setExpanded(true);
|
||||
prev.focus();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "ArrowRight":
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Sub-menu: open the menu and focus its first item
|
||||
if (this.menu != null && this.parent != this.menuBar) {
|
||||
this.setExpanded(true);
|
||||
(this.listItems()[0] || this).focus();
|
||||
}
|
||||
|
||||
// Top level: cycle to next sibling
|
||||
else {
|
||||
let menu = this;
|
||||
while (menu.parent != this.menuBar)
|
||||
menu = menu.parent;
|
||||
let expanded = this.menuBar.expandedMenu() != null;
|
||||
let items = this.menuBar.listItems();
|
||||
let next = items[(items.indexOf(menu) + 1) % items.length];
|
||||
next.focus();
|
||||
if (expanded)
|
||||
next.setExpanded(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Top-level: open the menu and focus its last item
|
||||
if (this.parent == this.menuBar) {
|
||||
if (this.menu == null)
|
||||
return;
|
||||
this.setExpanded(true);
|
||||
let items = this.listItems();
|
||||
(items[items.length - 1] || this).focus();
|
||||
}
|
||||
|
||||
// Sub-menu: cycle to previous sibling
|
||||
else {
|
||||
let items = this.parent.listItems();
|
||||
items[(items.indexOf(this) +
|
||||
items.length - 1) % items.length].focus();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "End":
|
||||
{
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Focus last sibling
|
||||
let expanded = this.isExpanded &&
|
||||
this.parent == this.menuBar;
|
||||
let items = this.parent.listItems();
|
||||
let last = items[items.length - 1] || this;
|
||||
last.focus();
|
||||
if (expanded)
|
||||
last.setExpanded(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case "Enter":
|
||||
case " ":
|
||||
|
||||
// Do nothing
|
||||
if (!this.isEnabled)
|
||||
break;
|
||||
|
||||
// Action item: activate the menu item
|
||||
if (this.menu == null)
|
||||
this.activate(this.type == "check" && e.key == " ");
|
||||
|
||||
// Sub-menu: open the menu and focus its first item
|
||||
else {
|
||||
this.setExpanded(true);
|
||||
let items = this.listItems();
|
||||
if (items[0])
|
||||
items[0].focus();
|
||||
}
|
||||
break;
|
||||
|
||||
case "Escape":
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Top-level (not specified by WAI-ARIA)
|
||||
if (this.parent == this.menuBar) {
|
||||
if (this.isExpanded)
|
||||
this.setExpanded(false);
|
||||
else this.menuBar.exit();
|
||||
}
|
||||
|
||||
// Sub-menu: close and focus parent
|
||||
else {
|
||||
this.parent.setExpanded(false);
|
||||
this.parent.focus();
|
||||
}
|
||||
break;
|
||||
|
||||
case "Home":
|
||||
{
|
||||
|
||||
// Error checking
|
||||
if (!this.parent)
|
||||
break;
|
||||
|
||||
// Focus first sibling
|
||||
let expanded = this.isExpanded &&
|
||||
this.parent == this.menuBar;
|
||||
let first = this.parent.listItems()[0] || this;
|
||||
first.focus();
|
||||
if (expanded)
|
||||
first.setExpanded(true);
|
||||
}
|
||||
break;
|
||||
|
||||
// Do not handle the event
|
||||
default: return;
|
||||
}
|
||||
|
||||
// The event was handled
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Pointer press
|
||||
onPointerDown(e) {
|
||||
this.focus();
|
||||
|
||||
// Error checking
|
||||
if (
|
||||
!this.isEnabled ||
|
||||
this.element.hasPointerCapture(e.pointerId) ||
|
||||
e.button != 0
|
||||
) return;
|
||||
|
||||
// Configure event
|
||||
if (this.menu == null)
|
||||
this.element.setPointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Configure component
|
||||
if (this.menu != null)
|
||||
this.setExpanded(!this.isExpanded);
|
||||
else this.element.classList.add("active");
|
||||
}
|
||||
|
||||
// Pointer move
|
||||
onPointerMove(e) {
|
||||
|
||||
// Hovering over a menu when a sibling menu is already open
|
||||
let expanded = this.parent && this.parent.expandedMenu();
|
||||
if (this.menu != null && expanded != null && expanded != this) {
|
||||
|
||||
// Configure component
|
||||
this.setExpanded(true);
|
||||
this.focus();
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not dragging
|
||||
if (!this.element.hasPointerCapture(e.pointerId))
|
||||
return;
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Not an action item
|
||||
if (this.menu != null)
|
||||
return;
|
||||
|
||||
// Check if the cursor is within the bounds of the component
|
||||
this.element.classList[
|
||||
Toolkit.isInside(this.element, e) ? "add" : "remove"]("active");
|
||||
}
|
||||
|
||||
// Pointer release
|
||||
onPointerUp(e) {
|
||||
|
||||
// Error checking
|
||||
if (
|
||||
!this.isEnabled ||
|
||||
e.button != 0 ||
|
||||
(this.parent && this.parent.hasFocus() ?
|
||||
this.menu != null :
|
||||
!this.element.hasPointerCapture(e.pointerId)
|
||||
)
|
||||
) return;
|
||||
|
||||
// Configure event
|
||||
this.element.releasePointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Item is an action
|
||||
let bounds = this.getBounds();
|
||||
if (this.menu == null && Toolkit.isInside(this.element, e))
|
||||
this.activate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Invoke an action command
|
||||
activate(noExit) {
|
||||
if (this.menu != null)
|
||||
return;
|
||||
|
||||
if (this.type == "check")
|
||||
this.setChecked(!this.isChecked);
|
||||
|
||||
if (!noExit)
|
||||
this.menuBar.exit();
|
||||
|
||||
this.element.dispatchEvent(Toolkit.event("action", this));
|
||||
}
|
||||
|
||||
// Add a separator between groups of menu items
|
||||
addSeparator(options) {
|
||||
let sep = new Toolkit.MenuSeparator(this, options);
|
||||
this.add(sep);
|
||||
return sep;
|
||||
}
|
||||
|
||||
// Produce a list of child items
|
||||
listItems(invisible) {
|
||||
return this.children.filter(c=>
|
||||
c instanceof Toolkit.MenuItem &&
|
||||
(invisible || c.isVisible())
|
||||
);
|
||||
}
|
||||
|
||||
// Specify whether the menu item is checked
|
||||
setChecked(checked) {
|
||||
if (this.type != "check")
|
||||
return;
|
||||
this.isChecked = !!checked;
|
||||
this.setAttribute("aria-checked", this.isChecked);
|
||||
}
|
||||
|
||||
// Specify whether the menu item can be activated
|
||||
setEnabled(enabled) {
|
||||
this.isEnabled = enabled = !!enabled;
|
||||
this.setAttribute("aria-disabled", enabled ? null : "true");
|
||||
if (!enabled)
|
||||
this.setExpanded(false);
|
||||
}
|
||||
|
||||
// Specify whether the sub-menu is open
|
||||
setExpanded(expanded) {
|
||||
|
||||
// State is not changing
|
||||
expanded = !!expanded;
|
||||
if (this.menu == null || expanded === this.isExpanded)
|
||||
return;
|
||||
|
||||
// Position the sub-menu
|
||||
if (expanded) {
|
||||
let bndGUI = this.gui .getBounds();
|
||||
let bndMenu = this.menu.getBounds();
|
||||
let bndThis = this .getBounds();
|
||||
let bndParent = !this.parent ? bndThis : (
|
||||
this.parent == this.menuBar ? this.parent : this.parent.menu
|
||||
).getBounds();
|
||||
this.menu.element.style.left = Math.max(0,
|
||||
Math.min(
|
||||
(this.parent && this.parent == this.menuBar ?
|
||||
bndThis.left : bndThis.right) - bndParent.left,
|
||||
bndGUI.right - bndMenu.width
|
||||
)
|
||||
) + "px";
|
||||
this.menu.element.style.top = Math.max(0,
|
||||
Math.min(
|
||||
(this.parent && this.parent == this.menuBar ?
|
||||
bndThis.bottom : bndThis.top) - bndParent.top,
|
||||
bndGUI.bottom - bndMenu.height
|
||||
)
|
||||
) + "px";
|
||||
}
|
||||
|
||||
// Close all open sub-menus
|
||||
else for (let child of this.listItems())
|
||||
child.setExpanded(false);
|
||||
|
||||
// Configure component
|
||||
this.isExpanded = expanded;
|
||||
this.setAttribute("aria-expanded", expanded);
|
||||
this.menu.setVisible(expanded);
|
||||
if (expanded)
|
||||
this.element.classList.add("active");
|
||||
else this.element.classList.remove("active");
|
||||
}
|
||||
|
||||
// Specify the widget's display text
|
||||
setText(text) {
|
||||
this.text = (text || "").toString().trim();
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify what kind of menu item this is
|
||||
setType(type, arg) {
|
||||
this.type = type = (type || "").toString().trim() || "normal";
|
||||
switch (type) {
|
||||
case "check":
|
||||
this.setAttribute("role", "menuitemcheckbox");
|
||||
this.setChecked(arg);
|
||||
break;
|
||||
default: // normal
|
||||
this.setAttribute("role", "menuitem");
|
||||
this.setAttribute("aria-checked", null);
|
||||
break;
|
||||
}
|
||||
if (this.parent && "checkIcons" in this.parent)
|
||||
this.parent.checkIcons();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Replacement behavior for add()
|
||||
addHook(component) {
|
||||
|
||||
// Convert to sub-menu
|
||||
if (this.menu == null) {
|
||||
this.menu = new Toolkit.Menu(this);
|
||||
this.after(this.menu);
|
||||
this.setAttribute("aria-haspopup", "menu");
|
||||
this.setAttribute("aria-expanded", "false");
|
||||
if (this.parent && "checkIcons" in this.parent)
|
||||
this.parent.checkIcons();
|
||||
}
|
||||
|
||||
// Add the child component
|
||||
component.menuBar = this.menuBar;
|
||||
this.menu.append(component);
|
||||
if (component instanceof Toolkit.MenuItem && component.menu != null)
|
||||
this.menu.append(component.menu);
|
||||
|
||||
// Configure icon mode
|
||||
this.checkIcons();
|
||||
}
|
||||
|
||||
// Check whether any child menu items contain icons
|
||||
checkIcons() {
|
||||
if (this.menu == null)
|
||||
return;
|
||||
if (this.children.filter(c=>
|
||||
c instanceof Toolkit.MenuItem &&
|
||||
c.menu == null &&
|
||||
c.type != "normal"
|
||||
).length != 0)
|
||||
this.menu.element.classList.add("icons");
|
||||
else this.menu.element.classList.remove("icons");
|
||||
}
|
||||
|
||||
// Replacement behavior for remove()
|
||||
removeHook(component) {
|
||||
|
||||
// Remove the child component
|
||||
component.element.remove();
|
||||
if (component instanceof Toolkit.MenuItem && component.menu != null)
|
||||
component.menu.element.remove();
|
||||
|
||||
// Convert to action item
|
||||
if (this.children.length == 0) {
|
||||
this.menu.element.remove();
|
||||
this.menu = null;
|
||||
this.setAttribute("aria-haspopup", null);
|
||||
this.setAttribute("aria-expanded", "false");
|
||||
if (this.parent && "checkIcons" in this.parent)
|
||||
this.parent.checkIcons();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
super.translate();
|
||||
if (!("contents" in this))
|
||||
return;
|
||||
this.etext.innerText = this.gui.translate(this.text, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Retrieve the currently expanded sub-menu, if any
|
||||
expandedMenu() {
|
||||
return this.children.filter(c=>c.isExpanded)[0] || null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MenuBar //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Application menu bar
|
||||
class MenuBar extends Component {
|
||||
static Component = Component;
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-menu-bar",
|
||||
focusable: false,
|
||||
tagName : "div",
|
||||
tabStop : true,
|
||||
role : "menubar",
|
||||
style : {
|
||||
position: "relative",
|
||||
zIndex : "1"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.focusTarget = null;
|
||||
this.menuBar = this;
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("blur" , e=>this.onBlur (e), true);
|
||||
this.addEventListener("focus" , e=>this.onFocus (e), true);
|
||||
this.addEventListener("keydown", e=>this.onKeyDown(e), true);
|
||||
|
||||
// Configure widget
|
||||
this.gui.localize(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Focus lost
|
||||
onBlur(e) {
|
||||
if (this.contains(e.relatedTarget))
|
||||
return;
|
||||
let items = this.listItems();
|
||||
if (items[0])
|
||||
items[0].setFocusable(true, true);
|
||||
let menu = this.expandedMenu();
|
||||
if (menu != null)
|
||||
menu.setExpanded(false);
|
||||
}
|
||||
|
||||
// Focus gained
|
||||
onFocus(e) {
|
||||
if (this.contains(e.relatedTarget))
|
||||
return;
|
||||
let items = this.listItems();
|
||||
if (items[0])
|
||||
items[0].setFocusable(true, false);
|
||||
this.focusTarget = e.relatedTarget;
|
||||
}
|
||||
|
||||
// Key pressed
|
||||
onKeyDown(e) {
|
||||
if (e.key != "Tab")
|
||||
return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.exit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Produce a list of child items
|
||||
listItems(invisible) {
|
||||
return this.children.filter(c=>
|
||||
c instanceof Toolkit.MenuItem &&
|
||||
(invisible || c.isVisible())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Replacement behavior for add()
|
||||
addHook(component) {
|
||||
component.menuBar = this.menuBar;
|
||||
this.append(component);
|
||||
if (component instanceof Toolkit.MenuItem && component.menu != null)
|
||||
this.append(component.menu);
|
||||
let items = this.listItems();
|
||||
if (items[0])
|
||||
items[0].setFocusable(true, true);
|
||||
}
|
||||
|
||||
// Return control to the application
|
||||
exit() {
|
||||
this.onBlur({ relatedTarget: null });
|
||||
if (this.focusTarget)
|
||||
this.focusTarget.focus();
|
||||
else document.activeElement.blur();
|
||||
}
|
||||
|
||||
// Replacement behavior for remove()
|
||||
removeHook(component) {
|
||||
component.element.remove();
|
||||
if (component instanceof Toolkit.MenuItem && component.menu != null)
|
||||
component.menu.element.remove();
|
||||
let items = this.listItems();
|
||||
if (items[0])
|
||||
items[0].setFocusable(true, true);
|
||||
}
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Retrieve the currently expanded menu, if any
|
||||
expandedMenu() {
|
||||
return this.children.filter(c=>c.isExpanded)[0] || null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
export { Menu, MenuBar, MenuItem, MenuSeparator };
|
||||
@@ -0,0 +1,145 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TextBox //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Text entry field
|
||||
class TextBox extends Component {
|
||||
static Component = Component;
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-textbox",
|
||||
tagName : "input"
|
||||
});
|
||||
this.element.type = "text";
|
||||
this.setAttribute("spellcheck", "false");
|
||||
|
||||
// Configure instance fields
|
||||
this.isEnabled = null;
|
||||
this.maxLength = null;
|
||||
this.pattern = null;
|
||||
|
||||
// Configure component
|
||||
options = options || {};
|
||||
this.setEnabled(!("enabled" in options) || options.enabled);
|
||||
if ("maxLength" in options)
|
||||
this.setMaxLength(options.maxLength);
|
||||
if ("pattern" in options)
|
||||
this.setPattern(options.pattern);
|
||||
this.setText (options.text);
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("blur" , e=>this.commit ( ));
|
||||
this.addEventListener("pointerdown", e=>e.stopPropagation( ));
|
||||
this.addEventListener("keydown" , e=>this.onKeyDown (e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
e.stopPropagation();
|
||||
return;
|
||||
case "Enter":
|
||||
this.commit();
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Programmatically commit the text box
|
||||
commit() {
|
||||
this.event("action");
|
||||
}
|
||||
|
||||
// Retrieve the control's value
|
||||
getText() {
|
||||
return this.element.value;
|
||||
}
|
||||
|
||||
// Specify whether the button can be activated
|
||||
setEnabled(enabled) {
|
||||
this.isEnabled = enabled = !!enabled;
|
||||
this.setAttribute("disabled", enabled ? null : "true");
|
||||
}
|
||||
|
||||
// Specify the maximum length of the text
|
||||
setMaxLength(length) {
|
||||
|
||||
// Remove limitation
|
||||
if (length === null) {
|
||||
this.maxLength = null;
|
||||
this.setAttribute("maxlength", null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Error checking
|
||||
if (typeof length != "number" || isNaN(length))
|
||||
return;
|
||||
|
||||
// Range checking
|
||||
length = Math.floor(length);
|
||||
if (length < 0)
|
||||
return;
|
||||
|
||||
// Configure component
|
||||
this.maxLength = length;
|
||||
this.setAttribute("maxlength", length);
|
||||
}
|
||||
|
||||
// Specify a regex pattern for valid text characters
|
||||
setPattern(pattern) {
|
||||
/*
|
||||
Disabled because user agents may not prevent invalid input
|
||||
|
||||
// Error checking
|
||||
if (pattern && typeof pattern != "string")
|
||||
return;
|
||||
|
||||
// Configure component
|
||||
this.pattern = pattern = pattern || null;
|
||||
this.setAttribute("pattern", pattern);
|
||||
*/
|
||||
}
|
||||
|
||||
// Specify the widget's display text
|
||||
setText(text = "") {
|
||||
this.element.value = text.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { TextBox };
|
||||
@@ -0,0 +1,337 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
import { Button, CheckBox, Group, Radio } from /**/"./Button.js" ;
|
||||
import { DropDown } from /**/"./DropDown.js" ;
|
||||
import { Menu, MenuBar, MenuItem, MenuSeparator } from /**/"./MenuBar.js" ;
|
||||
import { ScrollBar, ScrollPane, SplitPane } from /**/"./ScrollBar.js";
|
||||
import { TextBox } from /**/"./TextBox.js" ;
|
||||
import { Desktop, Window } from /**/"./Window.js" ;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Toolkit //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Top-level user interface manager
|
||||
let Toolkit = globalThis.Toolkit = (class GUI extends Component {
|
||||
|
||||
static initializer() {
|
||||
|
||||
// Static state
|
||||
this.nextId = 0;
|
||||
|
||||
// Locale presets
|
||||
this.NO_LOCALE = { id: "(Null)" };
|
||||
|
||||
// Component classes
|
||||
this.components = [];
|
||||
Button .setToolkit(this); this.components.push(Button .Component);
|
||||
Component.setToolkit(this); this.components.push( Component);
|
||||
DropDown .setToolkit(this); this.components.push(DropDown .Component);
|
||||
MenuBar .setToolkit(this); this.components.push(MenuBar .Component);
|
||||
ScrollBar.setToolkit(this); this.components.push(ScrollBar.Component);
|
||||
TextBox .setToolkit(this); this.components.push(TextBox .Component);
|
||||
Window .setToolkit(this); this.components.push(Window .Component);
|
||||
this.Button = Button;
|
||||
this.CheckBox = CheckBox;
|
||||
this.Component = Component;
|
||||
this.Desktop = Desktop;
|
||||
this.DropDown = DropDown;
|
||||
this.Group = Group;
|
||||
this.Menu = Menu;
|
||||
this.MenuBar = MenuBar;
|
||||
this.MenuItem = MenuItem;
|
||||
this.MenuSeparator = MenuSeparator;
|
||||
this.Radio = Radio;
|
||||
this.ScrollBar = ScrollBar;
|
||||
this.ScrollPane = ScrollPane;
|
||||
this.SplitPane = SplitPane;
|
||||
this.TextBox = TextBox;
|
||||
this.Window = Window;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Static Methods //////////////////////////////
|
||||
|
||||
// Monitor resize events on an element
|
||||
static addResizeListener(element, listener) {
|
||||
|
||||
// Establish a ResizeObserver
|
||||
if (!("resizeListeners" in element)) {
|
||||
element.resizeListeners = [];
|
||||
element.resizeObserver = new ResizeObserver(
|
||||
(e,o)=>element.dispatchEvent(this.event("resize")));
|
||||
element.resizeObserver.observe(element);
|
||||
}
|
||||
|
||||
// Associate the listener
|
||||
if (element.resizeListeners.indexOf(listener) == -1) {
|
||||
element.resizeListeners.push(listener);
|
||||
element.addEventListener("resize", listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Stop monitoring resize events on an element
|
||||
static clearResizeListeners(element) {
|
||||
while ("resizeListeners" in element)
|
||||
this.removeResizeListener(element, element.resizeListeners[0]);
|
||||
}
|
||||
|
||||
// Produce a custom event object
|
||||
static event(type, component, fields) {
|
||||
let event = new Event(type, {
|
||||
bubbles : true,
|
||||
cancelable: true
|
||||
});
|
||||
if (component)
|
||||
event.component = component;
|
||||
if (fields)
|
||||
Object.assign(event, fields);
|
||||
return event;
|
||||
}
|
||||
|
||||
// Produce a unique element ID
|
||||
static id() {
|
||||
return "tk" + (this.nextId++);
|
||||
}
|
||||
|
||||
// Determine whether an object is a component
|
||||
// The user agent may not resolve imports to the same classes
|
||||
static isComponent(o) {
|
||||
return !!this.components.find(c=>o instanceof c);
|
||||
}
|
||||
|
||||
// Determine whether a pointer event is inside an element
|
||||
static isInside(element, e) {
|
||||
let bounds = element.getBoundingClientRect();
|
||||
return (
|
||||
e.offsetX >= 0 && e.offsetX < bounds.width &&
|
||||
e.offsetY >= 0 && e.offsetY < bounds.height
|
||||
);
|
||||
}
|
||||
|
||||
// Generate a list of focusable child elements
|
||||
static listFocusables(element) {
|
||||
return Array.from(element.querySelectorAll(
|
||||
"*:not(*:not(a[href], area, button, details, input, " +
|
||||
"textarea, select, [tabindex='0'])):not([disabled])"
|
||||
)).filter(e=>{
|
||||
for (; e instanceof Element; e = e.parentNode) {
|
||||
let style = getComputedStyle(e);
|
||||
if (style.display == "none" || style.visibility == "hidden")
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Stop monitoring resize events on an element
|
||||
static removeResizeListener(element, listener) {
|
||||
|
||||
// Error checking
|
||||
if (!("resizeListeners" in element))
|
||||
return;
|
||||
let index = element.resizeListeners.indexOf(listener);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
// Remove the listener
|
||||
element.removeEventListener("resize", element.resizeListeners[index]);
|
||||
element.resizeListeners.splice(index, 1);
|
||||
|
||||
// No more listeners: delete the ResizeObserver
|
||||
if (element.resizeListeners.length == 0) {
|
||||
element.resizeObserver.unobserve(element);
|
||||
delete element.resizeListeners;
|
||||
delete element.resizeObserver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Compute pointer event screen coordinates
|
||||
static screenCoords(e) {
|
||||
return {
|
||||
x: e.screenX / window.devicePixelRatio,
|
||||
y: e.screenY / window.devicePixelRatio
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(options) {
|
||||
super(null, options);
|
||||
|
||||
// Configure instance fields
|
||||
this.locale = Toolkit.NO_LOCALE;
|
||||
this.localized = [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Specify the locale to use for translated strings
|
||||
setLocale(locale) {
|
||||
this.locale = locale || Toolkit.NO_LOCALE;
|
||||
for (let component of this.localized)
|
||||
component.translate();
|
||||
}
|
||||
|
||||
// Translate a string in the selected locale
|
||||
translate(key, component) {
|
||||
|
||||
// Front-end method
|
||||
if (key === undefined) {
|
||||
super.translate();
|
||||
return;
|
||||
}
|
||||
|
||||
// Working variables
|
||||
let subs = component ? component.substitutions : {};
|
||||
key = (key || "").toString().trim();
|
||||
|
||||
// Error checking
|
||||
if (this.locale == null || key == "")
|
||||
return key;
|
||||
|
||||
// Resolve the key first in the substitutions then in the locale
|
||||
let text = key;
|
||||
key = key.toLowerCase();
|
||||
if (key in subs)
|
||||
text = subs[key];
|
||||
else if (key in this.locale)
|
||||
text = this.locale[key];
|
||||
else return "!" + text.toUpperCase();
|
||||
|
||||
// Process all substitutions
|
||||
for (;;) {
|
||||
|
||||
// Working variables
|
||||
let sIndex = 0;
|
||||
let rIndex = -1;
|
||||
let lIndex = -1;
|
||||
let zIndex = -1;
|
||||
|
||||
// Locate the inner-most {} or [] pair
|
||||
for (;;) {
|
||||
let match = Toolkit.subCtrl(text, sIndex);
|
||||
|
||||
// No control characters found
|
||||
if (match == -1)
|
||||
break;
|
||||
sIndex = match + 1;
|
||||
|
||||
// Processing by control character
|
||||
switch (text.charAt(match)) {
|
||||
|
||||
// Opening a substitution group
|
||||
case "{": rIndex = match; continue;
|
||||
case "[": lIndex = match; continue;
|
||||
|
||||
// Closing a recursion group
|
||||
case "}":
|
||||
if (rIndex != -1) {
|
||||
lIndex = -1;
|
||||
zIndex = match;
|
||||
}
|
||||
break;
|
||||
|
||||
// Closing a literal group
|
||||
case "]":
|
||||
if (lIndex != -1) {
|
||||
rIndex = -1;
|
||||
zIndex = match;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Process a recursion substitution
|
||||
if (rIndex != -1) {
|
||||
text =
|
||||
text.substring(0, rIndex) +
|
||||
this.translate(
|
||||
text.substring(rIndex + 1, zIndex),
|
||||
component
|
||||
) +
|
||||
text.substring(zIndex + 1)
|
||||
;
|
||||
}
|
||||
|
||||
// Process a literal substitution
|
||||
else if (lIndex != -1) {
|
||||
text =
|
||||
text.substring(0, lIndex) +
|
||||
text.substring(lIndex + 1, zIndex)
|
||||
.replaceAll("{", "{{")
|
||||
.replaceAll("}", "}}")
|
||||
.replaceAll("[", "[[")
|
||||
.replaceAll("]", "]]")
|
||||
+
|
||||
text.substring(zIndex + 1)
|
||||
;
|
||||
}
|
||||
|
||||
// No more substitutions
|
||||
else break;
|
||||
}
|
||||
|
||||
// Unescape all remaining control characters
|
||||
return (text
|
||||
.replaceAll("{{", "{")
|
||||
.replaceAll("}}", "}")
|
||||
.replaceAll("[[", "[")
|
||||
.replaceAll("]]", "]")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Reduce an object to a single level of depth
|
||||
static flatten(obj, ret = {}, id) {
|
||||
for (let entry of Object.entries(obj)) {
|
||||
let key = (id ? id + "." : "") + entry[0].toLowerCase();
|
||||
let value = entry[1];
|
||||
if (value instanceof Object)
|
||||
this.flatten(value, ret, key);
|
||||
else ret[key] = value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Register a component for localization management
|
||||
localize(component) {
|
||||
if (this.localized.indexOf(component) != -1)
|
||||
return;
|
||||
this.localized.push(component);
|
||||
component.translate();
|
||||
}
|
||||
|
||||
// Locate a substitution control character in a string
|
||||
static subCtrl(text, index) {
|
||||
for (; index < text.length; index++) {
|
||||
let c = text.charAt(index);
|
||||
if ("{}[]".indexOf(c) == -1)
|
||||
continue;
|
||||
if (index < text.length - 1 || text.charAt(index + 1) != c)
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}).initializer();
|
||||
|
||||
|
||||
|
||||
export { Toolkit };
|
||||
@@ -0,0 +1,699 @@
|
||||
import { Component } from /**/"./Component.js";
|
||||
let Toolkit;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Window //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Standalone movable dialog
|
||||
class Window extends Component {
|
||||
static Component = Component;
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className : "tk tk-window",
|
||||
focusable : true,
|
||||
role : "dialog",
|
||||
tabStop : false,
|
||||
tagName : "div",
|
||||
visibility: true,
|
||||
style : {
|
||||
position: "absolute"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.firstShown = false;
|
||||
this.lastFocus = null;
|
||||
|
||||
// DOM container
|
||||
this.contents = document.createElement("div");
|
||||
this.contents.style.display = "flex";
|
||||
this.contents.style.flexDirection = "column";
|
||||
this.element.append(this.contents);
|
||||
|
||||
// Sizing borders
|
||||
this.borders = {}
|
||||
this.border("n" ); this.border("w" );
|
||||
this.border("e" ); this.border("s" );
|
||||
this.border("nw"); this.border("ne");
|
||||
this.border("sw"); this.border("se");
|
||||
|
||||
// Title bar
|
||||
this.titleBar = document.createElement("div");
|
||||
this.titleBar.className = "tk tk-title";
|
||||
this.titleBar.style.display = "flex";
|
||||
this.contents.append(this.titleBar);
|
||||
this.titleBar.addEventListener(
|
||||
"pointerdown", e=>this.onTitlePointerDown(e));
|
||||
this.titleBar.addEventListener(
|
||||
"pointermove", e=>this.onTitlePointerMove(e));
|
||||
this.titleBar.addEventListener(
|
||||
"pointerup" , e=>this.onTitlePointerUp (e));
|
||||
|
||||
// Title bar text
|
||||
this.titleText = document.createElement("div");
|
||||
this.titleText.className = "tk tk-text";
|
||||
this.titleText.id = Toolkit.id();
|
||||
this.titleText.style.flexGrow = "1";
|
||||
this.titleText.style.position = "relative";
|
||||
this.titleBar.append(this.titleText);
|
||||
this.setAttribute("aria-labelledby", this.titleText.id);
|
||||
|
||||
// Close button
|
||||
this.titleClose = document.createElement("div");
|
||||
this.titleClose.className = "tk tk-close";
|
||||
this.titleClose.setAttribute("aria-hidden", "true");
|
||||
this.titleBar.append(this.titleClose);
|
||||
this.titleClose.addEventListener(
|
||||
"pointerdown", e=>this.onClosePointerDown(e));
|
||||
this.titleClose.addEventListener(
|
||||
"pointermove", e=>this.onClosePointerMove(e));
|
||||
this.titleClose.addEventListener(
|
||||
"pointerup" , e=>this.onClosePointerUp (e));
|
||||
|
||||
// Window client area
|
||||
this.client = document.createElement("div");
|
||||
this.client.className = "tk tk-client";
|
||||
this.client.style.flexGrow = "1";
|
||||
this.client.style.minHeight = "0";
|
||||
this.client.style.minWidth = "0";
|
||||
this.client.style.overflow = "hidden";
|
||||
this.client.style.position = "relative";
|
||||
this.contents.append(this.client);
|
||||
|
||||
// User agent behavior override
|
||||
let observer = new ResizeObserver(
|
||||
()=>this.titleBar.style.width =
|
||||
this.client.getBoundingClientRect().width + "px"
|
||||
);
|
||||
observer.observe(this.client);
|
||||
|
||||
// Configure element
|
||||
this.setAttribute("aria-modal", "false");
|
||||
this.setBounds(
|
||||
options.x , options.y,
|
||||
options.width, options.height
|
||||
);
|
||||
|
||||
// Configure component
|
||||
this.gui.localize(this);
|
||||
this.setTitle (options.title );
|
||||
this.setCloseToolTip(options.closeToolTip);
|
||||
this.addEventListener("focus" , e=>this.onFocus(e), true);
|
||||
this.addEventListener("keydown" , e=>this.onWindowKeyDown (e));
|
||||
this.addEventListener("pointerdown", e=>this.onWindowPointerDown(e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Border pointer down
|
||||
onBorderPointerDown(e, edge) {
|
||||
if (e.target.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
e.target.setPointerCapture(e.pointerId);
|
||||
e.preventDefault();
|
||||
let bndClient = this.client.getBoundingClientRect();
|
||||
let bndWindow = this .getBounds ();
|
||||
let bndDesktop = this.parent ? this.parent.getBounds() : bndWindow;
|
||||
let coords = Toolkit.screenCoords(e);
|
||||
this.drag = {
|
||||
clickX : coords.x,
|
||||
clickY : coords.y,
|
||||
mode : "resize",
|
||||
pointerId : e.pointerId,
|
||||
startHeight: bndClient.height,
|
||||
startWidth : bndClient.width,
|
||||
startX : bndWindow.x - bndDesktop.x,
|
||||
startY : bndWindow.y - bndDesktop.y,
|
||||
target : e.target
|
||||
};
|
||||
}
|
||||
|
||||
// Border pointer move
|
||||
onBorderPointerMove(e, edge) {
|
||||
if (!e.target.hasPointerCapture(e.pointerId))
|
||||
return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
let bndWindow = this.getBounds();
|
||||
this["resize" + edge.toUpperCase()](
|
||||
Toolkit.screenCoords(e),
|
||||
this.client .getBoundingClientRect(),
|
||||
this.parent ? this.parent.getBounds() : bndWindow,
|
||||
bndWindow,
|
||||
this.titleBar.getBoundingClientRect()
|
||||
);
|
||||
}
|
||||
|
||||
// Border pointer up
|
||||
onBorderPointerUp(e, edge) {
|
||||
if (!e.target.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
e.target.releasePointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Close pointer down
|
||||
onClosePointerDown(e) {
|
||||
if (this.titleClose.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
this.titleClose.setPointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.titleClose.classList.add("active");
|
||||
this.drag = {
|
||||
mode: "close",
|
||||
x : e.offsetX,
|
||||
y : e.offsetY
|
||||
};
|
||||
}
|
||||
|
||||
// Close pointer move
|
||||
onClosePointerMove(e) {
|
||||
if (!this.titleClose.hasPointerCapture(e.pointerId))
|
||||
return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (Toolkit.isInside(this.titleClose, e))
|
||||
this.titleClose.classList.add("active");
|
||||
else this.titleClose.classList.remove("active");
|
||||
}
|
||||
|
||||
// Close pointer up
|
||||
onClosePointerUp(e) {
|
||||
if (!this.titleClose.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
this.titleClose.releasePointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.titleClose.classList.remove("active");
|
||||
if (Toolkit.isInside(this.titleClose, e))
|
||||
this.element.dispatchEvent(Toolkit.event("close", this));
|
||||
this.drag = null;
|
||||
}
|
||||
|
||||
// Focus capture
|
||||
onFocus(e) {
|
||||
|
||||
// Bring this window to the foreground of its siblings
|
||||
if (!this.contains(e.relatedTarget) && this.parent)
|
||||
this.parent.bringToFront(this);
|
||||
|
||||
// The target is not the window itself
|
||||
if (e.target != this.element) {
|
||||
this.lastFocus = e.target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the first focusable child
|
||||
if (this.lastFocus == null)
|
||||
this.lastFocus = Toolkit.listFocusables(this.element)[0] || null;
|
||||
|
||||
// Send focus to the most recently focused element
|
||||
if (this.lastFocus)
|
||||
this.lastFocus.focus();
|
||||
}
|
||||
|
||||
// Title pointer down
|
||||
onTitlePointerDown(e) {
|
||||
if (this.titleBar.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
this.titleBar.setPointerCapture(e.pointerId);
|
||||
e.preventDefault();
|
||||
let bndWindow = this.getBounds();
|
||||
let bndDesktop = this.parent ? this.parent.getBounds() : bndWindow;
|
||||
let coords = Toolkit.screenCoords(e);
|
||||
this.drag = {
|
||||
clickX : coords.x,
|
||||
clickY : coords.y,
|
||||
mode : "move",
|
||||
pointerId: e.pointerId,
|
||||
startX : bndWindow.x - bndDesktop.x,
|
||||
startY : bndWindow.y - bndDesktop.y
|
||||
};
|
||||
}
|
||||
|
||||
// Title pointer move
|
||||
onTitlePointerMove(e) {
|
||||
if (!this.titleBar.hasPointerCapture(e.pointerId))
|
||||
return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
let coords = Toolkit.screenCoords(e);
|
||||
let valid = this.getValidLocations(
|
||||
this.drag.startX + coords.x - this.drag.clickX,
|
||||
this.drag.startY + coords.y - this.drag.clickY
|
||||
);
|
||||
this.setLocation(valid.x, valid.y);
|
||||
}
|
||||
|
||||
// Title pointer up
|
||||
onTitlePointerUp(e) {
|
||||
if (!this.titleBar.hasPointerCapture(e.pointerId) || e.button != 0)
|
||||
return;
|
||||
this.titleBar.releasePointerCapture(e.pointerId);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.drag = null;
|
||||
}
|
||||
|
||||
// Window key press
|
||||
onWindowKeyDown(e) {
|
||||
|
||||
// Process by key
|
||||
switch (e.key) {
|
||||
|
||||
// Undo un-committed bounds modifications
|
||||
case "Escape":
|
||||
|
||||
// Not dragging
|
||||
if (this.drag == null)
|
||||
return;
|
||||
|
||||
// Moving
|
||||
if (this.drag.mode == "move") {
|
||||
this.titleBar.releasePointerCapture(this.drag.pointerId);
|
||||
this.setLocation(this.drag.startX, this.drag.startY);
|
||||
this.drag = null;
|
||||
}
|
||||
|
||||
// Resizing
|
||||
else if (this.drag.mode == "resize") {
|
||||
this.drag.target
|
||||
.releasePointerCapture(this.drag.pointerId);
|
||||
this.setBounds(
|
||||
this.drag.startX , this.drag.startY,
|
||||
this.drag.startWidth, this.drag.startHeight
|
||||
);
|
||||
this.drag = null;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Transfer focus to another element
|
||||
case "Tab":
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
// The event was handled
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Window pointer down
|
||||
onWindowPointerDown(e) {
|
||||
this.focus(e);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Add a DOM element to this component's element
|
||||
append(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.client.append(element);
|
||||
}
|
||||
|
||||
// Position the window in the center of the parent Desktop
|
||||
center() {
|
||||
if (!this.parent)
|
||||
return;
|
||||
let bndParent = this.parent.getBounds();
|
||||
let bndWindow = this .getBounds();
|
||||
this.setLocation(
|
||||
Math.max(Math.floor((bndParent.width - bndWindow.width ) / 2), 0),
|
||||
Math.max(Math.floor((bndParent.height - bndWindow.height) / 2), 0)
|
||||
);
|
||||
}
|
||||
|
||||
// Programmatically close the window
|
||||
close() {
|
||||
this.event("close");
|
||||
}
|
||||
|
||||
// Add a DOM element to the beginning of this component's children
|
||||
prepend(child) {
|
||||
let element = child instanceof Element ? child : child.element;
|
||||
this.element.prepend(element);
|
||||
}
|
||||
|
||||
// Specify a new position and size for the window
|
||||
setBounds(x, y, width, height) {
|
||||
this.setSize(width, height);
|
||||
this.setLocation(x, y);
|
||||
}
|
||||
|
||||
// Specify the over text for the close button
|
||||
setCloseToolTip(key) {
|
||||
this.closeToolTip = key;
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify a new position for the window
|
||||
setLocation(x, y) {
|
||||
Object.assign(this.element.style, {
|
||||
left: Math.round(parseFloat(x) || 0) + "px",
|
||||
top : Math.round(parseFloat(y) || 0) + "px"
|
||||
});
|
||||
}
|
||||
|
||||
// Specify a new size for the window
|
||||
setSize(width, height) {
|
||||
Object.assign(this.client.style, {
|
||||
width : Math.max(Math.round(parseFloat(width ) || 0, 32)) + "px",
|
||||
height: Math.max(Math.round(parseFloat(height) || 0, 32)) + "px"
|
||||
});
|
||||
}
|
||||
|
||||
// Specify the window title text
|
||||
setTitle(key) {
|
||||
this.title = key;
|
||||
this.translate();
|
||||
}
|
||||
|
||||
// Specify whether the component is visible
|
||||
setVisible(visible) {
|
||||
super.setVisible(visible);
|
||||
if (!visible || this.firstShown)
|
||||
return;
|
||||
this.firstShown = true;
|
||||
this.event("firstshow", this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Ensure the window is partially visible within its desktop
|
||||
contain() {
|
||||
let valid = this.getValidLocations();
|
||||
this.setLocation(valid.x, valid.y);
|
||||
}
|
||||
|
||||
// Determine the range of valid window coordinates
|
||||
getValidLocations(x, y) {
|
||||
|
||||
// Measure the bounding boxes of the relevant elements
|
||||
let bndClient = this.client .getBoundingClientRect();
|
||||
let bndWindow = this .getBounds ();
|
||||
let bndTitleBar = this.titleBar.getBoundingClientRect();
|
||||
let bndDesktop = this.parent ? this.parent.getBounds() : bndWindow;
|
||||
|
||||
// Compute the minimum and maximum valid window coordinates
|
||||
let ret = {
|
||||
maxX: bndDesktop .width - bndTitleBar.height -
|
||||
bndTitleBar.x + bndWindow .x,
|
||||
maxY: bndDesktop .height - bndClient .y +
|
||||
bndWindow .y,
|
||||
minX: bndTitleBar.height - bndWindow .width +
|
||||
bndWindow .right - bndTitleBar.right,
|
||||
minY: 0
|
||||
};
|
||||
|
||||
// Compute the effective "best" window coordinates
|
||||
ret.x = Math.max(ret.minX, Math.min(ret.maxX,
|
||||
x === undefined ? bndWindow.x - bndDesktop.x : x));
|
||||
ret.y = Math.max(ret.minY, Math.min(ret.maxY,
|
||||
y === undefined ? bndWindow.y - bndDesktop.y : y));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Update the global Toolkit object
|
||||
static setToolkit(toolkit) {
|
||||
Toolkit = toolkit;
|
||||
}
|
||||
|
||||
// Regenerate localized display text
|
||||
translate() {
|
||||
if (!this.titleText)
|
||||
return;
|
||||
this.titleText.innerText = this.gui.translate(this.title, this);
|
||||
if (this.closeToolTip)
|
||||
this.titleClose.setAttribute("title",
|
||||
this.gui.translate(this.closeToolTip, this));
|
||||
else this.titleClose.removeAttribute("title");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Produce a border element and add it to the window
|
||||
border(edge) {
|
||||
let border = this.borders[edge] = document.createElement("div");
|
||||
border.className = "tk tk-" + edge;
|
||||
border.style.cursor = edge + "-resize";
|
||||
border.style.position = "absolute";
|
||||
this.contents.append(border);
|
||||
border.addEventListener(
|
||||
"pointerdown", e=>this.onBorderPointerDown(e, edge));
|
||||
border.addEventListener(
|
||||
"pointermove", e=>this.onBorderPointerMove(e, edge));
|
||||
border.addEventListener(
|
||||
"pointerup" , e=>this.onBorderPointerUp (e, edge));
|
||||
}
|
||||
|
||||
// Compute client bounds when resizing on the east border
|
||||
constrainE(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let w = this.drag.startWidth + coords.x - this.drag.clickX;
|
||||
w = Math.max(w, bndTitleBar.height * 4);
|
||||
if (bndClient.x - bndDesktop.x < 0)
|
||||
w = Math.max(w, bndDesktop.x - bndClient.x + bndTitleBar.height);
|
||||
return w;
|
||||
}
|
||||
|
||||
// Compute client bounds when resizing on the north border
|
||||
constrainN(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let delta = coords.y - this.drag.clickY;
|
||||
let y = this.drag.startY + delta;
|
||||
let h = this.drag.startHeight - delta;
|
||||
let min = Math.max(0, bndClient.bottom - bndDesktop.bottom);
|
||||
if (h < min) {
|
||||
delta = min - h;
|
||||
h += delta;
|
||||
y -= delta;
|
||||
}
|
||||
if (y < 0) {
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
return {
|
||||
height: h,
|
||||
y : y
|
||||
};
|
||||
}
|
||||
|
||||
// Compute client bounds when resizing on the south border
|
||||
constrainS(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
return Math.max(0, this.drag.startHeight+coords.y-this.drag.clickY);
|
||||
}
|
||||
|
||||
// Compute client bounds when resizing on the west border
|
||||
constrainW(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let delta = coords.x - this.drag.clickX;
|
||||
let x = this.drag.startX + delta;
|
||||
let w = this.drag.startWidth - delta;
|
||||
let min = bndTitleBar.height * 4;
|
||||
if (bndClient.right - bndDesktop.right > 0) {
|
||||
min = Math.max(min, bndClient.right -
|
||||
bndDesktop.right + bndTitleBar.height);
|
||||
}
|
||||
if (w < min) {
|
||||
delta = min - w;
|
||||
w += delta;
|
||||
x -= delta;
|
||||
}
|
||||
return {
|
||||
x : x,
|
||||
width: w
|
||||
};
|
||||
}
|
||||
|
||||
// Resize on the east border
|
||||
resizeE(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
this.setSize(
|
||||
this.constrainE(coords,bndClient,bndDesktop,bndWindow,bndTitleBar),
|
||||
this.drag.startHeight
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the north border
|
||||
resizeN(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let con = this.constrainN(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
this.setBounds(
|
||||
this.drag.startX , con.y,
|
||||
this.drag.startWidth, con.height
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the northeast border
|
||||
resizeNE(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let con = this.constrainN(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
this.setBounds(
|
||||
this.drag.startX, con.y,
|
||||
this.constrainE(coords,bndClient,bndDesktop,bndWindow,bndTitleBar),
|
||||
con.height
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the northwest border
|
||||
resizeNW(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let conN = this.constrainN(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
let conW = this.constrainW(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
this.setBounds(conW.x, conN.y, conW.width, conN.height);
|
||||
}
|
||||
|
||||
// Resize on the south border
|
||||
resizeS(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
this.setSize(
|
||||
this.drag.startWidth,
|
||||
this.constrainS(coords,bndClient,bndDesktop,bndWindow,bndTitleBar),
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the southeast border
|
||||
resizeSE(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
this.setSize(
|
||||
this.constrainE(coords,bndClient,bndDesktop,bndWindow,bndTitleBar),
|
||||
this.constrainS(coords,bndClient,bndDesktop,bndWindow,bndTitleBar)
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the southwest border
|
||||
resizeSW(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let con = this.constrainW(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
this.setBounds(
|
||||
con.x , this.drag.startY,
|
||||
con.width,
|
||||
this.constrainS(coords,bndClient,bndDesktop,bndWindow,bndTitleBar)
|
||||
);
|
||||
}
|
||||
|
||||
// Resize on the west border
|
||||
resizeW(coords, bndClient, bndDesktop, bndWindow, bndTitleBar) {
|
||||
let con = this.constrainW(coords,
|
||||
bndClient, bndDesktop, bndWindow, bndTitleBar);
|
||||
this.setBounds(
|
||||
con.x , this.drag.startY,
|
||||
con.width, this.drag.startHeight
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Desktop //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Parent container for encapsulating groups of Windows
|
||||
class Desktop extends Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(gui, options) {
|
||||
super(gui, options, {
|
||||
className: "tk tk-desktop",
|
||||
role : "group",
|
||||
tagName : "div",
|
||||
style : {
|
||||
position: "relative"
|
||||
}
|
||||
});
|
||||
|
||||
// Configure event handlers
|
||||
this.addEventListener("resize", e=>this.onResize(e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Element resized
|
||||
onResize(e) {
|
||||
for (let wnd of this.children)
|
||||
wnd.contain();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Re-order windows to bring a particular one to the foreground
|
||||
bringToFront(wnd) {
|
||||
|
||||
// The window is not a child of this Desktop
|
||||
let index = this.children.indexOf(wnd);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
// The window is already in the foreground
|
||||
let afters = this.children.slice(index + 1).map(c=>c.element);
|
||||
if (afters.length == 0)
|
||||
return;
|
||||
|
||||
// Record scroll pane positions
|
||||
let scrolls = [];
|
||||
for (let after of afters)
|
||||
for (let scroll of
|
||||
after.querySelectorAll(".tk-scrollpane > .tk-viewport")) {
|
||||
scrolls.push({
|
||||
element: scroll,
|
||||
left : scroll.scrollLeft,
|
||||
top : scroll.scrollTop
|
||||
});
|
||||
}
|
||||
|
||||
// Update window collection
|
||||
wnd.element.before(... this.children.slice(index+1).map(c=>c.element));
|
||||
this.children.splice(index, 1);
|
||||
this.children.push(wnd);
|
||||
|
||||
// Restore scroll pane positions
|
||||
for (let scroll of scrolls) {
|
||||
Object.assign(scroll.element, {
|
||||
scrollLeft: scroll.left,
|
||||
scrollTop : scroll.top
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Position a window in the center of the viewable area
|
||||
center(wnd) {
|
||||
|
||||
// The window is not a child of the desktop pane
|
||||
if (this.children.indexOf(wnd) == -1)
|
||||
return;
|
||||
|
||||
let bndDesktop = this.getBounds();
|
||||
let bndWindow = wnd .getBounds();
|
||||
wnd.setLocation(
|
||||
Math.max(0, Math.round((bndDesktop.width - bndWindow.width) / 2)),
|
||||
Math.max(0, Math.round((bndDesktop.height-bndWindow.height) / 2))
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
export { Desktop, Window };
|
||||
@@ -0,0 +1,123 @@
|
||||
/* This file is included into vb.c and cannot be compiled on its own. */
|
||||
#ifdef VBAPI
|
||||
|
||||
|
||||
|
||||
/***************************** Utility Functions *****************************/
|
||||
|
||||
/* Read a data unit from a memory buffer */
|
||||
static int32_t busReadBuffer(uint8_t *mem, int type) {
|
||||
|
||||
/* Little-endian implementation */
|
||||
#ifdef VB_LITTLEENDIAN
|
||||
switch (type) {
|
||||
case VB_S8 : return *(int8_t *)mem;
|
||||
case VB_U8 : return * mem;
|
||||
case VB_S16: return *(int16_t *)mem;
|
||||
case VB_U16: return *(uint16_t *)mem;
|
||||
}
|
||||
return *(int32_t *)mem;
|
||||
|
||||
/* Generic implementation */
|
||||
#else
|
||||
switch (type) {
|
||||
case VB_S8 : return (int8_t) *mem;
|
||||
case VB_U8 : return *mem;
|
||||
case VB_S16: return (int16_t ) (int8_t) mem[1] << 8 | mem[0];
|
||||
case VB_U16: return (uint16_t) mem[1] << 8 | mem[0];
|
||||
}
|
||||
return (int32_t ) mem[3] << 24 | (uint32_t) mem[2] << 16 |
|
||||
(uint32_t) mem[1] << 8 | mem[0];
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* Write a data unit to a memory buffer */
|
||||
static void busWriteBuffer(uint8_t *mem, int type, int32_t value) {
|
||||
|
||||
/* Little-endian implementation */
|
||||
#ifdef VB_LITTLEENDIAN
|
||||
switch (type) {
|
||||
case VB_S16: case VB_U16: *(uint16_t *)mem = value; return;
|
||||
case VB_S8 : case VB_U8 : * mem = value; return;
|
||||
}
|
||||
*(int32_t *)mem = value;
|
||||
|
||||
/* Generic implementation */
|
||||
#else
|
||||
switch (type) {
|
||||
case VB_S32:
|
||||
mem[3] = value >> 24;
|
||||
mem[2] = value >> 16;
|
||||
/* Fallthrough */
|
||||
case VB_S16:
|
||||
case VB_U16:
|
||||
mem[1] = value >> 8;
|
||||
}
|
||||
mem[0] = value;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************** Module Functions ******************************/
|
||||
|
||||
/* Read a data unit from the bus */
|
||||
static int32_t busRead(VB *sim, uint32_t address, int type, int debug) {
|
||||
|
||||
/* Force address alignment */
|
||||
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
|
||||
|
||||
/* Process by address range */
|
||||
switch (address >> 24 & 7) {
|
||||
case 0 : return 0; /* VIP */
|
||||
case 1 : return 0 * debug; /* VSU */
|
||||
case 2 : return 0; /* Miscellaneous hardware */
|
||||
case 3 : return 0; /* Unmapped */
|
||||
case 4 : return 0; /* Game pak expansion */
|
||||
case 5 : return /* WRAM */
|
||||
busReadBuffer(&sim->wram[address & 0xFFFF], type);
|
||||
case 6 : return sim->cart.sram == NULL ? 0 : /* Game pak RAM */
|
||||
busReadBuffer(&sim->cart.sram
|
||||
[address & (sim->cart.sramSize - 1)], type);
|
||||
default: return sim->cart.rom == NULL ? 0 : /* Game pak ROM */
|
||||
busReadBuffer(&sim->cart.rom
|
||||
[address & (sim->cart.romSize - 1)], type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write a data unit to the bus */
|
||||
static void busWrite(
|
||||
VB *sim, uint32_t address, int type, uint32_t value, int debug) {
|
||||
|
||||
/* Force address alignment */
|
||||
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
|
||||
|
||||
/* Process by address range */
|
||||
switch (address >> 24 & 7) {
|
||||
case 0 : return; /* VIP */
|
||||
case 1 : return; /* VSU */
|
||||
case 2 : return; /* Miscellaneous hardware */
|
||||
case 3 : return; /* Unmapped */
|
||||
case 4 : return; /* Game pak expansion */
|
||||
case 5 : /* WRAM */
|
||||
busWriteBuffer(&sim->wram[address & 0xFFFF], type, value);
|
||||
return;
|
||||
case 6 : /* Cartridge RAM */
|
||||
if (sim->cart.sram != NULL)
|
||||
busWriteBuffer(&sim->cart.sram
|
||||
[address & (sim->cart.sramSize - 1)], type, value);
|
||||
return;
|
||||
default: /* Cartridge ROM */
|
||||
if (debug && sim->cart.rom != NULL)
|
||||
busWriteBuffer(&sim->cart.rom
|
||||
[address & (sim->cart.romSize - 1)], type, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* VBAPI */
|
||||
@@ -0,0 +1,308 @@
|
||||
#ifdef VB_EXPORT
|
||||
#define VBAPI VB_EXPORT
|
||||
#else
|
||||
#define VBAPI
|
||||
#endif
|
||||
|
||||
/* Header includes */
|
||||
#include <float.h>
|
||||
#include <vb.h>
|
||||
|
||||
|
||||
|
||||
/********************************* Constants *********************************/
|
||||
|
||||
/* Type sizes */
|
||||
static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||
|
||||
|
||||
|
||||
/********************************** Macros ***********************************/
|
||||
|
||||
/* Sign-extend a value of some number of bits to 32 bits */
|
||||
#define SignExtend(v,b) \
|
||||
((v) | (((v) & (1 << ((b) - 1))) ? (uint32_t) 0xFFFFFFFF << (b) : 0))
|
||||
|
||||
|
||||
|
||||
|
||||
/*************************** Subsystem Components ****************************/
|
||||
|
||||
/* Component includes */
|
||||
#include "bus.c"
|
||||
#include "cpu.c"
|
||||
|
||||
|
||||
|
||||
/***************************** Module Functions ******************************/
|
||||
|
||||
/* Process a simulation for some number of clocks */
|
||||
static int sysEmulate(VB *sim, uint32_t clocks) {
|
||||
int broke;
|
||||
broke = cpuEmulate(sim, clocks);
|
||||
return broke;
|
||||
}
|
||||
|
||||
/* Determine the number of clocks before a break condititon could occur */
|
||||
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
||||
clocks = cpuUntil(sim, clocks);
|
||||
return clocks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************* API Functions *******************************/
|
||||
|
||||
/* Associate two simulations as peers, or remove an association */
|
||||
void vbConnect(VB *sim1, VB *sim2) {
|
||||
|
||||
/* Disconnect */
|
||||
if (sim2 == NULL) {
|
||||
if (sim1->peer != NULL)
|
||||
sim1->peer->peer = NULL;
|
||||
sim1->peer = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disconnect any existing link associations */
|
||||
if (sim1->peer != NULL && sim1->peer != sim2)
|
||||
sim1->peer->peer = NULL;
|
||||
if (sim2->peer != NULL && sim2->peer != sim1)
|
||||
sim2->peer->peer = NULL;
|
||||
|
||||
/* Link the two simulations */
|
||||
sim1->peer = sim2;
|
||||
sim2->peer = sim1;
|
||||
}
|
||||
|
||||
/* Process one simulation */
|
||||
int vbEmulate(VB *sim, uint32_t *clocks) {
|
||||
int broke; /* The simulation requested an application break */
|
||||
uint32_t until; /* Maximum clocks before a break could happen */
|
||||
|
||||
/* Process the simulation until a break condition occurs */
|
||||
do {
|
||||
until = *clocks;
|
||||
until = sysUntil (sim, until);
|
||||
broke = sysEmulate(sim, until);
|
||||
*clocks -= until;
|
||||
} while (!broke && *clocks > 0);
|
||||
|
||||
return broke;
|
||||
}
|
||||
|
||||
/* Process multiple simulations */
|
||||
int vbEmulateMulti(VB **sims, int count, uint32_t *clocks) {
|
||||
int broke; /* The simulation requested an application break */
|
||||
uint32_t until; /* Maximum clocks before a break could happen */
|
||||
int x; /* Iterator */
|
||||
|
||||
/* Process simulations until a break condition occurs */
|
||||
do {
|
||||
broke = 0;
|
||||
until = *clocks;
|
||||
for (x = 0; x < count; x++)
|
||||
until = sysUntil (sims[x], until);
|
||||
for (x = 0; x < count; x++)
|
||||
broke |= sysEmulate(sims[x], until);
|
||||
*clocks -= until;
|
||||
} while (!broke && *clocks > 0);
|
||||
|
||||
return broke;
|
||||
}
|
||||
|
||||
/* Retrieve a current breakpoint callback */
|
||||
void* vbGetCallback(VB *sim, int type) {
|
||||
void **field; /* Pointer to field within simulation */
|
||||
|
||||
/* Select the field to update */
|
||||
switch (type) {
|
||||
case VB_ONEXCEPTION: field = (void *) &sim->onException; break;
|
||||
case VB_ONEXECUTE : field = (void *) &sim->onExecute ; break;
|
||||
case VB_ONFETCH : field = (void *) &sim->onFetch ; break;
|
||||
case VB_ONREAD : field = (void *) &sim->onRead ; break;
|
||||
case VB_ONWRITE : field = (void *) &sim->onWrite ; break;
|
||||
default: return NULL;
|
||||
}
|
||||
|
||||
/* Retrieve the simulation field */
|
||||
return *field;
|
||||
}
|
||||
|
||||
/* Retrieve the value of PC */
|
||||
uint32_t vbGetProgramCounter(VB *sim) {
|
||||
return sim->cpu.pc;
|
||||
}
|
||||
|
||||
/* Retrieve the value of a program register */
|
||||
int32_t vbGetProgramRegister(VB *sim, int id) {
|
||||
return id < 1 || id > 31 ? 0 : sim->cpu.program[id];
|
||||
}
|
||||
|
||||
/* Retrieve the ROM buffer */
|
||||
void* vbGetROM(VB *sim, uint32_t *size) {
|
||||
if (size != NULL)
|
||||
*size = sim->cart.romSize;
|
||||
return sim->cart.rom;
|
||||
}
|
||||
|
||||
/* Retrieve the SRAM buffer */
|
||||
void* vbGetSRAM(VB *sim, uint32_t *size) {
|
||||
if (size != NULL)
|
||||
*size = sim->cart.sramSize;
|
||||
return sim->cart.sram;
|
||||
}
|
||||
|
||||
/* Retrieve the value of a system register */
|
||||
uint32_t vbGetSystemRegister(VB *sim, int id) {
|
||||
switch (id) {
|
||||
case VB_ADTRE: return sim->cpu.adtre;
|
||||
case VB_CHCW : return sim->cpu.chcw.ice << 1;
|
||||
case VB_EIPC : return sim->cpu.eipc;
|
||||
case VB_EIPSW: return sim->cpu.eipsw;
|
||||
case VB_FEPC : return sim->cpu.fepc;
|
||||
case VB_FEPSW: return sim->cpu.fepsw;
|
||||
case VB_PIR : return 0x00005346;
|
||||
case VB_TKCW : return 0x000000E0;
|
||||
case 29 : return sim->cpu.sr29;
|
||||
case 30 : return 0x00000004;
|
||||
case 31 : return sim->cpu.sr31;
|
||||
case VB_ECR : return
|
||||
(uint32_t) sim->cpu.ecr.fecc << 16 | sim->cpu.ecr.eicc;
|
||||
case VB_PSW : return
|
||||
(uint32_t) sim->cpu.psw.i << 16 |
|
||||
(uint32_t) sim->cpu.psw.np << 15 |
|
||||
(uint32_t) sim->cpu.psw.ep << 14 |
|
||||
(uint32_t) sim->cpu.psw.ae << 13 |
|
||||
(uint32_t) sim->cpu.psw.id << 12 |
|
||||
(uint32_t) sim->cpu.psw.fro << 9 |
|
||||
(uint32_t) sim->cpu.psw.fiv << 8 |
|
||||
(uint32_t) sim->cpu.psw.fzd << 7 |
|
||||
(uint32_t) sim->cpu.psw.fov << 6 |
|
||||
(uint32_t) sim->cpu.psw.fud << 5 |
|
||||
(uint32_t) sim->cpu.psw.fpr << 4 |
|
||||
(uint32_t) sim->cpu.psw.cy << 3 |
|
||||
(uint32_t) sim->cpu.psw.ov << 2 |
|
||||
(uint32_t) sim->cpu.psw.s << 1 |
|
||||
(uint32_t) sim->cpu.psw.z
|
||||
;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare a simulation state instance for use */
|
||||
void vbInit(VB *sim) {
|
||||
|
||||
/* Breakpoint callbacks */
|
||||
sim->onException = NULL;
|
||||
sim->onExecute = NULL;
|
||||
sim->onFetch = NULL;
|
||||
sim->onRead = NULL;
|
||||
sim->onWrite = NULL;
|
||||
|
||||
/* System */
|
||||
sim->peer = NULL;
|
||||
|
||||
/* Cartridge */
|
||||
sim->cart.rom = NULL;
|
||||
sim->cart.romSize = 0;
|
||||
sim->cart.sram = NULL;
|
||||
sim->cart.sramSize = 0;
|
||||
|
||||
/* Everything else */
|
||||
vbReset(sim);
|
||||
}
|
||||
|
||||
/* Read a data unit from the bus */
|
||||
int32_t vbRead(VB *sim, uint32_t address, int type, int debug) {
|
||||
return type < 0 || type >= (int) sizeof TYPE_SIZES ? 0 :
|
||||
busRead(sim, address, type, debug);
|
||||
}
|
||||
|
||||
/* Simulate a hardware reset */
|
||||
void vbReset(VB *sim) {
|
||||
uint32_t x; /* Iterator */
|
||||
|
||||
/* Subsystem components */
|
||||
cpuReset(sim);
|
||||
|
||||
/* WRAM (the hardware does not do this) */
|
||||
for (x = 0; x < 0x10000; x++)
|
||||
sim->wram[x] = 0x00;
|
||||
}
|
||||
|
||||
/* Specify a breakpoint callback */
|
||||
void* vbSetCallback(VB *sim, int type, void *callback) {
|
||||
void **field; /* Pointer to field within simulation */
|
||||
void *prev; /* Previous value within field */
|
||||
|
||||
/* Select the field to update */
|
||||
switch (type) {
|
||||
case VB_ONEXCEPTION: field = (void *) &sim->onException; break;
|
||||
case VB_ONEXECUTE : field = (void *) &sim->onExecute ; break;
|
||||
case VB_ONFETCH : field = (void *) &sim->onFetch ; break;
|
||||
case VB_ONREAD : field = (void *) &sim->onRead ; break;
|
||||
case VB_ONWRITE : field = (void *) &sim->onWrite ; break;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Update the simulation field */
|
||||
prev = *field;
|
||||
*field = callback;
|
||||
return prev;
|
||||
}
|
||||
|
||||
/* Specify a new value for PC */
|
||||
uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
|
||||
value &= 0xFFFFFFFE;
|
||||
sim->cpu.busWait = 0;
|
||||
sim->cpu.causeCode = 0;
|
||||
sim->cpu.clocks = 0;
|
||||
sim->cpu.fetch = 0;
|
||||
sim->cpu.pc = value;
|
||||
sim->cpu.state = CPU_FETCH;
|
||||
sim->cpu.substring = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Specify a new value for a program register */
|
||||
int32_t vbSetProgramRegister(VB *sim, int id, int32_t value) {
|
||||
return id < 1 || id > 31 ? 0 : (sim->cpu.program[id] = value);
|
||||
}
|
||||
|
||||
/* Supply a ROM buffer */
|
||||
int vbSetROM(VB *sim, void *rom, uint32_t size) {
|
||||
|
||||
/* Check the buffer size */
|
||||
if (size < 1024 || size > 0x1000000 || ((size - 1) & size) != 0)
|
||||
return 0;
|
||||
|
||||
/* Configure the ROM buffer */
|
||||
sim->cart.rom = (uint8_t *) rom;
|
||||
sim->cart.romSize = size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Supply an SRAM buffer */
|
||||
int vbSetSRAM(VB *sim, void *sram, uint32_t size) {
|
||||
|
||||
/* Check the buffer size */
|
||||
if (size == 0 || ((size - 1) & size) != 0)
|
||||
return 0;
|
||||
|
||||
/* Configure the SRAM buffer */
|
||||
sim->cart.sram = (uint8_t *) sram;
|
||||
sim->cart.sramSize = size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Specify a new value for a system register */
|
||||
uint32_t vbSetSystemRegister(VB *sim, int id, uint32_t value) {
|
||||
return cpuSetSystemRegister(sim, id, value, 1);
|
||||
}
|
||||
|
||||
/* Write a data unit to the bus */
|
||||
void vbWrite(VB *sim, uint32_t address, int type, int32_t value, int debug) {
|
||||
if (type >= 0 && type < (int32_t) sizeof TYPE_SIZES)
|
||||
busWrite(sim, address, type, value, debug);
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
#ifndef __VB_H__
|
||||
#define __VB_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef VBAPI
|
||||
#define VBAPI extern
|
||||
#endif
|
||||
|
||||
/* Header includes */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
/********************************* Constants *********************************/
|
||||
|
||||
/* Memory access types */
|
||||
#define VB_CANCEL -1
|
||||
#define VB_S8 0
|
||||
#define VB_U8 1
|
||||
#define VB_S16 2
|
||||
#define VB_U16 3
|
||||
#define VB_S32 4
|
||||
|
||||
/* System register IDs */
|
||||
#define VB_ADTRE 25
|
||||
#define VB_CHCW 24
|
||||
#define VB_ECR 4
|
||||
#define VB_EIPC 0
|
||||
#define VB_EIPSW 1
|
||||
#define VB_FEPC 2
|
||||
#define VB_FEPSW 3
|
||||
#define VB_PIR 6
|
||||
#define VB_PSW 5
|
||||
#define VB_TKCW 7
|
||||
|
||||
/* PC types */
|
||||
#define VB_PC 0
|
||||
#define VB_PC_FROM 1
|
||||
#define VB_PC_TO 2
|
||||
|
||||
/* Breakpoint callback types */
|
||||
#define VB_ONEXCEPTION 0
|
||||
#define VB_ONEXECUTE 1
|
||||
#define VB_ONFETCH 2
|
||||
#define VB_ONREAD 3
|
||||
#define VB_ONWRITE 4
|
||||
|
||||
|
||||
|
||||
/*********************************** Types ***********************************/
|
||||
|
||||
/* Forward references */
|
||||
typedef struct VB VB;
|
||||
|
||||
/* Memory access */
|
||||
typedef struct {
|
||||
uint32_t address; /* Bus address being accessed */
|
||||
uint32_t clocks; /* Number of clocks required to complete */
|
||||
int32_t value; /* Value read (callback's responsibility) or to write */
|
||||
int8_t type; /* Data type of value */
|
||||
} VB_ACCESS;
|
||||
|
||||
/* CPU instruction */
|
||||
typedef struct {
|
||||
|
||||
/* Public fields */
|
||||
uint32_t address; /* Bus address */
|
||||
uint16_t bits[2]; /* Binary instruction code */
|
||||
uint8_t size; /* Size in bytes of the instruction */
|
||||
|
||||
/* Implementation fields */
|
||||
int32_t aux[2]; /* Auxiliary storage for CAXI and bit strings */
|
||||
uint8_t id; /* Internal operation ID */
|
||||
} VB_INSTRUCTION;
|
||||
|
||||
/* Breakpoint callbacks */
|
||||
typedef int (*VB_EXCEPTIONPROC)(VB *, uint16_t);
|
||||
typedef int (*VB_EXECUTEPROC )(VB *, VB_INSTRUCTION *);
|
||||
typedef int (*VB_FETCHPROC )(VB *, int, VB_ACCESS *);
|
||||
typedef int (*VB_READPROC )(VB *, VB_ACCESS *);
|
||||
typedef int (*VB_WRITEPROC )(VB *, VB_ACCESS *);
|
||||
|
||||
/* Simulation state */
|
||||
struct VB {
|
||||
|
||||
/* Game pak */
|
||||
struct {
|
||||
uint8_t *rom; /* Active ROM buffer */
|
||||
uint32_t romSize; /* Size of ROM data */
|
||||
uint8_t *sram; /* Active SRAM buffer */
|
||||
uint32_t sramSize; /* Size of SRAM data */
|
||||
} cart;
|
||||
|
||||
/* CPU */
|
||||
struct {
|
||||
|
||||
/* System registers */
|
||||
uint32_t adtre; /* Address trap register for execution */
|
||||
uint32_t eipc; /* Exception/Interrupt PC */
|
||||
uint32_t eipsw; /* Exception/Interrupt PSW */
|
||||
uint32_t fepc; /* Fatal error PC */
|
||||
uint32_t fepsw; /* Fatal error PSW */
|
||||
uint32_t sr29; /* Unknown system register */
|
||||
uint32_t sr31; /* Unknown system register */
|
||||
|
||||
/* Cache control word */
|
||||
struct {
|
||||
int8_t ice; /* Instruction cache enable */
|
||||
} chcw;
|
||||
|
||||
/* Exception cause register */
|
||||
struct {
|
||||
uint16_t eicc; /* Exception/interrupt cause code */
|
||||
uint16_t fecc; /* Fatal error cause code */
|
||||
} ecr;
|
||||
|
||||
/* Program status word */
|
||||
struct {
|
||||
int8_t ae; /* Address trap enable */
|
||||
int8_t cy; /* Carry */
|
||||
int8_t ep; /* Exception pending */
|
||||
int8_t fiv; /* Floating invalid */
|
||||
int8_t fov; /* Floating overflow */
|
||||
int8_t fpr; /* Floating precision */
|
||||
int8_t fro; /* Floating reserved operand */
|
||||
int8_t fud; /* Floating underflow */
|
||||
int8_t fzd; /* Floating zero divide */
|
||||
int8_t i; /* Interrupt level */
|
||||
int8_t id; /* Interrupt disable */
|
||||
int8_t np; /* NMI pending */
|
||||
int8_t ov; /* Overflow */
|
||||
int8_t s; /* Sign */
|
||||
int8_t z; /* Zero */
|
||||
} psw;
|
||||
|
||||
/* Other registers */
|
||||
uint32_t pc; /* Program counter */
|
||||
int32_t program[32]; /* program registers */
|
||||
|
||||
/* Other fields */
|
||||
VB_ACCESS access; /* Memory access descriptor */
|
||||
VB_INSTRUCTION inst; /* Instruction descriptor */
|
||||
uint8_t irq[5]; /* Interrupt request lines */
|
||||
uint8_t busWait; /* Memory access counter */
|
||||
uint16_t causeCode; /* Exception cause code */
|
||||
uint32_t clocks; /* Clocks until next action */
|
||||
int16_t fetch; /* Index of fetch unit */
|
||||
uint8_t state; /* Operations state */
|
||||
uint8_t substring; /* A bit string operation is in progress */
|
||||
} cpu;
|
||||
|
||||
/* Breakpoint callbacks */
|
||||
VB_EXCEPTIONPROC onException; /* CPU exception */
|
||||
VB_EXECUTEPROC onExecute; /* Instruction execute */
|
||||
VB_FETCHPROC onFetch; /* Instruction fetch */
|
||||
VB_READPROC onRead; /* Memory read */
|
||||
VB_WRITEPROC onWrite; /* Memory write */
|
||||
|
||||
/* Other fields */
|
||||
VB *peer; /* Communications peer */
|
||||
uint8_t wram[0x10000]; /* Main memory */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/******************************* API Commands ********************************/
|
||||
|
||||
VBAPI void vbConnect (VB *sim1, VB *sim2);
|
||||
VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
|
||||
VBAPI int vbEmulateMulti (VB **sims, int count, uint32_t *clocks);
|
||||
VBAPI void* vbGetCallback (VB *sim, int type);
|
||||
VBAPI uint32_t vbGetProgramCounter (VB *sim);
|
||||
VBAPI int32_t vbGetProgramRegister (VB *sim, int id);
|
||||
VBAPI void* vbGetROM (VB *sim, uint32_t *size);
|
||||
VBAPI void* vbGetSRAM (VB *sim, uint32_t *size);
|
||||
VBAPI uint32_t vbGetSystemRegister (VB *sim, int id);
|
||||
VBAPI void vbInit (VB *sim);
|
||||
VBAPI int32_t vbRead (VB *sim, uint32_t address, int type, int debug);
|
||||
VBAPI void vbReset (VB *sim);
|
||||
VBAPI void* vbSetCallback (VB *sim, int type, void *callback);
|
||||
VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value);
|
||||
VBAPI int32_t vbSetProgramRegister (VB *sim, int id, int32_t value);
|
||||
VBAPI int vbSetROM (VB *sim, void *rom, uint32_t size);
|
||||
VBAPI int vbSetSRAM (VB *sim, void *sram, uint32_t size);
|
||||
VBAPI uint32_t vbSetSystemRegister (VB *sim, int id, uint32_t value);
|
||||
VBAPI void vbWrite (VB *sim, uint32_t address, int type, int32_t value, int debug);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __VB_H__ */
|
||||
@@ -1,705 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Expression Evaluator</title>
|
||||
<style>
|
||||
:root {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size : 16px;
|
||||
text-align : justify;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
a { text-decoration : underline; }
|
||||
a.ext { text-decoration-style: dotted; }
|
||||
body { margin : 8px; }
|
||||
circle, path, rect {
|
||||
fill : none;
|
||||
paint-order : markers fill stroke;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
code, pre, .mono { font-family: Consolas, monospace; }
|
||||
h1 {
|
||||
border-radius: 8px;
|
||||
font-size : 20px;
|
||||
font-weight : normal;
|
||||
padding : 4px 8px;
|
||||
}
|
||||
h1.top {
|
||||
font-weight: bold;
|
||||
font-size : 24px;
|
||||
text-align : center;
|
||||
}
|
||||
h1.bottom {
|
||||
font-size : 16px;
|
||||
padding : 6px 8px;
|
||||
text-align: right;
|
||||
}
|
||||
h2 {
|
||||
font-size : 18px;
|
||||
font-weight: bold;
|
||||
margin-top : 32px;
|
||||
}
|
||||
sup, .small { font-size: 10px; }
|
||||
table {
|
||||
border : none;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
td, th { padding-right : 12px; }
|
||||
tr { vertical-align: top; }
|
||||
.b {
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
font-size : 1px;
|
||||
height : 4px;
|
||||
padding : 0;
|
||||
width : 16px;
|
||||
}
|
||||
.bb { border-bottom-width: 1px; }
|
||||
.bc, .bd, .be {
|
||||
font-size : 12px;
|
||||
height : 32px;
|
||||
position : relative;
|
||||
text-align: center;
|
||||
}
|
||||
.bd { height: 48px; }
|
||||
.be { height: 72px; }
|
||||
.bh {
|
||||
font-size : 10px;
|
||||
padding : 2px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.bl { border-left-width : 1px; }
|
||||
.br { border-right-width : 1px; }
|
||||
.bs, .by, .bz {
|
||||
box-sizing : border-box;
|
||||
height : 17px;
|
||||
left : 17px;
|
||||
line-height : 17px;
|
||||
margin-left : -1px;
|
||||
position : absolute;
|
||||
text-align : center;
|
||||
top : -4px;
|
||||
transform : rotate(90deg);
|
||||
transform-origin: 0 0;
|
||||
white-space : nowrap;
|
||||
width : 41px;
|
||||
}
|
||||
.by {
|
||||
font-size: 10px;
|
||||
width : 82px;
|
||||
}
|
||||
.bz {
|
||||
font-size: 10px;
|
||||
width : 57px;
|
||||
}
|
||||
.bt { border-top-width : 1px; }
|
||||
.center { text-align : center; }
|
||||
.ednote {
|
||||
border-radius: 8px;
|
||||
border-style : solid;
|
||||
border-width : 1px;
|
||||
font-style : italic;
|
||||
margin-left : 16px;
|
||||
margin-right : 16px;
|
||||
padding : 4px 8px;
|
||||
}
|
||||
.indent { margin-left : 16px; }
|
||||
.middle { vertical-align: middle; }
|
||||
.minor { font-size : 14px; }
|
||||
.nowrap { white-space : nowrap; }
|
||||
.narrow { width : 1px; }
|
||||
.outdent { margin-left : -16px; }
|
||||
.right { text-align : right; }
|
||||
|
||||
:root { color : #000000; }
|
||||
a { color : #0099ff; }
|
||||
a.ext { color : #00bb66; }
|
||||
a.redlink { color : #ff3366; }
|
||||
body { background : #ffffff; }
|
||||
h1, .shade { background : #d4d4d4; }
|
||||
.b { border-color: #666666; }
|
||||
.ednote { border-color: #999999; color: #666666; }
|
||||
.fill { fill : #ffffff; }
|
||||
.stroke { stroke : #666666; }
|
||||
|
||||
.bordered {
|
||||
border-style: solid;
|
||||
border-color: #999999;
|
||||
border-width: 1px 0 0 1px;
|
||||
}
|
||||
|
||||
.bordered td {
|
||||
border-style: solid;
|
||||
border-color: #999999;
|
||||
border-width: 0 1px 1px 0;
|
||||
}
|
||||
|
||||
.bordered tr > :first-child, .op1 {
|
||||
padding: 1px 12px 1px 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bordered td.open { border-bottom: none; }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1 class="top">Expression Evaluator</h1>
|
||||
<p>
|
||||
The emulator is capable of evaluating expressions within breakpoint
|
||||
conditions and Go To prompts. These expressions inspect the current emulation
|
||||
state and can be used to dynamically process registers, variables and other
|
||||
data elements that may change from one invocation to the next.
|
||||
</p>
|
||||
<p>
|
||||
Expressions are organized into series of tokens with three main modes of
|
||||
significance:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Value</td>
|
||||
<td>
|
||||
Numeric value that can be used in computation. This may take the form of
|
||||
a literal number provided directly in the expression, or one of the
|
||||
named symbols that represent things in the CPU state, current
|
||||
instruction and/or current memory access.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Operator</td>
|
||||
<td>
|
||||
Modifies one or two values through a given operation. Unary operators
|
||||
modify only the value on their right, while binary operators consider the
|
||||
values on their left and right and produce a new value as the result.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Group</td>
|
||||
<td>
|
||||
Operand groups, mainly through the use of parentheses <code>()</code>,
|
||||
override the default order of operations. Anything enclosed in a group
|
||||
is processed before any operations adjacent to that group.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Expressions are case-insensitive.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<h2>Values</h2>
|
||||
<p>
|
||||
Values come in two forms:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Literal</td>
|
||||
<td>Provided directly by the expression.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Symbol</td>
|
||||
<td>Refers to something in the current emulation state.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Values may be one of four data types:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Signed Word</td>
|
||||
<td>
|
||||
32-bit, two's complement integer.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Unsigned Word</td>
|
||||
<td>
|
||||
32-bit, unsigned integer.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Float</td>
|
||||
<td>
|
||||
32-bit floating short in IEEE-754 format.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Boolean</td>
|
||||
<td>
|
||||
1-bit value representing whether a condition is true or false.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Signed word and unsigned word are represented as sequences of character
|
||||
digits. A numeric token that begins with the characters <code>0x</code>
|
||||
will be interpreted as hexadecimal, allowing letters <code>A</code> through
|
||||
<code>F</code> to be used as digits as well. If the given value can be
|
||||
represented in the signed word data type, the token is a signed word.
|
||||
Otherwise, if it can be represented in the unsigned word data type, the token
|
||||
is an unsigned word. If the value cannot be represented in either data type,
|
||||
a parsing error occurs.
|
||||
</p>
|
||||
<p>
|
||||
Floats, like words, are represented as sequences of character digits. They
|
||||
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
|
||||
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.
|
||||
Float values in the expression evaluator are subjected to the following
|
||||
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
|
||||
changed to positive zero.
|
||||
</p>
|
||||
<p>
|
||||
Booleans are mechanically identical to signed words, but the semantics are
|
||||
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
|
||||
literals are specified with the named values <code>true</code> and
|
||||
<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.
|
||||
</p>
|
||||
<p>
|
||||
Operators exist for converting between data types (see Operators below). A
|
||||
Go To expression will only be valid if its final data type is signed word or
|
||||
unsigned word.
|
||||
</p>
|
||||
<p>
|
||||
The following symbols may be used for accessing information about the current
|
||||
instruction being executed:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="mono narrow">address</td>
|
||||
<td>Current memory access's address.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">break</td>
|
||||
<td>
|
||||
Identifies what type of break scenario is being considered. See below for
|
||||
a list of identifiers.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">code</td>
|
||||
<td>Current exception's exception code.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">cond</td>
|
||||
<td>
|
||||
Condition code for <code>Bcond</code> and <code>SETF</code>
|
||||
instructions. See below for a list of conditions.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">disp</td>
|
||||
<td>Displacement offset for jumps and memory accesses.</td>
|
||||
</tr>
|
||||
<!--tr>
|
||||
<td class="mono">fetch</td>
|
||||
<td>
|
||||
Data unit index during a fetch operation, or <code>-1</code> if the read
|
||||
operation is not a fetch.
|
||||
</td>
|
||||
</tr-->
|
||||
<tr>
|
||||
<td class="mono">format</td>
|
||||
<td>Current instruction's encoding format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">id</td>
|
||||
<td>
|
||||
Identifies which specific instruction is being executed, considering
|
||||
<code>opcode</code> and, where applicable, <code>subopcode</code>. See
|
||||
below for a list of identifiers.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">imm</td>
|
||||
<td>Immediate operand.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">opcode</td>
|
||||
<td>Current instruction's top-level opcode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">reg1</td>
|
||||
<td>Source register index.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">reg2</td>
|
||||
<td>Destination register index.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">regid</td>
|
||||
<td>
|
||||
System register index in <code>LDSR</code> and <code>STSR</code>
|
||||
instructions.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">size</td>
|
||||
<td>Number of bytes occupied by the current instruction.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">subopcode</td>
|
||||
<td>Current instruction's secondary opcode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">type</td>
|
||||
<td>Data type of the current memory access.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">value</td>
|
||||
<td>Value read or to be written by the current memory access.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">vector</td>
|
||||
<td>Vector for <code>TRAP</code> instructions.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
The following symbols may be used in conjunction with the <code>break</code>
|
||||
symbol:
|
||||
</p>
|
||||
<div class="indent mono" style="column-width: 75px;">
|
||||
<div>exception</div><div>execute</div><div>read</div><div>write</div>
|
||||
</div>
|
||||
<p>
|
||||
The following symbols may be used in conjunction with the <code>cond</code>
|
||||
symbol:
|
||||
</p>
|
||||
<div class="indent mono" style="column-width: 75px;">
|
||||
<div>c </div><div>e </div><div>f </div><div>ge</div><div>gt</div>
|
||||
<div>h </div><div>l </div><div>le</div><div>lt</div><div>n </div>
|
||||
<div>nc</div><div>ne</div><div>nh</div><div>nl</div><div>nv</div>
|
||||
<div>nz</div><div>p </div><div>t </div><div>v </div><div>z </div>
|
||||
</div>
|
||||
<p>
|
||||
The following symbols may be used in conjunction with the <code>id</code>
|
||||
symbol:
|
||||
</p>
|
||||
<div class="indent mono" style="column-width: 75px;">
|
||||
<div>illegal</div><div>add_imm</div><div>add_reg</div><div>addf.s </div>
|
||||
<div>addi </div><div>and </div><div>andbsu </div><div>andi </div>
|
||||
<div>andnbsu</div><div>bcond </div><div>caxi </div><div>cli </div>
|
||||
<div>cmp_imm</div><div>cmp_reg</div><div>cmpf.s </div><div>cvt.sw </div>
|
||||
<div>cvt.ws </div><div>div </div><div>divf.s </div><div>divu </div>
|
||||
<div>halt </div><div>in.b </div><div>in.h </div><div>in.w </div>
|
||||
<div>jal </div><div>jmp </div><div>jr </div><div>ld.b </div>
|
||||
<div>ld.h </div><div>ld.w </div><div>ldsr </div><div>mov_imm</div>
|
||||
<div>mov_reg</div><div>movbsu </div><div>movea </div><div>movhi </div>
|
||||
<div>mpyhw </div><div>mul </div><div>mulf.s </div><div>mulu </div>
|
||||
<div>not </div><div>notbsu </div><div>or </div><div>orbsu </div>
|
||||
<div>ori </div><div>ornbsu </div><div>out.b </div><div>out.h </div>
|
||||
<div>out.w </div><div>reti </div><div>rev </div><div>sar_imm</div>
|
||||
<div>sar_reg</div><div>sch0bsd</div><div>sch0bsu</div><div>sch1bsd</div>
|
||||
<div>sch1bsu</div><div>sei </div><div>setf </div><div>shl_imm</div>
|
||||
<div>shl_reg</div><div>shr_imm</div><div>shr_reg</div><div>st.b </div>
|
||||
<div>st.h </div><div>st.w </div><div>stsr </div><div>sub </div>
|
||||
<div>subf.s </div><div>trap </div><div>trnc.sw</div><div>xb </div>
|
||||
<div>xh </div><div>xor </div><div>xorbsu </div><div>xori </div>
|
||||
<div>xornbsu</div>
|
||||
</div>
|
||||
<p>
|
||||
The following symbols may be used to retrieve the contents of a CPU program
|
||||
register:
|
||||
</p>
|
||||
<div class="indent mono" style="column-width: 75px;">
|
||||
<div>r0 </div><div>r1 </div><div>r2 </div><div>r3 </div><div>r4 </div>
|
||||
<div>r5 </div><div>r6 </div><div>r7 </div><div>r8 </div><div>r9 </div>
|
||||
<div>r10</div><div>r11</div><div>r12</div><div>r13</div><div>r14</div>
|
||||
<div>r15</div><div>r16</div><div>r17</div><div>r18</div><div>r19</div>
|
||||
<div>r20</div><div>r21</div><div>r22</div><div>r23</div><div>r24</div>
|
||||
<div>r25</div><div>r26</div><div>r27</div><div>r28</div><div>r29</div>
|
||||
<div>r30</div><div>r31</div><div>gp </div><div>hp </div><div>lp </div>
|
||||
<div>sp </div><div>tp </div>
|
||||
</div>
|
||||
<p>
|
||||
The following symbols may be used to retrieve the contents of a CPU system
|
||||
register:
|
||||
</p>
|
||||
<div class="indent mono" style="column-width: 75px;">
|
||||
<div>adtre</div><div>chcw </div><div>ecr </div><div>eipc</div>
|
||||
<div>eipsw</div><div>fepc </div><div>fepsw</div><div>pc </div>
|
||||
<div>pir </div><div>psw </div><div>tkcw </div><div>sr29</div>
|
||||
<div>sr30 </div><div>sr31 </div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Operators</h2>
|
||||
<p>
|
||||
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
|
||||
operator). Binary operators appear between the values they modify.
|
||||
</p>
|
||||
<p>
|
||||
Each operator considers the types of its operands in order to produce a new
|
||||
value of the appropriate type. If the operands of a binary operator have
|
||||
different types, one of the values will be converted to the other type before
|
||||
performing the operation. The conversion selects the "greater" of the two
|
||||
types, in the following order (higher is "greater"):
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr><td class="center narrow">↑</td><td>Float</td></tr>
|
||||
<tr><td class="center"></td><td>Unsigned word</td></tr>
|
||||
<tr><td class="center"></td><td>Signed word</td></tr>
|
||||
<tr><td class="center">↓</td><td>Boolean</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
For example, if an operation contains both a signed word and a float, the
|
||||
signed word value is converted to float before performing the operation.
|
||||
</p>
|
||||
<p>
|
||||
Operators have assigned precedence that specifies the order of operations.
|
||||
For example, in the expression <code>1 + 2 * 3</code>, the multiplication
|
||||
happens before the addition because it has higher precedence. Parentheses
|
||||
<code>()</code> may be used to encapsulate operations and guarantee that they
|
||||
are evaluated first regardless of the relative precedence of the adjacent
|
||||
operators. For example, in the expression <code>(1 + 2) * 3</code>, the
|
||||
addition happens before the multiplication because it is enclosed in
|
||||
parentheses.
|
||||
</p>
|
||||
<p>
|
||||
The following operators may be used in expressions. Groups listed higher have
|
||||
higher precedence and happen before groups listed lower. Operators within
|
||||
groups have the same precedence and are processed according to the order in
|
||||
which they appear in the expression: right-to-left for unary operators,
|
||||
left-to-right for binary operators.
|
||||
</p>
|
||||
<table class="indent bordered">
|
||||
<tr>
|
||||
<td rowSpan="18" class="middle">Unary</td>
|
||||
<td class="mono open op1">~</td>
|
||||
<td class="open">Not Bitwise</td>
|
||||
<td class="open">Cannot be used with a float value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">!</td>
|
||||
<td class="open">Not Logical</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">-</td>
|
||||
<td class="open">Negate</td>
|
||||
<td class="open">Cannot be used with an unsigned word value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">bool</td>
|
||||
<td class="open">Cast to Boolean</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">ceil</td>
|
||||
<td class="open">Round Up</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">float</td>
|
||||
<td class="open">Cast to Float</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">floor</td>
|
||||
<td class="open">Round Down</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">round</td>
|
||||
<td class="open">Round to Nearest</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>s8</code>, <code>byte</code></td>
|
||||
<td class="open">Cast to Signed Byte</td>
|
||||
<td class="open">Result is of type signed word.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>s16</code>, <code>halfword</code></td>
|
||||
<td class="open">Cast to Signed Halfword</td>
|
||||
<td class="open">Result is of type signed word.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>s32</code>, <code>word</code></td>
|
||||
<td class="open">Cast to Signed Word</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">trunc</td>
|
||||
<td class="open">Truncate</td>
|
||||
<td class="open">Removes any fraction.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code><code>u8</code>, ubyte</code></td>
|
||||
<td class="open">Cast to Unsigned Byte</td>
|
||||
<td class="open">Result is of type unsigned word.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>u16</code>, <code>uhalfword</code></td>
|
||||
<td class="open">Cast to Unsigned Halfword</td>
|
||||
<td class="open">Result is of type unsigned word.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>u32</code>, <code>uword</code></td>
|
||||
<td class="open">Cast to Unsigned Word</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">xfloat</td>
|
||||
<td class="open">Reinterpret as Float</td>
|
||||
<td class="open">The binary value is not modified.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="open"><code>xs32</code>, <code>xword</code></td>
|
||||
<td class="open">Reinterpret as Signed Word</td>
|
||||
<td class="open">The binary value is not modified.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>xu32</code>, <code>xuword</code></td>
|
||||
<td>Reinterpret as Unsgned Word</td>
|
||||
<td>The binary value is not modified.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan="20" class="middle">Binary</td>
|
||||
<td class="mono open op1">/</td>
|
||||
<td class="open">Divide</td>
|
||||
<td class="open">Zero divisor yields zero as result.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">*</td>
|
||||
<td class="open">Multiply</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">%</td>
|
||||
<td>Remainder</td>
|
||||
<td>Zero divisor yields zero as result.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">+</td>
|
||||
<td class="open">Add</td>
|
||||
<td class="open"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">-</td>
|
||||
<td>Subtract</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open"><<</td>
|
||||
<td class="open">Shift Left</td>
|
||||
<td class="open">Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">>></td>
|
||||
<td class="open">Shift Right Arithmetic</td>
|
||||
<td class="open">Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">>>></td>
|
||||
<td>Shift Right Logical</td>
|
||||
<td>Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">></td>
|
||||
<td class="open">Greater</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">>=</td>
|
||||
<td class="open">Greater or Equal</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open"><</td>
|
||||
<td class="open">Less</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono"><=</td>
|
||||
<td>Less or Equal</td>
|
||||
<td>Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">==</td>
|
||||
<td class="open">Equal</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">!=</td>
|
||||
<td>Not Equal</td>
|
||||
<td>Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">&</td>
|
||||
<td>And Bitwise</td>
|
||||
<td>Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">^</td>
|
||||
<td>Exclusive Or Bitwise</td>
|
||||
<td>Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">|</td>
|
||||
<td>Or Bitwise</td>
|
||||
<td>Cannot be used with float values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">&&</td>
|
||||
<td>And Logical</td>
|
||||
<td>If left is true, returns right; else returns left.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">^^</td>
|
||||
<td>Exclusive Or Logical</td>
|
||||
<td>If only one operand is true, returns the truthy value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">||</td>
|
||||
<td>Or Logical</td>
|
||||
<td>If left is true, returns left; else returns right.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<h2>Memory Read</h2>
|
||||
<p>
|
||||
A value can be read from the memory bus of the emulation state. This is done
|
||||
by enclosing the part of the expression that represents the address in square
|
||||
brackets <code>[]</code>. This functions in an identical manner to
|
||||
parentheses <code>()</code>, but will additionally perform the read
|
||||
operation.
|
||||
</p>
|
||||
<p>
|
||||
Under most circumstances, a signed word read is performed. Certain operators
|
||||
can be placed in front of the group to alter how the read is performed:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• <code>float</code></td>
|
||||
<td>Behaves like <code>xfloat</code> instead.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• <code>s8</code>, <code>u8</code></td>
|
||||
<td>Performs a byte read of the specified signedness.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• <code>s16</code>, <code>u16</code></td>
|
||||
<td>Performs a halfword read of the specified signedness.</td>
|
||||
</tr>
|
||||
</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> </h1>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 220 B |
@@ -1,588 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef CLASSFILE_CONSTANTS_H
|
||||
#define CLASSFILE_CONSTANTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Classfile version number for this information */
|
||||
#define JVM_CLASSFILE_MAJOR_VERSION 59
|
||||
#define JVM_CLASSFILE_MINOR_VERSION 0
|
||||
|
||||
/* Flags */
|
||||
|
||||
enum {
|
||||
JVM_ACC_PUBLIC = 0x0001,
|
||||
JVM_ACC_PRIVATE = 0x0002,
|
||||
JVM_ACC_PROTECTED = 0x0004,
|
||||
JVM_ACC_STATIC = 0x0008,
|
||||
JVM_ACC_FINAL = 0x0010,
|
||||
JVM_ACC_SYNCHRONIZED = 0x0020,
|
||||
JVM_ACC_SUPER = 0x0020,
|
||||
JVM_ACC_VOLATILE = 0x0040,
|
||||
JVM_ACC_BRIDGE = 0x0040,
|
||||
JVM_ACC_TRANSIENT = 0x0080,
|
||||
JVM_ACC_VARARGS = 0x0080,
|
||||
JVM_ACC_NATIVE = 0x0100,
|
||||
JVM_ACC_INTERFACE = 0x0200,
|
||||
JVM_ACC_ABSTRACT = 0x0400,
|
||||
JVM_ACC_STRICT = 0x0800,
|
||||
JVM_ACC_SYNTHETIC = 0x1000,
|
||||
JVM_ACC_ANNOTATION = 0x2000,
|
||||
JVM_ACC_ENUM = 0x4000,
|
||||
JVM_ACC_MODULE = 0x8000
|
||||
};
|
||||
|
||||
#define JVM_ACC_PUBLIC_BIT 0
|
||||
#define JVM_ACC_PRIVATE_BIT 1
|
||||
#define JVM_ACC_PROTECTED_BIT 2
|
||||
#define JVM_ACC_STATIC_BIT 3
|
||||
#define JVM_ACC_FINAL_BIT 4
|
||||
#define JVM_ACC_SYNCHRONIZED_BIT 5
|
||||
#define JVM_ACC_SUPER_BIT 5
|
||||
#define JVM_ACC_VOLATILE_BIT 6
|
||||
#define JVM_ACC_BRIDGE_BIT 6
|
||||
#define JVM_ACC_TRANSIENT_BIT 7
|
||||
#define JVM_ACC_VARARGS_BIT 7
|
||||
#define JVM_ACC_NATIVE_BIT 8
|
||||
#define JVM_ACC_INTERFACE_BIT 9
|
||||
#define JVM_ACC_ABSTRACT_BIT 10
|
||||
#define JVM_ACC_STRICT_BIT 11
|
||||
#define JVM_ACC_SYNTHETIC_BIT 12
|
||||
#define JVM_ACC_ANNOTATION_BIT 13
|
||||
#define JVM_ACC_ENUM_BIT 14
|
||||
|
||||
/* Used in newarray instruction. */
|
||||
|
||||
enum {
|
||||
JVM_T_BOOLEAN = 4,
|
||||
JVM_T_CHAR = 5,
|
||||
JVM_T_FLOAT = 6,
|
||||
JVM_T_DOUBLE = 7,
|
||||
JVM_T_BYTE = 8,
|
||||
JVM_T_SHORT = 9,
|
||||
JVM_T_INT = 10,
|
||||
JVM_T_LONG = 11
|
||||
};
|
||||
|
||||
/* Constant Pool Entries */
|
||||
|
||||
enum {
|
||||
JVM_CONSTANT_Utf8 = 1,
|
||||
JVM_CONSTANT_Unicode = 2, /* unused */
|
||||
JVM_CONSTANT_Integer = 3,
|
||||
JVM_CONSTANT_Float = 4,
|
||||
JVM_CONSTANT_Long = 5,
|
||||
JVM_CONSTANT_Double = 6,
|
||||
JVM_CONSTANT_Class = 7,
|
||||
JVM_CONSTANT_String = 8,
|
||||
JVM_CONSTANT_Fieldref = 9,
|
||||
JVM_CONSTANT_Methodref = 10,
|
||||
JVM_CONSTANT_InterfaceMethodref = 11,
|
||||
JVM_CONSTANT_NameAndType = 12,
|
||||
JVM_CONSTANT_MethodHandle = 15, // JSR 292
|
||||
JVM_CONSTANT_MethodType = 16, // JSR 292
|
||||
JVM_CONSTANT_Dynamic = 17,
|
||||
JVM_CONSTANT_InvokeDynamic = 18,
|
||||
JVM_CONSTANT_Module = 19,
|
||||
JVM_CONSTANT_Package = 20,
|
||||
JVM_CONSTANT_ExternalMax = 20
|
||||
};
|
||||
|
||||
/* JVM_CONSTANT_MethodHandle subtypes */
|
||||
enum {
|
||||
JVM_REF_getField = 1,
|
||||
JVM_REF_getStatic = 2,
|
||||
JVM_REF_putField = 3,
|
||||
JVM_REF_putStatic = 4,
|
||||
JVM_REF_invokeVirtual = 5,
|
||||
JVM_REF_invokeStatic = 6,
|
||||
JVM_REF_invokeSpecial = 7,
|
||||
JVM_REF_newInvokeSpecial = 8,
|
||||
JVM_REF_invokeInterface = 9
|
||||
};
|
||||
|
||||
/* StackMapTable type item numbers */
|
||||
|
||||
enum {
|
||||
JVM_ITEM_Top = 0,
|
||||
JVM_ITEM_Integer = 1,
|
||||
JVM_ITEM_Float = 2,
|
||||
JVM_ITEM_Double = 3,
|
||||
JVM_ITEM_Long = 4,
|
||||
JVM_ITEM_Null = 5,
|
||||
JVM_ITEM_UninitializedThis = 6,
|
||||
JVM_ITEM_Object = 7,
|
||||
JVM_ITEM_Uninitialized = 8
|
||||
};
|
||||
|
||||
/* Type signatures */
|
||||
|
||||
enum {
|
||||
JVM_SIGNATURE_SLASH = '/',
|
||||
JVM_SIGNATURE_DOT = '.',
|
||||
JVM_SIGNATURE_SPECIAL = '<',
|
||||
JVM_SIGNATURE_ENDSPECIAL = '>',
|
||||
JVM_SIGNATURE_ARRAY = '[',
|
||||
JVM_SIGNATURE_BYTE = 'B',
|
||||
JVM_SIGNATURE_CHAR = 'C',
|
||||
JVM_SIGNATURE_CLASS = 'L',
|
||||
JVM_SIGNATURE_ENDCLASS = ';',
|
||||
JVM_SIGNATURE_ENUM = 'E',
|
||||
JVM_SIGNATURE_FLOAT = 'F',
|
||||
JVM_SIGNATURE_DOUBLE = 'D',
|
||||
JVM_SIGNATURE_FUNC = '(',
|
||||
JVM_SIGNATURE_ENDFUNC = ')',
|
||||
JVM_SIGNATURE_INT = 'I',
|
||||
JVM_SIGNATURE_LONG = 'J',
|
||||
JVM_SIGNATURE_SHORT = 'S',
|
||||
JVM_SIGNATURE_VOID = 'V',
|
||||
JVM_SIGNATURE_BOOLEAN = 'Z'
|
||||
};
|
||||
|
||||
/* Opcodes */
|
||||
|
||||
enum {
|
||||
JVM_OPC_nop = 0,
|
||||
JVM_OPC_aconst_null = 1,
|
||||
JVM_OPC_iconst_m1 = 2,
|
||||
JVM_OPC_iconst_0 = 3,
|
||||
JVM_OPC_iconst_1 = 4,
|
||||
JVM_OPC_iconst_2 = 5,
|
||||
JVM_OPC_iconst_3 = 6,
|
||||
JVM_OPC_iconst_4 = 7,
|
||||
JVM_OPC_iconst_5 = 8,
|
||||
JVM_OPC_lconst_0 = 9,
|
||||
JVM_OPC_lconst_1 = 10,
|
||||
JVM_OPC_fconst_0 = 11,
|
||||
JVM_OPC_fconst_1 = 12,
|
||||
JVM_OPC_fconst_2 = 13,
|
||||
JVM_OPC_dconst_0 = 14,
|
||||
JVM_OPC_dconst_1 = 15,
|
||||
JVM_OPC_bipush = 16,
|
||||
JVM_OPC_sipush = 17,
|
||||
JVM_OPC_ldc = 18,
|
||||
JVM_OPC_ldc_w = 19,
|
||||
JVM_OPC_ldc2_w = 20,
|
||||
JVM_OPC_iload = 21,
|
||||
JVM_OPC_lload = 22,
|
||||
JVM_OPC_fload = 23,
|
||||
JVM_OPC_dload = 24,
|
||||
JVM_OPC_aload = 25,
|
||||
JVM_OPC_iload_0 = 26,
|
||||
JVM_OPC_iload_1 = 27,
|
||||
JVM_OPC_iload_2 = 28,
|
||||
JVM_OPC_iload_3 = 29,
|
||||
JVM_OPC_lload_0 = 30,
|
||||
JVM_OPC_lload_1 = 31,
|
||||
JVM_OPC_lload_2 = 32,
|
||||
JVM_OPC_lload_3 = 33,
|
||||
JVM_OPC_fload_0 = 34,
|
||||
JVM_OPC_fload_1 = 35,
|
||||
JVM_OPC_fload_2 = 36,
|
||||
JVM_OPC_fload_3 = 37,
|
||||
JVM_OPC_dload_0 = 38,
|
||||
JVM_OPC_dload_1 = 39,
|
||||
JVM_OPC_dload_2 = 40,
|
||||
JVM_OPC_dload_3 = 41,
|
||||
JVM_OPC_aload_0 = 42,
|
||||
JVM_OPC_aload_1 = 43,
|
||||
JVM_OPC_aload_2 = 44,
|
||||
JVM_OPC_aload_3 = 45,
|
||||
JVM_OPC_iaload = 46,
|
||||
JVM_OPC_laload = 47,
|
||||
JVM_OPC_faload = 48,
|
||||
JVM_OPC_daload = 49,
|
||||
JVM_OPC_aaload = 50,
|
||||
JVM_OPC_baload = 51,
|
||||
JVM_OPC_caload = 52,
|
||||
JVM_OPC_saload = 53,
|
||||
JVM_OPC_istore = 54,
|
||||
JVM_OPC_lstore = 55,
|
||||
JVM_OPC_fstore = 56,
|
||||
JVM_OPC_dstore = 57,
|
||||
JVM_OPC_astore = 58,
|
||||
JVM_OPC_istore_0 = 59,
|
||||
JVM_OPC_istore_1 = 60,
|
||||
JVM_OPC_istore_2 = 61,
|
||||
JVM_OPC_istore_3 = 62,
|
||||
JVM_OPC_lstore_0 = 63,
|
||||
JVM_OPC_lstore_1 = 64,
|
||||
JVM_OPC_lstore_2 = 65,
|
||||
JVM_OPC_lstore_3 = 66,
|
||||
JVM_OPC_fstore_0 = 67,
|
||||
JVM_OPC_fstore_1 = 68,
|
||||
JVM_OPC_fstore_2 = 69,
|
||||
JVM_OPC_fstore_3 = 70,
|
||||
JVM_OPC_dstore_0 = 71,
|
||||
JVM_OPC_dstore_1 = 72,
|
||||
JVM_OPC_dstore_2 = 73,
|
||||
JVM_OPC_dstore_3 = 74,
|
||||
JVM_OPC_astore_0 = 75,
|
||||
JVM_OPC_astore_1 = 76,
|
||||
JVM_OPC_astore_2 = 77,
|
||||
JVM_OPC_astore_3 = 78,
|
||||
JVM_OPC_iastore = 79,
|
||||
JVM_OPC_lastore = 80,
|
||||
JVM_OPC_fastore = 81,
|
||||
JVM_OPC_dastore = 82,
|
||||
JVM_OPC_aastore = 83,
|
||||
JVM_OPC_bastore = 84,
|
||||
JVM_OPC_castore = 85,
|
||||
JVM_OPC_sastore = 86,
|
||||
JVM_OPC_pop = 87,
|
||||
JVM_OPC_pop2 = 88,
|
||||
JVM_OPC_dup = 89,
|
||||
JVM_OPC_dup_x1 = 90,
|
||||
JVM_OPC_dup_x2 = 91,
|
||||
JVM_OPC_dup2 = 92,
|
||||
JVM_OPC_dup2_x1 = 93,
|
||||
JVM_OPC_dup2_x2 = 94,
|
||||
JVM_OPC_swap = 95,
|
||||
JVM_OPC_iadd = 96,
|
||||
JVM_OPC_ladd = 97,
|
||||
JVM_OPC_fadd = 98,
|
||||
JVM_OPC_dadd = 99,
|
||||
JVM_OPC_isub = 100,
|
||||
JVM_OPC_lsub = 101,
|
||||
JVM_OPC_fsub = 102,
|
||||
JVM_OPC_dsub = 103,
|
||||
JVM_OPC_imul = 104,
|
||||
JVM_OPC_lmul = 105,
|
||||
JVM_OPC_fmul = 106,
|
||||
JVM_OPC_dmul = 107,
|
||||
JVM_OPC_idiv = 108,
|
||||
JVM_OPC_ldiv = 109,
|
||||
JVM_OPC_fdiv = 110,
|
||||
JVM_OPC_ddiv = 111,
|
||||
JVM_OPC_irem = 112,
|
||||
JVM_OPC_lrem = 113,
|
||||
JVM_OPC_frem = 114,
|
||||
JVM_OPC_drem = 115,
|
||||
JVM_OPC_ineg = 116,
|
||||
JVM_OPC_lneg = 117,
|
||||
JVM_OPC_fneg = 118,
|
||||
JVM_OPC_dneg = 119,
|
||||
JVM_OPC_ishl = 120,
|
||||
JVM_OPC_lshl = 121,
|
||||
JVM_OPC_ishr = 122,
|
||||
JVM_OPC_lshr = 123,
|
||||
JVM_OPC_iushr = 124,
|
||||
JVM_OPC_lushr = 125,
|
||||
JVM_OPC_iand = 126,
|
||||
JVM_OPC_land = 127,
|
||||
JVM_OPC_ior = 128,
|
||||
JVM_OPC_lor = 129,
|
||||
JVM_OPC_ixor = 130,
|
||||
JVM_OPC_lxor = 131,
|
||||
JVM_OPC_iinc = 132,
|
||||
JVM_OPC_i2l = 133,
|
||||
JVM_OPC_i2f = 134,
|
||||
JVM_OPC_i2d = 135,
|
||||
JVM_OPC_l2i = 136,
|
||||
JVM_OPC_l2f = 137,
|
||||
JVM_OPC_l2d = 138,
|
||||
JVM_OPC_f2i = 139,
|
||||
JVM_OPC_f2l = 140,
|
||||
JVM_OPC_f2d = 141,
|
||||
JVM_OPC_d2i = 142,
|
||||
JVM_OPC_d2l = 143,
|
||||
JVM_OPC_d2f = 144,
|
||||
JVM_OPC_i2b = 145,
|
||||
JVM_OPC_i2c = 146,
|
||||
JVM_OPC_i2s = 147,
|
||||
JVM_OPC_lcmp = 148,
|
||||
JVM_OPC_fcmpl = 149,
|
||||
JVM_OPC_fcmpg = 150,
|
||||
JVM_OPC_dcmpl = 151,
|
||||
JVM_OPC_dcmpg = 152,
|
||||
JVM_OPC_ifeq = 153,
|
||||
JVM_OPC_ifne = 154,
|
||||
JVM_OPC_iflt = 155,
|
||||
JVM_OPC_ifge = 156,
|
||||
JVM_OPC_ifgt = 157,
|
||||
JVM_OPC_ifle = 158,
|
||||
JVM_OPC_if_icmpeq = 159,
|
||||
JVM_OPC_if_icmpne = 160,
|
||||
JVM_OPC_if_icmplt = 161,
|
||||
JVM_OPC_if_icmpge = 162,
|
||||
JVM_OPC_if_icmpgt = 163,
|
||||
JVM_OPC_if_icmple = 164,
|
||||
JVM_OPC_if_acmpeq = 165,
|
||||
JVM_OPC_if_acmpne = 166,
|
||||
JVM_OPC_goto = 167,
|
||||
JVM_OPC_jsr = 168,
|
||||
JVM_OPC_ret = 169,
|
||||
JVM_OPC_tableswitch = 170,
|
||||
JVM_OPC_lookupswitch = 171,
|
||||
JVM_OPC_ireturn = 172,
|
||||
JVM_OPC_lreturn = 173,
|
||||
JVM_OPC_freturn = 174,
|
||||
JVM_OPC_dreturn = 175,
|
||||
JVM_OPC_areturn = 176,
|
||||
JVM_OPC_return = 177,
|
||||
JVM_OPC_getstatic = 178,
|
||||
JVM_OPC_putstatic = 179,
|
||||
JVM_OPC_getfield = 180,
|
||||
JVM_OPC_putfield = 181,
|
||||
JVM_OPC_invokevirtual = 182,
|
||||
JVM_OPC_invokespecial = 183,
|
||||
JVM_OPC_invokestatic = 184,
|
||||
JVM_OPC_invokeinterface = 185,
|
||||
JVM_OPC_invokedynamic = 186,
|
||||
JVM_OPC_new = 187,
|
||||
JVM_OPC_newarray = 188,
|
||||
JVM_OPC_anewarray = 189,
|
||||
JVM_OPC_arraylength = 190,
|
||||
JVM_OPC_athrow = 191,
|
||||
JVM_OPC_checkcast = 192,
|
||||
JVM_OPC_instanceof = 193,
|
||||
JVM_OPC_monitorenter = 194,
|
||||
JVM_OPC_monitorexit = 195,
|
||||
JVM_OPC_wide = 196,
|
||||
JVM_OPC_multianewarray = 197,
|
||||
JVM_OPC_ifnull = 198,
|
||||
JVM_OPC_ifnonnull = 199,
|
||||
JVM_OPC_goto_w = 200,
|
||||
JVM_OPC_jsr_w = 201,
|
||||
JVM_OPC_MAX = 201
|
||||
};
|
||||
|
||||
/* Opcode length initializer, use with something like:
|
||||
* unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
|
||||
*/
|
||||
#define JVM_OPCODE_LENGTH_INITIALIZER { \
|
||||
1, /* nop */ \
|
||||
1, /* aconst_null */ \
|
||||
1, /* iconst_m1 */ \
|
||||
1, /* iconst_0 */ \
|
||||
1, /* iconst_1 */ \
|
||||
1, /* iconst_2 */ \
|
||||
1, /* iconst_3 */ \
|
||||
1, /* iconst_4 */ \
|
||||
1, /* iconst_5 */ \
|
||||
1, /* lconst_0 */ \
|
||||
1, /* lconst_1 */ \
|
||||
1, /* fconst_0 */ \
|
||||
1, /* fconst_1 */ \
|
||||
1, /* fconst_2 */ \
|
||||
1, /* dconst_0 */ \
|
||||
1, /* dconst_1 */ \
|
||||
2, /* bipush */ \
|
||||
3, /* sipush */ \
|
||||
2, /* ldc */ \
|
||||
3, /* ldc_w */ \
|
||||
3, /* ldc2_w */ \
|
||||
2, /* iload */ \
|
||||
2, /* lload */ \
|
||||
2, /* fload */ \
|
||||
2, /* dload */ \
|
||||
2, /* aload */ \
|
||||
1, /* iload_0 */ \
|
||||
1, /* iload_1 */ \
|
||||
1, /* iload_2 */ \
|
||||
1, /* iload_3 */ \
|
||||
1, /* lload_0 */ \
|
||||
1, /* lload_1 */ \
|
||||
1, /* lload_2 */ \
|
||||
1, /* lload_3 */ \
|
||||
1, /* fload_0 */ \
|
||||
1, /* fload_1 */ \
|
||||
1, /* fload_2 */ \
|
||||
1, /* fload_3 */ \
|
||||
1, /* dload_0 */ \
|
||||
1, /* dload_1 */ \
|
||||
1, /* dload_2 */ \
|
||||
1, /* dload_3 */ \
|
||||
1, /* aload_0 */ \
|
||||
1, /* aload_1 */ \
|
||||
1, /* aload_2 */ \
|
||||
1, /* aload_3 */ \
|
||||
1, /* iaload */ \
|
||||
1, /* laload */ \
|
||||
1, /* faload */ \
|
||||
1, /* daload */ \
|
||||
1, /* aaload */ \
|
||||
1, /* baload */ \
|
||||
1, /* caload */ \
|
||||
1, /* saload */ \
|
||||
2, /* istore */ \
|
||||
2, /* lstore */ \
|
||||
2, /* fstore */ \
|
||||
2, /* dstore */ \
|
||||
2, /* astore */ \
|
||||
1, /* istore_0 */ \
|
||||
1, /* istore_1 */ \
|
||||
1, /* istore_2 */ \
|
||||
1, /* istore_3 */ \
|
||||
1, /* lstore_0 */ \
|
||||
1, /* lstore_1 */ \
|
||||
1, /* lstore_2 */ \
|
||||
1, /* lstore_3 */ \
|
||||
1, /* fstore_0 */ \
|
||||
1, /* fstore_1 */ \
|
||||
1, /* fstore_2 */ \
|
||||
1, /* fstore_3 */ \
|
||||
1, /* dstore_0 */ \
|
||||
1, /* dstore_1 */ \
|
||||
1, /* dstore_2 */ \
|
||||
1, /* dstore_3 */ \
|
||||
1, /* astore_0 */ \
|
||||
1, /* astore_1 */ \
|
||||
1, /* astore_2 */ \
|
||||
1, /* astore_3 */ \
|
||||
1, /* iastore */ \
|
||||
1, /* lastore */ \
|
||||
1, /* fastore */ \
|
||||
1, /* dastore */ \
|
||||
1, /* aastore */ \
|
||||
1, /* bastore */ \
|
||||
1, /* castore */ \
|
||||
1, /* sastore */ \
|
||||
1, /* pop */ \
|
||||
1, /* pop2 */ \
|
||||
1, /* dup */ \
|
||||
1, /* dup_x1 */ \
|
||||
1, /* dup_x2 */ \
|
||||
1, /* dup2 */ \
|
||||
1, /* dup2_x1 */ \
|
||||
1, /* dup2_x2 */ \
|
||||
1, /* swap */ \
|
||||
1, /* iadd */ \
|
||||
1, /* ladd */ \
|
||||
1, /* fadd */ \
|
||||
1, /* dadd */ \
|
||||
1, /* isub */ \
|
||||
1, /* lsub */ \
|
||||
1, /* fsub */ \
|
||||
1, /* dsub */ \
|
||||
1, /* imul */ \
|
||||
1, /* lmul */ \
|
||||
1, /* fmul */ \
|
||||
1, /* dmul */ \
|
||||
1, /* idiv */ \
|
||||
1, /* ldiv */ \
|
||||
1, /* fdiv */ \
|
||||
1, /* ddiv */ \
|
||||
1, /* irem */ \
|
||||
1, /* lrem */ \
|
||||
1, /* frem */ \
|
||||
1, /* drem */ \
|
||||
1, /* ineg */ \
|
||||
1, /* lneg */ \
|
||||
1, /* fneg */ \
|
||||
1, /* dneg */ \
|
||||
1, /* ishl */ \
|
||||
1, /* lshl */ \
|
||||
1, /* ishr */ \
|
||||
1, /* lshr */ \
|
||||
1, /* iushr */ \
|
||||
1, /* lushr */ \
|
||||
1, /* iand */ \
|
||||
1, /* land */ \
|
||||
1, /* ior */ \
|
||||
1, /* lor */ \
|
||||
1, /* ixor */ \
|
||||
1, /* lxor */ \
|
||||
3, /* iinc */ \
|
||||
1, /* i2l */ \
|
||||
1, /* i2f */ \
|
||||
1, /* i2d */ \
|
||||
1, /* l2i */ \
|
||||
1, /* l2f */ \
|
||||
1, /* l2d */ \
|
||||
1, /* f2i */ \
|
||||
1, /* f2l */ \
|
||||
1, /* f2d */ \
|
||||
1, /* d2i */ \
|
||||
1, /* d2l */ \
|
||||
1, /* d2f */ \
|
||||
1, /* i2b */ \
|
||||
1, /* i2c */ \
|
||||
1, /* i2s */ \
|
||||
1, /* lcmp */ \
|
||||
1, /* fcmpl */ \
|
||||
1, /* fcmpg */ \
|
||||
1, /* dcmpl */ \
|
||||
1, /* dcmpg */ \
|
||||
3, /* ifeq */ \
|
||||
3, /* ifne */ \
|
||||
3, /* iflt */ \
|
||||
3, /* ifge */ \
|
||||
3, /* ifgt */ \
|
||||
3, /* ifle */ \
|
||||
3, /* if_icmpeq */ \
|
||||
3, /* if_icmpne */ \
|
||||
3, /* if_icmplt */ \
|
||||
3, /* if_icmpge */ \
|
||||
3, /* if_icmpgt */ \
|
||||
3, /* if_icmple */ \
|
||||
3, /* if_acmpeq */ \
|
||||
3, /* if_acmpne */ \
|
||||
3, /* goto */ \
|
||||
3, /* jsr */ \
|
||||
2, /* ret */ \
|
||||
99, /* tableswitch */ \
|
||||
99, /* lookupswitch */ \
|
||||
1, /* ireturn */ \
|
||||
1, /* lreturn */ \
|
||||
1, /* freturn */ \
|
||||
1, /* dreturn */ \
|
||||
1, /* areturn */ \
|
||||
1, /* return */ \
|
||||
3, /* getstatic */ \
|
||||
3, /* putstatic */ \
|
||||
3, /* getfield */ \
|
||||
3, /* putfield */ \
|
||||
3, /* invokevirtual */ \
|
||||
3, /* invokespecial */ \
|
||||
3, /* invokestatic */ \
|
||||
5, /* invokeinterface */ \
|
||||
5, /* invokedynamic */ \
|
||||
3, /* new */ \
|
||||
2, /* newarray */ \
|
||||
3, /* anewarray */ \
|
||||
1, /* arraylength */ \
|
||||
1, /* athrow */ \
|
||||
3, /* checkcast */ \
|
||||
3, /* instanceof */ \
|
||||
1, /* monitorenter */ \
|
||||
1, /* monitorexit */ \
|
||||
0, /* wide */ \
|
||||
4, /* multianewarray */ \
|
||||
3, /* ifnull */ \
|
||||
3, /* ifnonnull */ \
|
||||
5, /* goto_w */ \
|
||||
5 /* jsr_w */ \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CLASSFILE_CONSTANTS */
|
||||
@@ -1,356 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_H_
|
||||
#define _JAVASOFT_JAWT_H_
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AWT native interface.
|
||||
*
|
||||
* The AWT native interface allows a native C or C++ application a means
|
||||
* by which to access native structures in AWT. This is to facilitate moving
|
||||
* legacy C and C++ applications to Java and to target the needs of the
|
||||
* developers who need to do their own native rendering to canvases
|
||||
* for performance or other reasons.
|
||||
*
|
||||
* Conversely it also provides mechanisms for an application which already
|
||||
* has a native window to provide that to AWT for AWT rendering.
|
||||
*
|
||||
* Since every platform may be different in its native data structures
|
||||
* and APIs for windowing systems the application must necessarily
|
||||
* provided per-platform source and compile and deliver per-platform
|
||||
* native code to use this API.
|
||||
*
|
||||
* These interfaces are not part of the Java SE specification and
|
||||
* a VM is not required to implement this API. However it is strongly
|
||||
* recommended that all implementations which support headful AWT
|
||||
* also support these interfaces.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* AWT Native Drawing Surface (JAWT_DrawingSurface).
|
||||
*
|
||||
* For each platform, there is a native drawing surface structure. This
|
||||
* platform-specific structure can be found in jawt_md.h. It is recommended
|
||||
* that additional platforms follow the same model. It is also recommended
|
||||
* that VMs on all platforms support the existing structures in jawt_md.h.
|
||||
*
|
||||
*******************
|
||||
* EXAMPLE OF USAGE:
|
||||
*******************
|
||||
*
|
||||
* In Win32, a programmer wishes to access the HWND of a canvas to perform
|
||||
* native rendering into it. The programmer has declared the paint() method
|
||||
* for their canvas subclass to be native:
|
||||
*
|
||||
*
|
||||
* MyCanvas.java:
|
||||
*
|
||||
* import java.awt.*;
|
||||
*
|
||||
* public class MyCanvas extends Canvas {
|
||||
*
|
||||
* static {
|
||||
* System.loadLibrary("mylib");
|
||||
* }
|
||||
*
|
||||
* public native void paint(Graphics g);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* myfile.c:
|
||||
*
|
||||
* #include "jawt_md.h"
|
||||
* #include <assert.h>
|
||||
*
|
||||
* JNIEXPORT void JNICALL
|
||||
* Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
|
||||
* {
|
||||
* JAWT awt;
|
||||
* JAWT_DrawingSurface* ds;
|
||||
* JAWT_DrawingSurfaceInfo* dsi;
|
||||
* JAWT_Win32DrawingSurfaceInfo* dsi_win;
|
||||
* jboolean result;
|
||||
* jint lock;
|
||||
*
|
||||
* // Get the AWT. Request version 9 to access features in that release.
|
||||
* awt.version = JAWT_VERSION_9;
|
||||
* result = JAWT_GetAWT(env, &awt);
|
||||
* assert(result != JNI_FALSE);
|
||||
*
|
||||
* // Get the drawing surface
|
||||
* ds = awt.GetDrawingSurface(env, canvas);
|
||||
* assert(ds != NULL);
|
||||
*
|
||||
* // Lock the drawing surface
|
||||
* lock = ds->Lock(ds);
|
||||
* assert((lock & JAWT_LOCK_ERROR) == 0);
|
||||
*
|
||||
* // Get the drawing surface info
|
||||
* dsi = ds->GetDrawingSurfaceInfo(ds);
|
||||
*
|
||||
* // Get the platform-specific drawing info
|
||||
* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
|
||||
*
|
||||
* //////////////////////////////
|
||||
* // !!! DO PAINTING HERE !!! //
|
||||
* //////////////////////////////
|
||||
*
|
||||
* // Free the drawing surface info
|
||||
* ds->FreeDrawingSurfaceInfo(dsi);
|
||||
*
|
||||
* // Unlock the drawing surface
|
||||
* ds->Unlock(ds);
|
||||
*
|
||||
* // Free the drawing surface
|
||||
* awt.FreeDrawingSurface(ds);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* JAWT_Rectangle
|
||||
* Structure for a native rectangle.
|
||||
*/
|
||||
typedef struct jawt_Rectangle {
|
||||
jint x;
|
||||
jint y;
|
||||
jint width;
|
||||
jint height;
|
||||
} JAWT_Rectangle;
|
||||
|
||||
struct jawt_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurfaceInfo
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurfaceInfo {
|
||||
/*
|
||||
* Pointer to the platform-specific information. This can be safely
|
||||
* cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
|
||||
* JAWT_X11DrawingSurfaceInfo on Linux and Solaris. On Mac OS X this is a
|
||||
* pointer to a NSObject that conforms to the JAWT_SurfaceLayers
|
||||
* protocol. See jawt_md.h for details.
|
||||
*/
|
||||
void* platformInfo;
|
||||
/* Cached pointer to the underlying drawing surface */
|
||||
struct jawt_DrawingSurface* ds;
|
||||
/* Bounding rectangle of the drawing surface */
|
||||
JAWT_Rectangle bounds;
|
||||
/* Number of rectangles in the clip */
|
||||
jint clipSize;
|
||||
/* Clip rectangle array */
|
||||
JAWT_Rectangle* clip;
|
||||
} JAWT_DrawingSurfaceInfo;
|
||||
|
||||
#define JAWT_LOCK_ERROR 0x00000001
|
||||
#define JAWT_LOCK_CLIP_CHANGED 0x00000002
|
||||
#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
|
||||
#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurface
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
* All operations on a JAWT_DrawingSurface MUST be performed from the same
|
||||
* thread as the call to GetDrawingSurface.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurface {
|
||||
/*
|
||||
* Cached reference to the Java environment of the calling thread.
|
||||
* If Lock(), Unlock(), GetDrawingSurfaceInfo() or
|
||||
* FreeDrawingSurfaceInfo() are called from a different thread,
|
||||
* this data member should be set before calling those functions.
|
||||
*/
|
||||
JNIEnv* env;
|
||||
/* Cached reference to the target object */
|
||||
jobject target;
|
||||
/*
|
||||
* Lock the surface of the target component for native rendering.
|
||||
* When finished drawing, the surface must be unlocked with
|
||||
* Unlock(). This function returns a bitmask with one or more of the
|
||||
* following values:
|
||||
*
|
||||
* JAWT_LOCK_ERROR - When an error has occurred and the surface could not
|
||||
* be locked.
|
||||
*
|
||||
* JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
|
||||
*
|
||||
* JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
|
||||
*
|
||||
* JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
|
||||
*/
|
||||
jint (JNICALL *Lock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Get the drawing surface info.
|
||||
* The value returned may be cached, but the values may change if
|
||||
* additional calls to Lock() or Unlock() are made.
|
||||
* Lock() must be called before this can return a valid value.
|
||||
* Returns NULL if an error has occurred.
|
||||
* When finished with the returned value, FreeDrawingSurfaceInfo must be
|
||||
* called.
|
||||
*/
|
||||
JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Free the drawing surface info.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurfaceInfo)
|
||||
(JAWT_DrawingSurfaceInfo* dsi);
|
||||
/*
|
||||
* Unlock the drawing surface of the target component for native rendering.
|
||||
*/
|
||||
void (JNICALL *Unlock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
} JAWT_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT
|
||||
* Structure for containing native AWT functions.
|
||||
*/
|
||||
typedef struct jawt {
|
||||
/*
|
||||
* Version of this structure. This must always be set before
|
||||
* calling JAWT_GetAWT(). It affects the functions returned.
|
||||
* Must be one of the known pre-defined versions.
|
||||
*/
|
||||
jint version;
|
||||
/*
|
||||
* Return a drawing surface from a target jobject. This value
|
||||
* may be cached.
|
||||
* Returns NULL if an error has occurred.
|
||||
* Target must be a java.awt.Component (should be a Canvas
|
||||
* or Window for native rendering).
|
||||
* FreeDrawingSurface() must be called when finished with the
|
||||
* returned JAWT_DrawingSurface.
|
||||
*/
|
||||
JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
|
||||
(JNIEnv* env, jobject target);
|
||||
/*
|
||||
* Free the drawing surface allocated in GetDrawingSurface.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurface)
|
||||
(JAWT_DrawingSurface* ds);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Locks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Lock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Unlocks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Unlock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Returns a reference to a java.awt.Component from a native
|
||||
* platform handle. On Windows, this corresponds to an HWND;
|
||||
* on Solaris and Linux, this is a Drawable. For other platforms,
|
||||
* see the appropriate machine-dependent header file for a description.
|
||||
* The reference returned by this function is a local
|
||||
* reference that is only valid in this environment.
|
||||
* This function returns a NULL reference if no component could be
|
||||
* found with matching platform information.
|
||||
*/
|
||||
jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
|
||||
|
||||
/**
|
||||
* Since 9
|
||||
* Creates a java.awt.Frame placed in a native container. Container is
|
||||
* referenced by the native platform handle. For example on Windows this
|
||||
* corresponds to an HWND. For other platforms, see the appropriate
|
||||
* machine-dependent header file for a description. The reference returned
|
||||
* by this function is a local reference that is only valid in this
|
||||
* environment. This function returns a NULL reference if no frame could be
|
||||
* created with matching platform information.
|
||||
*/
|
||||
jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo);
|
||||
|
||||
/**
|
||||
* Since 9
|
||||
* Moves and resizes the embedded frame. The new location of the top-left
|
||||
* corner is specified by x and y parameters relative to the native parent
|
||||
* component. The new size is specified by width and height.
|
||||
*
|
||||
* The embedded frame should be created by CreateEmbeddedFrame() method, or
|
||||
* this function will not have any effect.
|
||||
*
|
||||
* java.awt.Component.setLocation() and java.awt.Component.setBounds() for
|
||||
* EmbeddedFrame really don't move it within the native parent. These
|
||||
* methods always locate the embedded frame at (0, 0) for backward
|
||||
* compatibility. To allow moving embedded frames this method was
|
||||
* introduced, and it works just the same way as setLocation() and
|
||||
* setBounds() for usual, non-embedded components.
|
||||
*
|
||||
* Using usual get/setLocation() and get/setBounds() together with this new
|
||||
* method is not recommended.
|
||||
*/
|
||||
void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame,
|
||||
jint x, jint y, jint w, jint h);
|
||||
/**
|
||||
* Since 9
|
||||
* Synthesize a native message to activate or deactivate an EmbeddedFrame
|
||||
* window depending on the value of parameter doActivate, if "true"
|
||||
* activates the window; otherwise, deactivates the window.
|
||||
*
|
||||
* The embedded frame should be created by CreateEmbeddedFrame() method, or
|
||||
* this function will not have any effect.
|
||||
*/
|
||||
void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env,
|
||||
jobject embeddedFrame, jboolean doActivate);
|
||||
} JAWT;
|
||||
|
||||
/*
|
||||
* Get the AWT native structure. This function returns JNI_FALSE if
|
||||
* an error occurs.
|
||||
*/
|
||||
_JNI_IMPORT_OR_EXPORT_
|
||||
jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
|
||||
|
||||
/*
|
||||
* Specify one of these constants as the JAWT.version
|
||||
* Specifying an earlier version will limit the available functions to
|
||||
* those provided in that earlier version of JAWT.
|
||||
* See the "Since" note on each API. Methods with no "Since"
|
||||
* may be presumed to be present in JAWT_VERSION_1_3.
|
||||
*/
|
||||
#define JAWT_VERSION_1_3 0x00010003
|
||||
#define JAWT_VERSION_1_4 0x00010004
|
||||
#define JAWT_VERSION_1_7 0x00010007
|
||||
#define JAWT_VERSION_9 0x00090000
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_H_ */
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Java Debug Wire Protocol Transport Service Provider Interface.
|
||||
*/
|
||||
|
||||
#ifndef JDWPTRANSPORT_H
|
||||
#define JDWPTRANSPORT_H
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
enum {
|
||||
JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
|
||||
JDWPTRANSPORT_VERSION_1_1 = 0x00010001
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct jdwpTransportNativeInterface_;
|
||||
|
||||
struct _jdwpTransportEnv;
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef _jdwpTransportEnv jdwpTransportEnv;
|
||||
#else
|
||||
typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Errors. Universal errors with JVMTI/JVMDI equivalents keep the
|
||||
* values the same.
|
||||
*/
|
||||
typedef enum {
|
||||
JDWPTRANSPORT_ERROR_NONE = 0,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
|
||||
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
|
||||
JDWPTRANSPORT_ERROR_INTERNAL = 113,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
|
||||
JDWPTRANSPORT_ERROR_IO_ERROR = 202,
|
||||
JDWPTRANSPORT_ERROR_TIMEOUT = 203,
|
||||
JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
|
||||
} jdwpTransportError;
|
||||
|
||||
|
||||
/*
|
||||
* Structure to define capabilities
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int can_timeout_attach :1;
|
||||
unsigned int can_timeout_accept :1;
|
||||
unsigned int can_timeout_handshake :1;
|
||||
unsigned int reserved3 :1;
|
||||
unsigned int reserved4 :1;
|
||||
unsigned int reserved5 :1;
|
||||
unsigned int reserved6 :1;
|
||||
unsigned int reserved7 :1;
|
||||
unsigned int reserved8 :1;
|
||||
unsigned int reserved9 :1;
|
||||
unsigned int reserved10 :1;
|
||||
unsigned int reserved11 :1;
|
||||
unsigned int reserved12 :1;
|
||||
unsigned int reserved13 :1;
|
||||
unsigned int reserved14 :1;
|
||||
unsigned int reserved15 :1;
|
||||
} JDWPTransportCapabilities;
|
||||
|
||||
|
||||
/*
|
||||
* Structures to define packet layout.
|
||||
*
|
||||
* See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
|
||||
*/
|
||||
|
||||
#define JDWP_HEADER_SIZE 11
|
||||
|
||||
enum {
|
||||
/*
|
||||
* If additional flags are added that apply to jdwpCmdPacket,
|
||||
* then debugLoop.c: reader() will need to be updated to
|
||||
* accept more than JDWPTRANSPORT_FLAGS_NONE.
|
||||
*/
|
||||
JDWPTRANSPORT_FLAGS_NONE = 0x0,
|
||||
JDWPTRANSPORT_FLAGS_REPLY = 0x80
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jbyte cmdSet;
|
||||
jbyte cmd;
|
||||
jbyte *data;
|
||||
} jdwpCmdPacket;
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jshort errorCode;
|
||||
jbyte *data;
|
||||
} jdwpReplyPacket;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
jdwpCmdPacket cmd;
|
||||
jdwpReplyPacket reply;
|
||||
} type;
|
||||
} jdwpPacket;
|
||||
|
||||
/*
|
||||
* JDWP functions called by the transport.
|
||||
*/
|
||||
typedef struct jdwpTransportCallback {
|
||||
void *(*alloc)(jint numBytes); /* Call this for all allocations */
|
||||
void (*free)(void *buffer); /* Call this for all deallocations */
|
||||
} jdwpTransportCallback;
|
||||
|
||||
typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
|
||||
jdwpTransportCallback *callback,
|
||||
jint version,
|
||||
jdwpTransportEnv** env);
|
||||
|
||||
/*
|
||||
* JDWP transport configuration from the agent.
|
||||
*/
|
||||
typedef struct jdwpTransportConfiguration {
|
||||
/* Field added in JDWPTRANSPORT_VERSION_1_1: */
|
||||
const char* allowed_peers; /* Peers allowed for connection */
|
||||
} jdwpTransportConfiguration;
|
||||
|
||||
|
||||
/* Function Interface */
|
||||
|
||||
struct jdwpTransportNativeInterface_ {
|
||||
/* 1 : RESERVED */
|
||||
void *reserved1;
|
||||
|
||||
/* 2 : Get Capabilities */
|
||||
jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
|
||||
JDWPTransportCapabilities *capabilities_ptr);
|
||||
|
||||
/* 3 : Attach */
|
||||
jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
jlong attach_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 4: StartListening */
|
||||
jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
char** actual_address);
|
||||
|
||||
/* 5: StopListening */
|
||||
jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
|
||||
|
||||
/* 6: Accept */
|
||||
jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
|
||||
jlong accept_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 7: IsOpen */
|
||||
jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
|
||||
|
||||
/* 8: Close */
|
||||
jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
|
||||
|
||||
/* 9: ReadPacket */
|
||||
jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
|
||||
jdwpPacket *pkt);
|
||||
|
||||
/* 10: Write Packet */
|
||||
jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
|
||||
const jdwpPacket* pkt);
|
||||
|
||||
/* 11: GetLastError */
|
||||
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
|
||||
char** error);
|
||||
|
||||
/* 12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
|
||||
jdwpTransportConfiguration *config);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Use inlined functions so that C++ code can use syntax such as
|
||||
* env->Attach("mymachine:5000", 10*1000, 0);
|
||||
*
|
||||
* rather than using C's :-
|
||||
*
|
||||
* (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
|
||||
*/
|
||||
struct _jdwpTransportEnv {
|
||||
const struct jdwpTransportNativeInterface_ *functions;
|
||||
#ifdef __cplusplus
|
||||
|
||||
jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
|
||||
return functions->GetCapabilities(this, capabilities_ptr);
|
||||
}
|
||||
|
||||
jdwpTransportError Attach(const char* address, jlong attach_timeout,
|
||||
jlong handshake_timeout) {
|
||||
return functions->Attach(this, address, attach_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jdwpTransportError StartListening(const char* address,
|
||||
char** actual_address) {
|
||||
return functions->StartListening(this, address, actual_address);
|
||||
}
|
||||
|
||||
jdwpTransportError StopListening(void) {
|
||||
return functions->StopListening(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
|
||||
return functions->Accept(this, accept_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jboolean IsOpen(void) {
|
||||
return functions->IsOpen(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Close(void) {
|
||||
return functions->Close(this);
|
||||
}
|
||||
|
||||
jdwpTransportError ReadPacket(jdwpPacket *pkt) {
|
||||
return functions->ReadPacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError WritePacket(const jdwpPacket* pkt) {
|
||||
return functions->WritePacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError GetLastError(char** error) {
|
||||
return functions->GetLastError(this, error);
|
||||
}
|
||||
|
||||
/* SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
|
||||
return functions->SetTransportConfiguration(this, config);
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* JDWPTRANSPORT_H */
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file defines the data structures sent by the VM
|
||||
* through the JVMTI CompiledMethodLoad callback function via the
|
||||
* "void * compile_info" parameter. The memory pointed to by the
|
||||
* compile_info parameter may not be referenced after returning from
|
||||
* the CompiledMethodLoad callback. These are VM implementation
|
||||
* specific data structures that may evolve in future releases. A
|
||||
* JVMTI agent should interpret a non-NULL compile_info as a pointer
|
||||
* to a region of memory containing a list of records. In a typical
|
||||
* usage scenario, a JVMTI agent would cast each record to a
|
||||
* jvmtiCompiledMethodLoadRecordHeader, a struct that represents
|
||||
* arbitrary information. This struct contains a kind field to indicate
|
||||
* the kind of information being passed, and a pointer to the next
|
||||
* record. If the kind field indicates inlining information, then the
|
||||
* agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord.
|
||||
* This record contains an array of PCStackInfo structs, which indicate
|
||||
* for every pc address what are the methods on the invocation stack.
|
||||
* The "methods" and "bcis" fields in each PCStackInfo struct specify a
|
||||
* 1-1 mapping between these inlined methods and their bytecode indices.
|
||||
* This can be used to derive the proper source lines of the inlined
|
||||
* methods.
|
||||
*/
|
||||
|
||||
#ifndef _JVMTI_CMLR_H_
|
||||
#define _JVMTI_CMLR_H_
|
||||
|
||||
enum {
|
||||
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
|
||||
|
||||
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION = 0x00000000
|
||||
|
||||
/*
|
||||
* This comment is for the "JDK import from HotSpot" sanity check:
|
||||
* version: 1.0.0
|
||||
*/
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
JVMTI_CMLR_DUMMY = 1,
|
||||
JVMTI_CMLR_INLINE_INFO = 2
|
||||
} jvmtiCMLRKind;
|
||||
|
||||
/*
|
||||
* Record that represents arbitrary information passed through JVMTI
|
||||
* CompiledMethodLoadEvent void pointer.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
|
||||
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
|
||||
jint majorinfoversion; /* major and minor info version values. Init'ed */
|
||||
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
|
||||
|
||||
struct _jvmtiCompiledMethodLoadRecordHeader* next;
|
||||
} jvmtiCompiledMethodLoadRecordHeader;
|
||||
|
||||
/*
|
||||
* Record that gives information about the methods on the compile-time
|
||||
* stack at a specific pc address of a compiled method. Each element in
|
||||
* the methods array maps to same element in the bcis array.
|
||||
*/
|
||||
typedef struct _PCStackInfo {
|
||||
void* pc; /* the pc address for this compiled method */
|
||||
jint numstackframes; /* number of methods on the stack */
|
||||
jmethodID* methods; /* array of numstackframes method ids */
|
||||
jint* bcis; /* array of numstackframes bytecode indices */
|
||||
} PCStackInfo;
|
||||
|
||||
/*
|
||||
* Record that contains inlining information for each pc address of
|
||||
* an nmethod.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
jint numpcs; /* number of pc descriptors in this nmethod */
|
||||
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
|
||||
} jvmtiCompiledMethodLoadInlineRecord;
|
||||
|
||||
/*
|
||||
* Dummy record used to test that we can pass records with different
|
||||
* information through the void pointer provided that they can be cast
|
||||
* to a jvmtiCompiledMethodLoadRecordHeader.
|
||||
*/
|
||||
|
||||
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
char message[50];
|
||||
} jvmtiCompiledMethodLoadDummyRecord;
|
||||
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_MD_H_
|
||||
#define _JAVASOFT_JAWT_MD_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include "jawt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* X11-specific declarations for AWT native interface.
|
||||
* See notes in jawt.h for an example of use.
|
||||
*/
|
||||
typedef struct jawt_X11DrawingSurfaceInfo {
|
||||
Drawable drawable;
|
||||
Display* display;
|
||||
VisualID visualID;
|
||||
Colormap colormapID;
|
||||
int depth;
|
||||
/*
|
||||
* Since 1.4
|
||||
* Returns a pixel value from a set of RGB values.
|
||||
* This is useful for paletted color (256 color) modes.
|
||||
*/
|
||||
int (JNICALL *GetAWTColor)(JAWT_DrawingSurface* ds,
|
||||
int r, int g, int b);
|
||||
} JAWT_X11DrawingSurfaceInfo;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_MD_H_ */
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef JNIEXPORT
|
||||
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
|
||||
#ifdef ARM
|
||||
#define JNIEXPORT __attribute__((externally_visible,visibility("default")))
|
||||
#else
|
||||
#define JNIEXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
#else
|
||||
#define JNIEXPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
|
||||
#ifdef ARM
|
||||
#define JNIIMPORT __attribute__((externally_visible,visibility("default")))
|
||||
#else
|
||||
#define JNIIMPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
#else
|
||||
#define JNIIMPORT
|
||||
#endif
|
||||
|
||||
#define JNICALL
|
||||
|
||||
typedef int jint;
|
||||
#ifdef _LP64
|
||||
typedef long jlong;
|
||||
#else
|
||||
typedef long long jlong;
|
||||
#endif
|
||||
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
@@ -1,588 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CLASSFILE_CONSTANTS_H
|
||||
#define CLASSFILE_CONSTANTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Classfile version number for this information */
|
||||
#define JVM_CLASSFILE_MAJOR_VERSION 58
|
||||
#define JVM_CLASSFILE_MINOR_VERSION 0
|
||||
|
||||
/* Flags */
|
||||
|
||||
enum {
|
||||
JVM_ACC_PUBLIC = 0x0001,
|
||||
JVM_ACC_PRIVATE = 0x0002,
|
||||
JVM_ACC_PROTECTED = 0x0004,
|
||||
JVM_ACC_STATIC = 0x0008,
|
||||
JVM_ACC_FINAL = 0x0010,
|
||||
JVM_ACC_SYNCHRONIZED = 0x0020,
|
||||
JVM_ACC_SUPER = 0x0020,
|
||||
JVM_ACC_VOLATILE = 0x0040,
|
||||
JVM_ACC_BRIDGE = 0x0040,
|
||||
JVM_ACC_TRANSIENT = 0x0080,
|
||||
JVM_ACC_VARARGS = 0x0080,
|
||||
JVM_ACC_NATIVE = 0x0100,
|
||||
JVM_ACC_INTERFACE = 0x0200,
|
||||
JVM_ACC_ABSTRACT = 0x0400,
|
||||
JVM_ACC_STRICT = 0x0800,
|
||||
JVM_ACC_SYNTHETIC = 0x1000,
|
||||
JVM_ACC_ANNOTATION = 0x2000,
|
||||
JVM_ACC_ENUM = 0x4000,
|
||||
JVM_ACC_MODULE = 0x8000
|
||||
};
|
||||
|
||||
#define JVM_ACC_PUBLIC_BIT 0
|
||||
#define JVM_ACC_PRIVATE_BIT 1
|
||||
#define JVM_ACC_PROTECTED_BIT 2
|
||||
#define JVM_ACC_STATIC_BIT 3
|
||||
#define JVM_ACC_FINAL_BIT 4
|
||||
#define JVM_ACC_SYNCHRONIZED_BIT 5
|
||||
#define JVM_ACC_SUPER_BIT 5
|
||||
#define JVM_ACC_VOLATILE_BIT 6
|
||||
#define JVM_ACC_BRIDGE_BIT 6
|
||||
#define JVM_ACC_TRANSIENT_BIT 7
|
||||
#define JVM_ACC_VARARGS_BIT 7
|
||||
#define JVM_ACC_NATIVE_BIT 8
|
||||
#define JVM_ACC_INTERFACE_BIT 9
|
||||
#define JVM_ACC_ABSTRACT_BIT 10
|
||||
#define JVM_ACC_STRICT_BIT 11
|
||||
#define JVM_ACC_SYNTHETIC_BIT 12
|
||||
#define JVM_ACC_ANNOTATION_BIT 13
|
||||
#define JVM_ACC_ENUM_BIT 14
|
||||
|
||||
/* Used in newarray instruction. */
|
||||
|
||||
enum {
|
||||
JVM_T_BOOLEAN = 4,
|
||||
JVM_T_CHAR = 5,
|
||||
JVM_T_FLOAT = 6,
|
||||
JVM_T_DOUBLE = 7,
|
||||
JVM_T_BYTE = 8,
|
||||
JVM_T_SHORT = 9,
|
||||
JVM_T_INT = 10,
|
||||
JVM_T_LONG = 11
|
||||
};
|
||||
|
||||
/* Constant Pool Entries */
|
||||
|
||||
enum {
|
||||
JVM_CONSTANT_Utf8 = 1,
|
||||
JVM_CONSTANT_Unicode = 2, /* unused */
|
||||
JVM_CONSTANT_Integer = 3,
|
||||
JVM_CONSTANT_Float = 4,
|
||||
JVM_CONSTANT_Long = 5,
|
||||
JVM_CONSTANT_Double = 6,
|
||||
JVM_CONSTANT_Class = 7,
|
||||
JVM_CONSTANT_String = 8,
|
||||
JVM_CONSTANT_Fieldref = 9,
|
||||
JVM_CONSTANT_Methodref = 10,
|
||||
JVM_CONSTANT_InterfaceMethodref = 11,
|
||||
JVM_CONSTANT_NameAndType = 12,
|
||||
JVM_CONSTANT_MethodHandle = 15, // JSR 292
|
||||
JVM_CONSTANT_MethodType = 16, // JSR 292
|
||||
JVM_CONSTANT_Dynamic = 17,
|
||||
JVM_CONSTANT_InvokeDynamic = 18,
|
||||
JVM_CONSTANT_Module = 19,
|
||||
JVM_CONSTANT_Package = 20,
|
||||
JVM_CONSTANT_ExternalMax = 20
|
||||
};
|
||||
|
||||
/* JVM_CONSTANT_MethodHandle subtypes */
|
||||
enum {
|
||||
JVM_REF_getField = 1,
|
||||
JVM_REF_getStatic = 2,
|
||||
JVM_REF_putField = 3,
|
||||
JVM_REF_putStatic = 4,
|
||||
JVM_REF_invokeVirtual = 5,
|
||||
JVM_REF_invokeStatic = 6,
|
||||
JVM_REF_invokeSpecial = 7,
|
||||
JVM_REF_newInvokeSpecial = 8,
|
||||
JVM_REF_invokeInterface = 9
|
||||
};
|
||||
|
||||
/* StackMapTable type item numbers */
|
||||
|
||||
enum {
|
||||
JVM_ITEM_Top = 0,
|
||||
JVM_ITEM_Integer = 1,
|
||||
JVM_ITEM_Float = 2,
|
||||
JVM_ITEM_Double = 3,
|
||||
JVM_ITEM_Long = 4,
|
||||
JVM_ITEM_Null = 5,
|
||||
JVM_ITEM_UninitializedThis = 6,
|
||||
JVM_ITEM_Object = 7,
|
||||
JVM_ITEM_Uninitialized = 8
|
||||
};
|
||||
|
||||
/* Type signatures */
|
||||
|
||||
enum {
|
||||
JVM_SIGNATURE_SLASH = '/',
|
||||
JVM_SIGNATURE_DOT = '.',
|
||||
JVM_SIGNATURE_SPECIAL = '<',
|
||||
JVM_SIGNATURE_ENDSPECIAL = '>',
|
||||
JVM_SIGNATURE_ARRAY = '[',
|
||||
JVM_SIGNATURE_BYTE = 'B',
|
||||
JVM_SIGNATURE_CHAR = 'C',
|
||||
JVM_SIGNATURE_CLASS = 'L',
|
||||
JVM_SIGNATURE_ENDCLASS = ';',
|
||||
JVM_SIGNATURE_ENUM = 'E',
|
||||
JVM_SIGNATURE_FLOAT = 'F',
|
||||
JVM_SIGNATURE_DOUBLE = 'D',
|
||||
JVM_SIGNATURE_FUNC = '(',
|
||||
JVM_SIGNATURE_ENDFUNC = ')',
|
||||
JVM_SIGNATURE_INT = 'I',
|
||||
JVM_SIGNATURE_LONG = 'J',
|
||||
JVM_SIGNATURE_SHORT = 'S',
|
||||
JVM_SIGNATURE_VOID = 'V',
|
||||
JVM_SIGNATURE_BOOLEAN = 'Z'
|
||||
};
|
||||
|
||||
/* Opcodes */
|
||||
|
||||
enum {
|
||||
JVM_OPC_nop = 0,
|
||||
JVM_OPC_aconst_null = 1,
|
||||
JVM_OPC_iconst_m1 = 2,
|
||||
JVM_OPC_iconst_0 = 3,
|
||||
JVM_OPC_iconst_1 = 4,
|
||||
JVM_OPC_iconst_2 = 5,
|
||||
JVM_OPC_iconst_3 = 6,
|
||||
JVM_OPC_iconst_4 = 7,
|
||||
JVM_OPC_iconst_5 = 8,
|
||||
JVM_OPC_lconst_0 = 9,
|
||||
JVM_OPC_lconst_1 = 10,
|
||||
JVM_OPC_fconst_0 = 11,
|
||||
JVM_OPC_fconst_1 = 12,
|
||||
JVM_OPC_fconst_2 = 13,
|
||||
JVM_OPC_dconst_0 = 14,
|
||||
JVM_OPC_dconst_1 = 15,
|
||||
JVM_OPC_bipush = 16,
|
||||
JVM_OPC_sipush = 17,
|
||||
JVM_OPC_ldc = 18,
|
||||
JVM_OPC_ldc_w = 19,
|
||||
JVM_OPC_ldc2_w = 20,
|
||||
JVM_OPC_iload = 21,
|
||||
JVM_OPC_lload = 22,
|
||||
JVM_OPC_fload = 23,
|
||||
JVM_OPC_dload = 24,
|
||||
JVM_OPC_aload = 25,
|
||||
JVM_OPC_iload_0 = 26,
|
||||
JVM_OPC_iload_1 = 27,
|
||||
JVM_OPC_iload_2 = 28,
|
||||
JVM_OPC_iload_3 = 29,
|
||||
JVM_OPC_lload_0 = 30,
|
||||
JVM_OPC_lload_1 = 31,
|
||||
JVM_OPC_lload_2 = 32,
|
||||
JVM_OPC_lload_3 = 33,
|
||||
JVM_OPC_fload_0 = 34,
|
||||
JVM_OPC_fload_1 = 35,
|
||||
JVM_OPC_fload_2 = 36,
|
||||
JVM_OPC_fload_3 = 37,
|
||||
JVM_OPC_dload_0 = 38,
|
||||
JVM_OPC_dload_1 = 39,
|
||||
JVM_OPC_dload_2 = 40,
|
||||
JVM_OPC_dload_3 = 41,
|
||||
JVM_OPC_aload_0 = 42,
|
||||
JVM_OPC_aload_1 = 43,
|
||||
JVM_OPC_aload_2 = 44,
|
||||
JVM_OPC_aload_3 = 45,
|
||||
JVM_OPC_iaload = 46,
|
||||
JVM_OPC_laload = 47,
|
||||
JVM_OPC_faload = 48,
|
||||
JVM_OPC_daload = 49,
|
||||
JVM_OPC_aaload = 50,
|
||||
JVM_OPC_baload = 51,
|
||||
JVM_OPC_caload = 52,
|
||||
JVM_OPC_saload = 53,
|
||||
JVM_OPC_istore = 54,
|
||||
JVM_OPC_lstore = 55,
|
||||
JVM_OPC_fstore = 56,
|
||||
JVM_OPC_dstore = 57,
|
||||
JVM_OPC_astore = 58,
|
||||
JVM_OPC_istore_0 = 59,
|
||||
JVM_OPC_istore_1 = 60,
|
||||
JVM_OPC_istore_2 = 61,
|
||||
JVM_OPC_istore_3 = 62,
|
||||
JVM_OPC_lstore_0 = 63,
|
||||
JVM_OPC_lstore_1 = 64,
|
||||
JVM_OPC_lstore_2 = 65,
|
||||
JVM_OPC_lstore_3 = 66,
|
||||
JVM_OPC_fstore_0 = 67,
|
||||
JVM_OPC_fstore_1 = 68,
|
||||
JVM_OPC_fstore_2 = 69,
|
||||
JVM_OPC_fstore_3 = 70,
|
||||
JVM_OPC_dstore_0 = 71,
|
||||
JVM_OPC_dstore_1 = 72,
|
||||
JVM_OPC_dstore_2 = 73,
|
||||
JVM_OPC_dstore_3 = 74,
|
||||
JVM_OPC_astore_0 = 75,
|
||||
JVM_OPC_astore_1 = 76,
|
||||
JVM_OPC_astore_2 = 77,
|
||||
JVM_OPC_astore_3 = 78,
|
||||
JVM_OPC_iastore = 79,
|
||||
JVM_OPC_lastore = 80,
|
||||
JVM_OPC_fastore = 81,
|
||||
JVM_OPC_dastore = 82,
|
||||
JVM_OPC_aastore = 83,
|
||||
JVM_OPC_bastore = 84,
|
||||
JVM_OPC_castore = 85,
|
||||
JVM_OPC_sastore = 86,
|
||||
JVM_OPC_pop = 87,
|
||||
JVM_OPC_pop2 = 88,
|
||||
JVM_OPC_dup = 89,
|
||||
JVM_OPC_dup_x1 = 90,
|
||||
JVM_OPC_dup_x2 = 91,
|
||||
JVM_OPC_dup2 = 92,
|
||||
JVM_OPC_dup2_x1 = 93,
|
||||
JVM_OPC_dup2_x2 = 94,
|
||||
JVM_OPC_swap = 95,
|
||||
JVM_OPC_iadd = 96,
|
||||
JVM_OPC_ladd = 97,
|
||||
JVM_OPC_fadd = 98,
|
||||
JVM_OPC_dadd = 99,
|
||||
JVM_OPC_isub = 100,
|
||||
JVM_OPC_lsub = 101,
|
||||
JVM_OPC_fsub = 102,
|
||||
JVM_OPC_dsub = 103,
|
||||
JVM_OPC_imul = 104,
|
||||
JVM_OPC_lmul = 105,
|
||||
JVM_OPC_fmul = 106,
|
||||
JVM_OPC_dmul = 107,
|
||||
JVM_OPC_idiv = 108,
|
||||
JVM_OPC_ldiv = 109,
|
||||
JVM_OPC_fdiv = 110,
|
||||
JVM_OPC_ddiv = 111,
|
||||
JVM_OPC_irem = 112,
|
||||
JVM_OPC_lrem = 113,
|
||||
JVM_OPC_frem = 114,
|
||||
JVM_OPC_drem = 115,
|
||||
JVM_OPC_ineg = 116,
|
||||
JVM_OPC_lneg = 117,
|
||||
JVM_OPC_fneg = 118,
|
||||
JVM_OPC_dneg = 119,
|
||||
JVM_OPC_ishl = 120,
|
||||
JVM_OPC_lshl = 121,
|
||||
JVM_OPC_ishr = 122,
|
||||
JVM_OPC_lshr = 123,
|
||||
JVM_OPC_iushr = 124,
|
||||
JVM_OPC_lushr = 125,
|
||||
JVM_OPC_iand = 126,
|
||||
JVM_OPC_land = 127,
|
||||
JVM_OPC_ior = 128,
|
||||
JVM_OPC_lor = 129,
|
||||
JVM_OPC_ixor = 130,
|
||||
JVM_OPC_lxor = 131,
|
||||
JVM_OPC_iinc = 132,
|
||||
JVM_OPC_i2l = 133,
|
||||
JVM_OPC_i2f = 134,
|
||||
JVM_OPC_i2d = 135,
|
||||
JVM_OPC_l2i = 136,
|
||||
JVM_OPC_l2f = 137,
|
||||
JVM_OPC_l2d = 138,
|
||||
JVM_OPC_f2i = 139,
|
||||
JVM_OPC_f2l = 140,
|
||||
JVM_OPC_f2d = 141,
|
||||
JVM_OPC_d2i = 142,
|
||||
JVM_OPC_d2l = 143,
|
||||
JVM_OPC_d2f = 144,
|
||||
JVM_OPC_i2b = 145,
|
||||
JVM_OPC_i2c = 146,
|
||||
JVM_OPC_i2s = 147,
|
||||
JVM_OPC_lcmp = 148,
|
||||
JVM_OPC_fcmpl = 149,
|
||||
JVM_OPC_fcmpg = 150,
|
||||
JVM_OPC_dcmpl = 151,
|
||||
JVM_OPC_dcmpg = 152,
|
||||
JVM_OPC_ifeq = 153,
|
||||
JVM_OPC_ifne = 154,
|
||||
JVM_OPC_iflt = 155,
|
||||
JVM_OPC_ifge = 156,
|
||||
JVM_OPC_ifgt = 157,
|
||||
JVM_OPC_ifle = 158,
|
||||
JVM_OPC_if_icmpeq = 159,
|
||||
JVM_OPC_if_icmpne = 160,
|
||||
JVM_OPC_if_icmplt = 161,
|
||||
JVM_OPC_if_icmpge = 162,
|
||||
JVM_OPC_if_icmpgt = 163,
|
||||
JVM_OPC_if_icmple = 164,
|
||||
JVM_OPC_if_acmpeq = 165,
|
||||
JVM_OPC_if_acmpne = 166,
|
||||
JVM_OPC_goto = 167,
|
||||
JVM_OPC_jsr = 168,
|
||||
JVM_OPC_ret = 169,
|
||||
JVM_OPC_tableswitch = 170,
|
||||
JVM_OPC_lookupswitch = 171,
|
||||
JVM_OPC_ireturn = 172,
|
||||
JVM_OPC_lreturn = 173,
|
||||
JVM_OPC_freturn = 174,
|
||||
JVM_OPC_dreturn = 175,
|
||||
JVM_OPC_areturn = 176,
|
||||
JVM_OPC_return = 177,
|
||||
JVM_OPC_getstatic = 178,
|
||||
JVM_OPC_putstatic = 179,
|
||||
JVM_OPC_getfield = 180,
|
||||
JVM_OPC_putfield = 181,
|
||||
JVM_OPC_invokevirtual = 182,
|
||||
JVM_OPC_invokespecial = 183,
|
||||
JVM_OPC_invokestatic = 184,
|
||||
JVM_OPC_invokeinterface = 185,
|
||||
JVM_OPC_invokedynamic = 186,
|
||||
JVM_OPC_new = 187,
|
||||
JVM_OPC_newarray = 188,
|
||||
JVM_OPC_anewarray = 189,
|
||||
JVM_OPC_arraylength = 190,
|
||||
JVM_OPC_athrow = 191,
|
||||
JVM_OPC_checkcast = 192,
|
||||
JVM_OPC_instanceof = 193,
|
||||
JVM_OPC_monitorenter = 194,
|
||||
JVM_OPC_monitorexit = 195,
|
||||
JVM_OPC_wide = 196,
|
||||
JVM_OPC_multianewarray = 197,
|
||||
JVM_OPC_ifnull = 198,
|
||||
JVM_OPC_ifnonnull = 199,
|
||||
JVM_OPC_goto_w = 200,
|
||||
JVM_OPC_jsr_w = 201,
|
||||
JVM_OPC_MAX = 201
|
||||
};
|
||||
|
||||
/* Opcode length initializer, use with something like:
|
||||
* unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
|
||||
*/
|
||||
#define JVM_OPCODE_LENGTH_INITIALIZER { \
|
||||
1, /* nop */ \
|
||||
1, /* aconst_null */ \
|
||||
1, /* iconst_m1 */ \
|
||||
1, /* iconst_0 */ \
|
||||
1, /* iconst_1 */ \
|
||||
1, /* iconst_2 */ \
|
||||
1, /* iconst_3 */ \
|
||||
1, /* iconst_4 */ \
|
||||
1, /* iconst_5 */ \
|
||||
1, /* lconst_0 */ \
|
||||
1, /* lconst_1 */ \
|
||||
1, /* fconst_0 */ \
|
||||
1, /* fconst_1 */ \
|
||||
1, /* fconst_2 */ \
|
||||
1, /* dconst_0 */ \
|
||||
1, /* dconst_1 */ \
|
||||
2, /* bipush */ \
|
||||
3, /* sipush */ \
|
||||
2, /* ldc */ \
|
||||
3, /* ldc_w */ \
|
||||
3, /* ldc2_w */ \
|
||||
2, /* iload */ \
|
||||
2, /* lload */ \
|
||||
2, /* fload */ \
|
||||
2, /* dload */ \
|
||||
2, /* aload */ \
|
||||
1, /* iload_0 */ \
|
||||
1, /* iload_1 */ \
|
||||
1, /* iload_2 */ \
|
||||
1, /* iload_3 */ \
|
||||
1, /* lload_0 */ \
|
||||
1, /* lload_1 */ \
|
||||
1, /* lload_2 */ \
|
||||
1, /* lload_3 */ \
|
||||
1, /* fload_0 */ \
|
||||
1, /* fload_1 */ \
|
||||
1, /* fload_2 */ \
|
||||
1, /* fload_3 */ \
|
||||
1, /* dload_0 */ \
|
||||
1, /* dload_1 */ \
|
||||
1, /* dload_2 */ \
|
||||
1, /* dload_3 */ \
|
||||
1, /* aload_0 */ \
|
||||
1, /* aload_1 */ \
|
||||
1, /* aload_2 */ \
|
||||
1, /* aload_3 */ \
|
||||
1, /* iaload */ \
|
||||
1, /* laload */ \
|
||||
1, /* faload */ \
|
||||
1, /* daload */ \
|
||||
1, /* aaload */ \
|
||||
1, /* baload */ \
|
||||
1, /* caload */ \
|
||||
1, /* saload */ \
|
||||
2, /* istore */ \
|
||||
2, /* lstore */ \
|
||||
2, /* fstore */ \
|
||||
2, /* dstore */ \
|
||||
2, /* astore */ \
|
||||
1, /* istore_0 */ \
|
||||
1, /* istore_1 */ \
|
||||
1, /* istore_2 */ \
|
||||
1, /* istore_3 */ \
|
||||
1, /* lstore_0 */ \
|
||||
1, /* lstore_1 */ \
|
||||
1, /* lstore_2 */ \
|
||||
1, /* lstore_3 */ \
|
||||
1, /* fstore_0 */ \
|
||||
1, /* fstore_1 */ \
|
||||
1, /* fstore_2 */ \
|
||||
1, /* fstore_3 */ \
|
||||
1, /* dstore_0 */ \
|
||||
1, /* dstore_1 */ \
|
||||
1, /* dstore_2 */ \
|
||||
1, /* dstore_3 */ \
|
||||
1, /* astore_0 */ \
|
||||
1, /* astore_1 */ \
|
||||
1, /* astore_2 */ \
|
||||
1, /* astore_3 */ \
|
||||
1, /* iastore */ \
|
||||
1, /* lastore */ \
|
||||
1, /* fastore */ \
|
||||
1, /* dastore */ \
|
||||
1, /* aastore */ \
|
||||
1, /* bastore */ \
|
||||
1, /* castore */ \
|
||||
1, /* sastore */ \
|
||||
1, /* pop */ \
|
||||
1, /* pop2 */ \
|
||||
1, /* dup */ \
|
||||
1, /* dup_x1 */ \
|
||||
1, /* dup_x2 */ \
|
||||
1, /* dup2 */ \
|
||||
1, /* dup2_x1 */ \
|
||||
1, /* dup2_x2 */ \
|
||||
1, /* swap */ \
|
||||
1, /* iadd */ \
|
||||
1, /* ladd */ \
|
||||
1, /* fadd */ \
|
||||
1, /* dadd */ \
|
||||
1, /* isub */ \
|
||||
1, /* lsub */ \
|
||||
1, /* fsub */ \
|
||||
1, /* dsub */ \
|
||||
1, /* imul */ \
|
||||
1, /* lmul */ \
|
||||
1, /* fmul */ \
|
||||
1, /* dmul */ \
|
||||
1, /* idiv */ \
|
||||
1, /* ldiv */ \
|
||||
1, /* fdiv */ \
|
||||
1, /* ddiv */ \
|
||||
1, /* irem */ \
|
||||
1, /* lrem */ \
|
||||
1, /* frem */ \
|
||||
1, /* drem */ \
|
||||
1, /* ineg */ \
|
||||
1, /* lneg */ \
|
||||
1, /* fneg */ \
|
||||
1, /* dneg */ \
|
||||
1, /* ishl */ \
|
||||
1, /* lshl */ \
|
||||
1, /* ishr */ \
|
||||
1, /* lshr */ \
|
||||
1, /* iushr */ \
|
||||
1, /* lushr */ \
|
||||
1, /* iand */ \
|
||||
1, /* land */ \
|
||||
1, /* ior */ \
|
||||
1, /* lor */ \
|
||||
1, /* ixor */ \
|
||||
1, /* lxor */ \
|
||||
3, /* iinc */ \
|
||||
1, /* i2l */ \
|
||||
1, /* i2f */ \
|
||||
1, /* i2d */ \
|
||||
1, /* l2i */ \
|
||||
1, /* l2f */ \
|
||||
1, /* l2d */ \
|
||||
1, /* f2i */ \
|
||||
1, /* f2l */ \
|
||||
1, /* f2d */ \
|
||||
1, /* d2i */ \
|
||||
1, /* d2l */ \
|
||||
1, /* d2f */ \
|
||||
1, /* i2b */ \
|
||||
1, /* i2c */ \
|
||||
1, /* i2s */ \
|
||||
1, /* lcmp */ \
|
||||
1, /* fcmpl */ \
|
||||
1, /* fcmpg */ \
|
||||
1, /* dcmpl */ \
|
||||
1, /* dcmpg */ \
|
||||
3, /* ifeq */ \
|
||||
3, /* ifne */ \
|
||||
3, /* iflt */ \
|
||||
3, /* ifge */ \
|
||||
3, /* ifgt */ \
|
||||
3, /* ifle */ \
|
||||
3, /* if_icmpeq */ \
|
||||
3, /* if_icmpne */ \
|
||||
3, /* if_icmplt */ \
|
||||
3, /* if_icmpge */ \
|
||||
3, /* if_icmpgt */ \
|
||||
3, /* if_icmple */ \
|
||||
3, /* if_acmpeq */ \
|
||||
3, /* if_acmpne */ \
|
||||
3, /* goto */ \
|
||||
3, /* jsr */ \
|
||||
2, /* ret */ \
|
||||
99, /* tableswitch */ \
|
||||
99, /* lookupswitch */ \
|
||||
1, /* ireturn */ \
|
||||
1, /* lreturn */ \
|
||||
1, /* freturn */ \
|
||||
1, /* dreturn */ \
|
||||
1, /* areturn */ \
|
||||
1, /* return */ \
|
||||
3, /* getstatic */ \
|
||||
3, /* putstatic */ \
|
||||
3, /* getfield */ \
|
||||
3, /* putfield */ \
|
||||
3, /* invokevirtual */ \
|
||||
3, /* invokespecial */ \
|
||||
3, /* invokestatic */ \
|
||||
5, /* invokeinterface */ \
|
||||
5, /* invokedynamic */ \
|
||||
3, /* new */ \
|
||||
2, /* newarray */ \
|
||||
3, /* anewarray */ \
|
||||
1, /* arraylength */ \
|
||||
1, /* athrow */ \
|
||||
3, /* checkcast */ \
|
||||
3, /* instanceof */ \
|
||||
1, /* monitorenter */ \
|
||||
1, /* monitorexit */ \
|
||||
0, /* wide */ \
|
||||
4, /* multianewarray */ \
|
||||
3, /* ifnull */ \
|
||||
3, /* ifnonnull */ \
|
||||
5, /* goto_w */ \
|
||||
5 /* jsr_w */ \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CLASSFILE_CONSTANTS */
|
||||
@@ -1,356 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_H_
|
||||
#define _JAVASOFT_JAWT_H_
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AWT native interface.
|
||||
*
|
||||
* The AWT native interface allows a native C or C++ application a means
|
||||
* by which to access native structures in AWT. This is to facilitate moving
|
||||
* legacy C and C++ applications to Java and to target the needs of the
|
||||
* developers who need to do their own native rendering to canvases
|
||||
* for performance or other reasons.
|
||||
*
|
||||
* Conversely it also provides mechanisms for an application which already
|
||||
* has a native window to provide that to AWT for AWT rendering.
|
||||
*
|
||||
* Since every platform may be different in its native data structures
|
||||
* and APIs for windowing systems the application must necessarily
|
||||
* provided per-platform source and compile and deliver per-platform
|
||||
* native code to use this API.
|
||||
*
|
||||
* These interfaces are not part of the Java SE specification and
|
||||
* a VM is not required to implement this API. However it is strongly
|
||||
* recommended that all implementations which support headful AWT
|
||||
* also support these interfaces.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* AWT Native Drawing Surface (JAWT_DrawingSurface).
|
||||
*
|
||||
* For each platform, there is a native drawing surface structure. This
|
||||
* platform-specific structure can be found in jawt_md.h. It is recommended
|
||||
* that additional platforms follow the same model. It is also recommended
|
||||
* that VMs on all platforms support the existing structures in jawt_md.h.
|
||||
*
|
||||
*******************
|
||||
* EXAMPLE OF USAGE:
|
||||
*******************
|
||||
*
|
||||
* In Win32, a programmer wishes to access the HWND of a canvas to perform
|
||||
* native rendering into it. The programmer has declared the paint() method
|
||||
* for their canvas subclass to be native:
|
||||
*
|
||||
*
|
||||
* MyCanvas.java:
|
||||
*
|
||||
* import java.awt.*;
|
||||
*
|
||||
* public class MyCanvas extends Canvas {
|
||||
*
|
||||
* static {
|
||||
* System.loadLibrary("mylib");
|
||||
* }
|
||||
*
|
||||
* public native void paint(Graphics g);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* myfile.c:
|
||||
*
|
||||
* #include "jawt_md.h"
|
||||
* #include <assert.h>
|
||||
*
|
||||
* JNIEXPORT void JNICALL
|
||||
* Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
|
||||
* {
|
||||
* JAWT awt;
|
||||
* JAWT_DrawingSurface* ds;
|
||||
* JAWT_DrawingSurfaceInfo* dsi;
|
||||
* JAWT_Win32DrawingSurfaceInfo* dsi_win;
|
||||
* jboolean result;
|
||||
* jint lock;
|
||||
*
|
||||
* // Get the AWT. Request version 9 to access features in that release.
|
||||
* awt.version = JAWT_VERSION_9;
|
||||
* result = JAWT_GetAWT(env, &awt);
|
||||
* assert(result != JNI_FALSE);
|
||||
*
|
||||
* // Get the drawing surface
|
||||
* ds = awt.GetDrawingSurface(env, canvas);
|
||||
* assert(ds != NULL);
|
||||
*
|
||||
* // Lock the drawing surface
|
||||
* lock = ds->Lock(ds);
|
||||
* assert((lock & JAWT_LOCK_ERROR) == 0);
|
||||
*
|
||||
* // Get the drawing surface info
|
||||
* dsi = ds->GetDrawingSurfaceInfo(ds);
|
||||
*
|
||||
* // Get the platform-specific drawing info
|
||||
* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
|
||||
*
|
||||
* //////////////////////////////
|
||||
* // !!! DO PAINTING HERE !!! //
|
||||
* //////////////////////////////
|
||||
*
|
||||
* // Free the drawing surface info
|
||||
* ds->FreeDrawingSurfaceInfo(dsi);
|
||||
*
|
||||
* // Unlock the drawing surface
|
||||
* ds->Unlock(ds);
|
||||
*
|
||||
* // Free the drawing surface
|
||||
* awt.FreeDrawingSurface(ds);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* JAWT_Rectangle
|
||||
* Structure for a native rectangle.
|
||||
*/
|
||||
typedef struct jawt_Rectangle {
|
||||
jint x;
|
||||
jint y;
|
||||
jint width;
|
||||
jint height;
|
||||
} JAWT_Rectangle;
|
||||
|
||||
struct jawt_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurfaceInfo
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurfaceInfo {
|
||||
/*
|
||||
* Pointer to the platform-specific information. This can be safely
|
||||
* cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
|
||||
* JAWT_X11DrawingSurfaceInfo on Linux and Solaris. On Mac OS X this is a
|
||||
* pointer to a NSObject that conforms to the JAWT_SurfaceLayers
|
||||
* protocol. See jawt_md.h for details.
|
||||
*/
|
||||
void* platformInfo;
|
||||
/* Cached pointer to the underlying drawing surface */
|
||||
struct jawt_DrawingSurface* ds;
|
||||
/* Bounding rectangle of the drawing surface */
|
||||
JAWT_Rectangle bounds;
|
||||
/* Number of rectangles in the clip */
|
||||
jint clipSize;
|
||||
/* Clip rectangle array */
|
||||
JAWT_Rectangle* clip;
|
||||
} JAWT_DrawingSurfaceInfo;
|
||||
|
||||
#define JAWT_LOCK_ERROR 0x00000001
|
||||
#define JAWT_LOCK_CLIP_CHANGED 0x00000002
|
||||
#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
|
||||
#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurface
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
* All operations on a JAWT_DrawingSurface MUST be performed from the same
|
||||
* thread as the call to GetDrawingSurface.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurface {
|
||||
/*
|
||||
* Cached reference to the Java environment of the calling thread.
|
||||
* If Lock(), Unlock(), GetDrawingSurfaceInfo() or
|
||||
* FreeDrawingSurfaceInfo() are called from a different thread,
|
||||
* this data member should be set before calling those functions.
|
||||
*/
|
||||
JNIEnv* env;
|
||||
/* Cached reference to the target object */
|
||||
jobject target;
|
||||
/*
|
||||
* Lock the surface of the target component for native rendering.
|
||||
* When finished drawing, the surface must be unlocked with
|
||||
* Unlock(). This function returns a bitmask with one or more of the
|
||||
* following values:
|
||||
*
|
||||
* JAWT_LOCK_ERROR - When an error has occurred and the surface could not
|
||||
* be locked.
|
||||
*
|
||||
* JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
|
||||
*
|
||||
* JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
|
||||
*
|
||||
* JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
|
||||
*/
|
||||
jint (JNICALL *Lock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Get the drawing surface info.
|
||||
* The value returned may be cached, but the values may change if
|
||||
* additional calls to Lock() or Unlock() are made.
|
||||
* Lock() must be called before this can return a valid value.
|
||||
* Returns NULL if an error has occurred.
|
||||
* When finished with the returned value, FreeDrawingSurfaceInfo must be
|
||||
* called.
|
||||
*/
|
||||
JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Free the drawing surface info.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurfaceInfo)
|
||||
(JAWT_DrawingSurfaceInfo* dsi);
|
||||
/*
|
||||
* Unlock the drawing surface of the target component for native rendering.
|
||||
*/
|
||||
void (JNICALL *Unlock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
} JAWT_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT
|
||||
* Structure for containing native AWT functions.
|
||||
*/
|
||||
typedef struct jawt {
|
||||
/*
|
||||
* Version of this structure. This must always be set before
|
||||
* calling JAWT_GetAWT(). It affects the functions returned.
|
||||
* Must be one of the known pre-defined versions.
|
||||
*/
|
||||
jint version;
|
||||
/*
|
||||
* Return a drawing surface from a target jobject. This value
|
||||
* may be cached.
|
||||
* Returns NULL if an error has occurred.
|
||||
* Target must be a java.awt.Component (should be a Canvas
|
||||
* or Window for native rendering).
|
||||
* FreeDrawingSurface() must be called when finished with the
|
||||
* returned JAWT_DrawingSurface.
|
||||
*/
|
||||
JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
|
||||
(JNIEnv* env, jobject target);
|
||||
/*
|
||||
* Free the drawing surface allocated in GetDrawingSurface.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurface)
|
||||
(JAWT_DrawingSurface* ds);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Locks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Lock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Unlocks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Unlock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Returns a reference to a java.awt.Component from a native
|
||||
* platform handle. On Windows, this corresponds to an HWND;
|
||||
* on Solaris and Linux, this is a Drawable. For other platforms,
|
||||
* see the appropriate machine-dependent header file for a description.
|
||||
* The reference returned by this function is a local
|
||||
* reference that is only valid in this environment.
|
||||
* This function returns a NULL reference if no component could be
|
||||
* found with matching platform information.
|
||||
*/
|
||||
jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
|
||||
|
||||
/**
|
||||
* Since 9
|
||||
* Creates a java.awt.Frame placed in a native container. Container is
|
||||
* referenced by the native platform handle. For example on Windows this
|
||||
* corresponds to an HWND. For other platforms, see the appropriate
|
||||
* machine-dependent header file for a description. The reference returned
|
||||
* by this function is a local reference that is only valid in this
|
||||
* environment. This function returns a NULL reference if no frame could be
|
||||
* created with matching platform information.
|
||||
*/
|
||||
jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo);
|
||||
|
||||
/**
|
||||
* Since 9
|
||||
* Moves and resizes the embedded frame. The new location of the top-left
|
||||
* corner is specified by x and y parameters relative to the native parent
|
||||
* component. The new size is specified by width and height.
|
||||
*
|
||||
* The embedded frame should be created by CreateEmbeddedFrame() method, or
|
||||
* this function will not have any effect.
|
||||
*
|
||||
* java.awt.Component.setLocation() and java.awt.Component.setBounds() for
|
||||
* EmbeddedFrame really don't move it within the native parent. These
|
||||
* methods always locate the embedded frame at (0, 0) for backward
|
||||
* compatibility. To allow moving embedded frames this method was
|
||||
* introduced, and it works just the same way as setLocation() and
|
||||
* setBounds() for usual, non-embedded components.
|
||||
*
|
||||
* Using usual get/setLocation() and get/setBounds() together with this new
|
||||
* method is not recommended.
|
||||
*/
|
||||
void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame,
|
||||
jint x, jint y, jint w, jint h);
|
||||
/**
|
||||
* Since 9
|
||||
* Synthesize a native message to activate or deactivate an EmbeddedFrame
|
||||
* window depending on the value of parameter doActivate, if "true"
|
||||
* activates the window; otherwise, deactivates the window.
|
||||
*
|
||||
* The embedded frame should be created by CreateEmbeddedFrame() method, or
|
||||
* this function will not have any effect.
|
||||
*/
|
||||
void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env,
|
||||
jobject embeddedFrame, jboolean doActivate);
|
||||
} JAWT;
|
||||
|
||||
/*
|
||||
* Get the AWT native structure. This function returns JNI_FALSE if
|
||||
* an error occurs.
|
||||
*/
|
||||
_JNI_IMPORT_OR_EXPORT_
|
||||
jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
|
||||
|
||||
/*
|
||||
* Specify one of these constants as the JAWT.version
|
||||
* Specifying an earlier version will limit the available functions to
|
||||
* those provided in that earlier version of JAWT.
|
||||
* See the "Since" note on each API. Methods with no "Since"
|
||||
* may be presumed to be present in JAWT_VERSION_1_3.
|
||||
*/
|
||||
#define JAWT_VERSION_1_3 0x00010003
|
||||
#define JAWT_VERSION_1_4 0x00010004
|
||||
#define JAWT_VERSION_1_7 0x00010007
|
||||
#define JAWT_VERSION_9 0x00090000
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_H_ */
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Java Debug Wire Protocol Transport Service Provider Interface.
|
||||
*/
|
||||
|
||||
#ifndef JDWPTRANSPORT_H
|
||||
#define JDWPTRANSPORT_H
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
enum {
|
||||
JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
|
||||
JDWPTRANSPORT_VERSION_1_1 = 0x00010001
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct jdwpTransportNativeInterface_;
|
||||
|
||||
struct _jdwpTransportEnv;
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef _jdwpTransportEnv jdwpTransportEnv;
|
||||
#else
|
||||
typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Errors. Universal errors with JVMTI/JVMDI equivalents keep the
|
||||
* values the same.
|
||||
*/
|
||||
typedef enum {
|
||||
JDWPTRANSPORT_ERROR_NONE = 0,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
|
||||
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
|
||||
JDWPTRANSPORT_ERROR_INTERNAL = 113,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
|
||||
JDWPTRANSPORT_ERROR_IO_ERROR = 202,
|
||||
JDWPTRANSPORT_ERROR_TIMEOUT = 203,
|
||||
JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
|
||||
} jdwpTransportError;
|
||||
|
||||
|
||||
/*
|
||||
* Structure to define capabilities
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int can_timeout_attach :1;
|
||||
unsigned int can_timeout_accept :1;
|
||||
unsigned int can_timeout_handshake :1;
|
||||
unsigned int reserved3 :1;
|
||||
unsigned int reserved4 :1;
|
||||
unsigned int reserved5 :1;
|
||||
unsigned int reserved6 :1;
|
||||
unsigned int reserved7 :1;
|
||||
unsigned int reserved8 :1;
|
||||
unsigned int reserved9 :1;
|
||||
unsigned int reserved10 :1;
|
||||
unsigned int reserved11 :1;
|
||||
unsigned int reserved12 :1;
|
||||
unsigned int reserved13 :1;
|
||||
unsigned int reserved14 :1;
|
||||
unsigned int reserved15 :1;
|
||||
} JDWPTransportCapabilities;
|
||||
|
||||
|
||||
/*
|
||||
* Structures to define packet layout.
|
||||
*
|
||||
* See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
|
||||
*/
|
||||
|
||||
#define JDWP_HEADER_SIZE 11
|
||||
|
||||
enum {
|
||||
/*
|
||||
* If additional flags are added that apply to jdwpCmdPacket,
|
||||
* then debugLoop.c: reader() will need to be updated to
|
||||
* accept more than JDWPTRANSPORT_FLAGS_NONE.
|
||||
*/
|
||||
JDWPTRANSPORT_FLAGS_NONE = 0x0,
|
||||
JDWPTRANSPORT_FLAGS_REPLY = 0x80
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jbyte cmdSet;
|
||||
jbyte cmd;
|
||||
jbyte *data;
|
||||
} jdwpCmdPacket;
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jshort errorCode;
|
||||
jbyte *data;
|
||||
} jdwpReplyPacket;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
jdwpCmdPacket cmd;
|
||||
jdwpReplyPacket reply;
|
||||
} type;
|
||||
} jdwpPacket;
|
||||
|
||||
/*
|
||||
* JDWP functions called by the transport.
|
||||
*/
|
||||
typedef struct jdwpTransportCallback {
|
||||
void *(*alloc)(jint numBytes); /* Call this for all allocations */
|
||||
void (*free)(void *buffer); /* Call this for all deallocations */
|
||||
} jdwpTransportCallback;
|
||||
|
||||
typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
|
||||
jdwpTransportCallback *callback,
|
||||
jint version,
|
||||
jdwpTransportEnv** env);
|
||||
|
||||
/*
|
||||
* JDWP transport configuration from the agent.
|
||||
*/
|
||||
typedef struct jdwpTransportConfiguration {
|
||||
/* Field added in JDWPTRANSPORT_VERSION_1_1: */
|
||||
const char* allowed_peers; /* Peers allowed for connection */
|
||||
} jdwpTransportConfiguration;
|
||||
|
||||
|
||||
/* Function Interface */
|
||||
|
||||
struct jdwpTransportNativeInterface_ {
|
||||
/* 1 : RESERVED */
|
||||
void *reserved1;
|
||||
|
||||
/* 2 : Get Capabilities */
|
||||
jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
|
||||
JDWPTransportCapabilities *capabilities_ptr);
|
||||
|
||||
/* 3 : Attach */
|
||||
jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
jlong attach_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 4: StartListening */
|
||||
jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
char** actual_address);
|
||||
|
||||
/* 5: StopListening */
|
||||
jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
|
||||
|
||||
/* 6: Accept */
|
||||
jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
|
||||
jlong accept_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 7: IsOpen */
|
||||
jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
|
||||
|
||||
/* 8: Close */
|
||||
jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
|
||||
|
||||
/* 9: ReadPacket */
|
||||
jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
|
||||
jdwpPacket *pkt);
|
||||
|
||||
/* 10: Write Packet */
|
||||
jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
|
||||
const jdwpPacket* pkt);
|
||||
|
||||
/* 11: GetLastError */
|
||||
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
|
||||
char** error);
|
||||
|
||||
/* 12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
|
||||
jdwpTransportConfiguration *config);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Use inlined functions so that C++ code can use syntax such as
|
||||
* env->Attach("mymachine:5000", 10*1000, 0);
|
||||
*
|
||||
* rather than using C's :-
|
||||
*
|
||||
* (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
|
||||
*/
|
||||
struct _jdwpTransportEnv {
|
||||
const struct jdwpTransportNativeInterface_ *functions;
|
||||
#ifdef __cplusplus
|
||||
|
||||
jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
|
||||
return functions->GetCapabilities(this, capabilities_ptr);
|
||||
}
|
||||
|
||||
jdwpTransportError Attach(const char* address, jlong attach_timeout,
|
||||
jlong handshake_timeout) {
|
||||
return functions->Attach(this, address, attach_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jdwpTransportError StartListening(const char* address,
|
||||
char** actual_address) {
|
||||
return functions->StartListening(this, address, actual_address);
|
||||
}
|
||||
|
||||
jdwpTransportError StopListening(void) {
|
||||
return functions->StopListening(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
|
||||
return functions->Accept(this, accept_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jboolean IsOpen(void) {
|
||||
return functions->IsOpen(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Close(void) {
|
||||
return functions->Close(this);
|
||||
}
|
||||
|
||||
jdwpTransportError ReadPacket(jdwpPacket *pkt) {
|
||||
return functions->ReadPacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError WritePacket(const jdwpPacket* pkt) {
|
||||
return functions->WritePacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError GetLastError(char** error) {
|
||||
return functions->GetLastError(this, error);
|
||||
}
|
||||
|
||||
/* SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
|
||||
return functions->SetTransportConfiguration(this, config);
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* JDWPTRANSPORT_H */
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file defines the data structures sent by the VM
|
||||
* through the JVMTI CompiledMethodLoad callback function via the
|
||||
* "void * compile_info" parameter. The memory pointed to by the
|
||||
* compile_info parameter may not be referenced after returning from
|
||||
* the CompiledMethodLoad callback. These are VM implementation
|
||||
* specific data structures that may evolve in future releases. A
|
||||
* JVMTI agent should interpret a non-NULL compile_info as a pointer
|
||||
* to a region of memory containing a list of records. In a typical
|
||||
* usage scenario, a JVMTI agent would cast each record to a
|
||||
* jvmtiCompiledMethodLoadRecordHeader, a struct that represents
|
||||
* arbitrary information. This struct contains a kind field to indicate
|
||||
* the kind of information being passed, and a pointer to the next
|
||||
* record. If the kind field indicates inlining information, then the
|
||||
* agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord.
|
||||
* This record contains an array of PCStackInfo structs, which indicate
|
||||
* for every pc address what are the methods on the invocation stack.
|
||||
* The "methods" and "bcis" fields in each PCStackInfo struct specify a
|
||||
* 1-1 mapping between these inlined methods and their bytecode indices.
|
||||
* This can be used to derive the proper source lines of the inlined
|
||||
* methods.
|
||||
*/
|
||||
|
||||
#ifndef _JVMTI_CMLR_H_
|
||||
#define _JVMTI_CMLR_H_
|
||||
|
||||
enum {
|
||||
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
|
||||
|
||||
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION = 0x00000000
|
||||
|
||||
/*
|
||||
* This comment is for the "JDK import from HotSpot" sanity check:
|
||||
* version: 1.0.0
|
||||
*/
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
JVMTI_CMLR_DUMMY = 1,
|
||||
JVMTI_CMLR_INLINE_INFO = 2
|
||||
} jvmtiCMLRKind;
|
||||
|
||||
/*
|
||||
* Record that represents arbitrary information passed through JVMTI
|
||||
* CompiledMethodLoadEvent void pointer.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
|
||||
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
|
||||
jint majorinfoversion; /* major and minor info version values. Init'ed */
|
||||
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
|
||||
|
||||
struct _jvmtiCompiledMethodLoadRecordHeader* next;
|
||||
} jvmtiCompiledMethodLoadRecordHeader;
|
||||
|
||||
/*
|
||||
* Record that gives information about the methods on the compile-time
|
||||
* stack at a specific pc address of a compiled method. Each element in
|
||||
* the methods array maps to same element in the bcis array.
|
||||
*/
|
||||
typedef struct _PCStackInfo {
|
||||
void* pc; /* the pc address for this compiled method */
|
||||
jint numstackframes; /* number of methods on the stack */
|
||||
jmethodID* methods; /* array of numstackframes method ids */
|
||||
jint* bcis; /* array of numstackframes bytecode indices */
|
||||
} PCStackInfo;
|
||||
|
||||
/*
|
||||
* Record that contains inlining information for each pc address of
|
||||
* an nmethod.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
jint numpcs; /* number of pc descriptors in this nmethod */
|
||||
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
|
||||
} jvmtiCompiledMethodLoadInlineRecord;
|
||||
|
||||
/*
|
||||
* Dummy record used to test that we can pass records with different
|
||||
* information through the void pointer provided that they can be cast
|
||||
* to a jvmtiCompiledMethodLoadRecordHeader.
|
||||
*/
|
||||
|
||||
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
char message[50];
|
||||
} jvmtiCompiledMethodLoadDummyRecord;
|
||||
|
||||
#endif
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file defining callback typedefs for Windows routines
|
||||
* which are called from Java (responding to events, etc.).
|
||||
*/
|
||||
|
||||
#ifndef __AccessBridgeCallbacks_H__
|
||||
#define __AccessBridgeCallbacks_H__
|
||||
|
||||
#include <jni.h>
|
||||
#include "AccessBridgePackages.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*AccessBridge_PropertyChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *property, wchar_t *oldValue, wchar_t *newValue);
|
||||
|
||||
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
|
||||
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
|
||||
|
||||
typedef void (*AccessBridge_FocusGainedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_FocusLostFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_CaretUpdateFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_MouseClickedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseEnteredFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseExitedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MousePressedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseReleasedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_MenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MenuDeselectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MenuSelectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuWillBecomeInvisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuWillBecomeVisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_PropertyNameChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldName, wchar_t *newName);
|
||||
typedef void (*AccessBridge_PropertyDescriptionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldDescription, wchar_t *newDescription);
|
||||
typedef void (*AccessBridge_PropertyStateChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldState, wchar_t *newState);
|
||||
typedef void (*AccessBridge_PropertyValueChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldValue, wchar_t *newValue);
|
||||
typedef void (*AccessBridge_PropertySelectionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyTextChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyCaretChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
int oldPosition, int newPosition);
|
||||
typedef void (*AccessBridge_PropertyVisibleDataChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyChildChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
JOBJECT64 oldChild, JOBJECT64 newChild);
|
||||
typedef void (*AccessBridge_PropertyActiveDescendentChangeFP) (long vmID, JOBJECT64 event,
|
||||
JOBJECT64 source,
|
||||
JOBJECT64 oldActiveDescendent,
|
||||
JOBJECT64 newActiveDescendent);
|
||||
|
||||
typedef void (*AccessBridge_PropertyTableModelChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 src,
|
||||
wchar_t *oldValue, wchar_t *newValue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,725 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* Note: In addition to this header file AccessBridgeCalls.c must be compiled and
|
||||
* linked to your application. AccessBridgeCalls.c implements the Java Access
|
||||
* Bridge API and also hides the complexities associated with interfacing to the
|
||||
* associated Java Access Bridge DLL which is installed when Java is installed.
|
||||
*
|
||||
* AccessBridgeCalls.c is available for download from the OpenJDK repository using
|
||||
* the following link:
|
||||
*
|
||||
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/raw-file/tip/src/jdk.accessibility/windows/native/bridge/AccessBridgeCalls.c
|
||||
*
|
||||
* Also note that the API is used in the jaccessinspector and jaccesswalker tools.
|
||||
* The source for those tools is available in the OpenJDK repository at these links:
|
||||
*
|
||||
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp
|
||||
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wrapper functions around calls to the AccessBridge DLL
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <jni.h>
|
||||
#include "AccessBridgeCallbacks.h"
|
||||
#include "AccessBridgePackages.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define null NULL
|
||||
|
||||
typedef JOBJECT64 AccessibleContext;
|
||||
typedef JOBJECT64 AccessibleText;
|
||||
typedef JOBJECT64 AccessibleValue;
|
||||
typedef JOBJECT64 AccessibleSelection;
|
||||
typedef JOBJECT64 Java_Object;
|
||||
typedef JOBJECT64 PropertyChangeEvent;
|
||||
typedef JOBJECT64 FocusEvent;
|
||||
typedef JOBJECT64 CaretEvent;
|
||||
typedef JOBJECT64 MouseEvent;
|
||||
typedef JOBJECT64 MenuEvent;
|
||||
typedef JOBJECT64 AccessibleTable;
|
||||
typedef JOBJECT64 AccessibleHyperlink;
|
||||
typedef JOBJECT64 AccessibleHypertext;
|
||||
|
||||
|
||||
typedef void (*Windows_runFP) ();
|
||||
|
||||
typedef void (*SetPropertyChangeFP) (AccessBridge_PropertyChangeFP fp);
|
||||
|
||||
typedef void (*SetJavaShutdownFP) (AccessBridge_JavaShutdownFP fp);
|
||||
typedef void (*SetFocusGainedFP) (AccessBridge_FocusGainedFP fp);
|
||||
typedef void (*SetFocusLostFP) (AccessBridge_FocusLostFP fp);
|
||||
|
||||
typedef void (*SetCaretUpdateFP) (AccessBridge_CaretUpdateFP fp);
|
||||
|
||||
typedef void (*SetMouseClickedFP) (AccessBridge_MouseClickedFP fp);
|
||||
typedef void (*SetMouseEnteredFP) (AccessBridge_MouseEnteredFP fp);
|
||||
typedef void (*SetMouseExitedFP) (AccessBridge_MouseExitedFP fp);
|
||||
typedef void (*SetMousePressedFP) (AccessBridge_MousePressedFP fp);
|
||||
typedef void (*SetMouseReleasedFP) (AccessBridge_MouseReleasedFP fp);
|
||||
|
||||
typedef void (*SetMenuCanceledFP) (AccessBridge_MenuCanceledFP fp);
|
||||
typedef void (*SetMenuDeselectedFP) (AccessBridge_MenuDeselectedFP fp);
|
||||
typedef void (*SetMenuSelectedFP) (AccessBridge_MenuSelectedFP fp);
|
||||
typedef void (*SetPopupMenuCanceledFP) (AccessBridge_PopupMenuCanceledFP fp);
|
||||
typedef void (*SetPopupMenuWillBecomeInvisibleFP) (AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
|
||||
typedef void (*SetPopupMenuWillBecomeVisibleFP) (AccessBridge_PopupMenuWillBecomeVisibleFP fp);
|
||||
|
||||
typedef void (*SetPropertyNameChangeFP) (AccessBridge_PropertyNameChangeFP fp);
|
||||
typedef void (*SetPropertyDescriptionChangeFP) (AccessBridge_PropertyDescriptionChangeFP fp);
|
||||
typedef void (*SetPropertyStateChangeFP) (AccessBridge_PropertyStateChangeFP fp);
|
||||
typedef void (*SetPropertyValueChangeFP) (AccessBridge_PropertyValueChangeFP fp);
|
||||
typedef void (*SetPropertySelectionChangeFP) (AccessBridge_PropertySelectionChangeFP fp);
|
||||
typedef void (*SetPropertyTextChangeFP) (AccessBridge_PropertyTextChangeFP fp);
|
||||
typedef void (*SetPropertyCaretChangeFP) (AccessBridge_PropertyCaretChangeFP fp);
|
||||
typedef void (*SetPropertyVisibleDataChangeFP) (AccessBridge_PropertyVisibleDataChangeFP fp);
|
||||
typedef void (*SetPropertyChildChangeFP) (AccessBridge_PropertyChildChangeFP fp);
|
||||
typedef void (*SetPropertyActiveDescendentChangeFP) (AccessBridge_PropertyActiveDescendentChangeFP fp);
|
||||
|
||||
typedef void (*SetPropertyTableModelChangeFP) (AccessBridge_PropertyTableModelChangeFP fp);
|
||||
|
||||
typedef void (*ReleaseJavaObjectFP) (long vmID, Java_Object object);
|
||||
|
||||
typedef BOOL (*GetVersionInfoFP) (long vmID, AccessBridgeVersionInfo *info);
|
||||
|
||||
typedef BOOL (*IsJavaWindowFP) (HWND window);
|
||||
typedef BOOL (*IsSameObjectFP) (long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
|
||||
typedef BOOL (*GetAccessibleContextFromHWNDFP) (HWND window, long *vmID, AccessibleContext *ac);
|
||||
typedef HWND (*getHWNDFromAccessibleContextFP) (long vmID, AccessibleContext ac);
|
||||
|
||||
typedef BOOL (*GetAccessibleContextAtFP) (long vmID, AccessibleContext acParent,
|
||||
jint x, jint y, AccessibleContext *ac);
|
||||
typedef BOOL (*GetAccessibleContextWithFocusFP) (HWND window, long *vmID, AccessibleContext *ac);
|
||||
typedef BOOL (*GetAccessibleContextInfoFP) (long vmID, AccessibleContext ac, AccessibleContextInfo *info);
|
||||
typedef AccessibleContext (*GetAccessibleChildFromContextFP) (long vmID, AccessibleContext ac, jint i);
|
||||
typedef AccessibleContext (*GetAccessibleParentFromContextFP) (long vmID, AccessibleContext ac);
|
||||
|
||||
/* begin AccessibleTable */
|
||||
typedef BOOL (*getAccessibleTableInfoFP) (long vmID, AccessibleContext ac, AccessibleTableInfo *tableInfo);
|
||||
typedef BOOL (*getAccessibleTableCellInfoFP) (long vmID, AccessibleTable accessibleTable,
|
||||
jint row, jint column, AccessibleTableCellInfo *tableCellInfo);
|
||||
|
||||
typedef BOOL (*getAccessibleTableRowHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
typedef BOOL (*getAccessibleTableColumnHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
typedef AccessibleContext (*getAccessibleTableRowDescriptionFP) (long vmID, AccessibleContext acParent, jint row);
|
||||
typedef AccessibleContext (*getAccessibleTableColumnDescriptionFP) (long vmID, AccessibleContext acParent, jint column);
|
||||
|
||||
typedef jint (*getAccessibleTableRowSelectionCountFP) (long vmID, AccessibleTable table);
|
||||
typedef BOOL (*isAccessibleTableRowSelectedFP) (long vmID, AccessibleTable table, jint row);
|
||||
typedef BOOL (*getAccessibleTableRowSelectionsFP) (long vmID, AccessibleTable table, jint count,
|
||||
jint *selections);
|
||||
|
||||
typedef jint (*getAccessibleTableColumnSelectionCountFP) (long vmID, AccessibleTable table);
|
||||
typedef BOOL (*isAccessibleTableColumnSelectedFP) (long vmID, AccessibleTable table, jint column);
|
||||
typedef BOOL (*getAccessibleTableColumnSelectionsFP) (long vmID, AccessibleTable table, jint count,
|
||||
jint *selections);
|
||||
|
||||
typedef jint (*getAccessibleTableRowFP) (long vmID, AccessibleTable table, jint index);
|
||||
typedef jint (*getAccessibleTableColumnFP) (long vmID, AccessibleTable table, jint index);
|
||||
typedef jint (*getAccessibleTableIndexFP) (long vmID, AccessibleTable table, jint row, jint column);
|
||||
/* end AccessibleTable */
|
||||
|
||||
/* AccessibleRelationSet */
|
||||
typedef BOOL (*getAccessibleRelationSetFP) (long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleRelationSetInfo *relationSetInfo);
|
||||
|
||||
/* AccessibleHypertext */
|
||||
typedef BOOL (*getAccessibleHypertextFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
typedef BOOL (*activateAccessibleHyperlinkFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHyperlink accessibleHyperlink);
|
||||
|
||||
typedef jint (*getAccessibleHyperlinkCountFP)(const long vmID,
|
||||
const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*getAccessibleHypertextExtFP) (const long vmID,
|
||||
const AccessibleContext accessibleContext,
|
||||
const jint nStartIndex,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
typedef jint (*getAccessibleHypertextLinkIndexFP)(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex);
|
||||
|
||||
typedef BOOL (*getAccessibleHyperlinkFP)(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex,
|
||||
AccessibleHyperlinkInfo *hyperlinkInfo);
|
||||
|
||||
|
||||
/* Accessible KeyBindings, Icons and Actions */
|
||||
typedef BOOL (*getAccessibleKeyBindingsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleKeyBindings *keyBindings);
|
||||
|
||||
typedef BOOL (*getAccessibleIconsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleIcons *icons);
|
||||
|
||||
typedef BOOL (*getAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActions *actions);
|
||||
|
||||
typedef BOOL (*doAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActionsToDo *actionsToDo, jint *failure);
|
||||
|
||||
|
||||
/* AccessibleText */
|
||||
|
||||
typedef BOOL (*GetAccessibleTextInfoFP) (long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
|
||||
typedef BOOL (*GetAccessibleTextItemsFP) (long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
|
||||
typedef BOOL (*GetAccessibleTextSelectionInfoFP) (long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
|
||||
typedef BOOL (*GetAccessibleTextAttributesFP) (long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
|
||||
typedef BOOL (*GetAccessibleTextRectFP) (long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
typedef BOOL (*GetAccessibleTextLineBoundsFP) (long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
|
||||
typedef BOOL (*GetAccessibleTextRangeFP) (long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
|
||||
|
||||
typedef BOOL (*GetCurrentAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
typedef BOOL (*GetMaximumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
typedef BOOL (*GetMinimumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
|
||||
typedef void (*AddAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*ClearAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
|
||||
typedef JOBJECT64 (*GetAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef int (*GetAccessibleSelectionCountFromContextFP) (long vmID, AccessibleSelection as);
|
||||
typedef BOOL (*IsAccessibleChildSelectedFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*RemoveAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*SelectAllAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
|
||||
|
||||
/* Utility methods */
|
||||
|
||||
typedef BOOL (*setTextContentsFP) (const long vmID, const AccessibleContext ac, const wchar_t *text);
|
||||
typedef AccessibleContext (*getParentWithRoleFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
|
||||
typedef AccessibleContext (*getParentWithRoleElseRootFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
|
||||
typedef AccessibleContext (*getTopLevelObjectFP) (const long vmID, const AccessibleContext ac);
|
||||
typedef int (*getObjectDepthFP) (const long vmID, const AccessibleContext ac);
|
||||
typedef AccessibleContext (*getActiveDescendentFP) (const long vmID, const AccessibleContext ac);
|
||||
|
||||
|
||||
typedef BOOL (*getVirtualAccessibleNameFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
wchar_t *name, int len);
|
||||
|
||||
typedef BOOL (*requestFocusFP) (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*selectTextRangeFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex);
|
||||
|
||||
typedef BOOL (*getTextAttributesInRangeFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex,
|
||||
AccessibleTextAttributesInfo *attributes, short *len);
|
||||
|
||||
typedef int (*getVisibleChildrenCountFP) (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*getVisibleChildrenFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, VisibleChildrenInfo *children);
|
||||
|
||||
typedef BOOL (*setCaretPositionFP) (const long vmID, const AccessibleContext accessibleContext, const int position);
|
||||
|
||||
typedef BOOL (*getCaretLocationFP) (long vmID, AccessibleContext ac, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
|
||||
typedef int (*getEventsWaitingFP) ();
|
||||
|
||||
typedef struct AccessBridgeFPsTag {
|
||||
Windows_runFP Windows_run;
|
||||
|
||||
SetPropertyChangeFP SetPropertyChange;
|
||||
|
||||
SetJavaShutdownFP SetJavaShutdown;
|
||||
SetFocusGainedFP SetFocusGained;
|
||||
SetFocusLostFP SetFocusLost;
|
||||
|
||||
SetCaretUpdateFP SetCaretUpdate;
|
||||
|
||||
SetMouseClickedFP SetMouseClicked;
|
||||
SetMouseEnteredFP SetMouseEntered;
|
||||
SetMouseExitedFP SetMouseExited;
|
||||
SetMousePressedFP SetMousePressed;
|
||||
SetMouseReleasedFP SetMouseReleased;
|
||||
|
||||
SetMenuCanceledFP SetMenuCanceled;
|
||||
SetMenuDeselectedFP SetMenuDeselected;
|
||||
SetMenuSelectedFP SetMenuSelected;
|
||||
SetPopupMenuCanceledFP SetPopupMenuCanceled;
|
||||
SetPopupMenuWillBecomeInvisibleFP SetPopupMenuWillBecomeInvisible;
|
||||
SetPopupMenuWillBecomeVisibleFP SetPopupMenuWillBecomeVisible;
|
||||
|
||||
SetPropertyNameChangeFP SetPropertyNameChange;
|
||||
SetPropertyDescriptionChangeFP SetPropertyDescriptionChange;
|
||||
SetPropertyStateChangeFP SetPropertyStateChange;
|
||||
SetPropertyValueChangeFP SetPropertyValueChange;
|
||||
SetPropertySelectionChangeFP SetPropertySelectionChange;
|
||||
SetPropertyTextChangeFP SetPropertyTextChange;
|
||||
SetPropertyCaretChangeFP SetPropertyCaretChange;
|
||||
SetPropertyVisibleDataChangeFP SetPropertyVisibleDataChange;
|
||||
SetPropertyChildChangeFP SetPropertyChildChange;
|
||||
SetPropertyActiveDescendentChangeFP SetPropertyActiveDescendentChange;
|
||||
|
||||
SetPropertyTableModelChangeFP SetPropertyTableModelChange;
|
||||
|
||||
ReleaseJavaObjectFP ReleaseJavaObject;
|
||||
GetVersionInfoFP GetVersionInfo;
|
||||
|
||||
IsJavaWindowFP IsJavaWindow;
|
||||
IsSameObjectFP IsSameObject;
|
||||
GetAccessibleContextFromHWNDFP GetAccessibleContextFromHWND;
|
||||
getHWNDFromAccessibleContextFP getHWNDFromAccessibleContext;
|
||||
|
||||
GetAccessibleContextAtFP GetAccessibleContextAt;
|
||||
GetAccessibleContextWithFocusFP GetAccessibleContextWithFocus;
|
||||
GetAccessibleContextInfoFP GetAccessibleContextInfo;
|
||||
GetAccessibleChildFromContextFP GetAccessibleChildFromContext;
|
||||
GetAccessibleParentFromContextFP GetAccessibleParentFromContext;
|
||||
|
||||
getAccessibleTableInfoFP getAccessibleTableInfo;
|
||||
getAccessibleTableCellInfoFP getAccessibleTableCellInfo;
|
||||
|
||||
getAccessibleTableRowHeaderFP getAccessibleTableRowHeader;
|
||||
getAccessibleTableColumnHeaderFP getAccessibleTableColumnHeader;
|
||||
|
||||
getAccessibleTableRowDescriptionFP getAccessibleTableRowDescription;
|
||||
getAccessibleTableColumnDescriptionFP getAccessibleTableColumnDescription;
|
||||
|
||||
getAccessibleTableRowSelectionCountFP getAccessibleTableRowSelectionCount;
|
||||
isAccessibleTableRowSelectedFP isAccessibleTableRowSelected;
|
||||
getAccessibleTableRowSelectionsFP getAccessibleTableRowSelections;
|
||||
|
||||
getAccessibleTableColumnSelectionCountFP getAccessibleTableColumnSelectionCount;
|
||||
isAccessibleTableColumnSelectedFP isAccessibleTableColumnSelected;
|
||||
getAccessibleTableColumnSelectionsFP getAccessibleTableColumnSelections;
|
||||
|
||||
getAccessibleTableRowFP getAccessibleTableRow;
|
||||
getAccessibleTableColumnFP getAccessibleTableColumn;
|
||||
getAccessibleTableIndexFP getAccessibleTableIndex;
|
||||
|
||||
getAccessibleRelationSetFP getAccessibleRelationSet;
|
||||
|
||||
getAccessibleHypertextFP getAccessibleHypertext;
|
||||
activateAccessibleHyperlinkFP activateAccessibleHyperlink;
|
||||
getAccessibleHyperlinkCountFP getAccessibleHyperlinkCount;
|
||||
getAccessibleHypertextExtFP getAccessibleHypertextExt;
|
||||
getAccessibleHypertextLinkIndexFP getAccessibleHypertextLinkIndex;
|
||||
getAccessibleHyperlinkFP getAccessibleHyperlink;
|
||||
|
||||
getAccessibleKeyBindingsFP getAccessibleKeyBindings;
|
||||
getAccessibleIconsFP getAccessibleIcons;
|
||||
getAccessibleActionsFP getAccessibleActions;
|
||||
doAccessibleActionsFP doAccessibleActions;
|
||||
|
||||
GetAccessibleTextInfoFP GetAccessibleTextInfo;
|
||||
GetAccessibleTextItemsFP GetAccessibleTextItems;
|
||||
GetAccessibleTextSelectionInfoFP GetAccessibleTextSelectionInfo;
|
||||
GetAccessibleTextAttributesFP GetAccessibleTextAttributes;
|
||||
GetAccessibleTextRectFP GetAccessibleTextRect;
|
||||
GetAccessibleTextLineBoundsFP GetAccessibleTextLineBounds;
|
||||
GetAccessibleTextRangeFP GetAccessibleTextRange;
|
||||
|
||||
GetCurrentAccessibleValueFromContextFP GetCurrentAccessibleValueFromContext;
|
||||
GetMaximumAccessibleValueFromContextFP GetMaximumAccessibleValueFromContext;
|
||||
GetMinimumAccessibleValueFromContextFP GetMinimumAccessibleValueFromContext;
|
||||
|
||||
AddAccessibleSelectionFromContextFP AddAccessibleSelectionFromContext;
|
||||
ClearAccessibleSelectionFromContextFP ClearAccessibleSelectionFromContext;
|
||||
GetAccessibleSelectionFromContextFP GetAccessibleSelectionFromContext;
|
||||
GetAccessibleSelectionCountFromContextFP GetAccessibleSelectionCountFromContext;
|
||||
IsAccessibleChildSelectedFromContextFP IsAccessibleChildSelectedFromContext;
|
||||
RemoveAccessibleSelectionFromContextFP RemoveAccessibleSelectionFromContext;
|
||||
SelectAllAccessibleSelectionFromContextFP SelectAllAccessibleSelectionFromContext;
|
||||
|
||||
setTextContentsFP setTextContents;
|
||||
getParentWithRoleFP getParentWithRole;
|
||||
getTopLevelObjectFP getTopLevelObject;
|
||||
getParentWithRoleElseRootFP getParentWithRoleElseRoot;
|
||||
getObjectDepthFP getObjectDepth;
|
||||
getActiveDescendentFP getActiveDescendent;
|
||||
|
||||
getVirtualAccessibleNameFP getVirtualAccessibleName;
|
||||
requestFocusFP requestFocus;
|
||||
selectTextRangeFP selectTextRange;
|
||||
getTextAttributesInRangeFP getTextAttributesInRange;
|
||||
getVisibleChildrenCountFP getVisibleChildrenCount;
|
||||
getVisibleChildrenFP getVisibleChildren;
|
||||
setCaretPositionFP setCaretPosition;
|
||||
getCaretLocationFP getCaretLocation;
|
||||
|
||||
getEventsWaitingFP getEventsWaiting;
|
||||
|
||||
} AccessBridgeFPs;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the world
|
||||
*/
|
||||
BOOL initializeAccessBridge();
|
||||
BOOL shutdownAccessBridge();
|
||||
|
||||
/**
|
||||
* Window routines
|
||||
*/
|
||||
BOOL IsJavaWindow(HWND window);
|
||||
|
||||
// Returns the virtual machine ID and AccessibleContext for a top-level window
|
||||
BOOL GetAccessibleContextFromHWND(HWND target, long *vmID, AccessibleContext *ac);
|
||||
|
||||
// Returns the HWND from the AccessibleContext of a top-level window
|
||||
HWND getHWNDFromAccessibleContext(long vmID, AccessibleContext ac);
|
||||
|
||||
|
||||
/**
|
||||
* Event handling routines
|
||||
*/
|
||||
void SetJavaShutdown(AccessBridge_JavaShutdownFP fp);
|
||||
void SetFocusGained(AccessBridge_FocusGainedFP fp);
|
||||
void SetFocusLost(AccessBridge_FocusLostFP fp);
|
||||
|
||||
void SetCaretUpdate(AccessBridge_CaretUpdateFP fp);
|
||||
|
||||
void SetMouseClicked(AccessBridge_MouseClickedFP fp);
|
||||
void SetMouseEntered(AccessBridge_MouseEnteredFP fp);
|
||||
void SetMouseExited(AccessBridge_MouseExitedFP fp);
|
||||
void SetMousePressed(AccessBridge_MousePressedFP fp);
|
||||
void SetMouseReleased(AccessBridge_MouseReleasedFP fp);
|
||||
|
||||
void SetMenuCanceled(AccessBridge_MenuCanceledFP fp);
|
||||
void SetMenuDeselected(AccessBridge_MenuDeselectedFP fp);
|
||||
void SetMenuSelected(AccessBridge_MenuSelectedFP fp);
|
||||
void SetPopupMenuCanceled(AccessBridge_PopupMenuCanceledFP fp);
|
||||
void SetPopupMenuWillBecomeInvisible(AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
|
||||
void SetPopupMenuWillBecomeVisible(AccessBridge_PopupMenuWillBecomeVisibleFP fp);
|
||||
|
||||
void SetPropertyNameChange(AccessBridge_PropertyNameChangeFP fp);
|
||||
void SetPropertyDescriptionChange(AccessBridge_PropertyDescriptionChangeFP fp);
|
||||
void SetPropertyStateChange(AccessBridge_PropertyStateChangeFP fp);
|
||||
void SetPropertyValueChange(AccessBridge_PropertyValueChangeFP fp);
|
||||
void SetPropertySelectionChange(AccessBridge_PropertySelectionChangeFP fp);
|
||||
void SetPropertyTextChange(AccessBridge_PropertyTextChangeFP fp);
|
||||
void SetPropertyCaretChange(AccessBridge_PropertyCaretChangeFP fp);
|
||||
void SetPropertyVisibleDataChange(AccessBridge_PropertyVisibleDataChangeFP fp);
|
||||
void SetPropertyChildChange(AccessBridge_PropertyChildChangeFP fp);
|
||||
void SetPropertyActiveDescendentChange(AccessBridge_PropertyActiveDescendentChangeFP fp);
|
||||
|
||||
void SetPropertyTableModelChange(AccessBridge_PropertyTableModelChangeFP fp);
|
||||
|
||||
|
||||
/**
|
||||
* General routines
|
||||
*/
|
||||
void ReleaseJavaObject(long vmID, Java_Object object);
|
||||
BOOL GetVersionInfo(long vmID, AccessBridgeVersionInfo *info);
|
||||
HWND GetHWNDFromAccessibleContext(long vmID, JOBJECT64 accesibleContext);
|
||||
|
||||
/**
|
||||
* Accessible Context routines
|
||||
*/
|
||||
BOOL GetAccessibleContextAt(long vmID, AccessibleContext acParent,
|
||||
jint x, jint y, AccessibleContext *ac);
|
||||
BOOL GetAccessibleContextWithFocus(HWND window, long *vmID, AccessibleContext *ac);
|
||||
BOOL GetAccessibleContextInfo(long vmID, AccessibleContext ac, AccessibleContextInfo *info);
|
||||
AccessibleContext GetAccessibleChildFromContext(long vmID, AccessibleContext ac, jint index);
|
||||
AccessibleContext GetAccessibleParentFromContext(long vmID, AccessibleContext ac);
|
||||
|
||||
/**
|
||||
* Accessible Text routines
|
||||
*/
|
||||
BOOL GetAccessibleTextInfo(long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
|
||||
BOOL GetAccessibleTextItems(long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
|
||||
BOOL GetAccessibleTextSelectionInfo(long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
|
||||
BOOL GetAccessibleTextAttributes(long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
|
||||
BOOL GetAccessibleTextRect(long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
BOOL GetAccessibleTextLineBounds(long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
|
||||
BOOL GetAccessibleTextRange(long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
|
||||
|
||||
/* begin AccessibleTable routines */
|
||||
BOOL getAccessibleTableInfo(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
BOOL getAccessibleTableCellInfo(long vmID, AccessibleTable accessibleTable, jint row, jint column,
|
||||
AccessibleTableCellInfo *tableCellInfo);
|
||||
|
||||
BOOL getAccessibleTableRowHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
BOOL getAccessibleTableColumnHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
AccessibleContext getAccessibleTableRowDescription(long vmID, AccessibleContext acParent, jint row);
|
||||
AccessibleContext getAccessibleTableColumnDescription(long vmID, AccessibleContext acParent, jint column);
|
||||
|
||||
jint getAccessibleTableRowSelectionCount(long vmID, AccessibleTable table);
|
||||
BOOL isAccessibleTableRowSelected(long vmID, AccessibleTable table, jint row);
|
||||
BOOL getAccessibleTableRowSelections(long vmID, AccessibleTable table, jint count, jint *selections);
|
||||
|
||||
jint getAccessibleTableColumnSelectionCount(long vmID, AccessibleTable table);
|
||||
BOOL isAccessibleTableColumnSelected(long vmID, AccessibleTable table, jint column);
|
||||
BOOL getAccessibleTableColumnSelections(long vmID, AccessibleTable table, jint count, jint *selections);
|
||||
|
||||
jint getAccessibleTableRow(long vmID, AccessibleTable table, jint index);
|
||||
jint getAccessibleTableColumn(long vmID, AccessibleTable table, jint index);
|
||||
jint getAccessibleTableIndex(long vmID, AccessibleTable table, jint row, jint column);
|
||||
/* end AccessibleTable */
|
||||
|
||||
/* ----- AccessibleRelationSet routines */
|
||||
BOOL getAccessibleRelationSet(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleRelationSetInfo *relationSetInfo);
|
||||
|
||||
/* ----- AccessibleHypertext routines */
|
||||
|
||||
/*
|
||||
* Returns hypertext information associated with a component.
|
||||
*/
|
||||
BOOL getAccessibleHypertext(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
/*
|
||||
* Requests that a hyperlink be activated.
|
||||
*/
|
||||
BOOL activateAccessibleHyperlink(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHyperlink accessibleHyperlink);
|
||||
|
||||
/*
|
||||
* Returns the number of hyperlinks in a component
|
||||
* Maps to AccessibleHypertext.getLinkCount.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
jint getAccessibleHyperlinkCount(const long vmID,
|
||||
const AccessibleHypertext hypertext);
|
||||
|
||||
/*
|
||||
* This method is used to iterate through the hyperlinks in a component. It
|
||||
* returns hypertext information for a component starting at hyperlink index
|
||||
* nStartIndex. No more than MAX_HYPERLINKS AccessibleHypertextInfo objects will
|
||||
* be returned for each call to this method.
|
||||
* Returns FALSE on error.
|
||||
*/
|
||||
BOOL getAccessibleHypertextExt(const long vmID,
|
||||
const AccessibleContext accessibleContext,
|
||||
const jint nStartIndex,
|
||||
/* OUT */ AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
/*
|
||||
* Returns the index into an array of hyperlinks that is associated with
|
||||
* a character index in document; maps to AccessibleHypertext.getLinkIndex
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
jint getAccessibleHypertextLinkIndex(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex);
|
||||
|
||||
/*
|
||||
* Returns the nth hyperlink in a document
|
||||
* Maps to AccessibleHypertext.getLink.
|
||||
* Returns FALSE on error
|
||||
*/
|
||||
BOOL getAccessibleHyperlink(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex,
|
||||
/* OUT */ AccessibleHyperlinkInfo *hyperlinkInfo);
|
||||
|
||||
/* Accessible KeyBindings, Icons and Actions */
|
||||
|
||||
/*
|
||||
* Returns a list of key bindings associated with a component.
|
||||
*/
|
||||
BOOL getAccessibleKeyBindings(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleKeyBindings *keyBindings);
|
||||
|
||||
/*
|
||||
* Returns a list of icons associate with a component.
|
||||
*/
|
||||
BOOL getAccessibleIcons(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleIcons *icons);
|
||||
|
||||
/*
|
||||
* Returns a list of actions that a component can perform.
|
||||
*/
|
||||
BOOL getAccessibleActions(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActions *actions);
|
||||
|
||||
/*
|
||||
* Request that a list of AccessibleActions be performed by a component.
|
||||
* Returns TRUE if all actions are performed. Returns FALSE
|
||||
* when the first requested action fails in which case "failure"
|
||||
* contains the index of the action that failed.
|
||||
*/
|
||||
BOOL doAccessibleActions(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActionsToDo *actionsToDo, jint *failure);
|
||||
|
||||
|
||||
|
||||
/* Additional utility methods */
|
||||
|
||||
/*
|
||||
* Returns whether two object references refer to the same object.
|
||||
*/
|
||||
BOOL IsSameObject(long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
|
||||
|
||||
/**
|
||||
* Sets editable text contents. The AccessibleContext must implement AccessibleEditableText and
|
||||
* be editable. The maximum text length that can be set is MAX_STRING_SIZE - 1.
|
||||
* Returns whether successful
|
||||
*/
|
||||
BOOL setTextContents (const long vmID, const AccessibleContext accessibleContext, const wchar_t *text);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context with the specified role that is the
|
||||
* ancestor of a given object. The role is one of the role strings
|
||||
* defined in AccessBridgePackages.h
|
||||
* If there is no ancestor object that has the specified role,
|
||||
* returns (AccessibleContext)0.
|
||||
*/
|
||||
AccessibleContext getParentWithRole (const long vmID, const AccessibleContext accessibleContext,
|
||||
const wchar_t *role);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context with the specified role that is the
|
||||
* ancestor of a given object. The role is one of the role strings
|
||||
* defined in AccessBridgePackages.h. If an object with the specified
|
||||
* role does not exist, returns the top level object for the Java Window.
|
||||
* Returns (AccessibleContext)0 on error.
|
||||
*/
|
||||
AccessibleContext getParentWithRoleElseRoot (const long vmID, const AccessibleContext accessibleContext,
|
||||
const wchar_t *role);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context for the top level object in
|
||||
* a Java Window. This is same Accessible Context that is obtained
|
||||
* from GetAccessibleContextFromHWND for that window. Returns
|
||||
* (AccessibleContext)0 on error.
|
||||
*/
|
||||
AccessibleContext getTopLevelObject (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Returns how deep in the object hierarchy a given object is.
|
||||
* The top most object in the object hierarchy has an object depth of 0.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
int getObjectDepth (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context of the current ActiveDescendent of an object.
|
||||
* This method assumes the ActiveDescendent is the component that is currently
|
||||
* selected in a container object.
|
||||
* Returns (AccessibleContext)0 on error or if there is no selection.
|
||||
*/
|
||||
AccessibleContext getActiveDescendent (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Accessible Value routines
|
||||
*/
|
||||
BOOL GetCurrentAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
BOOL GetMaximumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
BOOL GetMinimumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
|
||||
/**
|
||||
* Accessible Selection routines
|
||||
*/
|
||||
void AddAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void ClearAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
|
||||
JOBJECT64 GetAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
int GetAccessibleSelectionCountFromContext(long vmID, AccessibleSelection as);
|
||||
BOOL IsAccessibleChildSelectedFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void RemoveAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void SelectAllAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
|
||||
|
||||
/**
|
||||
* Additional methods for Teton
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the AccessibleName for a component based upon the JAWS algorithm. Returns
|
||||
* whether successful.
|
||||
*
|
||||
* Bug ID 4916682 - Implement JAWS AccessibleName policy
|
||||
*/
|
||||
BOOL getVirtualAccessibleName(const long vmID, const AccessibleContext accessibleContext,
|
||||
wchar_t *name, int len);
|
||||
|
||||
/**
|
||||
* Request focus for a component. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944757 - requestFocus method needed
|
||||
*/
|
||||
BOOL requestFocus(const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Selects text between two indices. Selection includes the text at the start index
|
||||
* and the text at the end index. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944758 - selectTextRange method needed
|
||||
*/
|
||||
BOOL selectTextRange(const long vmID, const AccessibleContext accessibleContext, const int startIndex,
|
||||
const int endIndex);
|
||||
|
||||
/**
|
||||
* Get text attributes between two indices. The attribute list includes the text at the
|
||||
* start index and the text at the end index. Returns whether successful;
|
||||
*
|
||||
* Bug ID 4944761 - getTextAttributes between two indices method needed
|
||||
*/
|
||||
BOOL getTextAttributesInRange(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex,
|
||||
AccessibleTextAttributesInfo *attributes, short *len);
|
||||
|
||||
/**
|
||||
* Returns the number of visible children of a component. Returns -1 on error.
|
||||
*
|
||||
* Bug ID 4944762- getVisibleChildren for list-like components needed
|
||||
*/
|
||||
int getVisibleChildrenCount(const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Gets the visible children of an AccessibleContext. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944762- getVisibleChildren for list-like components needed
|
||||
*/
|
||||
BOOL getVisibleChildren(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex,
|
||||
VisibleChildrenInfo *visibleChildrenInfo);
|
||||
|
||||
/**
|
||||
* Set the caret to a text position. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944770 - setCaretPosition method needed
|
||||
*/
|
||||
BOOL setCaretPosition(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int position);
|
||||
|
||||
/**
|
||||
* Gets the text caret location
|
||||
*/
|
||||
BOOL getCaretLocation(long vmID, AccessibleContext ac,
|
||||
AccessibleTextRectInfo *rectInfo, jint index);
|
||||
|
||||
/**
|
||||
* Gets the number of events waiting to fire
|
||||
*/
|
||||
int getEventsWaiting();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_MD_H_
|
||||
#define _JAVASOFT_JAWT_MD_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include "jawt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Win32-specific declarations for AWT native interface.
|
||||
* See notes in jawt.h for an example of use.
|
||||
*/
|
||||
typedef struct jawt_Win32DrawingSurfaceInfo {
|
||||
/* Native window, DDB, or DIB handle */
|
||||
union {
|
||||
HWND hwnd;
|
||||
HBITMAP hbitmap;
|
||||
void* pbits;
|
||||
};
|
||||
/*
|
||||
* This HDC should always be used instead of the HDC returned from
|
||||
* BeginPaint() or any calls to GetDC().
|
||||
*/
|
||||
HDC hdc;
|
||||
HPALETTE hpalette;
|
||||
} JAWT_Win32DrawingSurfaceInfo;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_MD_H_ */
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#define JNIEXPORT __declspec(dllexport)
|
||||
#define JNIIMPORT __declspec(dllimport)
|
||||
#define JNICALL __stdcall
|
||||
|
||||
// 'long' is always 32 bit on windows so this matches what jdk expects
|
||||
typedef long jint;
|
||||
typedef __int64 jlong;
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (C) 2020 Planet Virtual Boy
|
||||
Copyright (C) 2022 Guy Perfect
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
# Locale
|
||||
locale {
|
||||
id en-US
|
||||
name English (United States)
|
||||
}
|
||||
|
||||
# Main window
|
||||
app {
|
||||
|
||||
debug {
|
||||
(menu) Debug
|
||||
breakpoints Breakpoints
|
||||
console Console
|
||||
cpu CPU
|
||||
memory Memory
|
||||
# VIP section
|
||||
bg_maps BG Maps
|
||||
characters Characters
|
||||
frame_buffers Frame buffers
|
||||
objects Objects
|
||||
worlds Worlds
|
||||
}
|
||||
|
||||
file {
|
||||
(menu) File
|
||||
debug_mode Debug mode
|
||||
exit Exit
|
||||
game_mode Game mode
|
||||
load_rom Load ROM...
|
||||
new_window New window
|
||||
}
|
||||
|
||||
title {
|
||||
default PVB Emulator
|
||||
mixed {ctrl.number} {ctrl.filename} - {app.title.default}
|
||||
number {ctrl.number} {app.title.default}
|
||||
rom {ctrl.filename} - {app.title.default}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# BG Maps window
|
||||
bg_maps {
|
||||
address Address
|
||||
cell Cell
|
||||
character Character
|
||||
generic Generic
|
||||
grid Grid
|
||||
hflip H-Flip
|
||||
index Index
|
||||
map Map
|
||||
palette Palette
|
||||
scale Scale
|
||||
title BG Maps
|
||||
vflip V-Flip
|
||||
}
|
||||
|
||||
# Breakpoints window
|
||||
breakpoints {
|
||||
address Address
|
||||
condition Condition
|
||||
default_name New breakpoint
|
||||
delete Delete
|
||||
enabled Enabled
|
||||
exception Exception
|
||||
execute Execute
|
||||
name Name
|
||||
new New
|
||||
read Read
|
||||
title Breakpoints
|
||||
type Type
|
||||
write Write
|
||||
|
||||
error {
|
||||
badliteral_a Error:{err.position}: Unable to process address "{err.text}"
|
||||
badliteral_c Error:{err.position}: Unable to process literal "{err.text}"
|
||||
badoperand Error:{err.position}: Invalid operand to operator "{err.text}"
|
||||
badtoken Error:{err.position}: Unrecognized token "{err.text}"
|
||||
earlyeof Error:{err.position}: Unexpected end of input
|
||||
invalid Error:{err.position}: Token "{err.text}" cannot appear here
|
||||
nesting Error:{err.position}: Expected "{err.other}", but found "{err.text}"
|
||||
unexpected Error:{err.position}: Unexpected "{err.text}"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Characters window
|
||||
characters {
|
||||
address Address
|
||||
grid Grid
|
||||
index Index
|
||||
mirror Mirror
|
||||
Palette Palette
|
||||
scale Scale
|
||||
title Characters
|
||||
wide Wide
|
||||
}
|
||||
|
||||
# Console window
|
||||
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 {
|
||||
float Float
|
||||
hex Hex
|
||||
jump_from From
|
||||
jump_to To
|
||||
signed Signed
|
||||
title CPU
|
||||
unsigned Unsigned
|
||||
}
|
||||
|
||||
# File dialog
|
||||
dialog {
|
||||
ext_isx ISX modules (*.isx)
|
||||
ext_vb Virtual Boy ROMs (*.vb)
|
||||
load Load
|
||||
load_rom Load ROM
|
||||
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.
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
@@ -1,167 +1,54 @@
|
||||
# Java include directory pathnames
|
||||
# The Windows files need to be copied from a Windows JDK installation
|
||||
include_linux = jni/linux
|
||||
include_windows = jni/windows
|
||||
|
||||
# Default goal
|
||||
.PHONY: default
|
||||
default:
|
||||
@echo $(include_linux)
|
||||
@echo "Planet Virtual Boy Emulator"
|
||||
@echo " https://www.planetvb.com/"
|
||||
@echo " December 26, 2020"
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo
|
||||
@echo "Intended build environment: Debian i386 or amd64"
|
||||
@echo " gcc-multilib"
|
||||
@echo " mingw-w64"
|
||||
@echo " openjdk-15-jdk"
|
||||
@echo "Virtual Boy Emulator - April 20, 2022"
|
||||
@echo
|
||||
@echo "Usage: make <recipe>"
|
||||
@echo " Package recipes:"
|
||||
@echo " build Compiles the native modules and desktop application"
|
||||
@echo " clean Deletes all output files"
|
||||
@echo " core Check the native core library for style errors"
|
||||
@echo " desktop Compiles the Java desktop application"
|
||||
@echo " native Builds all native modules"
|
||||
@echo " pack Bundles everything in a .jar file"
|
||||
@echo " release Produces a .jar and deletes all intermediate files"
|
||||
@echo " Native recipes:"
|
||||
@echo " lin32 Builds native module linux_x86"
|
||||
@echo " lin64 Builds native module linux_x86-64"
|
||||
@echo " win32 Builds native module windows_x86"
|
||||
@echo " win64 Builds native module windows_x86-64"
|
||||
@echo "Target build environment is any Debian with the following packages:"
|
||||
@echo " emscripten"
|
||||
@echo " gcc"
|
||||
@echo " openjdk-17-jdk"
|
||||
@echo
|
||||
@echo "Available make targets:"
|
||||
@echo " build Perform all build commands"
|
||||
@echo " bundle Package the repository for HTML distribution"
|
||||
@echo " clean Remove output files"
|
||||
@echo " core Check the C core source for compiler warnings"
|
||||
@echo " wasm Build the WebAssembly core module"
|
||||
@echo
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Package Recipes #
|
||||
###############################################################################
|
||||
|
||||
# Perform a full build process of the native modules and desktop application
|
||||
.PHONY: build
|
||||
build:
|
||||
@echo " Check C core library for style issues"
|
||||
@make -s core
|
||||
@make -s desktop
|
||||
@make -s native
|
||||
@echo " Build WebAssembly core module"
|
||||
@make -s wasm
|
||||
@echo " Bundle directory tree into HTML file"
|
||||
@make -s bundle
|
||||
|
||||
.PHONY: bundle
|
||||
bundle:
|
||||
@java app/Bundle.java vbemu
|
||||
|
||||
# Delete all output files
|
||||
.PHONY: clean
|
||||
clean: clean_most
|
||||
@rm -f pvbemu_*.jar
|
||||
clean:
|
||||
@rm -f vbemu_*.html app/core/core.wasm
|
||||
|
||||
# Check the native core library for style errors
|
||||
.PHONY: core
|
||||
core:
|
||||
@echo " Checking native core library for style errors"
|
||||
$(eval coreargs = -c -Isrc/core/include -Werror -Wall -Wextra -Wpedantic \
|
||||
-fno-strict-aliasing -fsyntax-only src/core/vue.c)
|
||||
@gcc -std=c90 $(coreargs)
|
||||
@gcc -std=c90 -D VUE_BIGENDIAN $(coreargs)
|
||||
@gcc -std=c99 $(coreargs)
|
||||
@gcc -std=c99 -D VUE_BIGENDIAN $(coreargs)
|
||||
@gcc -std=c11 $(coreargs)
|
||||
@gcc -std=c11 -D VUE_BIGENDIAN $(coreargs)
|
||||
@gcc core/vb.c -I core \
|
||||
-fsyntax-only -Wall -Wextra -Werror -Wpedantic -std=c90
|
||||
@gcc core/vb.c -I core -D VB_LITTLEENDIAN \
|
||||
-fsyntax-only -Wall -Wextra -Werror -Wpedantic -std=c90
|
||||
@emcc core/vb.c -I core \
|
||||
-fsyntax-only -Wall -Wextra -Werror -Wpedantic -std=c90
|
||||
@emcc core/vb.c -I core -D VB_LITTLEENDIAN \
|
||||
-fsyntax-only -Wall -Wextra -Werror -Wpedantic -std=c90
|
||||
|
||||
# Compile the Java desktop application
|
||||
.PHONY: desktop
|
||||
desktop: clean_desktop
|
||||
@echo " Compiling Java desktop application"
|
||||
@javac -sourcepath src/desktop --release 10 -Xlint:unchecked \
|
||||
-Xlint:deprecation -h src/desktop/vue -d . src/desktop/Main.java
|
||||
|
||||
# Build all native modules
|
||||
.PHONY: native
|
||||
native:
|
||||
@make -s lin32
|
||||
@make -s lin64
|
||||
@make -s win32
|
||||
@make -s win64
|
||||
|
||||
# Package the release into a .jar file
|
||||
.PHONY: pack
|
||||
pack:
|
||||
$(eval jarname = "pvbemu_`date +%Y%m%d`.jar")
|
||||
@echo " Bundling into $(jarname)"
|
||||
@jar -cfe $(jarname) Main *.class license.txt \
|
||||
app images locale native util vue
|
||||
|
||||
# Performs a full build and packages it into a .jar
|
||||
.PHONY: release
|
||||
release:
|
||||
@make -s build
|
||||
@make -s pack
|
||||
@echo " Removing temporary files"
|
||||
@make -s clean_most
|
||||
|
||||
# Delete only Java .class files
|
||||
.PHONY: clean_desktop
|
||||
clean_desktop:
|
||||
@rm -r -f *.class app util vue
|
||||
|
||||
# Delete everything but the .jar
|
||||
.PHONY: clean_most
|
||||
clean_most: clean_desktop
|
||||
@rm -f src/desktop/vue/vue_NativeVue.h native/*.dll native/*.so
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Native Recipes #
|
||||
###############################################################################
|
||||
|
||||
# JNI header file
|
||||
src/desktop/vue/vue_NativeVue.h: src/desktop/vue/NativeVue.java
|
||||
@javac -h src/desktop/vue -sourcepath src/desktop -d . \
|
||||
src/desktop/vue/NativeVue.java
|
||||
@sleep 3
|
||||
|
||||
# linux_x86
|
||||
.PHONY: lin32_pre
|
||||
lin32_pre: src/desktop/vue/vue_NativeVue.h
|
||||
$(eval name = linux_x86)
|
||||
$(eval prefix = `uname -m`-linux-gnu-)
|
||||
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
|
||||
$(eval gccargs = -m32 -lm)
|
||||
$(eval ext = .so)
|
||||
.PHONY: lin32
|
||||
lin32: lin32_pre native_common
|
||||
|
||||
# linux_x86-64
|
||||
.PHONY: lin64_pre
|
||||
lin64_pre: src/desktop/vue/vue_NativeVue.h
|
||||
$(eval name = linux_x86-64)
|
||||
$(eval prefix = `uname -m`-linux-gnu-)
|
||||
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
|
||||
$(eval gccargs = -m64 -lm)
|
||||
$(eval ext = .so)
|
||||
.PHONY: lin64
|
||||
lin64: lin64_pre native_common
|
||||
|
||||
# windows_x86
|
||||
.PHONY: win32_pre
|
||||
win32_pre: src/desktop/vue/vue_NativeVue.h
|
||||
$(eval name = windows_x86)
|
||||
$(eval prefix = i686-w64-mingw32-)
|
||||
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
|
||||
$(eval ext = .dll)
|
||||
.PHONY: win32
|
||||
win32: win32_pre native_common
|
||||
|
||||
# windows_x86-64
|
||||
.PHONY: win64_pre
|
||||
win64_pre: src/desktop/vue/vue_NativeVue.h
|
||||
$(eval name = windows_x86-64)
|
||||
$(eval prefix = x86_64-w64-mingw32-)
|
||||
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
|
||||
$(eval ext = .dll)
|
||||
.PHONY: win64
|
||||
win64: win64_pre native_common
|
||||
|
||||
# Common recipe for building native modules
|
||||
.PHONY: native_common
|
||||
native_common:
|
||||
@echo " Building native module $(name)"
|
||||
@$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -O2 \
|
||||
-fno-strict-aliasing -fPIC -Werror \
|
||||
-o native/$(name)$(ext) src/desktop/vue/NativeVue.c src/core/vue.c
|
||||
.PHONY: wasm
|
||||
wasm:
|
||||
@emcc -o app/core/core.wasm wasm/wasm.c core/vb.c -Icore \
|
||||
-D VB_LITTLEENDIAN --no-entry -O2 -flto -s WASM=1 \
|
||||
-D "VB_EXPORT=__attribute__((used))" \
|
||||
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
|
||||
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing
|
||||
@rm -f app/core/*.wasm.tmp*
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/* This file is included through vue.c and cannot be built directly. */
|
||||
#ifdef VUEAPI
|
||||
|
||||
/*****************************************************************************
|
||||
* Module Functions *
|
||||
*****************************************************************************/
|
||||
|
||||
/* System reset */
|
||||
static void pakReset(Vue *vue) {
|
||||
vue->pak.wcr_exp1w = 0;
|
||||
vue->pak.wcr_rom1w = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,295 +0,0 @@
|
||||
#ifndef __VUE_H__
|
||||
#define __VUE_H__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* API management */
|
||||
#ifndef VUEAPI
|
||||
#define VUEAPI extern
|
||||
#endif
|
||||
|
||||
/* Header includes */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Constants *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Boolean values */
|
||||
#define VUE_FALSE 0
|
||||
#define VUE_TRUE 1
|
||||
|
||||
/* Memory access types */
|
||||
#define VUE_S8 0
|
||||
#define VUE_U8 1
|
||||
#define VUE_S16 2
|
||||
#define VUE_U16 3
|
||||
#define VUE_S32 4
|
||||
#define VUE_CANCEL 5
|
||||
|
||||
/* System register indexes */
|
||||
#define VUE_ADTRE 25
|
||||
#define VUE_CHCW 24
|
||||
#define VUE_ECR 4
|
||||
#define VUE_EIPC 0
|
||||
#define VUE_EIPSW 1
|
||||
#define VUE_FEPC 2
|
||||
#define VUE_FEPSW 3
|
||||
#define VUE_PIR 6
|
||||
#define VUE_PSW 5
|
||||
#define VUE_TKCW 7
|
||||
|
||||
/* Non-standard register indexes */
|
||||
#define VUE_PC -1
|
||||
#define VUE_JUMP_FROM -2
|
||||
#define VUE_JUMP_TO -3
|
||||
|
||||
/* Program register indexes */
|
||||
#define VUE_GP 4
|
||||
#define VUE_HP 2
|
||||
#define VUE_LP 31
|
||||
#define VUE_SP 3
|
||||
#define VUE_TP 5
|
||||
|
||||
/* Instruction IDs */
|
||||
#define VUE_ILLEGAL -1
|
||||
#define VUE_ADD_IMM 0
|
||||
#define VUE_ADD_REG 1
|
||||
#define VUE_ADDF_S 2
|
||||
#define VUE_ADDI 3
|
||||
#define VUE_AND 4
|
||||
#define VUE_ANDBSU 5
|
||||
#define VUE_ANDI 6
|
||||
#define VUE_ANDNBSU 7
|
||||
#define VUE_BCOND 8
|
||||
#define VUE_CAXI 9
|
||||
#define VUE_CLI 10
|
||||
#define VUE_CMP_IMM 11
|
||||
#define VUE_CMP_REG 12
|
||||
#define VUE_CMPF_S 13
|
||||
#define VUE_CVT_SW 14
|
||||
#define VUE_CVT_WS 15
|
||||
#define VUE_DIV 16
|
||||
#define VUE_DIVF_S 17
|
||||
#define VUE_DIVU 18
|
||||
#define VUE_HALT 19
|
||||
#define VUE_IN_B 20
|
||||
#define VUE_IN_H 21
|
||||
#define VUE_IN_W 22
|
||||
#define VUE_JAL 23
|
||||
#define VUE_JMP 24
|
||||
#define VUE_JR 25
|
||||
#define VUE_LD_B 26
|
||||
#define VUE_LD_H 27
|
||||
#define VUE_LD_W 28
|
||||
#define VUE_LDSR 29
|
||||
#define VUE_MOV_IMM 30
|
||||
#define VUE_MOV_REG 31
|
||||
#define VUE_MOVBSU 32
|
||||
#define VUE_MOVEA 33
|
||||
#define VUE_MOVHI 34
|
||||
#define VUE_MPYHW 35
|
||||
#define VUE_MUL 36
|
||||
#define VUE_MULF_S 37
|
||||
#define VUE_MULU 38
|
||||
#define VUE_NOT 39
|
||||
#define VUE_NOTBSU 40
|
||||
#define VUE_OR 41
|
||||
#define VUE_ORBSU 42
|
||||
#define VUE_ORI 43
|
||||
#define VUE_ORNBSU 44
|
||||
#define VUE_OUT_B 45
|
||||
#define VUE_OUT_H 46
|
||||
#define VUE_OUT_W 47
|
||||
#define VUE_RETI 48
|
||||
#define VUE_REV 49
|
||||
#define VUE_SAR_IMM 50
|
||||
#define VUE_SAR_REG 51
|
||||
#define VUE_SCH0BSD 52
|
||||
#define VUE_SCH0BSU 53
|
||||
#define VUE_SCH1BSD 54
|
||||
#define VUE_SCH1BSU 55
|
||||
#define VUE_SEI 56
|
||||
#define VUE_SETF 57
|
||||
#define VUE_SHL_IMM 58
|
||||
#define VUE_SHL_REG 59
|
||||
#define VUE_SHR_IMM 60
|
||||
#define VUE_SHR_REG 61
|
||||
#define VUE_ST_B 62
|
||||
#define VUE_ST_H 63
|
||||
#define VUE_ST_W 64
|
||||
#define VUE_STSR 65
|
||||
#define VUE_SUB 66
|
||||
#define VUE_SUBF_S 67
|
||||
#define VUE_TRAP 68
|
||||
#define VUE_TRNC_SW 69
|
||||
#define VUE_XB 70
|
||||
#define VUE_XH 71
|
||||
#define VUE_XOR 72
|
||||
#define VUE_XORBSU 73
|
||||
#define VUE_XORI 74
|
||||
#define VUE_XORNBSU 75
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Types *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Forward references */
|
||||
typedef struct Vue Vue;
|
||||
|
||||
/* Boolean */
|
||||
typedef int vbool;
|
||||
|
||||
/* Access state */
|
||||
typedef struct {
|
||||
uint32_t address; /* CPU bus address */
|
||||
int32_t value; /* Value read/to write */
|
||||
int8_t fetch; /* Index of machine code unit */
|
||||
int8_t type; /* Data type */
|
||||
} VueAccess;
|
||||
|
||||
/* Instruction state */
|
||||
typedef struct {
|
||||
int32_t bits; /* Binary encoding */
|
||||
int32_t disp; /* Displacement for jumps and branches */
|
||||
int32_t imm; /* Immediate operand */
|
||||
uint8_t cond; /* Condition for Bcond */
|
||||
int8_t format; /* Binary format */
|
||||
int8_t id; /* Library-specific identifier */
|
||||
uint8_t opcode; /* Instruction opcode */
|
||||
uint8_t reg1; /* Source/right register */
|
||||
uint8_t reg2; /* Destination/left register */
|
||||
uint8_t size; /* Number of bytes in encoding */
|
||||
uint8_t subopcode; /* Instruction subopcode */
|
||||
} VueInstruction;
|
||||
|
||||
/* Breakpoint handler callbacks */
|
||||
typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
|
||||
typedef int32_t (*VueOnException)(Vue *, uint16_t );
|
||||
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
|
||||
typedef int32_t (*VueOnFrame )(Vue * );
|
||||
|
||||
/* Emulation state */
|
||||
struct Vue {
|
||||
void *tag; /* Application reference */
|
||||
int32_t breakCode; /* Application break code */
|
||||
uint8_t wram[0x10000]; /* System memory */
|
||||
|
||||
/* Breakpoint handlers */
|
||||
VueOnException onException;
|
||||
VueOnExecute onExecute;
|
||||
VueOnFrame onFrame;
|
||||
VueOnAccess onRead;
|
||||
VueOnAccess onWrite;
|
||||
|
||||
/* CPU state */
|
||||
struct {
|
||||
VueAccess access; /* Access state */
|
||||
VueInstruction inst; /* Instruction state */
|
||||
uint32_t cycles; /* Cycles until next stage */
|
||||
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
||||
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
||||
uint16_t exception; /* Exception code */
|
||||
uint16_t irq; /* Interrupt lines */
|
||||
int fetch; /* Fetch unit index */
|
||||
int stage; /* Current processing stage */
|
||||
|
||||
/* Program registers */
|
||||
int32_t program[32];
|
||||
|
||||
/* System registers */
|
||||
int32_t adtre; /* Address Trap Register for Execution */
|
||||
int32_t eipc; /* Exception/interrupt PC */
|
||||
int32_t eipsw; /* Exception/interrupt PSW */
|
||||
int32_t fepc; /* Duplexed exception PC */
|
||||
int32_t fepsw; /* Duplexed exception PSW */
|
||||
int32_t pc; /* Program Counter */
|
||||
int32_t sr29; /* System register 29 */
|
||||
int32_t sr31; /* System register 31 */
|
||||
|
||||
/* Program Status Word */
|
||||
int8_t psw_ae; /* Address Trap Enable */
|
||||
int8_t psw_ep; /* Exception Pending */
|
||||
int8_t psw_id; /* Interrupt Disable */
|
||||
int8_t psw_cy; /* Carry */
|
||||
int8_t psw_fiv; /* Floating Reserved Operand */
|
||||
int8_t psw_fov; /* Floating Overflow */
|
||||
int8_t psw_fpr; /* Floating Precision */
|
||||
int8_t psw_fro; /* Floating Reserved Operand */
|
||||
int8_t psw_fud; /* Floating Underflow */
|
||||
int8_t psw_fzd; /* Floating Zero Divide */
|
||||
int8_t psw_i; /* Interrupt Level */
|
||||
int8_t psw_np; /* NMI Pending */
|
||||
int8_t psw_ov; /* Overflow */
|
||||
int8_t psw_s; /* Sign */
|
||||
int8_t psw_z; /* Zero */
|
||||
|
||||
/* Cache Control Word */
|
||||
uint16_t chcw_cec; /* Clear Entry Count */
|
||||
uint16_t chcw_cen; /* Clear Entry Number */
|
||||
int8_t chcw_icc; /* Instruction Cache Clear */
|
||||
int8_t chcw_icd; /* Instruction Cache Dump */
|
||||
int8_t chcw_ice; /* Instruction Cache Enable */
|
||||
int8_t chcw_icr; /* Instruction Cache Restore */
|
||||
int32_t chcw_sa; /* Spill-Out Base Address */
|
||||
|
||||
/* Exception Cause Register */
|
||||
uint16_t ecr_eicc; /* Exception/Interrupt Cause Code */
|
||||
uint16_t ecr_fecc; /* Fatal Error Cause Code */
|
||||
} cpu;
|
||||
|
||||
/* Game pak state */
|
||||
struct {
|
||||
uint8_t *ram; /* Cartridge SRAM */
|
||||
uint32_t ramSize; /* Number of bytes in cartridge SRAM */
|
||||
uint8_t *rom; /* Cartridge ROM */
|
||||
uint32_t romSize; /* Number of bytes in cartridge ROM */
|
||||
int8_t wcr_exp1w; /* Expansion one-wait mode */
|
||||
int8_t wcr_rom1w; /* ROM one-wait mode */
|
||||
} pak;
|
||||
|
||||
/* VIP state */
|
||||
struct {
|
||||
uint8_t vram[0x40000]; /* Video memory */
|
||||
} vip;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Function Prototypes *
|
||||
*****************************************************************************/
|
||||
|
||||
VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
|
||||
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
||||
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
|
||||
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
||||
VUEAPI void* vueGetTag (Vue *vue);
|
||||
VUEAPI void vueInitialize (Vue *vue);
|
||||
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
|
||||
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
|
||||
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
|
||||
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
|
||||
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
|
||||
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
|
||||
VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
||||
VUEAPI void vueReset (Vue *vue);
|
||||
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
||||
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
||||
VUEAPI void* vueSetTag (Vue *vue, void *tag);
|
||||
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
||||
VUEAPI vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __VUE_H__ */
|
||||
@@ -1,298 +0,0 @@
|
||||
/* This file is included through vue.c and cannot be built directly. */
|
||||
#ifdef VUEAPI
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Internal Functions *
|
||||
******************************************************************************/
|
||||
|
||||
/* Process the simulation */
|
||||
static void vipEmulate(Vue *vue, uint32_t cycles) {
|
||||
vue = vue;
|
||||
cycles = cycles;
|
||||
}
|
||||
|
||||
/* Read an I/O register */
|
||||
static int32_t vipReadRegister(Vue *vue, int32_t address) {
|
||||
vue=vue;
|
||||
/* Process by register */
|
||||
switch (address & ~1) {
|
||||
case 0x0005F800: break; /* INTPND */
|
||||
case 0x0005F802: break; /* INTENB */
|
||||
case 0x0005F804: break; /* INTCLR */
|
||||
case 0x0005F820: break; /* DPSTTS */
|
||||
case 0x0005F822: break; /* DPCTRL */
|
||||
case 0x0005F824: break; /* BRTA */
|
||||
case 0x0005F826: break; /* BRTB */
|
||||
case 0x0005F828: break; /* BRTC */
|
||||
case 0x0005F82A: break; /* REST */
|
||||
case 0x0005F82E: break; /* FRMCYC */
|
||||
case 0x0005F830: break; /* CTA */
|
||||
case 0x0005F840: break; /* XPSTTS */
|
||||
case 0x0005F842: break; /* XPCTRL */
|
||||
case 0x0005F844: break; /* VER */
|
||||
case 0x0005F848: break; /* SPT0 */
|
||||
case 0x0005F84A: break; /* SPT1 */
|
||||
case 0x0005F84C: break; /* SPT2 */
|
||||
case 0x0005F84E: break; /* SPT3 */
|
||||
case 0x0005F860: break; /* GPLT0 */
|
||||
case 0x0005F862: break; /* GPLT1 */
|
||||
case 0x0005F864: break; /* GPLT2 */
|
||||
case 0x0005F866: break; /* GPLT3 */
|
||||
case 0x0005F868: break; /* JPLT0 */
|
||||
case 0x0005F86A: break; /* JPLT1 */
|
||||
case 0x0005F86C: break; /* JPLT2 */
|
||||
case 0x0005F86E: break; /* JPLT3 */
|
||||
case 0x0005F870: break; /* BKCOL */
|
||||
}
|
||||
|
||||
/* Unmapped */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read an I/O register */
|
||||
static void vipWriteRegister(Vue *vue, int32_t address, int32_t value) {
|
||||
vue=vue;
|
||||
value=value;
|
||||
/* Process by register */
|
||||
switch (address & ~1) {
|
||||
case 0x0005F800: break; /* INTPND */
|
||||
case 0x0005F802: break; /* INTENB */
|
||||
case 0x0005F804: break; /* INTCLR */
|
||||
case 0x0005F820: break; /* DPSTTS */
|
||||
case 0x0005F822: break; /* DPCTRL */
|
||||
case 0x0005F824: break; /* BRTA */
|
||||
case 0x0005F826: break; /* BRTB */
|
||||
case 0x0005F828: break; /* BRTC */
|
||||
case 0x0005F82A: break; /* REST */
|
||||
case 0x0005F82E: break; /* FRMCYC */
|
||||
case 0x0005F830: break; /* CTA */
|
||||
case 0x0005F840: break; /* XPSTTS */
|
||||
case 0x0005F842: break; /* XPCTRL */
|
||||
case 0x0005F844: break; /* VER */
|
||||
case 0x0005F848: break; /* SPT0 */
|
||||
case 0x0005F84A: break; /* SPT1 */
|
||||
case 0x0005F84C: break; /* SPT2 */
|
||||
case 0x0005F84E: break; /* SPT3 */
|
||||
case 0x0005F860: break; /* GPLT0 */
|
||||
case 0x0005F862: break; /* GPLT1 */
|
||||
case 0x0005F864: break; /* GPLT2 */
|
||||
case 0x0005F866: break; /* GPLT3 */
|
||||
case 0x0005F868: break; /* JPLT0 */
|
||||
case 0x0005F86A: break; /* JPLT1 */
|
||||
case 0x0005F86C: break; /* JPLT2 */
|
||||
case 0x0005F86E: break; /* JPLT3 */
|
||||
case 0x0005F870: break; /* BKCOL */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Read a value from the CPU bus */
|
||||
static int32_t vipRead(Vue *vue, int32_t address, int32_t type) {
|
||||
int32_t value; /* Register value */
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
/* VRAM */
|
||||
if (address < 0x00040000)
|
||||
return readBuffer(vue->vip.vram, 0x40000, address, type);
|
||||
|
||||
/* Mirrors of character memory */
|
||||
if (address >= 0x00078000)
|
||||
return readBuffer(vue->vip.vram, 0x40000,
|
||||
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
|
||||
(address & 0x00001FFF),
|
||||
type);
|
||||
|
||||
/* I/O register or unmapped */
|
||||
value = vipReadRegister(vue, address);
|
||||
if (type < 2 && (address & 1) == 1)
|
||||
value >>= 8;
|
||||
switch (type) {
|
||||
case VUE_S8 : return SIGN_EXTEND(value, 8);
|
||||
case VUE_U8 : return value & 0x000000FF;
|
||||
case VUE_S16: return SIGN_EXTEND(value, 16);
|
||||
case VUE_S32: return value | vipReadRegister(vue, address + 2) << 16;
|
||||
}
|
||||
return value; /* U16 */
|
||||
}
|
||||
|
||||
/* Read bytes from the CPU bus */
|
||||
static void vipReadBytes(Vue *vue, int address, uint8_t *dest, int length) {
|
||||
int32_t count, size, value; /* Working variables */
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
/* Perform the operation */
|
||||
while (length > 0) {
|
||||
|
||||
/* VRAM */
|
||||
if (address < 0x00040000) {
|
||||
count = 0x00040000 - address;
|
||||
if (length < count)
|
||||
count = length;
|
||||
readBytes(vue->vip.vram, 0x40000, address, dest, count);
|
||||
}
|
||||
|
||||
/* Mirrors of character memory */
|
||||
else if (address >= 0x00078000) {
|
||||
count = 0x2000 - (address & 0x1FFF);
|
||||
if (length < count)
|
||||
count = length;
|
||||
readBytes(vue->vip.vram, 0x40000,
|
||||
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
|
||||
(address & 0x00001FFF),
|
||||
dest, count);
|
||||
}
|
||||
|
||||
/* I/O register or unmapped */
|
||||
else {
|
||||
count = 0x00078000 - address;
|
||||
if (length < count)
|
||||
count = length;
|
||||
|
||||
/* Read all registers in the range */
|
||||
while (count > 0) {
|
||||
value = vipReadRegister(vue, address);
|
||||
|
||||
/* Odd address */
|
||||
if ((address & 1) == 1) {
|
||||
*dest = value >> 8;
|
||||
address++;
|
||||
count --;
|
||||
length --;
|
||||
dest ++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Even address */
|
||||
size = count == 1 ? 1 : 2;
|
||||
*dest = value;
|
||||
if (size == 2)
|
||||
dest[1] = value >> 8;
|
||||
address += size;
|
||||
count -= size;
|
||||
length -= size;
|
||||
dest += size;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Advance to the next region */
|
||||
address += count;
|
||||
length -= count;
|
||||
dest += count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* System reset */
|
||||
static void vipReset(Vue *vue) {
|
||||
vue = vue;
|
||||
}
|
||||
|
||||
/* Determine the number of CPU cycles until a breakpoint could trigger */
|
||||
static uint32_t vipUntil(Vue *vue, uint32_t cycles) {
|
||||
vue = vue;
|
||||
return cycles;
|
||||
}
|
||||
|
||||
/* Write a value to the CPU bus */
|
||||
static void vipWrite(Vue *vue, int32_t address, int32_t type, int32_t value) {
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
/* VRAM */
|
||||
if (address < 0x00040000) {
|
||||
writeBuffer(vue->vip.vram, 0x40000, address, type, value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mirrors of character memory */
|
||||
if (address >= 0x00078000) {
|
||||
writeBuffer(vue->vip.vram, 0x40000,
|
||||
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
|
||||
(address & 0x00001FFF),
|
||||
type, value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* I/O register or unmapped */
|
||||
if (type < 2 && (address & 1) == 1)
|
||||
value <<= 8;
|
||||
vipWriteRegister(vue, address, value);
|
||||
if (type == VUE_S32)
|
||||
vipWriteRegister(vue, address + 2, value >> 16);
|
||||
}
|
||||
|
||||
/* Write bytes to the CPU bus */
|
||||
static void vipWriteBytes(Vue *vue, int address, uint8_t *src, int length) {
|
||||
int32_t count, size, value; /* Working variables */
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
/* Perform the operation */
|
||||
while (length > 0) {
|
||||
|
||||
/* VRAM */
|
||||
if (address < 0x00040000) {
|
||||
count = 0x00040000 - address;
|
||||
if (length < count)
|
||||
count = length;
|
||||
writeBytes(vue->vip.vram, 0x40000, address, src, count);
|
||||
}
|
||||
|
||||
/* Mirrors of character memory */
|
||||
else if (address >= 0x00078000) {
|
||||
count = 0x2000 - (address & 0x1FFF);
|
||||
if (length < count)
|
||||
count = length;
|
||||
writeBytes(vue->vip.vram, 0x40000,
|
||||
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
|
||||
(address & 0x00001FFF),
|
||||
src, count);
|
||||
}
|
||||
|
||||
/* I/O register or unmapped */
|
||||
else {
|
||||
count = 0x00078000 - address;
|
||||
if (length < count)
|
||||
count = length;
|
||||
|
||||
/* Read all registers in the range */
|
||||
while (count > 0) {
|
||||
value = *src;
|
||||
|
||||
/* Odd address */
|
||||
if ((address & 1) == 1) {
|
||||
vipWriteRegister(vue, address, value << 8);
|
||||
address++;
|
||||
count --;
|
||||
length --;
|
||||
src ++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Even address */
|
||||
size = count == 1 ? 1 : 2;
|
||||
if (size == 2)
|
||||
value |= src[1] << 8;
|
||||
vipWriteRegister(vue, address, value);
|
||||
address += size;
|
||||
count -= size;
|
||||
length -= size;
|
||||
src += size;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Advance to the next region */
|
||||
address += count;
|
||||
length -= count;
|
||||
src += count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,458 +0,0 @@
|
||||
#define VUEAPI
|
||||
#include <vue.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Macros *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Sign-extend a value */
|
||||
#define SIGN_EXTEND(bits, value) \
|
||||
((value) & 1 << (bits - 1) ? (value) | (~(1 << bits) + 1) : (value))
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Constants *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Memory access type sizes */
|
||||
static const uint32_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Module Functions *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Read a value from a byte buffer */
|
||||
static int32_t readBuffer(uint8_t *data, uint32_t datlen, uint32_t address,
|
||||
int32_t type) {
|
||||
uint32_t size = TYPE_SIZES[type]; /* Size of data type */
|
||||
int32_t value = 0;
|
||||
|
||||
/* Error checking */
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
|
||||
/* Common processing */
|
||||
address &= (~size + 1) & (datlen - 1);
|
||||
|
||||
/* The host is little-endian */
|
||||
#ifndef VUE_BIGENDIAN
|
||||
switch (type) {
|
||||
case VUE_S8 : value = *( int8_t *)&data[address]; break;
|
||||
case VUE_U8 : value = *(uint8_t *)&data[address]; break;
|
||||
case VUE_S16: value = *( int16_t *)&data[address]; break;
|
||||
case VUE_U16: value = *(uint16_t *)&data[address]; break;
|
||||
case VUE_S32: value = *( int32_t *)&data[address]; break;
|
||||
}
|
||||
|
||||
/* The host is big-endian */
|
||||
#else
|
||||
switch (type) {
|
||||
case VUE_S32: value =
|
||||
data[address + 3] << 24 |
|
||||
(data[address + 2] & 0xFF) << 16;
|
||||
/* Fallthrough */
|
||||
case VUE_S16: /* Fallthrough */
|
||||
case VUE_U16: value |=
|
||||
(data[address + 1] & 0xFF) << 8;
|
||||
/* Fallthrough */
|
||||
case VUE_S8 : /* Fallthrough */
|
||||
case VUE_U8 : value |=
|
||||
data[address ] & 0xFF;
|
||||
}
|
||||
|
||||
/* Sign-extend the value if appropriate */
|
||||
if ((type & 1) == 0) {
|
||||
size <<= 3;
|
||||
value = SIGN_EXTEND(size, value);
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Read bytes from a byte buffer */
|
||||
static void readBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
||||
uint8_t *dest, uint32_t length) {
|
||||
|
||||
/* The source does not exist */
|
||||
if (data == NULL) {
|
||||
while (length--)
|
||||
*dest++ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Transfer bytes from the source as a circular buffer */
|
||||
while (length--)
|
||||
*dest++ = data[address++ & (datlen - 1)];
|
||||
}
|
||||
|
||||
/* Write a value to a byte buffer */
|
||||
static void writeBuffer(uint8_t *data, uint32_t datlen, uint32_t address,
|
||||
int32_t type, int32_t value) {
|
||||
uint32_t size = TYPE_SIZES[type]; /* Size of data type */
|
||||
|
||||
/* Error checking */
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
/* Common processing */
|
||||
address &= (~size + 1) & (datlen - 1);
|
||||
|
||||
/* The host is big-endian */
|
||||
#ifdef VUE_BIGENDIAN
|
||||
switch (type) {
|
||||
case VUE_S32:
|
||||
value = (value >> 16 & 0x0000FFFF) | value << 16;
|
||||
/* Fallthrough */
|
||||
case VUE_S16: /* Fallthrough */
|
||||
case VUE_U16:
|
||||
value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Processing by data type */
|
||||
switch (type) {
|
||||
case VUE_S8 : /* Fallthrough */
|
||||
case VUE_U8 : *(int8_t *)&data[address] = (int8_t ) value; break;
|
||||
case VUE_S16: /* Fallthrough */
|
||||
case VUE_U16: *(int16_t *)&data[address] = (int16_t ) value; break;
|
||||
case VUE_S32: *(int32_t *)&data[address] = (int32_t ) value; break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write bytes to a byte buffer */
|
||||
static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
||||
uint8_t *src, uint32_t length) {
|
||||
|
||||
/* The destination does not exist */
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
/* Transfer bytes to the destination as a circular buffer */
|
||||
while (length--)
|
||||
data[address++ & (datlen - 1)] = *src++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Component Includes *
|
||||
*****************************************************************************/
|
||||
|
||||
#include "cpu.c"
|
||||
#include "gamepak.c"
|
||||
#include "vip.c"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Library Functions *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Process the simulation */
|
||||
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
|
||||
uint32_t cycles; /* Number of cycles to process */
|
||||
|
||||
/* Process up to the given number of cycles */
|
||||
do {
|
||||
|
||||
/* Determine the number of cycles where no breakpoint will occur */
|
||||
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
|
||||
cycles = cpuUntil (vue, cycles);
|
||||
/*cycles = padUntil (vue, cycles);*/
|
||||
/*cycles = linkUntil (vue, cycles);*/
|
||||
/*cycles = timerUntil(vue, cycles);*/
|
||||
cycles = vipUntil (vue, cycles);
|
||||
|
||||
/* Process all system components */
|
||||
vue->breakCode = 0;
|
||||
cpuEmulate (vue, cycles);
|
||||
/*padEmulate (vue, cycles);*/
|
||||
/*pakEmulate (vue, cycles);*/
|
||||
/*linkEmulate (vue, cycles);*/
|
||||
/*timerEmulate(vue, cycles);*/
|
||||
vipEmulate (vue, cycles);
|
||||
/*vsuEmulate (vue, cycles);*/
|
||||
maxCycles -= cycles;
|
||||
} while (vue->breakCode == 0 && maxCycles != 0);
|
||||
|
||||
/* A break condition has occurred */
|
||||
return maxCycles;
|
||||
}
|
||||
|
||||
/* Retrieve the most recent applicaiton break code */
|
||||
int32_t vueGetBreakCode(Vue *vue) {
|
||||
return vue == NULL ? 0 : vue->breakCode;
|
||||
}
|
||||
|
||||
/* Retrieve the most recent exception code */
|
||||
uint16_t vueGetExceptionCode(Vue *vue) {
|
||||
return vue == NULL ? 0 : vue->cpu.exception;
|
||||
}
|
||||
|
||||
/* Retrieve the value of a register */
|
||||
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL)
|
||||
return 0;
|
||||
|
||||
/* Non-indexed registers */
|
||||
if (system && index < 0) switch (index) {
|
||||
case VUE_JUMP_FROM:
|
||||
return vue->cpu.jumpFrom[vue->cpu.psw_np ? 2 : vue->cpu.psw_ep];
|
||||
case VUE_JUMP_TO :
|
||||
return vue->cpu.jumpTo [vue->cpu.psw_np ? 2 : vue->cpu.psw_ep];
|
||||
case VUE_PC : return vue->cpu.pc;
|
||||
}
|
||||
|
||||
/* Indexed registers */
|
||||
return
|
||||
index < 0 || index > 31 ? 0 :
|
||||
system ? cpuGetSystemRegister(vue, index) :
|
||||
vue->cpu.program[index]
|
||||
;
|
||||
}
|
||||
|
||||
/* Retrieve the application reference */
|
||||
void* vueGetTag(Vue *vue) {
|
||||
return vue == NULL ? NULL : vue->tag;
|
||||
}
|
||||
|
||||
/* Prepare an emulation state context for use */
|
||||
void vueInitialize(Vue *vue) {
|
||||
if (vue == NULL)
|
||||
return;
|
||||
vue->onException = NULL;
|
||||
vue->onExecute = NULL;
|
||||
vue->onFrame = NULL;
|
||||
vue->onRead = NULL;
|
||||
vue->onWrite = NULL;
|
||||
vue->pak.ram = NULL;
|
||||
vue->pak.rom = NULL;
|
||||
vueReset(vue);
|
||||
}
|
||||
|
||||
/* Specify an exception breakpoint callback */
|
||||
VueOnException vueOnException(Vue *vue, VueOnException callback) {
|
||||
VueOnException ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onException;
|
||||
vue->onException = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify an execute breakpoint callback */
|
||||
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
|
||||
VueOnExecute ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onExecute;
|
||||
vue->onExecute = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a frame breakpoint callback */
|
||||
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
|
||||
VueOnFrame ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onFrame;
|
||||
vue->onFrame = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a read breakpoint callback */
|
||||
VueOnAccess vueOnRead(Vue *vue, VueOnAccess callback) {
|
||||
VueOnAccess ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onRead;
|
||||
vue->onRead = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a write breakpoint callback */
|
||||
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
|
||||
VueOnAccess ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onWrite;
|
||||
vue->onWrite = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read a value from the CPU bus */
|
||||
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL || type < 0 || type > 4)
|
||||
return 0;
|
||||
|
||||
/* Perform the operation */
|
||||
switch (address >> 24 & 7) {
|
||||
case 0: return vipRead (vue , address,type);
|
||||
case 5: return readBuffer(vue->wram , 0x10000,address,type);
|
||||
case 6: return readBuffer(vue->pak.ram,vue->pak.ramSize,address,type);
|
||||
case 7: return readBuffer(vue->pak.rom,vue->pak.romSize,address,type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read bytes from the CPU bus */
|
||||
vbool vueReadBytes(Vue *vue, uint32_t address, uint8_t *dest, uint32_t length){
|
||||
uint32_t count; /* Bytes to read in one iteration */
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL || dest == NULL)
|
||||
return VUE_FALSE;
|
||||
|
||||
/* Perform the operation */
|
||||
while (length > 0) {
|
||||
count = 0x01000000 - (address & 0x00FFFFFF);
|
||||
if (length < count)
|
||||
count = length;
|
||||
switch (address >> 24 & 7) {
|
||||
case 0: vipReadBytes(vue ,
|
||||
address, dest, count); break;
|
||||
case 5: readBytes(vue->wram , 0x10000,
|
||||
address, dest, count); break;
|
||||
case 6: readBytes(vue->pak.ram, vue->pak.ramSize,
|
||||
address, dest, count); break;
|
||||
case 7: readBytes(vue->pak.rom, vue->pak.romSize,
|
||||
address, dest, count); break;
|
||||
default:
|
||||
for (address += count, length -= count; count--;)
|
||||
*dest++ = 0;
|
||||
continue;
|
||||
}
|
||||
address += count;
|
||||
length -= count;
|
||||
dest += count;
|
||||
}
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Initialize all system components */
|
||||
void vueReset(Vue *vue) {
|
||||
uint32_t x; /* Iterator */
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL)
|
||||
return;
|
||||
|
||||
/* Reset state */
|
||||
cpuReset(vue);
|
||||
pakReset(vue);
|
||||
for (x = 0; x < 0x10000; x++)
|
||||
vue->wram[x] = 0;
|
||||
}
|
||||
|
||||
/* Specify a value for a register */
|
||||
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL)
|
||||
return 0;
|
||||
|
||||
/* PC */
|
||||
if (index == VUE_PC && system) {
|
||||
if (vue->cpu.stage == CPU_EXECUTE || vue->cpu.stage == CPU_FETCH) {
|
||||
vue->cpu.fetch = -1;
|
||||
vue->cpu.stage = CPU_FETCH;
|
||||
}
|
||||
return vue->cpu.pc = value & 0xFFFFFFFE;
|
||||
}
|
||||
|
||||
/* Other */
|
||||
return
|
||||
index < 0 || index > 31 ? 0 :
|
||||
system ? cpuSetSystemRegister(vue, index, value, VUE_TRUE) :
|
||||
index == 0 ? 0 : (vue->cpu.program[index] = value)
|
||||
;
|
||||
}
|
||||
|
||||
/* Specify a new ROM buffer */
|
||||
vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
|
||||
|
||||
/* Error checking */
|
||||
if (
|
||||
vue == NULL ||
|
||||
rom == NULL ||
|
||||
size < 1024 || size > 0x01000000 ||
|
||||
(size & (size - 1)) != 0
|
||||
) return VUE_FALSE;
|
||||
|
||||
/* Accept the new ROM buffer */
|
||||
vue->pak.rom = rom;
|
||||
vue->pak.romSize = size;
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Specify a new application reference */
|
||||
void* vueSetTag(Vue *vue, void *tag) {
|
||||
void *ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->tag;
|
||||
vue->tag = tag;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write a value to the CPU bus */
|
||||
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL)
|
||||
return;
|
||||
|
||||
/* Perform the operation */
|
||||
switch (address >> 24 & 7) {
|
||||
case 0: vipWrite(vue ,
|
||||
address, type, value); break;
|
||||
case 5: writeBuffer(vue->wram , 0x10000,
|
||||
address, type, value); break;
|
||||
case 6: writeBuffer(vue->pak.ram, vue->pak.ramSize,
|
||||
address, type, value); break;
|
||||
case 7: writeBuffer(vue->pak.rom, vue->pak.romSize,
|
||||
address, type, value); break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write bytes to the CPU bus */
|
||||
vbool vueWriteBytes(Vue *vue, uint32_t address, uint8_t *src, uint32_t length){
|
||||
uint32_t count; /* Bytes to write in one iteration */
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL || src == NULL)
|
||||
return VUE_FALSE;
|
||||
|
||||
/* Perform the operation */
|
||||
while (length > 0) {
|
||||
count = 0x01000000 - (address & 0x00FFFFFF);
|
||||
if (length < count)
|
||||
count = length;
|
||||
switch (address >> 24 & 7) {
|
||||
case 0: vipWriteBytes(vue ,
|
||||
address, src, count); break;
|
||||
case 5: writeBytes(vue->wram , 0x10000,
|
||||
address, src, count); break;
|
||||
case 6: writeBytes(vue->pak.ram, vue->pak.ramSize,
|
||||
address, src, count); break;
|
||||
case 7: writeBytes(vue->pak.rom, vue->pak.ramSize,
|
||||
address, src, count); break;
|
||||
}
|
||||
address += count;
|
||||
length -= count;
|
||||
src += count;
|
||||
}
|
||||
return VUE_TRUE;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Java imports
|
||||
import java.io.*;
|
||||
|
||||
// Project imports
|
||||
import app.*;
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Desktop application primary class
|
||||
public class Main {
|
||||
|
||||
// Program entry point
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Configure Swing look-and-feel
|
||||
Util.setSystemLAF();
|
||||
|
||||
// Load the native module, if available
|
||||
for (String filename : Util.listFiles("native")) {
|
||||
File file = null;
|
||||
|
||||
// Skip any file that isn't a shared object library
|
||||
if (!filename.endsWith(".dll") && !filename.endsWith(".so"))
|
||||
continue;
|
||||
|
||||
// Write the contents of the native object file
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
file = File.createTempFile("native", "." + filename);
|
||||
stream = new FileOutputStream(file);
|
||||
stream.write(Util.fileRead("native/" + filename));
|
||||
} catch (Exception e) { file = null; }
|
||||
try { stream.close(); } catch (Exception e) { }
|
||||
|
||||
// Load the native object file into the JVM
|
||||
try {
|
||||
System.load(file.getAbsolutePath());
|
||||
Vue.setNativeID(filename.substring(0,
|
||||
filename.lastIndexOf(".")));
|
||||
break;
|
||||
}
|
||||
catch (Error e) { }
|
||||
}
|
||||
|
||||
// Use the native module
|
||||
boolean useNative = false;
|
||||
for (String arg : args)
|
||||
if (arg.trim().toLowerCase().equals("native"))
|
||||
useNative = true;
|
||||
|
||||
// Begin application operations
|
||||
new App(useNative);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Top-level software state manager
|
||||
public class App {
|
||||
|
||||
// Instance fields
|
||||
private boolean useNative; // Produce native core contexts
|
||||
private Localizer.Locale[] locales; // Language translations
|
||||
private ArrayList<MainWindow> windows; // Application windows
|
||||
|
||||
// Configuration fields
|
||||
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
|
||||
int rgbClear; // Transparent pixel color
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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 };
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public App(boolean useNative) {
|
||||
|
||||
// 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[][] {
|
||||
Arrays.copyOf(GREEN , 4),
|
||||
Arrays.copyOf(MAGENTA, 4),
|
||||
Arrays.copyOf(RED , 4)
|
||||
};
|
||||
rgbClear = 0x555555;
|
||||
|
||||
// Additional processing
|
||||
setUseNative(useNative);
|
||||
initLocales();
|
||||
addWindow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a new program window
|
||||
void addWindow() {
|
||||
windows.add(new MainWindow(this));
|
||||
windowsChanged();
|
||||
}
|
||||
|
||||
// Determine whether the native module is in use
|
||||
boolean getUseNative() {
|
||||
return useNative;
|
||||
}
|
||||
|
||||
// Retrieve a list of registered locales
|
||||
Localizer.Locale[] listLocales() {
|
||||
var ret = new Localizer.Locale[locales.length];
|
||||
System.arraycopy(locales, 0, ret, 0, locales.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Determine the number of a window in the collection
|
||||
int numberOf(MainWindow window) {
|
||||
return windows.indexOf(window) + 1;
|
||||
}
|
||||
|
||||
// Remove a program window
|
||||
void removeWindow(MainWindow window) {
|
||||
windows.remove(window);
|
||||
windowsChanged();
|
||||
}
|
||||
|
||||
// Specify whether using the native module
|
||||
boolean setUseNative(boolean useNative) {
|
||||
return this.useNative = useNative && Vue.getNativeID() != null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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() {
|
||||
|
||||
// Process all locale files
|
||||
var locales = new HashMap<String, Localizer.Locale>();
|
||||
for (String file : Util.listFiles("locale")) {
|
||||
|
||||
// Process the file
|
||||
try {
|
||||
var locale = Localizer.parse(Util.textRead("locale/" + file));
|
||||
String key = locale.id.toLowerCase();
|
||||
|
||||
// Register the locale
|
||||
if (locales.containsKey(key)) throw new RuntimeException(
|
||||
"Locale with ID '" + locale.id + "' already registered");
|
||||
locales.put(key, locale);
|
||||
|
||||
// The locale matches the one in the config
|
||||
if (key.equals("en-us"))
|
||||
localizer.setLocale(locale);
|
||||
}
|
||||
|
||||
// Could not process the file
|
||||
catch (Exception e) {
|
||||
System.err.println("Error parsing locale " +
|
||||
file + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Produce the list of registered locales
|
||||
this.locales = locales.values().toArray(
|
||||
new Localizer.Locale[locales.size()]);
|
||||
Arrays.sort(this.locales);
|
||||
|
||||
// Select a default locale
|
||||
if (localizer.getLocale() != null || this.locales.length == 0)
|
||||
return;
|
||||
var locale = locales.get("en-us");
|
||||
localizer.setLocale(locale != null ? locale : this.locales[0]);
|
||||
}
|
||||
|
||||
// A window has been added to or removed from the program state
|
||||
private void windowsChanged() {
|
||||
int count = windows.size();
|
||||
for (int x = 0; x < count; x++)
|
||||
windows.get(x).windowsChanged(x + 1, count == 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,512 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// VIP backgrounds window
|
||||
class BGMapsWindow extends ChildWindow {
|
||||
|
||||
// Instance fields
|
||||
private int cellIndex; // Current cell index
|
||||
private int character; // Cell character index
|
||||
private Point dragging; // Most recent pattern mouse position
|
||||
private boolean generic; // Use the generic palette
|
||||
private boolean grid; // Draw a grid around characters
|
||||
private boolean hFlip; // Cell is flipped horizontally
|
||||
private int mapIndex; // Current BG map index
|
||||
private int palette; // Palette index
|
||||
private int[] pix; // Image buffer
|
||||
private int scale; // Display scale
|
||||
private boolean vFlip; // Cell is flipped vertically
|
||||
private BufferedImage image; // BG map graphic
|
||||
|
||||
// UI components
|
||||
private JCheckBox chkGeneric; // Generic colors check box
|
||||
private JCheckBox chkGrid; // Grid check box
|
||||
private JCheckBox chkHFlip; // H-flip check box
|
||||
private JCheckBox chkVFlip; // V-flip check box
|
||||
private UComboBox cmbPalette; // Palette drop-down
|
||||
private UPanel client; // Client area
|
||||
private UPanel panCell; // Cell panel
|
||||
private UPanel panMap; // BG map panel
|
||||
private JScrollPane scrControls; // Controls panel
|
||||
private JScrollPane scrMap; // BG map container
|
||||
private JSlider sldScale; // Scale slider
|
||||
private JSpinner spnCellIndex; // Cell index spinner
|
||||
private JSpinner spnCharacter; // Character spinner
|
||||
private JSpinner spnMapIndex; // BG map index spinner
|
||||
private JTextField txtCellAddress; // Cell address text box
|
||||
private JTextField txtMapAddress; // BG map address text box
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
BGMapsWindow(MainWindow parent) {
|
||||
super(parent, "bg_maps.title");
|
||||
|
||||
// Configure instance fields
|
||||
generic = false;
|
||||
image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
|
||||
pix = new int[512 * 512];
|
||||
scale = 2;
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel(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 UPanel(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);
|
||||
|
||||
// BG Map controls
|
||||
label(ctrls, "bg_maps.map", true);
|
||||
spnMapIndex = spinner(ctrls, 0, 15, 0, true);
|
||||
spnMapIndex.addChangeListener(e->
|
||||
setMap((Integer) spnMapIndex.getValue()));
|
||||
label(ctrls, "bg_maps.address", false);
|
||||
txtMapAddress = textBox(ctrls);
|
||||
txtMapAddress.addFocusListener(Util.onFocus(null,
|
||||
e->onAddress(txtMapAddress)));
|
||||
|
||||
// Cell controls container
|
||||
var cell = new UPanel(new GridBagLayout());
|
||||
cell.setBorder(new TitledBorder(""));
|
||||
parent.app.localizer.add(cell, "bg_maps.cell");
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 2, 2, 2);
|
||||
ctrls.add(cell, gbc);
|
||||
|
||||
// Cell controls
|
||||
label(cell, "bg_maps.index", true);
|
||||
spnCellIndex = spinner(cell, 0, 4095, 0, true);
|
||||
spnCellIndex.addChangeListener(e->
|
||||
setCell((Integer) spnCellIndex.getValue()));
|
||||
label(cell, "bg_maps.address", false);
|
||||
txtCellAddress = textBox(cell);
|
||||
txtCellAddress.addFocusListener(Util.onFocus(null,
|
||||
e->onAddress(txtCellAddress)));
|
||||
label(cell, "bg_maps.character", false);
|
||||
spnCharacter = spinner(cell, 0, 2047, 0, false);
|
||||
spnCharacter.addChangeListener(e->onEdit());
|
||||
label(cell, "bg_maps.palette", false);
|
||||
cmbPalette = select(cell, new String[] {
|
||||
"palette.gplt0", "palette.gplt1", "palette.gplt2", "palette.gplt3"
|
||||
});
|
||||
cmbPalette.addActionListener(e->onEdit());
|
||||
label(cell, "bg_maps.hflip", false);
|
||||
chkHFlip = checkBox(cell);
|
||||
chkHFlip.addActionListener(e->onEdit());
|
||||
label(cell, "bg_maps.vflip", false);
|
||||
chkVFlip = checkBox(cell);
|
||||
chkVFlip.addActionListener(e->onEdit());
|
||||
|
||||
// Cell panel
|
||||
panCell = new UPanel();
|
||||
panCell.setOpaque(false);
|
||||
panCell.setPreferredSize(new Dimension(96, 96));
|
||||
panCell.addPaintListener((g,w,h)->onPaintCell(g, w, h));
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(2, 2, 2, 2);
|
||||
cell.add(panCell, gbc);
|
||||
terminator(cell);
|
||||
|
||||
// Fill the extra space above the view controls
|
||||
var spacer = new UPanel();
|
||||
spacer.setOpaque(false);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.weighty = 1;
|
||||
ctrls.add(spacer, gbc);
|
||||
|
||||
// View controls
|
||||
label(ctrls, "bg_maps.grid", false);
|
||||
chkGrid = checkBox(ctrls);
|
||||
chkGrid.addActionListener(e->{
|
||||
grid = chkGrid.isSelected(); onView(); });
|
||||
label(ctrls, "bg_maps.generic", false);
|
||||
chkGeneric = checkBox(ctrls);
|
||||
chkGeneric.setSelected(generic);
|
||||
chkGeneric.addActionListener(e->{
|
||||
generic = chkGeneric.isSelected(); refresh(); });
|
||||
label(ctrls, "bg_maps.scale", false);
|
||||
sldScale = slider(ctrls, 1, 10, scale);
|
||||
sldScale.addChangeListener(e->{
|
||||
scale = sldScale.getValue(); onView(); });
|
||||
terminator(ctrls);
|
||||
|
||||
// Configure BG map panel
|
||||
panMap = new UPanel();
|
||||
panMap.setBackground(SystemColor.control);
|
||||
panMap.addMouseListener(
|
||||
Util.onMouse(e->onMouse(e), e->onMouse(e)));
|
||||
panMap.addMouseMotionListener(
|
||||
Util.onMouseMove(null, e->onMouse(e)));
|
||||
panMap.addPaintListener((g,w,h)->onPaintMap(g, w, h));
|
||||
scrMap = new JScrollPane(panMap);
|
||||
client.add(scrMap, BorderLayout.CENTER);
|
||||
|
||||
// Configure component
|
||||
setContentPane(client);
|
||||
setCell(0);
|
||||
onView();
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update the display
|
||||
void refresh() {
|
||||
|
||||
// Update BG map graphic
|
||||
int src = 0x00020000 | mapIndex << 13;
|
||||
for (int index = 0; index < 4096; index++, src += 2) {
|
||||
int bits = parent.vram[src] & 0xFF | parent.vram[src + 1] << 8;
|
||||
var pal = parent.palettes[generic ? MainWindow.GENERIC :
|
||||
MainWindow.GPLT0 + (bits >> 14 & 3)][MainWindow.RED];
|
||||
parent.drawCharacter(
|
||||
bits & 0x07FF, (bits & 0x2000) != 0, (bits & 0x1000) != 0, pal,
|
||||
pix, index >> 6 << 12 | (index & 63) << 3, 512
|
||||
);
|
||||
}
|
||||
image.setRGB(0, 0, 512, 512, pix, 0, 512);
|
||||
|
||||
// Update display;
|
||||
panCell.repaint();
|
||||
panMap .repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Address text boxes commit
|
||||
private void onAddress(JTextField src) {
|
||||
int address;
|
||||
|
||||
// Parse the given address
|
||||
try { address = (int) Long.parseLong(src.getText(), 16); }
|
||||
catch (Exception e) {
|
||||
setMap(mapIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Restrict address range
|
||||
if ((address >> 24 & 7) != 0) {
|
||||
setMap(mapIndex);
|
||||
return;
|
||||
}
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
// Check if the address is in BG map memory
|
||||
if (address < 0x00020000 || address >= 0x00040000) {
|
||||
setMap(mapIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the map and character indexes
|
||||
address -= 0x00020000;
|
||||
mapIndex = address >> 13;
|
||||
setCell((address & 0x00001FFE) >> 1);
|
||||
}
|
||||
|
||||
// A cell value has changed
|
||||
private void onEdit() {
|
||||
|
||||
// Process cell properties
|
||||
character = (Integer) spnCharacter.getValue();
|
||||
hFlip = chkHFlip.isSelected();
|
||||
palette = cmbPalette.getSelectedIndex();
|
||||
vFlip = chkVFlip.isSelected();
|
||||
|
||||
// Update VRAM state
|
||||
parent.vue.write(
|
||||
0x00020000 | mapIndex << 13 | cellIndex << 1, Vue.U16,
|
||||
palette << 14 | (hFlip ? 0x2000 : 0) |
|
||||
(vFlip ? 0x1000 : 0) | character
|
||||
);
|
||||
parent.refreshDebug(false);
|
||||
}
|
||||
|
||||
// BG map panel mouse
|
||||
private void onMouse(MouseEvent e) {
|
||||
int id = e.getID();
|
||||
|
||||
// Mouse release
|
||||
if (id == MouseEvent.MOUSE_RELEASED) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1)
|
||||
dragging = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Mouse press
|
||||
if (id == MouseEvent.MOUSE_PRESSED) {
|
||||
client.requestFocus();
|
||||
if (e.getButton() != MouseEvent.BUTTON1)
|
||||
return;
|
||||
dragging = e.getPoint();
|
||||
}
|
||||
|
||||
// Not dragging
|
||||
if (dragging == null)
|
||||
return;
|
||||
|
||||
// Working variables
|
||||
int grid = this.grid ? 1 : 0;
|
||||
int size = 8 * scale + grid;
|
||||
int col = e.getX() / size;
|
||||
int row = e.getY() / size;
|
||||
|
||||
// The pixel is not within a cell
|
||||
if (col >= 64 || row >= 64)
|
||||
return;
|
||||
|
||||
// Calculate the index of the selected cell
|
||||
setCell(row << 6 | col);
|
||||
}
|
||||
|
||||
// Cell panel paint
|
||||
private void onPaintCell(Graphics2D g, int width, int height) {
|
||||
int scale = Math.max(1, Math.min(width >> 3, height >> 3));
|
||||
int x = (width - (scale << 3) + 1) >> 1;
|
||||
int y = (height - (scale << 3) + 1) >> 1;
|
||||
int ix = (cellIndex & 63) << 3;
|
||||
int iy = cellIndex >> 6 << 3;
|
||||
g.drawImage(image,
|
||||
x, y, x + scale * 8, y + scale * 8,
|
||||
ix, iy, ix + 8 , iy + 8 ,
|
||||
null);
|
||||
}
|
||||
|
||||
// BG map panel paint
|
||||
private void onPaintMap(Graphics2D g, int width, int height) {
|
||||
|
||||
// Drawing with a grid
|
||||
if (grid) {
|
||||
|
||||
int scale = 8 * this.scale;
|
||||
for (int y = 0, z = 0; y < 64; y++)
|
||||
for (int x = 0; x < 64; x++, z++) {
|
||||
int ix = (z & 63) << 3;
|
||||
int iy = z >> 6 << 3;
|
||||
int ox = x * (scale + 1);
|
||||
int oy = y * (scale + 1);
|
||||
g.drawImage(image,
|
||||
ox, oy, ox + scale, oy + scale,
|
||||
ix, iy, ix + 8 , iy + 8 ,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
// Not drawing with a grid
|
||||
else g.drawImage(image, 0, 0, 512 * scale, 512 * scale, null);
|
||||
|
||||
// Highlight the selected cell
|
||||
int size = 8 * scale + (grid ? 1 : 0);
|
||||
int X = (cellIndex & 63) * size;
|
||||
int Y = (cellIndex >> 6) * size;
|
||||
int light = SystemColor.textHighlight.getRGB() & 0x00FFFFFF;
|
||||
g.setColor(new Color(0xD0000000 | light, true));
|
||||
g.drawRect(X , Y , 8 * scale - 1, 8 * scale - 1);
|
||||
g.setColor(new Color(0x90000000 | light, true));
|
||||
g.drawRect(X + 1, Y + 1, 8 * scale - 3, 8 * scale - 3);
|
||||
g.setColor(new Color(0x50000000 | light, true));
|
||||
g.fillRect(X + 2, Y + 2, 8 * scale - 4, 8 * scale - 4);
|
||||
|
||||
}
|
||||
|
||||
// Window resize
|
||||
private void onResize() {
|
||||
var viewport = scrControls.getViewport();
|
||||
int inner = viewport.getView().getPreferredSize().width;
|
||||
int outer = viewport.getExtentSize().width;
|
||||
|
||||
// Size the controls container to match the inner component
|
||||
if (inner != outer) {
|
||||
scrControls.setPreferredSize(new Dimension(
|
||||
scrControls.getPreferredSize().width + inner - outer, 0));
|
||||
scrControls.revalidate();
|
||||
scrControls.repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// View control changed
|
||||
private void onView() {
|
||||
int grid = this.grid ? 1 : 0;
|
||||
int size = 8 * scale + grid;
|
||||
int pref = size * 64 - grid;
|
||||
scrMap.getVerticalScrollBar().setUnitIncrement(8 * scale + grid);
|
||||
panMap.setPreferredSize(new Dimension(pref, pref));
|
||||
panMap.revalidate();
|
||||
panMap.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a check box to the controls panel
|
||||
private JCheckBox checkBox(UPanel 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(UPanel 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);
|
||||
}
|
||||
|
||||
// Update controls
|
||||
private void refreshLite() {
|
||||
int mapAddr = mapIndex << 13 | 0x00020000;
|
||||
int cellAddr = mapAddr | cellIndex << 1;
|
||||
|
||||
int bits = parent.vram[cellAddr] & 0xFF | parent.vram[cellAddr+1] << 8;
|
||||
character = bits & 0x07FF;
|
||||
hFlip = (bits & 0x2000) != 0;
|
||||
palette = bits >> 14 & 3;
|
||||
vFlip = (bits & 0x1000) != 0;
|
||||
|
||||
chkHFlip .setSelected(hFlip);
|
||||
chkVFlip .setSelected(vFlip);
|
||||
cmbPalette .setSelectedIndex(palette);
|
||||
spnCellIndex .setValue(cellIndex);
|
||||
spnCharacter .setValue(character);
|
||||
spnMapIndex .setValue(mapIndex);
|
||||
txtMapAddress .setText(String.format("%08X", mapAddr ));
|
||||
txtCellAddress.setText(String.format("%08X", cellAddr));
|
||||
|
||||
panCell.repaint();
|
||||
panMap .repaint();
|
||||
}
|
||||
|
||||
// Add a combo box to the controls panel
|
||||
private UComboBox select(UPanel panel, String[] options) {
|
||||
var cmb = new UComboBox();
|
||||
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, 2, 2);
|
||||
panel.add(cmb, gbc);
|
||||
return cmb;
|
||||
}
|
||||
|
||||
// Specify the current cell index
|
||||
private void setCell(int index) {
|
||||
cellIndex = index;
|
||||
refreshLite();
|
||||
}
|
||||
|
||||
// Specify the current BG map index
|
||||
private void setMap(int index) {
|
||||
mapIndex = index;
|
||||
refreshLite();
|
||||
}
|
||||
|
||||
// Add a slider to the controls panel
|
||||
private JSlider slider(UPanel 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(UPanel panel, int min, int max, int value,
|
||||
boolean top) {
|
||||
var spn = new JSpinner(new SpinnerNumberModel(value, min, max, 1));
|
||||
var txt = new JSpinner.NumberEditor(spn, "#");
|
||||
txt.getTextField().addActionListener(e->client.requestFocus());
|
||||
spn.setEditor(txt);
|
||||
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;
|
||||
}
|
||||
|
||||
// Terminate a controls container
|
||||
private void terminator(UPanel panel) {
|
||||
var spacer = new UPanel();
|
||||
spacer.setOpaque(false);
|
||||
spacer.setPreferredSize(new Dimension(0, 0));
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.gridheight = GridBagConstraints.REMAINDER;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
panel.add(spacer, gbc);
|
||||
}
|
||||
|
||||
// Add a text box to the controls panel
|
||||
private JTextField textBox(UPanel panel) {
|
||||
var txt = new JTextField();
|
||||
txt.setFont(parent.app.fntMono);
|
||||
txt.addActionListener(e->client.requestFocus());
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,613 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Memory viewer and hex editor window
|
||||
class BreakpointsWindow extends ChildWindow {
|
||||
|
||||
// Private fields
|
||||
private ArrayList<Item> items; // List items
|
||||
private int selectedIndex; // Selected list item
|
||||
|
||||
// UI components
|
||||
private JButton btnDelete; // Delete button
|
||||
private JButton btnNew; // New button
|
||||
private JCheckBox chkEnabled; // Enabled check box
|
||||
private JCheckBox chkException; // Exception check box
|
||||
private JCheckBox chkExecute; // Execute check box
|
||||
private JCheckBox chkRead; // Read check box
|
||||
private JCheckBox chkWrite; // Write check box
|
||||
private UPanel client; // Client area
|
||||
private JLabel errAddress; // Address error text
|
||||
private JLabel errCondition; // Condition error text
|
||||
private UPanel lstBreakpoints; // Breakpoint list
|
||||
private UPanel spacer; // List termination spacer
|
||||
private JTextField txtAddress; // Address text box
|
||||
private JTextField txtCondition; // Condition text box
|
||||
private JTextField txtName; // Name text box
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Address error keys
|
||||
private static final String[] KEYS_ADDRESS = {
|
||||
null , // NONE
|
||||
"breakpoints.error.badliteral_a", // BADLITERAL
|
||||
null , // BADOPERAND
|
||||
null , // BADTOKEN
|
||||
"breakpoints.error.earlyeof" , // EARLYEOF
|
||||
null , // INVALID
|
||||
null , // NESTING
|
||||
"breakpoints.error.unexpected" // UNEXPECTED
|
||||
};
|
||||
|
||||
// Condition error keys
|
||||
private static final String[] KEYS_CONDITION = {
|
||||
null , // NONE
|
||||
"breakpoints.error.badliteral_c", // BADLITERAL
|
||||
"breakpoints.error.badoperand" , // BADOPERAND
|
||||
"breakpoints.error.badtoken" , // BADTOKEN
|
||||
"breakpoints.error.earlyeof" , // EARLYEOF
|
||||
"breakpoints.error.invalid" , // INVALID
|
||||
"breakpoints.error.nesting" , // NESTING
|
||||
"breakpoints.error.unexpected" // UNEXPECTED
|
||||
};
|
||||
|
||||
// List constraints
|
||||
private static final GridBagConstraints ENABLED;
|
||||
private static final GridBagConstraints NAME;
|
||||
private static final GridBagConstraints SPACER;
|
||||
private static final GridBagConstraints STATUS;
|
||||
|
||||
// Static initializer
|
||||
static {
|
||||
ENABLED = new GridBagConstraints();
|
||||
ENABLED.anchor = GridBagConstraints.CENTER;
|
||||
ENABLED.insets = new Insets(1, 2, 1, 2);
|
||||
NAME = new GridBagConstraints();
|
||||
NAME.fill = GridBagConstraints.HORIZONTAL;
|
||||
NAME.gridwidth = GridBagConstraints.REMAINDER;
|
||||
NAME.insets = new Insets(1, 0, 1, 2);
|
||||
NAME.weightx = 1;
|
||||
SPACER = new GridBagConstraints();
|
||||
SPACER.fill = GridBagConstraints.BOTH;
|
||||
SPACER.gridheight = GridBagConstraints.REMAINDER;
|
||||
SPACER.gridwidth = GridBagConstraints.REMAINDER;
|
||||
SPACER.weighty = 1;
|
||||
STATUS = new GridBagConstraints();
|
||||
STATUS.fill = GridBagConstraints.BOTH;
|
||||
STATUS.insets = new Insets(0, 0, 0, 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Text box commit
|
||||
private interface Commit {
|
||||
void run();
|
||||
}
|
||||
|
||||
// List item
|
||||
private class Item {
|
||||
Breakpoint breakpoint;
|
||||
JCheckBox chkEnabled;
|
||||
JLabel lblName;
|
||||
JLabel lblStatus;
|
||||
|
||||
// Constructor
|
||||
Item(Breakpoint brk) {
|
||||
var that = this;
|
||||
breakpoint = brk;
|
||||
brk.setFetch(false);
|
||||
chkEnabled = new JCheckBox();
|
||||
chkEnabled.setBorder(null);
|
||||
chkEnabled.setFocusable(false);
|
||||
chkEnabled.addActionListener(e->onEnabled(that));
|
||||
parent.app.localizer.add(chkEnabled,"null","breakpoints.enabled");
|
||||
lblName = new JLabel(brk.getName());
|
||||
lblStatus = new JLabel("", SwingConstants.CENTER);
|
||||
refresh(null, null);
|
||||
}
|
||||
|
||||
// Update the display
|
||||
void refresh(Instruction inst, Access acc) {
|
||||
if (
|
||||
breakpoint.getAddressError ().code != Breakpoint.NONE ||
|
||||
breakpoint.getConditionError().code != Breakpoint.NONE
|
||||
) lblStatus.setText("\u00d7"); // Times symbol
|
||||
else if (breakpoint.any() && breakpoint.evaluate(inst, acc))
|
||||
lblStatus.setText("\u2605"); // Star
|
||||
else lblStatus.setText(" ");
|
||||
int lineHeight = parent.app.fntDialog.metrics.getHeight();
|
||||
lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight));
|
||||
}
|
||||
|
||||
// Specify whether or not the item has focus
|
||||
void setFocused(boolean focused) {
|
||||
var color = focused ? SystemColor.textHighlightText :
|
||||
SystemColor.windowText;
|
||||
lblStatus.setForeground(color);
|
||||
lblName .setForeground(color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
BreakpointsWindow(MainWindow parent) {
|
||||
super(parent, "breakpoints.title");
|
||||
|
||||
// Configure instance fields
|
||||
items = new ArrayList<Item>();
|
||||
selectedIndex = -1;
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel(new BorderLayout());
|
||||
client.setBackground(SystemColor.control);
|
||||
client.setFocusable(true);
|
||||
client.setPreferredSize(new Dimension(320, 240));
|
||||
|
||||
// Configure breakpoint list
|
||||
lstBreakpoints = new UPanel(new GridBagLayout());
|
||||
lstBreakpoints.setFocusable(true);
|
||||
lstBreakpoints.setBackground(SystemColor.window);
|
||||
lstBreakpoints.addFocusListener(Util.onFocus(
|
||||
e->lstBreakpoints.repaint(), e->lstBreakpoints.repaint()));
|
||||
lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null));
|
||||
lstBreakpoints.addPaintListener((g,w,h)->onPaint(g, w, h));
|
||||
var scr = new JScrollPane(lstBreakpoints);
|
||||
scr.getVerticalScrollBar().setUnitIncrement(
|
||||
parent.app.fntDialog.metrics.getHeight());
|
||||
client.add(scr, BorderLayout.CENTER);
|
||||
spacer = new UPanel();
|
||||
spacer.setOpaque(false);
|
||||
lstBreakpoints.add(spacer, SPACER);
|
||||
|
||||
// Configure properties pane
|
||||
var props = new UPanel(new GridBagLayout());
|
||||
props.setBackground(SystemColor.control);
|
||||
props.addMouseListener(
|
||||
Util.onMouse(e->client.requestFocus(), null));
|
||||
client.add(props, BorderLayout.SOUTH);
|
||||
|
||||
// Configure properties
|
||||
label(props, "breakpoints.name", 2);
|
||||
txtName = textBox(props, false, 2, ()->onName());
|
||||
//label(props, "breakpoints.enabled", 0);
|
||||
//chkEnabled = checkBox(props, null, true);
|
||||
chkEnabled = new JCheckBox();
|
||||
chkEnabled.addActionListener(e->onEnabled(null));
|
||||
label(props, "breakpoints.type", 0);
|
||||
chkExecute = checkBox(props, "breakpoints.execute" , false);
|
||||
chkExecute .addActionListener(e->onType(chkExecute ));
|
||||
chkRead = checkBox(props, "breakpoints.read" , false);
|
||||
chkRead .addActionListener(e->onType(chkRead ));
|
||||
chkWrite = checkBox(props, "breakpoints.write" , false);
|
||||
chkWrite .addActionListener(e->onType(chkWrite ));
|
||||
chkException = checkBox(props, "breakpoints.exception", true );
|
||||
chkException.addActionListener(e->onType(chkException));
|
||||
label(props, "breakpoints.address", 0);
|
||||
txtAddress = textBox(props, true, 0, ()->onAddress());
|
||||
errAddress = error(props);
|
||||
label(props, "breakpoints.condition", 0);
|
||||
txtCondition = textBox(props, true, 0, ()->onCondition());
|
||||
errCondition = error(props);
|
||||
|
||||
// Configure buttons
|
||||
var buttons = new UPanel(new GridBagLayout());
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
props.add(buttons, gbc);
|
||||
var fill = new UPanel();
|
||||
fill.setOpaque(false);
|
||||
fill.addMouseListener(Util.onMouse(e->onDebug(e), null));
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
gbc.weightx = 1;
|
||||
buttons.add(fill, gbc);
|
||||
btnDelete = button(buttons, "breakpoints.delete", false);
|
||||
btnDelete.setEnabled(false);
|
||||
btnDelete.addActionListener(e->onDelete());
|
||||
btnNew = button(buttons, "breakpoints.new", true);
|
||||
btnNew.addActionListener(e->onNew());
|
||||
|
||||
// Configure component
|
||||
setContentPane(client);
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update the display
|
||||
void refresh() {
|
||||
var inst = new Instruction(parent.vue.read(
|
||||
parent.vue.getRegister(Vue.PC, true), Vue.S32));
|
||||
var acc = inst.access(parent.vue);
|
||||
for (var item : items)
|
||||
item.refresh(inst, acc);
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Address text box commit
|
||||
private void onAddress() {
|
||||
if (selectedIndex == -1)
|
||||
return;
|
||||
var item = items.get(selectedIndex);
|
||||
item.breakpoint.setAddresses(txtAddress.getText());
|
||||
checkErrors(item);
|
||||
item.refresh(null, null);
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// Condition text box commit
|
||||
private void onCondition() {
|
||||
if (selectedIndex == -1)
|
||||
return;
|
||||
var item = items.get(selectedIndex);
|
||||
item.breakpoint.setCondition(txtCondition.getText());
|
||||
checkErrors(item);
|
||||
item.refresh(null, null);
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// Debug interaction
|
||||
private void onDebug(MouseEvent e) {
|
||||
client.requestFocus();
|
||||
if (
|
||||
selectedIndex == -1 ||
|
||||
e.getButton() != MouseEvent.BUTTON1 ||
|
||||
(e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == 0
|
||||
) return;
|
||||
var brk = items.get(selectedIndex).breakpoint;
|
||||
System.out.println("Address");
|
||||
System.out.println(brk.getAddresses());
|
||||
System.out.println(brk.debugRanges());
|
||||
System.out.println("Condition");
|
||||
System.out.println(brk.getCondition());
|
||||
System.out.println(brk.debugTokens());
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// Delete button click
|
||||
private void onDelete() {
|
||||
var item = items.remove(selectedIndex);
|
||||
parent.vue.remove(item.breakpoint);
|
||||
lstBreakpoints.remove(item.chkEnabled);
|
||||
lstBreakpoints.remove(item.lblName);
|
||||
lstBreakpoints.remove(item.lblStatus);
|
||||
lstBreakpoints.revalidate();
|
||||
selectedIndex = -1;
|
||||
select(-1);
|
||||
}
|
||||
|
||||
// Enabled check box toggle
|
||||
private void onEnabled(Item item) {
|
||||
boolean enabled;
|
||||
|
||||
// Clicked from the properties pane
|
||||
if (item == null) {
|
||||
enabled = chkEnabled.isSelected();
|
||||
item = items.get(selectedIndex);
|
||||
item.chkEnabled.setSelected(enabled);
|
||||
client.requestFocus();
|
||||
}
|
||||
|
||||
// Clicked from the breakpoints list
|
||||
else {
|
||||
enabled = item.chkEnabled.isSelected();
|
||||
if (items.indexOf(item) == selectedIndex)
|
||||
chkEnabled.setSelected(enabled);
|
||||
lstBreakpoints.requestFocus();
|
||||
}
|
||||
|
||||
// Common processing
|
||||
item.breakpoint.setEnabled(enabled);
|
||||
item.refresh(null, null);
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// List mouse press
|
||||
private void onMouseDown(MouseEvent e) {
|
||||
int count = items.size();
|
||||
|
||||
// Left clicking
|
||||
if (count > 0 && e.getButton() == MouseEvent.BUTTON1) {
|
||||
int newIndex = e.getY() / items.get(0).lblStatus.getHeight();
|
||||
if (newIndex < count)
|
||||
select(newIndex);
|
||||
}
|
||||
|
||||
// Update layout
|
||||
lstBreakpoints.requestFocus();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// Name text box commit
|
||||
private void onName() {
|
||||
if (selectedIndex == -1)
|
||||
return;
|
||||
var item = items.get(selectedIndex);
|
||||
item.lblName.setText(item.breakpoint.setName(txtName.getText()));
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
item.refresh(null, null);
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// New button click
|
||||
private void onNew() {
|
||||
|
||||
var brk = parent.vue.breakpoint();
|
||||
brk.setEnabled(true);
|
||||
brk.setName (parent.app.localizer.get("breakpoints.default_name"));
|
||||
|
||||
var item = new Item(brk);
|
||||
item.chkEnabled.setSelected(true);
|
||||
item.lblName .setText(brk.getName());
|
||||
items.add(item);
|
||||
|
||||
int count = items.size();
|
||||
boolean first = count == 1;
|
||||
lstBreakpoints.remove(spacer);
|
||||
lstBreakpoints.add(item.chkEnabled, ENABLED);
|
||||
lstBreakpoints.add(item.lblStatus , STATUS );
|
||||
lstBreakpoints.add(item.lblName , NAME );
|
||||
lstBreakpoints.add(spacer , SPACER );
|
||||
|
||||
select(count - 1);
|
||||
lstBreakpoints.requestFocus();
|
||||
lstBreakpoints.revalidate();
|
||||
}
|
||||
|
||||
// List paint
|
||||
private void onPaint(Graphics2D g, int width, int height) {
|
||||
|
||||
// There is no selected item
|
||||
if (selectedIndex == -1)
|
||||
return;
|
||||
|
||||
// Select the display colors
|
||||
var item = items.get(selectedIndex);
|
||||
if (lstBreakpoints.isFocusOwner()) {
|
||||
g.setColor(SystemColor.textHighlight);
|
||||
item.setFocused(true);
|
||||
} else {
|
||||
g.setColor(SystemColor.control);
|
||||
item.setFocused(false);
|
||||
}
|
||||
|
||||
// Draw the selection background
|
||||
height = item.lblStatus.getHeight();
|
||||
g.fillRect(0, selectedIndex * height, width, height);
|
||||
}
|
||||
|
||||
// Type check box toggle
|
||||
private void onType(JCheckBox chk) {
|
||||
var item = items.get(selectedIndex);
|
||||
var brk = item.breakpoint;
|
||||
boolean selected = chk.isSelected();
|
||||
|
||||
// Configure controls
|
||||
if (chk == chkException)
|
||||
brk.setException(selected);
|
||||
else if (chk == chkExecute )
|
||||
brk.setExecute (selected);
|
||||
else if (chk == chkRead )
|
||||
brk.setRead (selected);
|
||||
else if (chk == chkWrite )
|
||||
brk.setWrite (selected);
|
||||
|
||||
// Common processing
|
||||
item.refresh(null, null);
|
||||
client.requestFocus();
|
||||
lstBreakpoints.revalidate();
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a button to the form
|
||||
private JButton button(UPanel panel, String key, boolean last) {
|
||||
var btn = new JButton();
|
||||
if (key != null)
|
||||
parent.app.localizer.add(btn, key);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.insets = new Insets(0, 0, 2, 2);
|
||||
if (last)
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
panel.add(btn, gbc);
|
||||
return btn;
|
||||
}
|
||||
|
||||
// Add a check box to the form
|
||||
private JCheckBox checkBox(UPanel panel, String key, boolean last) {
|
||||
var chk = new JCheckBox();
|
||||
if (key != null)
|
||||
parent.app.localizer.add(chk, key);
|
||||
chk.setBorder(null);
|
||||
chk.setEnabled(false);
|
||||
chk.setFocusable(false);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.insets = new Insets(0, 0, 2, last ? 2 : 6);
|
||||
if (last)
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
panel.add(chk, gbc);
|
||||
return chk;
|
||||
}
|
||||
|
||||
// Check for errors in the selected breakpoint
|
||||
private void checkErrors(Item item) {
|
||||
var brk = item.breakpoint;
|
||||
var loc = parent.app.localizer;
|
||||
|
||||
// Check for address errors
|
||||
var err = brk.getAddressError();
|
||||
if (err.code != Breakpoint.NONE) {
|
||||
loc.add(errAddress, KEYS_ADDRESS[err.code]);
|
||||
loc.put(errAddress, "err.position",
|
||||
Integer.toString(err.position));
|
||||
loc.put(errAddress, "err.text", err.text);
|
||||
errAddress.setVisible(true);
|
||||
} else errAddress.setVisible(false);
|
||||
|
||||
// Check for condition errors
|
||||
err = brk.getConditionError();
|
||||
if (err.code != Breakpoint.NONE) {
|
||||
loc.add(errCondition, KEYS_CONDITION[err.code]);
|
||||
loc.put(errCondition, "err.position",
|
||||
Integer.toString(err.position));
|
||||
loc.put(errCondition, "err.text", err.text);
|
||||
if (err.code == Breakpoint.NESTING)
|
||||
loc.put(errCondition, "err.other",
|
||||
err.text.equals(")") ? "]" : ")");
|
||||
errCondition.setVisible(true);
|
||||
} else errCondition.setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
// Add an error label to the form
|
||||
private JLabel error(UPanel panel) {
|
||||
var lbl = new JLabel();
|
||||
lbl.setForeground(Color.red);
|
||||
lbl.setVisible(false);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
gbc.gridx = 1;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 0, 2, 2);
|
||||
panel.add(lbl, gbc);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
// Add a label to the form
|
||||
private void label(UPanel panel, String key, int top) {
|
||||
var lbl = new JLabel();
|
||||
if (key != null)
|
||||
parent.app.localizer.add(lbl, key);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
gbc.insets = new Insets(top, 2, 2, 4);
|
||||
panel.add(lbl, gbc);
|
||||
}
|
||||
|
||||
// Select a particular list item
|
||||
private void select(int newIndex) {
|
||||
boolean enabled = newIndex != -1;
|
||||
|
||||
// The existing selection was de-selected
|
||||
if (selectedIndex != -1 && newIndex != selectedIndex)
|
||||
items.get(selectedIndex).setFocused(false);
|
||||
|
||||
// Selecting a new list item
|
||||
if (newIndex != -1)
|
||||
items.get(newIndex).setFocused(true);
|
||||
|
||||
// Configure instance fields
|
||||
selectedIndex = newIndex;
|
||||
|
||||
// Update control properties
|
||||
if (enabled) {
|
||||
var brk = items.get(selectedIndex).breakpoint;
|
||||
chkEnabled .setSelected(brk.isEnabled ());
|
||||
chkExecute .setSelected(brk.getExecute ());
|
||||
chkRead .setSelected(brk.getRead ());
|
||||
chkWrite .setSelected(brk.getWrite ());
|
||||
chkException.setSelected(brk.getException());
|
||||
txtAddress .setText (brk.getAddresses());
|
||||
txtCondition.setText (brk.getCondition());
|
||||
txtName .setText (brk.getName ());
|
||||
}
|
||||
|
||||
// Clear control properties
|
||||
else {
|
||||
chkEnabled .setSelected(false);
|
||||
chkExecute .setSelected(false);
|
||||
chkRead .setSelected(false);
|
||||
chkWrite .setSelected(false);
|
||||
chkException.setSelected(false);
|
||||
errAddress .setText(""); errAddress .setVisible(false);
|
||||
errCondition.setText(""); errCondition.setVisible(false);
|
||||
txtAddress .setText("");
|
||||
txtCondition.setText("");
|
||||
txtName .setText("");
|
||||
}
|
||||
|
||||
// Enable or disable controls
|
||||
btnDelete .setEnabled(enabled);
|
||||
chkEnabled .setEnabled(enabled);
|
||||
chkExecute .setEnabled(enabled);
|
||||
chkException.setEnabled(enabled);
|
||||
chkRead .setEnabled(enabled);
|
||||
chkWrite .setEnabled(enabled);
|
||||
txtAddress .setEnabled(enabled);
|
||||
txtCondition.setEnabled(enabled);
|
||||
txtName .setEnabled(enabled);
|
||||
|
||||
// Common processing
|
||||
if (selectedIndex != -1)
|
||||
checkErrors(items.get(selectedIndex));
|
||||
lstBreakpoints.repaint();
|
||||
}
|
||||
|
||||
// Add a text box to the form
|
||||
private JTextField textBox(UPanel panel, boolean mono, int top,
|
||||
Commit handler) {
|
||||
var txt = new JTextField();
|
||||
txt.setEnabled(false);
|
||||
if (mono)
|
||||
txt.setFont(parent.app.fntMono);
|
||||
txt.addActionListener(e->client.requestFocus());
|
||||
txt.addFocusListener (Util.onFocus(null, e->handler.run()));
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.CENTER;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(top, 0, 2, 2);
|
||||
gbc.weightx = 1;
|
||||
panel.add(txt, gbc);
|
||||
return txt;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// CPU window
|
||||
class CPUWindow extends ChildWindow {
|
||||
|
||||
// Instance fields
|
||||
MainWindow parent; // Containing window
|
||||
|
||||
// UI components
|
||||
private DisassemblerPane panDasm; // Disassembler client
|
||||
private RegisterList lstProgram; // Program register list
|
||||
private RegisterList lstSystem; // System register list
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
CPUWindow(MainWindow parent) {
|
||||
super(parent, "cpu.title");
|
||||
|
||||
// Configure instance fields
|
||||
this.parent = parent;
|
||||
|
||||
// Configure child panes
|
||||
panDasm = new DisassemblerPane(this);
|
||||
lstSystem = new RegisterList(this, true);
|
||||
lstProgram = new RegisterList(this, false);
|
||||
|
||||
// Configure layout
|
||||
var inner = Util.splitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
inner.setTopComponent(lstSystem);
|
||||
inner.setBottomComponent(lstProgram);
|
||||
var outer = Util.splitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
outer.setLeftComponent(panDasm);
|
||||
outer.setRightComponent(inner);
|
||||
outer.setResizeWeight(1);
|
||||
|
||||
// Configure component
|
||||
var client = getContentPane();
|
||||
client.add(outer);
|
||||
client.setPreferredSize(new Dimension(480, 300));
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update the display
|
||||
void refresh(boolean seekToPC) {
|
||||
panDasm .refresh(seekToPC);
|
||||
lstSystem .refresh();
|
||||
lstProgram.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,612 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.*;
|
||||
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
|
||||
private Point dragging; // Most recent pattern mouse position
|
||||
private int index; // Current character index
|
||||
private boolean grid; // Draw a grid around characters
|
||||
private int palette; // Palette index
|
||||
private int[] pix; // Image buffer
|
||||
private int scale; // Display scale
|
||||
private int wide; // Number of characters per row
|
||||
private BufferedImage image; // Character graphics
|
||||
|
||||
// UI components
|
||||
private JCheckBox chkGrid; // Grid check box
|
||||
private UPanel client; // Client area
|
||||
private UComboBox cmbPalette; // Palette drop-down
|
||||
private UPanel panCharacters; // Characters panel
|
||||
private UPanel panPalette; // Palette panel
|
||||
private UPanel panPattern; // Pattern panel
|
||||
private JScrollPane scrCharacters; // Characters container
|
||||
private JScrollPane scrControls; // Controls panel
|
||||
private JScrollBar scrWidth; // Width template scroll bar
|
||||
private JSlider sldScale; // Scale slider
|
||||
private JSpinner spnIndex; // Index spinner
|
||||
private JSpinner spnWide; // Wide spinner
|
||||
private JTextField txtAddress; // Address text box
|
||||
private JTextField txtMirror; // Mirror text box
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
CharactersWindow(MainWindow parent) {
|
||||
super(parent, "characters.title");
|
||||
|
||||
// Configure instance fields
|
||||
color = 0;
|
||||
grid = true;
|
||||
image = new BufferedImage(256, 512, BufferedImage.TYPE_INT_RGB);
|
||||
pix = new int[256 * 512];
|
||||
scale = 3;
|
||||
|
||||
// Template scroll bar to check control width in the current LAF
|
||||
scrWidth = new JScrollBar(JScrollBar.VERTICAL);
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel(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 UPanel(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);
|
||||
|
||||
// Character controls
|
||||
label(ctrls, "characters.index", true);
|
||||
spnIndex = spinner(ctrls, 0, 2047, 0, true);
|
||||
spnIndex.addChangeListener(e->
|
||||
setIndex((Integer) spnIndex.getValue()));
|
||||
label(ctrls, "characters.address", false);
|
||||
txtAddress = textBox(ctrls);
|
||||
txtAddress.addFocusListener(Util.onFocus(null,
|
||||
e->onAddress(txtAddress)));
|
||||
label(ctrls, "characters.mirror", false);
|
||||
txtMirror = textBox(ctrls);
|
||||
txtMirror.addFocusListener(Util.onFocus(null,
|
||||
e->onAddress(txtMirror)));
|
||||
|
||||
// Pattern panel
|
||||
panPattern = new UPanel();
|
||||
panPattern.setOpaque(false);
|
||||
panPattern.setPreferredSize(new Dimension(96, 96));
|
||||
panPattern.addMouseListener(
|
||||
Util.onMouse(e->onMousePattern(e), e->onMousePattern(e)));
|
||||
panPattern.addMouseMotionListener(
|
||||
Util.onMouseMove(null, e->onMousePattern(e)));
|
||||
panPattern.addPaintListener((g,w,h)->onPaintPattern(g, w, h));
|
||||
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 UPanel();
|
||||
panPalette.setOpaque(false);
|
||||
panPalette.setPreferredSize(new Dimension(0, 20 + 6));
|
||||
panPalette.addMouseListener(
|
||||
Util.onMouse(e->onMouseDownPalette(e), null));
|
||||
panPalette.addPaintListener((g,w,h)->onPaintPalette(g, w, h));
|
||||
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 UPanel();
|
||||
spacer.setOpaque(false);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.weighty = 1;
|
||||
ctrls.add(spacer, gbc);
|
||||
|
||||
// View controls
|
||||
label(ctrls, "characters.grid", false);
|
||||
chkGrid = checkBox(ctrls);
|
||||
chkGrid.setSelected(grid);
|
||||
chkGrid.addActionListener(e->{
|
||||
grid = chkGrid.isSelected(); onView(); });
|
||||
label(ctrls, "characters.wide", false);
|
||||
spnWide = spinner(ctrls, 0, 2048, 0, false);
|
||||
spnWide.addChangeListener(e->{
|
||||
wide = (Integer) spnWide.getValue(); onView(); });
|
||||
label(ctrls, "characters.palette", false);
|
||||
cmbPalette = select(ctrls, new String[] { "palette.generic",
|
||||
"palette.gplt0", "palette.gplt1", "palette.gplt2", "palette.gplt3",
|
||||
"palette.jplt0", "palette.jplt1", "palette.jplt2", "palette.jplt3"
|
||||
});
|
||||
cmbPalette.addActionListener(e->{
|
||||
palette = cmbPalette.getSelectedIndex(); repaint(); });
|
||||
label(ctrls, "characters.scale", false);
|
||||
sldScale = slider(ctrls, 1, 10, scale);
|
||||
sldScale.addChangeListener(e->{
|
||||
scale = sldScale.getValue(); onView(); });
|
||||
|
||||
// Terminate the list of controls
|
||||
spacer = new UPanel();
|
||||
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 UPanel();
|
||||
panCharacters.setBackground(SystemColor.control);
|
||||
panCharacters.addMouseListener(
|
||||
Util.onMouse(e->onMouseCharacters(e), e->onMouseCharacters(e)));
|
||||
panCharacters.addMouseMotionListener(
|
||||
Util.onMouseMove(null, e->onMouseCharacters(e)));
|
||||
panCharacters.addPaintListener((g,w,h)->onPaintCharacters(g, w, h));
|
||||
scrCharacters = new JScrollPane(panCharacters);
|
||||
client.add(scrCharacters, BorderLayout.CENTER);
|
||||
|
||||
// Configure component
|
||||
setContentPane(client);
|
||||
setIndex(0);
|
||||
onView();
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update the display
|
||||
void refresh() {
|
||||
|
||||
// Update characters graphic
|
||||
var pal = parent.palettes[palette][MainWindow.RED];
|
||||
for (int index = 0; index < 2048; index++)
|
||||
parent.drawCharacter(index, false, false, pal, pix,
|
||||
index >> 5 << 11 | (index & 31) << 3, 256);
|
||||
image.setRGB(0, 0, 256, 512, pix, 0, 256);
|
||||
|
||||
// Update display;
|
||||
panCharacters.repaint();
|
||||
panPattern .repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Address and Mirror text box commit
|
||||
private void onAddress(JTextField src) {
|
||||
int address;
|
||||
|
||||
// Parse the given address
|
||||
try { address = (int) Long.parseLong(src.getText(), 16); }
|
||||
catch (Exception e) {
|
||||
setIndex(index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Restrict address range
|
||||
if ((address >> 24 & 7) != 0) {
|
||||
setIndex(index);
|
||||
return;
|
||||
}
|
||||
address &= 0x0007FFFF;
|
||||
|
||||
// Character memory
|
||||
if ((address & 0x00066000) == 0x00006000)
|
||||
index = address >> 15 << 9 | address >> 4 & 511;
|
||||
|
||||
// Mirror of character memory
|
||||
else if (address >= 0x00078000)
|
||||
index = address - 0x00078000 >> 4;
|
||||
|
||||
// Common processing
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
// Characters mouse
|
||||
private void onMouseCharacters(MouseEvent e) {
|
||||
int id = e.getID();
|
||||
|
||||
// Mouse release
|
||||
if (id == MouseEvent.MOUSE_RELEASED) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1)
|
||||
dragging = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Mouse press
|
||||
if (id == MouseEvent.MOUSE_PRESSED) {
|
||||
client.requestFocus();
|
||||
if (e.getButton() != MouseEvent.BUTTON1)
|
||||
return;
|
||||
dragging = e.getPoint();
|
||||
}
|
||||
|
||||
// Not dragging
|
||||
if (dragging == null)
|
||||
return;
|
||||
|
||||
// Working variables
|
||||
int grid = this.grid ? 1 : 0;
|
||||
int size = 8 * scale + grid;
|
||||
int wide = this.wide != 0 ? this.wide :
|
||||
Math.max(1, (panCharacters.getWidth() + grid) / size);
|
||||
int col = e.getX() / size;
|
||||
int row = e.getY() / size;
|
||||
|
||||
// The pixel is not within a character
|
||||
if (col >= wide || row >= (2047 + wide) / wide)
|
||||
return;
|
||||
|
||||
// Calculate the index of the selected character
|
||||
int index = row * wide + col;
|
||||
if (index < 2048)
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Pattern mouse
|
||||
private void onMousePattern(MouseEvent e) {
|
||||
int id = e.getID();
|
||||
int scale = Math.max(1, Math.min(
|
||||
panPattern.getWidth () >> 3,
|
||||
panPattern.getHeight() >> 3
|
||||
));
|
||||
|
||||
// Mouse release
|
||||
if (id == MouseEvent.MOUSE_RELEASED) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1)
|
||||
dragging = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Mouse press
|
||||
if (id == MouseEvent.MOUSE_PRESSED) {
|
||||
client.requestFocus();
|
||||
if (e.getButton() != MouseEvent.BUTTON1)
|
||||
return;
|
||||
dragging = e.getPoint();
|
||||
dragging.x = dragging.x / scale - (dragging.x < 0 ? 1 : 0);
|
||||
dragging.y = dragging.y / scale - (dragging.y < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// Not dragging
|
||||
if (dragging == null)
|
||||
return;
|
||||
|
||||
// Retrieve the current point
|
||||
var pos = e.getPoint();
|
||||
pos.x = pos.x / scale - (pos.x < 0 ? 1 : 0);
|
||||
pos.y = pos.y / scale - (pos.y < 0 ? 1 : 0);
|
||||
|
||||
// Draw a line segment (adapted from Wikipedia)
|
||||
int dx = Math.abs(pos.x - dragging.x);
|
||||
int sx = dragging.x < pos.x ? 1 : -1;
|
||||
int dy = -Math.abs(pos.y - dragging.y);
|
||||
int sy = dragging.y < pos.y ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
int addr = index >> 9 << 15 | 0x00006000 | (index & 511) << 4;
|
||||
for (;;) {
|
||||
|
||||
// Draw the current pixel
|
||||
if ((dragging.x&7) == dragging.x && (dragging.y&7) == dragging.y) {
|
||||
int b = addr | dragging.y << 1 | dragging.x >> 2;
|
||||
int bit = (dragging.x & 3) << 1;
|
||||
parent.vram[b] = (byte)
|
||||
(parent.vram[b] & ~(3 << bit) | color << bit);
|
||||
}
|
||||
|
||||
// The last pixel has been drawn
|
||||
if (dragging.x == pos.x && dragging.y == pos.y)
|
||||
break;
|
||||
|
||||
// Advance to the next pixel
|
||||
int e2 = err << 1;
|
||||
if (e2 >= dy)
|
||||
{ err += dy; dragging.x += sx; }
|
||||
if (e2 <= dx)
|
||||
{ err += dx; dragging.y += sy; }
|
||||
}
|
||||
|
||||
// Update VRAM state
|
||||
parent.vue.writeBytes(addr, parent.vram, addr, 16);
|
||||
parent.refreshDebug(false);
|
||||
}
|
||||
|
||||
// Characters paint
|
||||
private void onPaintCharacters(Graphics2D g, int width, int height) {
|
||||
int grid = this.grid ? 1 : 0;
|
||||
int size = scale * 8 + grid;
|
||||
int wide = this.wide != 0 ? this.wide :
|
||||
Math.max(1, (width + grid) / size);
|
||||
int tall = (2047 + wide) / wide;
|
||||
|
||||
// Draw all characters
|
||||
for (int Y = 0, y = 0, z = 0; y < tall; y++, Y += size)
|
||||
for (int X = 0, x = 0; x < wide; x++, z++, X += size) {
|
||||
if (z == 2048)
|
||||
break;
|
||||
int ix = (z & 31) << 3;
|
||||
int iy = z >> 5 << 3;
|
||||
g.drawImage(image,
|
||||
X, Y, X + size - grid, Y + size - grid,
|
||||
ix, iy, ix + 8 , iy + 8 ,
|
||||
null);
|
||||
}
|
||||
|
||||
// Highlight the selected character
|
||||
int X = index % wide * size;
|
||||
int Y = index / wide * size;
|
||||
int light = SystemColor.textHighlight.getRGB() & 0x00FFFFFF;
|
||||
g.setColor(new Color(0xD0000000 | light, true));
|
||||
g.drawRect(X , Y , 8 * scale - 1, 8 * scale - 1);
|
||||
g.setColor(new Color(0x90000000 | light, true));
|
||||
g.drawRect(X + 1, Y + 1, 8 * scale - 3, 8 * scale - 3);
|
||||
g.setColor(new Color(0x50000000 | light, true));
|
||||
g.fillRect(X + 2, Y + 2, 8 * scale - 4, 8 * scale - 4);
|
||||
}
|
||||
|
||||
// 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;
|
||||
var pal = parent.palettes[palette][MainWindow.RED];
|
||||
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(new Color(pal[x]));
|
||||
g.fillRect(left + 3, top + 3, size , size );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Pattern paint
|
||||
private void onPaintPattern(Graphics2D g, int width, int height) {
|
||||
int scale = Math.max(1, Math.min(width >> 3, height >> 3));
|
||||
int x = (width - (scale << 3) + 1) >> 1;
|
||||
int y = (height - (scale << 3) + 1) >> 1;
|
||||
int ix = (index & 31) << 3;
|
||||
int iy = index >> 5 << 3;
|
||||
g.drawImage(image,
|
||||
x, y, x + scale * 8, y + scale * 8,
|
||||
ix, iy, ix + 8 , iy + 8 ,
|
||||
null);
|
||||
}
|
||||
|
||||
// Window resize
|
||||
private void onResize() {
|
||||
var viewport = scrControls.getViewport();
|
||||
int inner = viewport.getView().getPreferredSize().width;
|
||||
int outer = viewport.getExtentSize().width;
|
||||
|
||||
// Size the controls container to match the inner component
|
||||
if (inner != outer) {
|
||||
scrControls.setPreferredSize(new Dimension(
|
||||
scrControls.getPreferredSize().width + inner - outer, 0));
|
||||
scrControls.revalidate();
|
||||
scrControls.repaint();
|
||||
}
|
||||
|
||||
// The number of characters per row is dynamic
|
||||
if (wide == 0) {
|
||||
var bar = scrWidth.getPreferredSize().width;
|
||||
var border = scrCharacters.getBorder();
|
||||
int grid = this.grid ? 1 : 0;
|
||||
var insets = border == null ? new Insets(0, 0, 0, 0) :
|
||||
border.getBorderInsets(scrCharacters);
|
||||
var size = new Dimension(
|
||||
scrCharacters.getWidth () - insets.left - insets.right,
|
||||
scrCharacters.getHeight() - insets.top - insets.bottom
|
||||
);
|
||||
|
||||
// Calculate the dimensions without the vertical scroll bar
|
||||
int wide = Math.max(1, (size.width + grid) / (8 * scale + grid));
|
||||
int height = (2047 + wide) / wide * (8 * scale + grid) - grid;
|
||||
|
||||
// Calculate the dimensions with the vertical scroll bar
|
||||
if (height > size.height) {
|
||||
wide = Math.max(1, (size.width-bar+grid) / (8 * scale + grid));
|
||||
height = (2047 + wide) / wide * (8 * scale + grid) - grid;
|
||||
}
|
||||
|
||||
// Configure the component
|
||||
panCharacters.setPreferredSize(
|
||||
new Dimension(wide * (8 * scale + grid) - grid, height));
|
||||
panCharacters.revalidate();
|
||||
panCharacters.repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// View control changed
|
||||
private void onView() {
|
||||
|
||||
// Number of characters per row is dynamic: defer to resize processing
|
||||
if (wide == 0) {
|
||||
onResize();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the space occupied by the character images
|
||||
int grid = this.grid ? 1 : 0;
|
||||
panCharacters.setPreferredSize(new Dimension(
|
||||
wide * (scale * 8 + grid) - grid,
|
||||
(2047 + wide) / wide * (scale * 8 + grid) - grid
|
||||
));
|
||||
scrCharacters.getVerticalScrollBar().setUnitIncrement(8*scale+grid);
|
||||
panCharacters.revalidate();
|
||||
panCharacters.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a check box to the controls panel
|
||||
private JCheckBox checkBox(UPanel 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(UPanel 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 UComboBox select(UPanel panel, String[] options) {
|
||||
var cmb = new UComboBox();
|
||||
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, 2, 2);
|
||||
panel.add(cmb, gbc);
|
||||
return cmb;
|
||||
}
|
||||
|
||||
// Specify the current character index
|
||||
private void setIndex(int index) {
|
||||
this.index = index;
|
||||
spnIndex.setValue(index);
|
||||
txtAddress.setText(String.format("%08X",
|
||||
index >> 9 << 15 | 0x00006000 | (index & 511) << 4));
|
||||
txtMirror .setText(String.format("%08X",
|
||||
index << 4 | 0x00078000));
|
||||
repaint();
|
||||
}
|
||||
|
||||
// Add a slider to the controls panel
|
||||
private JSlider slider(UPanel 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(UPanel panel, int min, int max, int value,
|
||||
boolean top) {
|
||||
var spn = new JSpinner(new SpinnerNumberModel(value, min, max, 1));
|
||||
var txt = new JSpinner.NumberEditor(spn, "#");
|
||||
txt.getTextField().addActionListener(e->client.requestFocus());
|
||||
spn.setEditor(txt);
|
||||
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(UPanel panel) {
|
||||
var txt = new JTextField();
|
||||
txt.setFont(parent.app.fntMono);
|
||||
txt.addActionListener(e->client.requestFocus());
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
|
||||
// Child window
|
||||
class ChildWindow extends JInternalFrame {
|
||||
|
||||
// Instance fields
|
||||
MainWindow parent; // Parent application window
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
ChildWindow(MainWindow parent, String key) {
|
||||
super();
|
||||
|
||||
// Configure instanece fields
|
||||
this.parent = parent;
|
||||
|
||||
// Configure component
|
||||
parent.app.localizer.add(this, key);
|
||||
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
|
||||
setClosable(true);
|
||||
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||
setMaximizable(true);
|
||||
setResizable(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Show or hide the component
|
||||
public void setVisible(boolean visible) {
|
||||
|
||||
// Making visible
|
||||
if (visible) {
|
||||
|
||||
// Not currently visible: center in parent
|
||||
if (!isVisible()) {
|
||||
var parent = getParent().getParent();
|
||||
setLocation(
|
||||
Math.max(0, (parent.getWidth () - getWidth ()) / 2),
|
||||
Math.max(0, (parent.getHeight() - getHeight()) / 2)
|
||||
);
|
||||
}
|
||||
|
||||
// Already visible: bring to front
|
||||
else {
|
||||
moveToFront();
|
||||
try { setSelected(true); } catch (Exception e) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Change visibility
|
||||
super.setVisible(visible);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Console window
|
||||
class ConsoleWindow extends ChildWindow {
|
||||
|
||||
// Instance fields
|
||||
private boolean shown; // Component has been visible
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
ConsoleWindow(MainWindow parent) {
|
||||
super(parent, "console.title");
|
||||
getContentPane().setPreferredSize(new Dimension(384, 224));
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Show the component on the first transition to debug mode
|
||||
void firstShow() {
|
||||
if (!shown)
|
||||
setVisible(shown = true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Project imports
|
||||
import vue.*;
|
||||
|
||||
// Instruction disassembler
|
||||
class Disassembler {
|
||||
private Disassembler() { } // Cannot be instantiated
|
||||
static { setDefaults(); } // Initialize to default settings
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Global Settings //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static boolean bcondCombine; // Merge Bcond condition into mnemonic
|
||||
static boolean bcondNames; // Use symbolic condition names for Bcond
|
||||
static boolean condCaps; // Uppercase condition mnemonics
|
||||
static boolean destLast; // Destination register last
|
||||
static boolean dispDest; // Resolve jump destination addresses
|
||||
static boolean dispInside; // Load/store displacement inside brackets
|
||||
static boolean hexCaps; // Upercase hexadecimal
|
||||
static int hexMode; // Hexadecimal decoration
|
||||
static boolean immNumber; // Use number sign with immediate values
|
||||
static boolean jmpBrackets; // Square brackets around JMP
|
||||
static boolean jumpAddress; // Use jump destination addresses
|
||||
static boolean lower; // Use L instead of C in conditions
|
||||
static boolean mnemonicCaps; // Uppercase mnemonics
|
||||
static boolean programCaps; // Uppercase program register names
|
||||
static boolean programNames; // Use symbolic program register names
|
||||
static boolean setfCombine; // Merge SETF condition into mnemonic
|
||||
static boolean setfNames; // Use symbolic condition names for SETF
|
||||
static boolean systemCaps; // Uppercase system register names
|
||||
static boolean systemNames; // Use symbolic system register names
|
||||
static boolean zero; // Use Z instead of E in conditions
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Hexadecimal decorations
|
||||
static final int DOLLAR = 0;
|
||||
static final int H = 1;
|
||||
static final int ZEROX = 2;
|
||||
|
||||
// Condition mneonics
|
||||
static final String[] CONDITIONS = {
|
||||
"V" , "C" , "E" , "NH", "N", "T", "LT", "LE",
|
||||
"NV", "NC", "NE", "H" , "P", "F", "GE", "GT"
|
||||
};
|
||||
|
||||
// Mnemonics
|
||||
static final String[] MNEMONICS = {
|
||||
"---" , "ADD" , "ADD" , "ADDF.S", "ADDI" , "AND" ,
|
||||
"ANDBSU" , "ANDI" , "ANDNBSU", "Bcond" , "CAXI" , "CLI" ,
|
||||
"CMP" , "CMP" , "CMPF.S" , "CVT.SW", "CVT.WS" , "DIV" ,
|
||||
"DIVF.S" , "DIVU" , "HALT" , "IN.B" , "IN.H" , "IN.W" ,
|
||||
"JAL" , "JMP" , "JR" , "LD.B" , "LD.H" , "LD.W" ,
|
||||
"LDSR" , "MOV" , "MOV" , "MOVBSU", "MOVEA" , "MOVHI" ,
|
||||
"MPYHW" , "MUL" , "MULF.S" , "MULU" , "NOT" , "NOTBSU" ,
|
||||
"OR" , "ORBSU" , "ORI" , "ORNBSU", "OUT.B" , "OUT.H" ,
|
||||
"OUT.W" , "RETI" , "REV" , "SAR" , "SAR" , "SCH0BSD",
|
||||
"SCH0BSU", "SCH1BSD", "SCH1BSU", "SEI" , "SETF" , "SHL" ,
|
||||
"SHL" , "SHR" , "SHR" , "ST.B" , "ST.H" , "ST.W" ,
|
||||
"STSR" , "SUB" , "SUBF.S" , "TRAP" , "TRNC.SW", "XB" ,
|
||||
"XH" , "XOR" , "XORBSU" , "XORI" , "XORNBSU"
|
||||
};
|
||||
|
||||
// Program register names
|
||||
private static final String[] PROGRAMS = {
|
||||
null, null, "hp", "sp", "gp", "tp", null, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, "lp"
|
||||
};
|
||||
|
||||
// System register names
|
||||
private static final String[] SYSTEMS = {
|
||||
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
|
||||
null , null , null , null , null , null , null , null ,
|
||||
null , null , null , null , null , null , null , null ,
|
||||
"CHCW", "ADTRE", null , null , null , null , null , null
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One row of output
|
||||
static class Row {
|
||||
String address; // Bus address
|
||||
String bytes; // Encoded bytes in bus order
|
||||
String mnemonic; // Instruction mnemonic
|
||||
String operands; // Instruction operands, if any
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Static Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Reset all settings to their default values
|
||||
static void setDefaults() {
|
||||
bcondCombine = true;
|
||||
bcondNames = true;
|
||||
condCaps = true;
|
||||
destLast = true;
|
||||
dispDest = true;
|
||||
dispInside = false;
|
||||
hexCaps = true;
|
||||
hexMode = ZEROX;
|
||||
immNumber = false;
|
||||
jmpBrackets = true;
|
||||
jumpAddress = true;
|
||||
lower = true;
|
||||
mnemonicCaps = true;
|
||||
programCaps = false;
|
||||
programNames = true;
|
||||
setfCombine = false;
|
||||
setfNames = true;
|
||||
systemCaps = true;
|
||||
systemNames = true;
|
||||
zero = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Represent an instruction as text fields
|
||||
static void disassemble(int address, Instruction inst, Row row) {
|
||||
|
||||
// Address
|
||||
String x = hexCaps ? "X" : "x";
|
||||
row.address = String.format("%08" + x, address);
|
||||
|
||||
// Bytes
|
||||
x = "%02" + x;
|
||||
row.bytes = inst.size == 2 ?
|
||||
String.format(x + " " + x,
|
||||
inst.bits >> 16 & 0xFF,
|
||||
inst.bits >> 24 & 0xFF
|
||||
) : String.format(x + " " + x + " " + x + " " + x,
|
||||
inst.bits >> 16 & 0xFF,
|
||||
inst.bits >> 24 & 0xFF,
|
||||
inst.bits & 0xFF,
|
||||
inst.bits >> 8 & 0xFF
|
||||
)
|
||||
;
|
||||
|
||||
// Bcond mnemonic
|
||||
if (inst.id == Vue.BCOND && bcondCombine) {
|
||||
row.mnemonic = "B" + CONDITIONS[inst.cond];
|
||||
if (lower && (inst.cond & 7) == 1)
|
||||
row.mnemonic = inst.cond == 1 ? "BL" : "BNL";
|
||||
if (zero && (inst.cond & 7) == 2)
|
||||
row.mnemonic = inst.cond == 2 ? "BZ" : "BNZ";
|
||||
if ( (inst.cond & 7) == 5)
|
||||
row.mnemonic = inst.cond == 5 ? "BR" : "NOP";
|
||||
}
|
||||
|
||||
// SETF mnemonic
|
||||
else if (inst.id == Vue.SETF && setfCombine)
|
||||
row.mnemonic = "SETF" + CONDITIONS[inst.imm];
|
||||
|
||||
// All other mnemonics
|
||||
else row.mnemonic = MNEMONICS[inst.id + 1];
|
||||
|
||||
// Adjust mnemonic case
|
||||
if (!mnemonicCaps)
|
||||
row.mnemonic = row.mnemonic.toLowerCase();
|
||||
|
||||
// Operands by format
|
||||
row.operands = null;
|
||||
if (inst.id != Vue.ILLEGAL) switch (inst.format) {
|
||||
case 1: // Fallthrough
|
||||
case 7: row.operands = formatI_VII(inst ); break;
|
||||
case 2: row.operands = formatII (inst ); break;
|
||||
case 3: row.operands = formatIII (inst, address); break;
|
||||
case 4: row.operands = destination(inst, address); break;
|
||||
case 5: row.operands = formatV (inst ); break;
|
||||
case 6: row.operands = formatVI (inst ); break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Format the destination of a branch or jump
|
||||
private static String destination(Instruction inst, int address) {
|
||||
return !dispDest ? toHex(inst.disp, 1, false) :
|
||||
String.format("%08" + (hexCaps ? "X" : "x"), address + inst.disp);
|
||||
}
|
||||
|
||||
// Operands for Format I and Format VII
|
||||
private static String formatI_VII(Instruction inst) {
|
||||
String reg1 = program(inst.reg1);
|
||||
String reg2 = program(inst.reg2);
|
||||
|
||||
// One-operand instructions
|
||||
switch (inst.id) {
|
||||
case Vue.JMP:
|
||||
return String.format(jmpBrackets ? "[%s]" : "%s", reg1);
|
||||
case Vue.XB: // Fallthrough
|
||||
case Vue.XH:
|
||||
return reg2;
|
||||
}
|
||||
|
||||
// Generic
|
||||
return String.format("%s, %s",
|
||||
destLast ? reg1 : reg2,
|
||||
destLast ? reg2 : reg1
|
||||
);
|
||||
}
|
||||
|
||||
// Operands for Format II
|
||||
private static String formatII(Instruction inst) {
|
||||
|
||||
// Bit string
|
||||
if (inst.opcode == 0b011111)
|
||||
return null;
|
||||
|
||||
// Zero- or one-operand instructions
|
||||
switch (inst.id) {
|
||||
|
||||
// Zero-operand
|
||||
case Vue.CLI : // Fallthrough
|
||||
case Vue.HALT: // Fallthrough
|
||||
case Vue.RETI: // Fallthrough
|
||||
case Vue.SEI :
|
||||
return null;
|
||||
|
||||
// One-operand
|
||||
case Vue.TRAP:
|
||||
return Integer.toString(inst.imm);
|
||||
}
|
||||
|
||||
// Combined SETF
|
||||
String reg2 = program(inst.reg2);
|
||||
if (inst.id == Vue.SETF && setfCombine)
|
||||
return reg2;
|
||||
|
||||
// Two-operand instructions
|
||||
String imm = String.format("%s%d", immNumber ? "#" : "", inst.imm);
|
||||
switch (inst.id) {
|
||||
case Vue.LDSR: // Fallthrough
|
||||
case Vue.STSR:
|
||||
if (!systemNames || SYSTEMS[inst.imm] == null)
|
||||
break;
|
||||
imm = SYSTEMS[inst.imm];
|
||||
if (!systemCaps)
|
||||
imm = imm.toLowerCase();
|
||||
break;
|
||||
case Vue.SETF:
|
||||
if (!setfNames)
|
||||
break;
|
||||
imm = CONDITIONS[inst.imm];
|
||||
if (!condCaps)
|
||||
imm = imm.toLowerCase();
|
||||
break;
|
||||
}
|
||||
|
||||
// LDSR
|
||||
if (inst.id == Vue.LDSR) {
|
||||
String temp = imm;
|
||||
imm = reg2;
|
||||
reg2 = temp;
|
||||
}
|
||||
|
||||
// Generic
|
||||
return String.format("%s, %s",
|
||||
destLast ? imm : reg2,
|
||||
destLast ? reg2 : imm
|
||||
);
|
||||
}
|
||||
|
||||
// Operands for Format III
|
||||
private static String formatIII(Instruction inst, int address) {
|
||||
|
||||
// NOP
|
||||
if (bcondCombine && inst.cond == 13)
|
||||
return null;
|
||||
|
||||
// Format destination
|
||||
String dest = destination(inst, address);
|
||||
|
||||
// One-operand notation
|
||||
if (bcondCombine)
|
||||
return dest;
|
||||
|
||||
// Two-operand notation
|
||||
String cond = bcondNames ?
|
||||
CONDITIONS[inst.cond] : Integer.toString(inst.cond);
|
||||
if (bcondNames && !condCaps)
|
||||
cond = cond.toLowerCase();
|
||||
return String.format("%s, %s", cond, dest);
|
||||
}
|
||||
|
||||
// Operands for Format V
|
||||
private static String formatV(Instruction inst) {
|
||||
String reg1 = program(inst.reg1);
|
||||
String reg2 = program(inst.reg2);
|
||||
String imm = toHex(
|
||||
inst.id == Vue.MOVEA ? inst.imm & 0xFFFF : inst.imm,
|
||||
inst.id == Vue.ADDI ? 1 : 4,
|
||||
immNumber
|
||||
);
|
||||
return String.format("%s, %s, %s",
|
||||
destLast ? imm : reg2,
|
||||
reg1,
|
||||
destLast ? reg2 : imm
|
||||
);
|
||||
}
|
||||
|
||||
// Operands for Format VI
|
||||
private static String formatVI(Instruction inst) {
|
||||
String src = program(inst.reg1);
|
||||
String dest = program(inst.reg2);
|
||||
|
||||
// Data operand
|
||||
src = inst.disp == 0 ? String.format("[%s]", src) :
|
||||
dispInside ? String.format(
|
||||
"[%s %s %s]",
|
||||
src,
|
||||
inst.disp < 0 ? "-" : "+",
|
||||
toHex(Math.abs(inst.disp), 1, false)
|
||||
) : String.format(
|
||||
"%s[%s]",
|
||||
toHex(inst.disp, 1, false),
|
||||
src
|
||||
)
|
||||
;
|
||||
|
||||
// Write instruction
|
||||
switch (inst.id) {
|
||||
case Vue.OUT_B: // Fallthrough
|
||||
case Vue.OUT_H: // Fallthrough
|
||||
case Vue.OUT_W: // Fallthrough
|
||||
case Vue.ST_B : // Fallthrough
|
||||
case Vue.ST_H : // Fallthrough
|
||||
case Vue.ST_W :
|
||||
String temp = src;
|
||||
src = dest;
|
||||
dest = temp;
|
||||
}
|
||||
|
||||
// Format operands
|
||||
return String.format("%s, %s",
|
||||
destLast ? src : dest,
|
||||
destLast ? dest : src
|
||||
);
|
||||
}
|
||||
|
||||
// Format a program register
|
||||
private static String program(int index) {
|
||||
String ret = programNames ? PROGRAMS[index] : null;
|
||||
if (ret == null)
|
||||
ret = "r" + index;
|
||||
return programCaps ? ret.toUpperCase() : ret;
|
||||
}
|
||||
|
||||
// Represent an immediate value as hexadecimal
|
||||
private static String toHex(int value, int digits, boolean number) {
|
||||
return String.format(
|
||||
"%s%s%s%0" + digits + (hexCaps ? "X" : "x") + "%s",
|
||||
number ? "#" : "",
|
||||
value < 0 ? "-" : "",
|
||||
hexMode == DOLLAR ? "$" : hexMode == ZEROX ? "0x" : "",
|
||||
value < 0 ? -value : value,
|
||||
hexMode == H ? "h" : ""
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Disassembler UI
|
||||
class DisassemblerPane extends JScrollPane {
|
||||
|
||||
// Instance fields
|
||||
private int address; // Address of top row of output
|
||||
private CPUWindow parent; // Containing CPU window
|
||||
private boolean showBytes; // Display the bytes column
|
||||
private boolean shown; // Component has been shown
|
||||
private int[] widths; // Column widths
|
||||
|
||||
// UI components
|
||||
private UPanel client; // Client area
|
||||
private ArrayList<Row> rows; // Disassembler output
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One row of disassembler output
|
||||
private class Row extends Disassembler.Row {
|
||||
Instruction inst; // Decoded instruction
|
||||
JLabel[] labels; // Display text
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
DisassemblerPane(CPUWindow parent) {
|
||||
super(VERTICAL_SCROLLBAR_NEVER, HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
// Configure instance fields
|
||||
this.parent = parent;
|
||||
rows = new ArrayList<Row>();
|
||||
showBytes = true;
|
||||
widths = new int[5];
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel();
|
||||
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.addPaintListener((g,w,h)->onPaint(g, w, h));
|
||||
|
||||
// Configure component
|
||||
setViewportView(client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Key down
|
||||
private void onKeyDown(KeyEvent e) {
|
||||
int code = e.getKeyCode();
|
||||
int count = tall(false);
|
||||
int mods = e.getModifiersEx();
|
||||
var vue = parent.parent.vue;
|
||||
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
|
||||
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
|
||||
|
||||
// No Alt combinations
|
||||
if (alt) return;
|
||||
|
||||
// Goto
|
||||
if (ctrl && code == KeyEvent.VK_G) {
|
||||
String text = JOptionPane.showInputDialog(
|
||||
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
||||
Object eval = vue.evaluate(text);
|
||||
int addr = 0;
|
||||
if (eval instanceof Integer)
|
||||
addr = (Integer) eval;
|
||||
else if (eval instanceof Long)
|
||||
addr = (int) (long) (Long) eval;
|
||||
else return;
|
||||
if (!isVisible(addr))
|
||||
seek(addr, count / 3);
|
||||
return;
|
||||
}
|
||||
|
||||
// Processing by key code
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
var step = parent.parent.brkStep;
|
||||
switch (code) {
|
||||
case KeyEvent.VK_UP : seek(address, 1); break;
|
||||
case KeyEvent.VK_DOWN : seek(address, -1); break;
|
||||
case KeyEvent.VK_PAGE_UP : seek(address, count); break;
|
||||
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
|
||||
|
||||
// Single Step
|
||||
case KeyEvent.VK_F11:
|
||||
step.setCondition("!fetch&&pc!=" + pc);
|
||||
step.setEnabled(true);
|
||||
vue.emulate(20000000);
|
||||
if (step.evaluate())
|
||||
step.setEnabled(false);
|
||||
parent.parent.refreshDebug(true);
|
||||
break;
|
||||
|
||||
// Run to Next
|
||||
case KeyEvent.VK_F12:
|
||||
step.setCondition("!fetch&&pc==" +
|
||||
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
|
||||
step.setEnabled(true);
|
||||
vue.emulate(20000000);
|
||||
if (step.evaluate())
|
||||
step.setEnabled(false);
|
||||
parent.parent.refreshDebug(true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
private void onMouseWheel(MouseWheelEvent e) {
|
||||
seek(address, -e.getUnitsToScroll());
|
||||
}
|
||||
|
||||
// Client paint
|
||||
private void onPaint(Graphics2D g, int width, int height) {
|
||||
var vue = parent.parent.vue;
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
|
||||
// The view is being shown for the first time
|
||||
if (!shown) {
|
||||
shown = true;
|
||||
seek(pc, tall(false) / 3);
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure working variables
|
||||
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);
|
||||
widths[3] = widths[4] = 0;
|
||||
for (int y = 0; y < count; y++) {
|
||||
var row = y < rows.size() ? rows.get(y) : addRow();
|
||||
var color = SystemColor.windowText;
|
||||
|
||||
// Disassemble the instruction
|
||||
row.inst.decode(data, offset);
|
||||
Disassembler.disassemble(address, row.inst, row);
|
||||
updateRow(row);
|
||||
|
||||
// Highlight PC
|
||||
if (address == pc) {
|
||||
Color bg = SystemColor.control;
|
||||
if (client.isFocusOwner()) {
|
||||
bg = SystemColor.textHighlight;
|
||||
color = SystemColor.textHighlightText;
|
||||
}
|
||||
g.setColor(bg);
|
||||
g.fillRect(
|
||||
0, y * lineHeight,
|
||||
width, lineHeight
|
||||
);
|
||||
}
|
||||
|
||||
// Configure label text color
|
||||
for (var label : row.labels)
|
||||
label.setForeground(color);
|
||||
|
||||
// Advance to the next instruction
|
||||
int size = address + 2 == pc ? 2 : row.inst.size;
|
||||
address += size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// Configure all rows
|
||||
for (int y = 0; y < rows.size(); y++) {
|
||||
var row = rows.get(y);
|
||||
int top = y * lineHeight;
|
||||
|
||||
// Configure all labels
|
||||
for (int z = 0, x = 0; z < 5; z++) {
|
||||
var label = row.labels[z];
|
||||
|
||||
// The label is not part of the output
|
||||
if (y >= count || widths[z] == 0) {
|
||||
label.setVisible(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Configure the label
|
||||
label.setLocation(x, top);
|
||||
label.setSize(label.getPreferredSize());
|
||||
x += widths[z] + lineHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update the client's size
|
||||
var size = client.getPreferredSize();
|
||||
width = -lineHeight + 1;
|
||||
for (int x = 0; x < 5; x++)
|
||||
if (widths[x] != 0 && (x != 2 || showBytes))
|
||||
width += lineHeight + widths[x];
|
||||
if (width == size.width)
|
||||
return;
|
||||
client.setPreferredSize(new Dimension(width, 0));
|
||||
revalidate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Create a new row object
|
||||
private Row addRow() {
|
||||
var row = new Row();
|
||||
row.inst = new Instruction();
|
||||
row.labels = new JLabel[5];
|
||||
|
||||
// Initialize columns
|
||||
for (int x = 0; x < 5; x++) {
|
||||
var label = row.labels[x] = new JLabel();
|
||||
if (x != 4)
|
||||
label.setFont(parent.parent.app.fntMono);
|
||||
client.add(label);
|
||||
}
|
||||
|
||||
rows.add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
// Determine whether the instruction at an address is in the client view
|
||||
private boolean isVisible(int target) {
|
||||
int count = Math.max(1, tall(false));
|
||||
var data = new byte[count * 4];
|
||||
int offset = 0;
|
||||
var vue = parent.parent.vue;
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
target &= 0xFFFFFFFE;
|
||||
|
||||
// Load enough bytes to represent every fully visible instruction
|
||||
vue.readBytes(address, data, 0, data.length);
|
||||
|
||||
// Iterate through instructions
|
||||
for (int x = 0, address = this.address; x < count; x++) {
|
||||
|
||||
// Determine the instruction's size
|
||||
int size = address + 2 == pc ? 2 :
|
||||
Instruction.size(data[offset + 1] >> 2 & 0x3F);
|
||||
|
||||
// The current instruction is the target
|
||||
if (address == target || size == 4 && address + 2 == target)
|
||||
return true;
|
||||
|
||||
// Advance to the next instruction
|
||||
address += size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// The instruction is not visible in the view
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine the address of the top row of output
|
||||
private void seek(int target, int row) {
|
||||
var data = new byte[(Math.abs(row) + 9) * 4];
|
||||
int offset = 0;
|
||||
var vue = parent.parent.vue;
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
target &= 0xFFFFFFFE;
|
||||
|
||||
// Scrolling down
|
||||
if (row <= 0) {
|
||||
|
||||
// Load bytes starting up to 8 instructions prior to the target
|
||||
address = target - 32;
|
||||
vue.readBytes(address, data, 0, data.length);
|
||||
|
||||
// Iterate through instructions
|
||||
for (boolean found = false;;) {
|
||||
|
||||
// Determine the instruction's size
|
||||
int size = address + 2 == pc ? 2 :
|
||||
Instruction.size(data[offset + 1] >> 2 & 0x3F);
|
||||
|
||||
// The current instruction is the target
|
||||
if (address == target || size == 4 && address + 2 == target)
|
||||
found = true;
|
||||
if (found && row++ == 0)
|
||||
break;
|
||||
|
||||
// Advance to the next instruction
|
||||
address += size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Scrolling up
|
||||
else {
|
||||
|
||||
// Load bytes starting up to 8 instructions prior to the top row
|
||||
address = target - data.length + 4;
|
||||
vue.readBytes(address, data, 0, data.length);
|
||||
|
||||
// Iterate through instructions
|
||||
var addresses = new int[row]; // Circular buffer
|
||||
for (int index = 0;;) {
|
||||
|
||||
// Determine the instruction's size
|
||||
int size = pc == address + 2 ? 2 :
|
||||
Instruction.size(data[offset + 1] >> 2 & 63);
|
||||
|
||||
// The current instruction is the target
|
||||
if (address == target || size == 4 && address + 2 == target) {
|
||||
address = addresses[index];
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance to the next instruction
|
||||
addresses[index] = address;
|
||||
if (++index == row)
|
||||
index = 0;
|
||||
address += size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Common processing
|
||||
client.repaint();
|
||||
}
|
||||
|
||||
// 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 ? lineHeight - 1 : 0)
|
||||
) / lineHeight);
|
||||
}
|
||||
|
||||
// Update a row with its text and measure the column widths
|
||||
private void updateRow(Row row) {
|
||||
|
||||
for (int x = 0; x < 5; x++) {
|
||||
var label = row.labels[x];
|
||||
|
||||
String text = null;
|
||||
switch (x) {
|
||||
case 0: text = row.address ; break;
|
||||
case 1: text = row.bytes ; break;
|
||||
case 2: text = row.mnemonic; break;
|
||||
case 3: text = row.operands; break;
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
label.setText(text);
|
||||
label.setVisible(x == 1 ? showBytes : true);
|
||||
widths[x] = Math.max(widths[x],label.getPreferredSize().width);
|
||||
} else label.setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,477 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Main application window
|
||||
class MainWindow extends JFrame {
|
||||
|
||||
// Instance fields
|
||||
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
|
||||
private int number; // Window number within application
|
||||
private boolean only; // This is the only application window
|
||||
private File pwd; // Most recent working directory
|
||||
private ROM rom; // Currently loaded ROM
|
||||
private File romFile; // Currently loaded ROM file
|
||||
|
||||
// UI components
|
||||
private UPanel client; // Common client container
|
||||
private JDesktopPane desktop; // Container for child windows
|
||||
private JMenu mnuDebug; // Debug menu
|
||||
private UPanel video; // Video output
|
||||
private JMenuItem mnuFileDebugMode; // File -> Debug mode
|
||||
private JMenuItem mnuFileGameMode; // File -> Game mode
|
||||
|
||||
// Child windows
|
||||
private BGMapsWindow bgMaps;
|
||||
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;
|
||||
|
||||
// Static initializer
|
||||
static {
|
||||
APPICON = Util.imageRead("images/app_icon.png");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
MainWindow(App app) {
|
||||
super();
|
||||
|
||||
// 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"));
|
||||
|
||||
// Configure video pane
|
||||
video = new UPanel();
|
||||
video.setPreferredSize(new Dimension(384, 224));
|
||||
video.setFocusable(true);
|
||||
video.addPaintListener((g,w,h)->onPaintVideo(g, w, h));
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel(new BorderLayout());
|
||||
client.add(video, BorderLayout.CENTER);
|
||||
|
||||
// Configure window
|
||||
addWindowListener(Util.onClose(e->onClose()));
|
||||
setContentPane(client);
|
||||
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||
setIconImage(APPICON);
|
||||
setJMenuBar(initMenus());
|
||||
app.localizer.add(this, "app.title.default");
|
||||
|
||||
// Configure child windows
|
||||
desktop = new JDesktopPane();
|
||||
desktop.setBackground(SystemColor.controlShadow);
|
||||
desktop.add(bgMaps = new BGMapsWindow (this));
|
||||
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));
|
||||
|
||||
// Configure internal breakpoints
|
||||
brkStep = vue.breakpoint();
|
||||
brkStep.setRead(true);
|
||||
|
||||
// Display window
|
||||
refreshDebug(true);
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Menu Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Produce the window's menu bar
|
||||
private JMenuBar initMenus() {
|
||||
var bar = new JMenuBar();
|
||||
var loc = app.localizer;
|
||||
bar.add(initMenuFile (loc));
|
||||
bar.add(initMenuDebug(loc));
|
||||
return bar;
|
||||
}
|
||||
|
||||
// Initialize the Debug menu
|
||||
private JMenu initMenuDebug(Localizer loc) {
|
||||
mnuDebug = new JMenu();
|
||||
loc.add(mnuDebug, "app.debug.(menu)");
|
||||
mnuDebug.setVisible(false);
|
||||
|
||||
var mnuDebugBreakpoints = new JMenuItem();
|
||||
loc.add(mnuDebugBreakpoints, "app.debug.breakpoints");
|
||||
mnuDebugBreakpoints.addActionListener(e->breakpoints.setVisible(true));
|
||||
mnuDebug.add(mnuDebugBreakpoints);
|
||||
|
||||
var mnuDebugConsole = new JMenuItem();
|
||||
loc.add(mnuDebugConsole, "app.debug.console");
|
||||
mnuDebugConsole.addActionListener(e->console.setVisible(true));
|
||||
mnuDebug.add(mnuDebugConsole);
|
||||
|
||||
var mnuDebugCPU = new JMenuItem();
|
||||
loc.add(mnuDebugCPU, "app.debug.cpu");
|
||||
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
|
||||
mnuDebug.add(mnuDebugCPU);
|
||||
|
||||
var mnuDebugMemory = new JMenuItem();
|
||||
loc.add(mnuDebugMemory, "app.debug.memory");
|
||||
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
|
||||
mnuDebug.add(mnuDebugMemory);
|
||||
|
||||
mnuDebug.addSeparator();
|
||||
|
||||
var mnuDebugBGMaps = new JMenuItem();
|
||||
loc.add(mnuDebugBGMaps, "app.debug.bg_maps");
|
||||
mnuDebugBGMaps.addActionListener(e->bgMaps.setVisible(true));
|
||||
mnuDebug.add(mnuDebugBGMaps);
|
||||
|
||||
var mnuDebugCharacters = new JMenuItem();
|
||||
loc.add(mnuDebugCharacters, "app.debug.characters");
|
||||
mnuDebugCharacters.addActionListener(e->characters.setVisible(true));
|
||||
mnuDebug.add(mnuDebugCharacters);
|
||||
|
||||
var mnuDebugFrameBuffers = new JMenuItem();
|
||||
mnuDebugFrameBuffers.setEnabled(false);
|
||||
loc.add(mnuDebugFrameBuffers, "app.debug.frame_buffers");
|
||||
mnuDebug.add(mnuDebugFrameBuffers);
|
||||
|
||||
var mnuDebugObjects = new JMenuItem();
|
||||
mnuDebugObjects.setEnabled(false);
|
||||
loc.add(mnuDebugObjects, "app.debug.objects");
|
||||
mnuDebug.add(mnuDebugObjects);
|
||||
|
||||
var mnuDebugWorlds = new JMenuItem();
|
||||
mnuDebugWorlds.setEnabled(false);
|
||||
loc.add(mnuDebugWorlds, "app.debug.worlds");
|
||||
mnuDebug.add(mnuDebugWorlds);
|
||||
|
||||
return mnuDebug;
|
||||
}
|
||||
|
||||
// Initialize the File menu
|
||||
private JMenu initMenuFile(Localizer loc) {
|
||||
var mnuFile = new JMenu();
|
||||
loc.add(mnuFile, "app.file.(menu)");
|
||||
|
||||
var mnuFileLoadRom = new JMenuItem();
|
||||
loc.add(mnuFileLoadRom, "app.file.load_rom");
|
||||
mnuFileLoadRom.addActionListener(e->onLoadROM());
|
||||
mnuFile.add(mnuFileLoadRom);
|
||||
|
||||
mnuFileDebugMode = new JMenuItem();
|
||||
loc.add(mnuFileDebugMode, "app.file.debug_mode");
|
||||
mnuFileDebugMode.addActionListener(e->onDebugMode(true));
|
||||
mnuFile.add(mnuFileDebugMode);
|
||||
|
||||
mnuFileGameMode = new JMenuItem();
|
||||
loc.add(mnuFileGameMode, "app.file.game_mode");
|
||||
mnuFileGameMode.addActionListener(e->onDebugMode(false));
|
||||
mnuFileGameMode.setVisible(false);
|
||||
mnuFile.add(mnuFileGameMode);
|
||||
|
||||
mnuFile.addSeparator();
|
||||
|
||||
var mnuFileNewWindow = new JMenuItem();
|
||||
loc.add(mnuFileNewWindow, "app.file.new_window");
|
||||
mnuFileNewWindow.addActionListener(e->onNewWindow());
|
||||
mnuFile.add(mnuFileNewWindow);
|
||||
|
||||
var mnuFileExit = new JMenuItem();
|
||||
loc.add(mnuFileExit, "app.file.exit");
|
||||
mnuFileExit.addActionListener(e->onClose());
|
||||
mnuFile.add(mnuFileExit);
|
||||
|
||||
return mnuFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Draw a character into a pixel buffer
|
||||
void drawCharacter(int index, boolean hFlip, boolean vFlip, int[] palette,
|
||||
int[] buffer, int offset, int stride) {
|
||||
int addr = index >> 9 << 15 | 0x00006000 | (index & 511) << 4;
|
||||
|
||||
for (int y = 0; y < 8; y++, offset += stride - 8)
|
||||
for (int x = 0; x < 8; x++, offset += 1 ) {
|
||||
int ix = hFlip ? 7 - x : x;
|
||||
int iy = vFlip ? 7 - y : y;
|
||||
int b = addr | iy << 1 | ix >> 2;
|
||||
int bit = (ix & 3) << 1;
|
||||
buffer[offset] = palette[vram[b] >> bit & 3];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh all debug views
|
||||
void refreshDebug(boolean seekToPC) {
|
||||
vue.readBytes(0x00000000, vram, 0, vram.length);
|
||||
refreshPalettes();
|
||||
refreshDebugLite(seekToPC);
|
||||
}
|
||||
|
||||
// Refresh all debug views without retrieving video memory
|
||||
void refreshDebugLite(boolean seekToPC) {
|
||||
bgMaps .refresh();
|
||||
breakpoints.refresh();
|
||||
characters .refresh();
|
||||
cpu .refresh(seekToPC);
|
||||
memory .refresh();
|
||||
}
|
||||
|
||||
// A window has been added to or removed from the program state
|
||||
void windowsChanged(int number, boolean only) {
|
||||
this.number = number;
|
||||
this.only = only;
|
||||
app.localizer.put(this, "ctrl.number", "" + number);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Window close, File -> Exit
|
||||
private void onClose() {
|
||||
app.removeWindow(this);
|
||||
cpu.dispose();
|
||||
dispose();
|
||||
vue.dispose();
|
||||
}
|
||||
|
||||
// File -> Debug mode, File -> Game mode
|
||||
private void onDebugMode(boolean debugMode) {
|
||||
this.debugMode = debugMode;
|
||||
mnuFileDebugMode.setVisible(!debugMode);
|
||||
mnuFileGameMode .setVisible( debugMode);
|
||||
|
||||
// Transition to debug mode
|
||||
if (debugMode) {
|
||||
client.remove(video);
|
||||
client.add(desktop, BorderLayout.CENTER);
|
||||
console.setContentPane(video);
|
||||
console.firstShow();
|
||||
mnuDebug.setVisible(true);
|
||||
}
|
||||
|
||||
// Transition to game mode
|
||||
else {
|
||||
client.remove(desktop);
|
||||
client.add(video, BorderLayout.CENTER);
|
||||
mnuDebug.setVisible(false);
|
||||
}
|
||||
|
||||
client.revalidate();
|
||||
client.repaint();
|
||||
}
|
||||
|
||||
// File -> Load ROM
|
||||
private void onLoadROM() {
|
||||
var loc = app.localizer;
|
||||
|
||||
// Prompt the user to select a file
|
||||
var dlgFile = new JFileChooser(pwd);
|
||||
dlgFile.addChoosableFileFilter(new FileNameExtensionFilter(
|
||||
loc.get("dialog.ext_vb"), "vb"));
|
||||
dlgFile.addChoosableFileFilter(new FileNameExtensionFilter(
|
||||
loc.get("dialog.ext_isx"), "isx"));
|
||||
dlgFile.setAcceptAllFileFilterUsed(true);
|
||||
dlgFile.setDialogTitle(loc.get("dialog.load_rom"));
|
||||
int option = dlgFile.showDialog(this, loc.get("dialog.load"));
|
||||
|
||||
// The user did not select a file
|
||||
var file = dlgFile.getSelectedFile();
|
||||
if (option != JFileChooser.APPROVE_OPTION || file == null)
|
||||
return;
|
||||
|
||||
// Update the current directory
|
||||
pwd = file.getParentFile();
|
||||
|
||||
// Read the file
|
||||
var data = Util.fileRead(file);
|
||||
if (data == null) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
loc.get("dialog.load_rom_error"),
|
||||
loc.get("dialog.load_rom"),
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the ROM
|
||||
ROM rom = null;
|
||||
try { rom = new ROM(data); }
|
||||
catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
loc.get("dialog.load_rom_notvb"),
|
||||
loc.get("dialog.load_rom"),
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update instance fields
|
||||
this.rom = rom;
|
||||
romFile = file;
|
||||
loc.put(this, "ctrl.filename", file.getName());
|
||||
updateTitle();
|
||||
|
||||
// Update the emulation state
|
||||
var bytes = rom.toByteArray();
|
||||
// Pause emulation
|
||||
vue.setROM(bytes, 0, bytes.length);
|
||||
vue.reset();
|
||||
refreshDebug(true);
|
||||
// Resume emulation
|
||||
}
|
||||
|
||||
// File -> New window
|
||||
private void onNewWindow() {
|
||||
app.addWindow();
|
||||
}
|
||||
|
||||
// Video paint
|
||||
private void onPaintVideo(Graphics2D g, int width, int height) {
|
||||
int scale = Math.max(1, Math.min(width / 384, height / 224));
|
||||
|
||||
g.translate(
|
||||
Math.max(0, (width - scale * 384) / 2),
|
||||
Math.max(0, (height - scale * 224) / 2)
|
||||
);
|
||||
g.scale(scale, scale);
|
||||
|
||||
g.setColor(Color.black);
|
||||
g.fillRect(0, 0, 384, 224);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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];
|
||||
dest[0] = 0xFF000000 | app.rgbClear;
|
||||
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,
|
||||
only ? romFile != null ?
|
||||
"app.title.rom" :
|
||||
"app.title.default"
|
||||
: romFile != null ?
|
||||
"app.title.mixed" :
|
||||
"app.title.number"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
|
||||
// Memory viewer and hex editor window
|
||||
class MemoryWindow extends ChildWindow {
|
||||
|
||||
// Private fields
|
||||
private int address; // Address of top row
|
||||
|
||||
// UI components
|
||||
private UPanel client; // Client area
|
||||
private ArrayList<Row> rows; // Rows of text
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One row of output
|
||||
private class Row {
|
||||
JLabel address; // Address (row header)
|
||||
JLabel[] bytes; // Hexadecimal bytes
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
MemoryWindow(MainWindow parent) {
|
||||
super(parent, "memory.title");
|
||||
|
||||
// Configure instance fields
|
||||
address = 0x00000000;
|
||||
rows = new ArrayList<Row>();
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel();
|
||||
client.addComponentListener(Util.onResize(e->onResize()));
|
||||
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
|
||||
client.addMouseWheelListener(e->onWheel(e));
|
||||
client.setBackground(SystemColor.window);
|
||||
client.setFocusable(true);
|
||||
client.setPreferredSize(new Dimension(480, 360));
|
||||
|
||||
// Configure component
|
||||
var content = new UPanel(new BorderLayout());
|
||||
content.setBorder(new JScrollPane().getBorder());
|
||||
content.add(client, BorderLayout.CENTER);
|
||||
setContentPane(content);
|
||||
pack();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update the display
|
||||
void refresh() {
|
||||
|
||||
// The element is not ready
|
||||
if (client == null)
|
||||
return;
|
||||
|
||||
// Configure working variables
|
||||
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);
|
||||
|
||||
// Update visible rows
|
||||
for (int x = 0; x < count; x++) {
|
||||
Row row;
|
||||
if (x < rows.size())
|
||||
row = rows.get(x); // Retrieve row from collection
|
||||
else row = createRow(); // Produce a new row
|
||||
update(row, x * lineHeight, address + x * 16, data, x * 16);
|
||||
setVisible(row, true);
|
||||
}
|
||||
|
||||
// Hide any rows that are not visible
|
||||
for (int x = count; x < rows.size(); x++)
|
||||
setVisible(rows.get(x), false);
|
||||
|
||||
// Finalize layout
|
||||
client.revalidate();
|
||||
client.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event Handlers //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Key down
|
||||
private void onKeyDown(KeyEvent e) {
|
||||
int code = e.getKeyCode();
|
||||
int mods = e.getModifiersEx();
|
||||
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
|
||||
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
|
||||
int tall = Math.max(1, tall(false));
|
||||
|
||||
// No Alt combinations
|
||||
if (alt) return;
|
||||
|
||||
// Goto
|
||||
if (ctrl && code == KeyEvent.VK_G) {
|
||||
String text = JOptionPane.showInputDialog(
|
||||
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
||||
Object eval = parent.vue.evaluate(text);
|
||||
int addr = 0;
|
||||
if (eval instanceof Integer)
|
||||
addr = (Integer) eval;
|
||||
else if (eval instanceof Long)
|
||||
addr = (int) (long) (Long) eval;
|
||||
else return;
|
||||
setAddress(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Seek
|
||||
switch (code) {
|
||||
case KeyEvent.VK_UP : setAddress(address - 16); break;
|
||||
case KeyEvent.VK_DOWN : setAddress(address + 16); break;
|
||||
case KeyEvent.VK_PAGE_UP : setAddress(address - tall * 16); break;
|
||||
case KeyEvent.VK_PAGE_DOWN: setAddress(address + tall * 16); break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Client resize
|
||||
private void onResize() {
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
private void onWheel(MouseWheelEvent e) {
|
||||
int amount = e.getUnitsToScroll();
|
||||
int mods = e.getModifiersEx();
|
||||
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
|
||||
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
|
||||
|
||||
// No Alt or Ctrl combinations
|
||||
if (amount == 0 || alt || ctrl)
|
||||
return;
|
||||
|
||||
// Seek
|
||||
setAddress(address + 16 * amount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a new row of output
|
||||
private Row createRow() {
|
||||
var font = parent.app.fntMono;
|
||||
var row = new Row();
|
||||
|
||||
// Address label
|
||||
row.address = new JLabel();
|
||||
row.address.setFont(font);
|
||||
row.address.setForeground(SystemColor.windowText);
|
||||
row.address.setVisible(false);
|
||||
client.add(row.address);
|
||||
|
||||
// Byte labels
|
||||
row.bytes = new JLabel[16];
|
||||
for (int x = 0; x < row.bytes.length; x++) {
|
||||
var label = row.bytes[x] = new JLabel();
|
||||
label.setFont(font);
|
||||
label.setForeground(SystemColor.windowText);
|
||||
label.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
label.setVisible(false);
|
||||
client.add(label);
|
||||
}
|
||||
|
||||
// Add the row to the collection
|
||||
rows.add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
// Measure the minimum column widths for a row
|
||||
private void measure(Row row, int[] widths) {
|
||||
widths[0] = Math.max(widths[0], row.address.getPreferredSize().width);
|
||||
for (int x = 0; x < 16; x++)
|
||||
widths[1] = Math.max(widths[1],
|
||||
row.bytes[x].getPreferredSize().width);
|
||||
}
|
||||
|
||||
// Specify the address of the top row of output
|
||||
private void setAddress(int address) {
|
||||
this.address = address & 0xFFFFFFF0;
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Show or hide a row
|
||||
private void setVisible(Row row, boolean visible) {
|
||||
row.address.setVisible(visible);
|
||||
for (var label : row.bytes)
|
||||
label.setVisible(visible);
|
||||
}
|
||||
|
||||
// Determine how many rows of output are visible
|
||||
private int tall(boolean partial) {
|
||||
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 * hexDigitWidth, lineHeight);
|
||||
row.address.setText(String.format("%08X", address));
|
||||
|
||||
// Update bytes
|
||||
for (int z = 0, x = 10 * hexDigitWidth; z < 16; z++) {
|
||||
var label = row.bytes[z];
|
||||
label.setBounds(x, y, 2 * hexDigitWidth, lineHeight);
|
||||
label.setText(String.format("%02X", data[offset++] & 0xFF));
|
||||
x += hexDigitWidth * (z == 7 ? 4 : 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.nio.charset.*;
|
||||
import java.util.*;
|
||||
|
||||
// Cartridge ROM module
|
||||
public class ROM {
|
||||
|
||||
// Instance fields
|
||||
private byte[] data; // Binary data
|
||||
private int format; // File format of loaded file
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Formats
|
||||
public static final int RAW = 0;
|
||||
public static final int ISX = 1;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ISX code segments
|
||||
private static class Code {
|
||||
int address; // CPU address
|
||||
int offset; // Position within file
|
||||
int size; // Number of bytes
|
||||
Code(int o, int a, int s) { address = a; offset = o; size = s; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
ROM(byte[] data) {
|
||||
|
||||
// Attempt to decode as ISX
|
||||
try { isxDecode(data); return; } catch (Exception e) { }
|
||||
|
||||
// Attempt to decode as raw
|
||||
try { rawDecode(data); return; } catch (Exception e) { }
|
||||
|
||||
// Unable to identify the file contents
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Retrieve the game code from the ROM header
|
||||
public String getGameCode() {
|
||||
var bytes = new byte[4];
|
||||
System.arraycopy(data, data.length - 517, bytes, 0, 4);
|
||||
return new String(bytes, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
// Retrieve the file format of the loaded ROM file
|
||||
public int getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
// Retrieve the maker code from the ROM header
|
||||
public String getMaker() {
|
||||
var bytes = new byte[2];
|
||||
System.arraycopy(data, data.length - 519, bytes, 0, 2);
|
||||
return new String(bytes, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
// Retrieve the number of bytes in the ROM data
|
||||
public int getSize() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
// Retrieve the game title from the ROM header
|
||||
public String getTitle() {
|
||||
return ShiftJIS.decode(data, data.length - 544, 20).trim();
|
||||
}
|
||||
|
||||
// Retrieve the version number from the ROM header
|
||||
public int getVersion() {
|
||||
return data[data.length - 513] & 0xFF;
|
||||
}
|
||||
|
||||
// Produce a byte array containing the ROM contents
|
||||
public byte[] toByteArray() {
|
||||
var ret = new byte[data.length];
|
||||
System.arraycopy(data, 0, ret, 0, data.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Decode the file data as ISX
|
||||
private void isxDecode(byte[] data) {
|
||||
var codes = isxParse(data); // ISX code segments
|
||||
int head = -1; // Latest address in bottom half of ROM address space
|
||||
int tail = -1; // Earliest address in upper half of ROM address space
|
||||
|
||||
// Process all code segments
|
||||
for (var code : codes) {
|
||||
int end = code.address + code.size - 1;
|
||||
int start = code.address;
|
||||
boolean isHead = end >= 0; // Lower half of CPU address range
|
||||
boolean isTail = start < 0; // Upper half of CPU address range
|
||||
|
||||
// The segment spans the middle of the ROM address space
|
||||
if (isHead && isTail) {
|
||||
head = 0x7FFFFF;
|
||||
tail = 0x800000;
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the segment's position within the ROM address space
|
||||
end &= 0x00FFFFFF;
|
||||
start &= 0x00FFFFFF;
|
||||
if (isHead && (head == -1 || head < end )) head = end;
|
||||
if (isTail && (tail == -1 || tail > start)) tail = start;
|
||||
}
|
||||
|
||||
// Determine the required ROM size
|
||||
int size = 1024;
|
||||
int minSize = (head == -1 ? 0 : head + 1) +
|
||||
(tail == -1 ? 0 : 0x01000000 - tail);
|
||||
if (minSize == 0 || tail == -1 &&
|
||||
(minSize < 1024 || (minSize - 1 & minSize) != 0))
|
||||
throw new RuntimeException();
|
||||
for (; size < minSize; size <<= 1);
|
||||
|
||||
// Transfer the code segments into the ROM buffer
|
||||
this.data = new byte[size];
|
||||
for (var code : codes)
|
||||
System.arraycopy(
|
||||
data , code.offset,
|
||||
this.data, code.address & size - 1,
|
||||
code.size
|
||||
);
|
||||
|
||||
// The ROM is valid
|
||||
format = ISX;
|
||||
}
|
||||
|
||||
// Parse the code records from the ISX data
|
||||
private Code[] isxParse(byte[] data) {
|
||||
int count;
|
||||
int offset = 0;
|
||||
var ret = new ArrayList<Code>();
|
||||
|
||||
// Check for an extended ISX header
|
||||
if (readInt(data, 0, 3) == 0x585349) // ASCII "ISX"
|
||||
offset = 32;
|
||||
|
||||
// Process records
|
||||
while (offset != data.length)
|
||||
switch (data[offset++] & 0xFF) {
|
||||
|
||||
// SNES code
|
||||
case 0x01:
|
||||
if ((data[offset++] & 0xFF) >= 0x80) // Bank
|
||||
offset++; // BankHigh
|
||||
offset += 2; // Address
|
||||
offset += 2 + readInt(data, offset, 2); // Data
|
||||
break;
|
||||
|
||||
// SNES range
|
||||
case 0x03:
|
||||
// Count, { Bank, StartAddress, EndAddress, Type }
|
||||
offset += 2 + readInt(data, offset, 2) * 6;
|
||||
break;
|
||||
|
||||
// SNES symbol
|
||||
case 0x04:
|
||||
count = readInt(data, offset, 2); offset += 2;
|
||||
for (; count > 0; count--) // Name, Flags, Bank, Address
|
||||
offset += 5 + readInt(data, offset, 1);
|
||||
break;
|
||||
|
||||
// Virtual Boy code
|
||||
case 0x11:
|
||||
int address = readInt(data, offset, 4); offset += 4;
|
||||
int size = readInt(data, offset, 4); offset += 4;
|
||||
if ((address & 0x07000000) != 0x07000000 || size < 0 ||
|
||||
(address & 0x00FFFFFF) + size > 0x01000000)
|
||||
throw new RuntimeException();
|
||||
ret.add(new Code(offset, address, size));
|
||||
offset += size;
|
||||
break;
|
||||
|
||||
// Virtual Boy range
|
||||
case 0x13:
|
||||
// Count, { StartAddress, EndAddress, Type }
|
||||
offset += 2 + readInt(data, offset, 2) * 9;
|
||||
break;
|
||||
|
||||
// Virtual Boy symbol
|
||||
case 0x14:
|
||||
count = readInt(data, offset, 2); offset += 2;
|
||||
for (; count > 0; count--) // Name, Flags, Address
|
||||
offset += 7 + readInt(data, offset, 1);
|
||||
break;
|
||||
|
||||
// System
|
||||
case 0x20: case 0x21: case 0x22:
|
||||
offset += 4 + readInt(data, offset, 4); // Debug, undocumented
|
||||
break;
|
||||
|
||||
// Invalid record type
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
return ret.toArray(new Code[ret.size()]);
|
||||
}
|
||||
|
||||
// Decode the file as raw
|
||||
private void rawDecode(byte[] data) {
|
||||
|
||||
// Validate length
|
||||
if (
|
||||
data.length < 1024 || // Exception handlers and ROM header
|
||||
data.length > 0x00FFFFFF || // 24-bit bus width
|
||||
(data.length & data.length - 1) != 0 // Must be a power of 2
|
||||
) throw new RuntimeException();
|
||||
|
||||
// The ROM is valid
|
||||
this.data = data;
|
||||
format = RAW;
|
||||
}
|
||||
|
||||
// Read an integer from a byte array
|
||||
private static int readInt(byte[] data, int offset, int size) {
|
||||
int ret = 0;
|
||||
for (size--; size >= 0; size--)
|
||||
ret = ret << 8 | data[offset + size] & 0xFF;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,521 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// Register list item
|
||||
class Register {
|
||||
|
||||
// Instance fields
|
||||
private boolean expandable; // The expansion area can be shown
|
||||
private boolean expanded; // The expanded area is being shown
|
||||
private int index; // Register index
|
||||
private int mode; // Display mode for program registers
|
||||
private String name; // Register name
|
||||
private RegisterList parent; // Containing register list
|
||||
private int type; // Expansion controls type
|
||||
private int value; // Current register value
|
||||
|
||||
// UI components
|
||||
UPanel expansion; // Expansion container
|
||||
JLabel btnExpand; // Expand button
|
||||
UPanel indent; // Expansion area indentation
|
||||
JLabel lblName; // Register name
|
||||
JTextField txtValue; // Register value
|
||||
ArrayList<JComponent> controls; // Expansion controls
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Modes
|
||||
static final int FLOAT = 3;
|
||||
static final int HEX = 0;
|
||||
static final int SIGNED = 1;
|
||||
static final int UNSIGNED = 2;
|
||||
|
||||
// Types
|
||||
static final int PLAIN = -2;
|
||||
static final int PROGRAM = -3;
|
||||
|
||||
// Expand button labels
|
||||
static final String COLLAPSE = "-";
|
||||
static final String EXPAND = "+";
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
Register(RegisterList parent, String name, int index, int type) {
|
||||
parent.add(index, this);
|
||||
|
||||
// Configure instance fields
|
||||
controls = new ArrayList<JComponent>();
|
||||
expandable = type != PLAIN && index != 0;
|
||||
this.index = index;
|
||||
mode = HEX;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.type = type;
|
||||
|
||||
// Click handler for expand and name controls
|
||||
MouseListener expand = !expandable ? null : Util.onMouse(e->{
|
||||
if (e.getButton() == 1) setExpanded(!expanded); }, null);
|
||||
|
||||
// Expand button
|
||||
btnExpand = new JLabel(expandable ? EXPAND : "");
|
||||
btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
if (expandable)
|
||||
btnExpand.addMouseListener(expand);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTH;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
parent.add(gbc, btnExpand);
|
||||
|
||||
// Name label
|
||||
lblName = new JLabel(" ");
|
||||
if (expandable)
|
||||
lblName.addMouseListener(expand);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTH;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 1;
|
||||
parent.add(gbc, lblName);
|
||||
|
||||
// Value text box
|
||||
txtValue = new JTextField();
|
||||
txtValue.setBorder(null);
|
||||
txtValue.setOpaque(false);
|
||||
txtValue.setText("00000000");
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 4, 0, 0);
|
||||
parent.add(gbc, txtValue);
|
||||
|
||||
// Value changed
|
||||
txtValue.addActionListener(e->parent.requestFocus());
|
||||
txtValue.addFocusListener(Util.onFocus(null, e->{
|
||||
String text = txtValue.getText();
|
||||
int val = value;
|
||||
try { switch (mode) {
|
||||
case HEX : val = (int) Long.parseLong(text, 16); break;
|
||||
case SIGNED : // Fallthrough
|
||||
case UNSIGNED: val = (int) Long.parseLong(text, 10); break;
|
||||
case FLOAT : val =
|
||||
Float.floatToIntBits(Float.parseFloat(text) ); break;
|
||||
}} catch (Exception x) { }
|
||||
setValue(val);
|
||||
}));
|
||||
|
||||
// Expansion controls
|
||||
switch (type) {
|
||||
case PROGRAM : initProgram(); break;
|
||||
case Vue.CHCW: initCHCW (); break;
|
||||
case Vue.ECR : initECR (); break;
|
||||
case Vue.PC : initPC (); break;
|
||||
case Vue.PIR : initPIR (); break;
|
||||
case Vue.PSW : initPSW (); break;
|
||||
case Vue.TKCW: initTKCW (); break;
|
||||
default: configure(); return;
|
||||
}
|
||||
|
||||
// Expansion indentation
|
||||
if (index != Vue.PC) {
|
||||
indent = new UPanel();
|
||||
indent.setOpaque(false);
|
||||
indent.setPreferredSize(new Dimension(0, 0));
|
||||
indent.setVisible(false);
|
||||
gbc = new GridBagConstraints();
|
||||
parent.add(gbc, indent);
|
||||
}
|
||||
|
||||
// Expansion area
|
||||
expansion.setOpaque(false);
|
||||
expansion.setVisible(false);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
if (index == Vue.PC)
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 4, 0, 0);
|
||||
gbc.weightx = 1;
|
||||
parent.add(gbc, expansion);
|
||||
|
||||
// Handling for PSW
|
||||
if (index == Vue.PSW && type == Vue.PSW)
|
||||
setExpanded(true);
|
||||
|
||||
// Apply application settings
|
||||
configure();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Expansion Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Expansion controls for CHCW
|
||||
private void initCHCW() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
addCheckBox("ICE", 1, false, true);
|
||||
}
|
||||
|
||||
// Expansion controls for ECR
|
||||
private void initECR() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
addTextBox("EICC", 0, 16, false, true);
|
||||
addTextBox("FECC", 16, 16, false, true);
|
||||
}
|
||||
|
||||
// Expansion controls for program registers
|
||||
private void initProgram() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
var group = new ButtonGroup();
|
||||
group.add(addRadioButton("cpu.hex" , HEX ));
|
||||
group.add(addRadioButton("cpu.signed" , SIGNED ));
|
||||
group.add(addRadioButton("cpu.unsigned", UNSIGNED));
|
||||
group.add(addRadioButton("cpu.float" , FLOAT ));
|
||||
}
|
||||
|
||||
// Expansion controls for PC
|
||||
private void initPC() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
|
||||
// Configure controls
|
||||
for (int x = 0; x < 2; x++) {
|
||||
|
||||
// Indentation
|
||||
indent = new UPanel();
|
||||
indent.setOpaque(false);
|
||||
indent.setPreferredSize(new Dimension(0, 0));
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.weightx = 1;
|
||||
expansion.add(indent, gbc);
|
||||
|
||||
// Name label
|
||||
var label = new JLabel();
|
||||
parent.parent.parent.app.localizer.add(label,
|
||||
x == 0 ? "cpu.jump_from" : "cpu.jump_to" );
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
expansion.add(label, gbc);
|
||||
controls.add(label);
|
||||
|
||||
// Value text box
|
||||
var txt = new JTextField();
|
||||
txt.addActionListener(e->parent.requestFocus());
|
||||
txt.addFocusListener(Util.onFocus(null,
|
||||
e->txt.setText((String) txt.getClientProperty("text"))));
|
||||
txt.putClientProperty("index",
|
||||
x == 0 ? Vue.JUMP_FROM : Vue.JUMP_TO);
|
||||
txt.setBorder(null);
|
||||
txt.setOpaque(false);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 4, 0, 0);
|
||||
expansion.add(txt, gbc);
|
||||
controls.add(txt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Expansion controls for PSW
|
||||
private void initPSW() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
addCheckBox("Z" , 0, false, false);
|
||||
addCheckBox("FRO", 9, false, true );
|
||||
addCheckBox("S" , 1, false, false);
|
||||
addCheckBox("FIV", 8, false, true );
|
||||
addCheckBox("OV" , 2, false, false);
|
||||
addCheckBox("FZD", 7, false, true );
|
||||
addCheckBox("CY" , 3, false, false);
|
||||
addCheckBox("FOV", 6, false, true );
|
||||
addCheckBox("EP" , 14, false, false);
|
||||
addCheckBox("FUD", 5, false, true );
|
||||
addCheckBox("NP" , 15, false, false);
|
||||
addCheckBox("FPR", 4, false, true );
|
||||
addCheckBox("AE" , 13, false, true );
|
||||
addCheckBox("ID" , 12, false, false);
|
||||
addTextBox ("I" , 16, 4, false, false);
|
||||
}
|
||||
|
||||
// Expansion controls for PIR
|
||||
private void initPIR() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
addTextBox("PT", 0, 16, true, true);
|
||||
}
|
||||
|
||||
// Expansion controls for TKCW
|
||||
private void initTKCW() {
|
||||
expansion = new UPanel(new GridBagLayout());
|
||||
addCheckBox("OTM", 8, true, false);
|
||||
addCheckBox("FVT", 5, true, true );
|
||||
addCheckBox("FIT", 7, true, false);
|
||||
addCheckBox("FUT", 4, true, true );
|
||||
addCheckBox("FZT", 6, true, false);
|
||||
addCheckBox("FPT", 3, true, true );
|
||||
addCheckBox("RDI", 2, true, false);
|
||||
addTextBox ("RD", 0, 2, true, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Apply configuration settings
|
||||
void configure() {
|
||||
String name = null;
|
||||
|
||||
// System register
|
||||
if (type != PROGRAM) {
|
||||
name = this.name;
|
||||
if (!Disassembler.systemCaps)
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
|
||||
// Program register
|
||||
else {
|
||||
if (Disassembler.programNames)
|
||||
name = this.name;
|
||||
if (name == null)
|
||||
name = "r" + index;
|
||||
if (Disassembler.programCaps)
|
||||
name = name.toUpperCase();
|
||||
}
|
||||
|
||||
// Name label
|
||||
lblName.setText(name);
|
||||
|
||||
// Expand button
|
||||
var size = btnExpand.getPreferredSize();
|
||||
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 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(fntMono);
|
||||
int digits = type == Vue.PC ? 8 :
|
||||
(Integer) ctrl.getClientProperty("digits");
|
||||
size = ctrl.getPreferredSize();
|
||||
size.width = digits * hexDigitWidth + 4;
|
||||
ctrl.setPreferredSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh controls
|
||||
void refresh() {
|
||||
var vue = parent.parent.parent.vue;
|
||||
|
||||
// Value text box
|
||||
value = vue.getRegister(index, type != PROGRAM);
|
||||
txtValue.setText(
|
||||
type != PROGRAM || mode == HEX ?
|
||||
String.format("%08X", value) :
|
||||
mode == SIGNED ? Integer.toString(value) :
|
||||
mode == UNSIGNED ? Long.toString(value & 0xFFFFFFFFL) :
|
||||
Float.toString(Float.intBitsToFloat(value))
|
||||
);
|
||||
|
||||
// Expansion controls
|
||||
for (var control : controls) {
|
||||
|
||||
// Check box
|
||||
if (control instanceof JCheckBox) {
|
||||
var ctrl = (JCheckBox) control;
|
||||
int bit = (Integer) ctrl.getClientProperty("bit");
|
||||
ctrl.setSelected((value & 1 << bit) != 0);
|
||||
}
|
||||
|
||||
// Text box
|
||||
if (control instanceof JTextField) {
|
||||
var ctrl = (JTextField) control;
|
||||
int digits; // Maximum digits that can be displayed
|
||||
boolean hex; // The value is hexadecimal
|
||||
int val; // The value to be displayed
|
||||
|
||||
// Jump history
|
||||
if (type == Vue.PC) {
|
||||
digits = 8;
|
||||
hex = true;
|
||||
val = vue.getRegister((Integer)
|
||||
ctrl.getClientProperty("index"), true);
|
||||
}
|
||||
|
||||
// All other values
|
||||
else {
|
||||
int bit = (Integer) ctrl.getClientProperty("bit" );
|
||||
digits = (Integer) ctrl.getClientProperty("digits");
|
||||
hex = (Boolean) ctrl.getClientProperty("hex");
|
||||
int width = (Integer) ctrl.getClientProperty("width");
|
||||
val = value >> bit & (1 << width) - 1;
|
||||
}
|
||||
|
||||
// Update the text
|
||||
String text = !hex ? Integer.toString(val) : String.format(
|
||||
"%0" + digits + (Disassembler.hexCaps ? "X" : "x"), val);
|
||||
ctrl.putClientProperty("text", text);
|
||||
ctrl.setText(text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Specify whether the expansion area is expanded
|
||||
void setExpanded(boolean expanded) {
|
||||
|
||||
// Error checking
|
||||
if (!expandable)
|
||||
return;
|
||||
|
||||
// Update controls
|
||||
this.expanded = expanded;
|
||||
btnExpand.setText(expanded ? "-" : "+");
|
||||
if (type != Vue.PC)
|
||||
indent.setVisible(expanded);
|
||||
expansion.setVisible(expanded);
|
||||
parent.revalidate();
|
||||
}
|
||||
|
||||
// Change the display mode of a program register
|
||||
void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
txtValue.setFont(mode!=HEX ? null : parent.parent.parent.app.fntMono);
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a check box to the expansion area
|
||||
private void addCheckBox(String name, int bit, boolean readOnly,
|
||||
boolean last) {
|
||||
int mask = 1 << bit;
|
||||
|
||||
// Configure control
|
||||
var ctrl = new JCheckBox(name);
|
||||
ctrl.putClientProperty("bit", bit);
|
||||
ctrl.setBorder(null);
|
||||
ctrl.setEnabled(!readOnly);
|
||||
ctrl.setFocusable(false);
|
||||
ctrl.setOpaque(false);
|
||||
controls.add(ctrl);
|
||||
|
||||
// Event handler
|
||||
ctrl.addItemListener(e->setValue(
|
||||
e.getStateChange() == ItemEvent.SELECTED ?
|
||||
value | mask : value & ~mask
|
||||
));
|
||||
|
||||
// Configure expansion area
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.gridwidth = last ? GridBagConstraints.REMAINDER : 2;
|
||||
gbc.insets = new Insets(0, 4, 0, 4);
|
||||
expansion.add(ctrl, gbc);
|
||||
}
|
||||
|
||||
// Add a radio button to the expansion area
|
||||
private JRadioButton addRadioButton(String key, int mode) {
|
||||
|
||||
// Configure control
|
||||
var ctrl = new JRadioButton();
|
||||
parent.parent.parent.app.localizer.add(ctrl, key);
|
||||
ctrl.setBorder(null);
|
||||
ctrl.setFocusable(false);
|
||||
ctrl.setOpaque(false);
|
||||
ctrl.setSelected(mode == HEX);
|
||||
controls.add(ctrl);
|
||||
|
||||
// Event handler
|
||||
ctrl.addItemListener(e->{
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) setMode(mode); });
|
||||
|
||||
// Configure expansion area
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 4, 0, 0);
|
||||
expansion.add(ctrl, gbc);
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
// Add a text box to the expansion area
|
||||
private void addTextBox(String name, int bit, int width, boolean readOnly,
|
||||
boolean hex) {
|
||||
int mask = (1 << width) - 1;
|
||||
|
||||
// Configure control
|
||||
var ctrl = new JTextField();
|
||||
ctrl.putClientProperty("bit", bit);
|
||||
ctrl.putClientProperty("digits",
|
||||
Integer.toString(mask, hex ? 16 : 10).length());
|
||||
ctrl.putClientProperty("hex", hex);
|
||||
ctrl.putClientProperty("width", width);
|
||||
ctrl.setBorder(null);
|
||||
ctrl.setEnabled(!readOnly);
|
||||
ctrl.setOpaque(false);
|
||||
controls.add(ctrl);
|
||||
|
||||
// Event handlers
|
||||
ctrl.addActionListener(e->parent.requestFocus());
|
||||
ctrl.addFocusListener(Util.onFocus(null, e->{
|
||||
int val = value >> bit & mask;
|
||||
try { val = Integer.parseInt(ctrl.getText(), hex ? 16 : 10); }
|
||||
catch (Exception x) { }
|
||||
setValue(value & ~(mask << bit) | (val & mask) << bit);
|
||||
}));
|
||||
|
||||
// Configure expansion area
|
||||
var label = new JLabel(name);
|
||||
label.setEnabled(!readOnly);
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.insets = new Insets(0, 4, 0, 4);
|
||||
expansion.add(label, gbc);
|
||||
gbc = new GridBagConstraints();
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.insets = new Insets(0, 4, 0, 4);
|
||||
expansion.add(ctrl , gbc);
|
||||
}
|
||||
|
||||
// Update the register value
|
||||
private void setValue(int value) {
|
||||
parent.parent.parent.vue.setRegister(index, type != PROGRAM, value);
|
||||
refresh();
|
||||
if (index == Vue.PSW && type == Vue.PSW)
|
||||
parent.registers.get(Vue.PC).refresh();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package app;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Project imports
|
||||
import util.*;
|
||||
import vue.*;
|
||||
|
||||
// List of CPU registers
|
||||
class RegisterList extends JScrollPane {
|
||||
|
||||
// Package fields
|
||||
CPUWindow parent; // Containing CPU window
|
||||
HashMap<Integer, Register> registers; // Register items
|
||||
|
||||
// Private fields
|
||||
private boolean shown; // Component has been shown
|
||||
|
||||
// UI components
|
||||
private UPanel client; // Client area
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
RegisterList(CPUWindow parent, boolean system) {
|
||||
super(VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
// Configure instance fields
|
||||
this.parent = parent;
|
||||
registers = new HashMap<Integer, Register>();
|
||||
shown = true;
|
||||
|
||||
// Configure client area
|
||||
client = new UPanel(new GridBagLayout()) {
|
||||
public Dimension getPreferredSize() {
|
||||
var ret = super.getPreferredSize();
|
||||
if (!shown) ret.height = system ? getInitialHeight() : 0;
|
||||
return ret;
|
||||
}
|
||||
public void paintComponent(Graphics g) {
|
||||
shown = true;
|
||||
super.paintComponent(g);
|
||||
}
|
||||
};
|
||||
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
|
||||
client.setBackground(SystemColor.window);
|
||||
client.setFocusable(true);
|
||||
|
||||
// Initialize system registers
|
||||
if (system) {
|
||||
new Register(this, "PC" , Vue.PC , Vue.PC );
|
||||
new Register(this, "PSW" , Vue.PSW , Vue.PSW );
|
||||
new Register(this, "EIPC" , Vue.EIPC , Register.PLAIN);
|
||||
new Register(this, "EIPSW", Vue.EIPSW, Vue.PSW );
|
||||
new Register(this, "FEPC" , Vue.FEPC , Register.PLAIN);
|
||||
new Register(this, "FEPSW", Vue.FEPSW, Vue.PSW );
|
||||
new Register(this, "ECR" , Vue.ECR , Vue.ECR );
|
||||
new Register(this, "ADTRE", Vue.ADTRE, Register.PLAIN);
|
||||
new Register(this, "CHCW" , Vue.CHCW , Vue.CHCW );
|
||||
new Register(this, "PIR" , Vue.PIR , Vue.PIR );
|
||||
new Register(this, "TKCW" , Vue.TKCW , Vue.TKCW );
|
||||
new Register(this, "29" , 29, Register.PLAIN);
|
||||
new Register(this, "30" , 30, Register.PLAIN);
|
||||
new Register(this, "31" , 31, Register.PLAIN);
|
||||
}
|
||||
|
||||
// Initialize program registers
|
||||
else for (int x = 0; x < 32; x++) {
|
||||
String name = null;
|
||||
switch (x) {
|
||||
case Vue.GP: name = "gp"; break;
|
||||
case Vue.HP: name = "hp"; break;
|
||||
case Vue.LP: name = "lp"; break;
|
||||
case Vue.SP: name = "sp"; break;
|
||||
case Vue.TP: name = "tp"; break;
|
||||
}
|
||||
new Register(this, name, x, Register.PROGRAM);
|
||||
}
|
||||
|
||||
// List terminator
|
||||
var spacer = new UPanel();
|
||||
spacer.setOpaque(false);
|
||||
spacer.setPreferredSize(new Dimension(0, 0));
|
||||
var gbc = new GridBagConstraints();
|
||||
gbc.gridheight = GridBagConstraints.REMAINDER;
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.weighty = 1;
|
||||
client.add(spacer, gbc);
|
||||
|
||||
// Configure component
|
||||
shown = false;
|
||||
setViewportView(client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Draw the component
|
||||
public void paintComponent(Graphics g) {
|
||||
if (!shown) {
|
||||
shown = true;
|
||||
revalidate();
|
||||
}
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
// Transfer focus to the client area
|
||||
public void requestFocus() {
|
||||
client.requestFocus();
|
||||
}
|
||||
|
||||
// Recalculate layout
|
||||
public void revalidate() {
|
||||
if (client != null)
|
||||
client.revalidate();
|
||||
super.revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a register component to the client area
|
||||
void add(GridBagConstraints gbc, JComponent comp) {
|
||||
client.add(comp, gbc);
|
||||
}
|
||||
|
||||
// Associate a register with its index
|
||||
void add(int index, Register register) {
|
||||
registers.put(index, register);
|
||||
}
|
||||
|
||||
// Apply configuration settings
|
||||
void configure2() {
|
||||
|
||||
// Configure registers
|
||||
for (var reg : registers.values())
|
||||
reg.configure();
|
||||
|
||||
// Configure component
|
||||
getVerticalScrollBar().setUnitIncrement(
|
||||
parent.parent.app.fntMono.metrics.getHeight());
|
||||
}
|
||||
|
||||
// Update the display
|
||||
void refresh() {
|
||||
for (var reg : registers.values())
|
||||
reg.refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Determine the initial height upon first show
|
||||
private int getInitialHeight() {
|
||||
int height = 0;
|
||||
var layout = (GridBagLayout) client.getLayout();
|
||||
int ret = 0;
|
||||
int row = 0;
|
||||
|
||||
// Process controls until the target row is found
|
||||
for (var control : client.getComponents()) {
|
||||
var ctrl = (JComponent) control;
|
||||
|
||||
// Track the tallest control on the row
|
||||
if (ctrl.isVisible())
|
||||
height = Math.max(height, ctrl.getPreferredSize().height);
|
||||
|
||||
// This is not the last control on the row
|
||||
if (layout.getConstraints(control).gridwidth !=
|
||||
GridBagConstraints.REMAINDER)
|
||||
continue;
|
||||
|
||||
// Advance to the next row
|
||||
ret += height;
|
||||
height = 0;
|
||||
row++;
|
||||
|
||||
// The target row has been reached
|
||||
if (row == 4)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,585 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
import javax.sound.sampled.*;
|
||||
|
||||
// Background timer using either a realtime clock or audio for timing
|
||||
public class FrameTimer {
|
||||
|
||||
// Instance fields
|
||||
private Callback callback; // Event handler
|
||||
private double rate; // Frame rate in seconds
|
||||
private int source; // Timing source
|
||||
private int state; // Operation state
|
||||
|
||||
// Audio fields
|
||||
private int block; // Current byte buffer index
|
||||
private byte[][] blocks; // Reusable byte buffers
|
||||
private double buffer; // Buffer length in seconds
|
||||
private int channels; // Number channels
|
||||
private double delay; // Delay in seconds
|
||||
private int frames; // Audio frames per timer frame
|
||||
private SourceDataLine line; // Output line
|
||||
private int sampleRate; // Rate in samples per second
|
||||
private boolean written; // Samples have been written this frame
|
||||
private LinkedBlockingQueue<byte[]> queue; // Sample queue
|
||||
|
||||
// Threading fields
|
||||
private Thread audio; // Background audio processing
|
||||
private CyclicBarrier cycAudio; // Synchronization with audio thread
|
||||
private CyclicBarrier cycTimer; // Synchronization with timer thread
|
||||
private ReentrantLock lock; // Allows callback to control the timer
|
||||
private Thread timer; // Background timer processing
|
||||
|
||||
// Type for timer event handler
|
||||
public interface Callback {
|
||||
void onFrame(FrameTimer source);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Sources
|
||||
public static final int CLOCK = 0;
|
||||
public static final int AUDIO = 1;
|
||||
|
||||
// States
|
||||
public static final int STOPPED = 0;
|
||||
public static final int RUNNING = 1;
|
||||
public static final int PAUSED = 2;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public FrameTimer() {
|
||||
buffer = 0.05;
|
||||
cycAudio = new CyclicBarrier(2);
|
||||
cycTimer = new CyclicBarrier(2);
|
||||
channels = 2;
|
||||
delay = 0.1;
|
||||
lock = new ReentrantLock();
|
||||
queue = new LinkedBlockingQueue<byte[]>();
|
||||
rate = 1;
|
||||
sampleRate = 44100;
|
||||
state = STOPPED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Retrieve the audio buffer size
|
||||
public synchronized double getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Retrieve the number of audio channels
|
||||
public synchronized int getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
// Retrieve the audio delay
|
||||
public synchronized double getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
// Calculate the number of audio frames per timer frame
|
||||
public synchronized int getFrames() {
|
||||
return audioFrames(rate);
|
||||
}
|
||||
|
||||
// Retrieve the frame rate
|
||||
public synchronized double getRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
// Retrieve the audio sampling rate
|
||||
public synchronized int getSampleRate() {
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
// Retrieve the timing source
|
||||
public synchronized int getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
// Retrieve the operation state
|
||||
public synchronized int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Pause the timer
|
||||
public synchronized boolean pause() {
|
||||
boolean isTimer = Thread.currentThread() == timer;
|
||||
|
||||
// The timer thread already owns the lock
|
||||
if (isTimer && lock.isHeldByCurrentThread()) {
|
||||
if (state == RUNNING)
|
||||
state = PAUSED;
|
||||
return state == PAUSED;
|
||||
}
|
||||
|
||||
// Obtain the lock
|
||||
lock.lock();
|
||||
|
||||
// Invalid state
|
||||
if (state != RUNNING) {
|
||||
lock.unlock();
|
||||
return state == PAUSED;
|
||||
}
|
||||
|
||||
// Configure state
|
||||
state = PAUSED;
|
||||
|
||||
// Pause timer thread
|
||||
if (!isTimer) try {
|
||||
timer.interrupt();
|
||||
lock.unlock();
|
||||
cycTimer.await();
|
||||
} catch (Exception e) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resume running the paused timer
|
||||
public synchronized boolean resume() {
|
||||
boolean isTimer = Thread.currentThread() == timer;
|
||||
|
||||
// The timer thread already owns the lock
|
||||
if (isTimer && lock.isHeldByCurrentThread()) {
|
||||
if (state == PAUSED)
|
||||
state = RUNNING;
|
||||
return state == RUNNING;
|
||||
}
|
||||
|
||||
// Obtain the lock
|
||||
lock.lock();
|
||||
|
||||
// Invalid state
|
||||
if (state != PAUSED) {
|
||||
lock.unlock();
|
||||
return state == RUNNING;
|
||||
}
|
||||
|
||||
// Configure state
|
||||
state = RUNNING;
|
||||
|
||||
// Resume timer thread
|
||||
if (!isTimer) try {
|
||||
lock.unlock();
|
||||
cycTimer.await();
|
||||
} catch (Exception e) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Specify the audio buffer length
|
||||
public synchronized double setBuffer(double buffer) {
|
||||
return this.buffer = state == STOPPED && buffer > 0 ?
|
||||
buffer : this.buffer;
|
||||
}
|
||||
|
||||
// Specify the callback handler
|
||||
public synchronized boolean setCallback(Callback callback) {
|
||||
if (state != STOPPED)
|
||||
return false;
|
||||
this.callback = callback;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Specify the number of audio channels
|
||||
public synchronized int setChannels(int channels) {
|
||||
return this.channels = state == STOPPED && (channels - 1 & ~1) != 0 ?
|
||||
channels : this.channels;
|
||||
}
|
||||
|
||||
// Specify the audio delay
|
||||
public synchronized double setDelay(double delay) {
|
||||
return this.delay = state == STOPPED && delay > 0 ? delay : this.delay;
|
||||
}
|
||||
|
||||
// Specify the frame rate
|
||||
public synchronized double setRate(double rate) {
|
||||
return this.rate = state == STOPPED && rate > 0 ? rate : this.rate;
|
||||
}
|
||||
|
||||
// Specify the audio sampling rate
|
||||
public synchronized int setSampleRate(int sampleRate) {
|
||||
return this.sampleRate = state == STOPPED && sampleRate > 0 ?
|
||||
sampleRate : this.sampleRate;
|
||||
}
|
||||
|
||||
// Begin timer operations
|
||||
public synchronized boolean start(int source) {
|
||||
|
||||
// Error checking
|
||||
if (source != CLOCK && source != AUDIO || state != STOPPED ||
|
||||
callback == null || Thread.currentThread() == timer ||
|
||||
source == AUDIO && buffer > delay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Audio processing
|
||||
if (source == AUDIO) try {
|
||||
|
||||
// Open the output line
|
||||
AudioFormat fmt =
|
||||
new AudioFormat(sampleRate, 16, channels, true, false);
|
||||
line = AudioSystem.getSourceDataLine(fmt);
|
||||
line.open(fmt);
|
||||
|
||||
// Configure audio fields
|
||||
frames = audioFrames(rate);
|
||||
block = 0;
|
||||
blocks = new byte[(int) Math.ceil(delay / rate)]
|
||||
[frames * channels * 2];
|
||||
}
|
||||
|
||||
// Could not open the audio line
|
||||
catch (Exception e) {
|
||||
line = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure state
|
||||
this.source = source;
|
||||
state = RUNNING;
|
||||
|
||||
// Spawn and start timer thread
|
||||
cycTimer.reset();
|
||||
timer = new Thread(()->timer());
|
||||
timer.setDaemon(true);
|
||||
timer.start();
|
||||
|
||||
// Spawn and start audio thread
|
||||
if (source == AUDIO) {
|
||||
cycAudio.reset();
|
||||
audio = new Thread(()->audio());
|
||||
audio.setDaemon(true);
|
||||
audio.start();
|
||||
}
|
||||
|
||||
// Synchronize with timer thread
|
||||
try { cycTimer.await(); } catch (Exception e) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// End timer operations
|
||||
public synchronized boolean stop() {
|
||||
boolean isTimer = Thread.currentThread() == timer;
|
||||
|
||||
// The timer thread already owns the lock
|
||||
if (isTimer && lock.isHeldByCurrentThread())
|
||||
return true;
|
||||
|
||||
// Obtain the lock
|
||||
lock.lock();
|
||||
|
||||
// Invalid state
|
||||
if (state == STOPPED) {
|
||||
lock.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Configure state
|
||||
boolean paused = state == PAUSED;
|
||||
state = STOPPED;
|
||||
|
||||
// Stop timer thread
|
||||
if (!isTimer) try {
|
||||
if (!paused) timer.interrupt();
|
||||
lock.unlock();
|
||||
if ( paused) cycTimer.await();
|
||||
cycTimer.await();
|
||||
} catch (Exception e) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write audio samples as bytes to output
|
||||
public boolean write(byte[] samples, int offset) {
|
||||
int size = frames * channels * 2;
|
||||
|
||||
// Error checking
|
||||
if (Thread.currentThread() != timer || state == STOPPED || written ||
|
||||
samples == null || offset < 0 || samples.length < offset + size)
|
||||
return false;
|
||||
|
||||
// Add a new sample block to the queue
|
||||
block = (block + 1) % blocks.length;
|
||||
byte[] block = blocks[this.block];
|
||||
System.arraycopy(samples, offset, block, 0, size);
|
||||
queue.offer(block);
|
||||
return written = true;
|
||||
}
|
||||
|
||||
// Write audio samples as shorts to output
|
||||
public boolean write(short[] samples, int offset) {
|
||||
int size = frames * channels;
|
||||
|
||||
// Error checking
|
||||
if (Thread.currentThread() != timer || state == STOPPED || written ||
|
||||
samples == null || offset < 0 || samples.length < offset + size)
|
||||
return false;
|
||||
|
||||
// Encode the samples as bytes
|
||||
block = (block + 1) % blocks.length;
|
||||
byte[] block = blocks[this.block];
|
||||
for (int src = 0, dest = 0; src < size; src++) {
|
||||
short sample = samples[offset + src];
|
||||
block[dest++] = (byte) sample;
|
||||
block[dest++] = (byte) (sample >> 8);
|
||||
}
|
||||
queue.offer(block);
|
||||
return written = true;
|
||||
}
|
||||
|
||||
// Write audio samples as floats to output (range -1 to +1)
|
||||
public boolean write(float[] samples, int offset) {
|
||||
int size = frames * channels;
|
||||
|
||||
// Error checking
|
||||
if (Thread.currentThread() != timer || state == STOPPED || written ||
|
||||
samples == null || offset < 0 || samples.length < offset + size)
|
||||
return false;
|
||||
|
||||
// Encode the samples as bytes
|
||||
block = (block + 1) % blocks.length;
|
||||
byte[] block = blocks[this.block];
|
||||
for (int src = 0, dest = 0; src < size; src++) {
|
||||
short sample = (short) Math.round(32767 *
|
||||
Math.min(1, Math.max(-1, samples[offset + src])) );
|
||||
block[dest++] = (byte) sample;
|
||||
block[dest++] = (byte) (sample >> 8);
|
||||
}
|
||||
queue.offer(block);
|
||||
return written = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methds //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculate the number of audio sampling frames in some number of seconds
|
||||
private int audioFrames(double seconds) {
|
||||
return Math.max(1, (int) Math.round(seconds * sampleRate));
|
||||
}
|
||||
|
||||
// Handler for pause operations -- invoked by timer thread
|
||||
private long onPause(long reference, boolean isCallback) {
|
||||
|
||||
// Track the current time
|
||||
if (source == CLOCK)
|
||||
reference = System.nanoTime() - reference;
|
||||
|
||||
// Pause audio thread
|
||||
else try {
|
||||
audio.interrupt();
|
||||
line.stop();
|
||||
cycAudio.await();
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Synchronization
|
||||
try {
|
||||
if (!isCallback)
|
||||
cycTimer.await(); // Synchronize with invoking thread
|
||||
else lock.unlock();
|
||||
cycTimer.await(); // Wait for resume() or stop()
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Calculate a new reference time
|
||||
if (source == CLOCK)
|
||||
reference = System.nanoTime() - reference;
|
||||
|
||||
// Unpause audio thread
|
||||
else try { cycAudio.await(); } catch (Exception e) { }
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
// Handler for stop operations -- invoked by timer thread
|
||||
private int onStop(boolean paused, boolean isCallback) {
|
||||
|
||||
// Stop the audio thread
|
||||
if (source == AUDIO) try {
|
||||
audio.interrupt();
|
||||
line.stop();
|
||||
cycAudio.await();
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Synchronization
|
||||
try {
|
||||
if (!isCallback || paused)
|
||||
cycTimer.await(); // Synchronize with invoking thread
|
||||
else lock.unlock();
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Cleanup
|
||||
blocks = null;
|
||||
timer = null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Thread Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Audio thread entry point
|
||||
private void audio() {
|
||||
|
||||
// Synchronize with timer thread
|
||||
try { cycAudio.await(); } catch (Exception e) { }
|
||||
|
||||
// Initialize working variables
|
||||
byte[] block = new byte[audioFrames(delay ) * channels * 2];
|
||||
byte[] buffer = new byte[audioFrames(this.buffer) * channels * 2];
|
||||
int blockPos = 0;
|
||||
int bufferPos = -buffer.length; // Less than 0 means not full
|
||||
|
||||
// Audio processing
|
||||
line.start();
|
||||
for (;;) {
|
||||
|
||||
// Fill the buffer with samples, blocking until full
|
||||
while (bufferPos < 0) {
|
||||
|
||||
// Load bytes from the current block
|
||||
if (blockPos < block.length) {
|
||||
int size = Math.min(block.length - blockPos, -bufferPos);
|
||||
System.arraycopy(block, blockPos, buffer,
|
||||
bufferPos + buffer.length, size);
|
||||
blockPos += size;
|
||||
bufferPos += size;
|
||||
}
|
||||
|
||||
// Fetch a new sample block, blocking until one is available
|
||||
else try {
|
||||
block = queue.take();
|
||||
blockPos = 0;
|
||||
}
|
||||
|
||||
// The timer state has changed
|
||||
catch (Exception e) {
|
||||
audio.interrupt(); // take() clears interrupt status
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Send samples to the output, blocking until sent
|
||||
if (!audio.isInterrupted()) {
|
||||
bufferPos +=
|
||||
line.write(buffer, bufferPos, buffer.length - bufferPos);
|
||||
if (bufferPos == buffer.length)
|
||||
bufferPos = -buffer.length;
|
||||
}
|
||||
|
||||
// Check for changes to the timer state
|
||||
if (state == RUNNING)
|
||||
continue;
|
||||
Thread.interrupted();
|
||||
|
||||
// Timer has paused
|
||||
if (state == PAUSED) try {
|
||||
cycAudio.await(); // Synchronize with timer thread
|
||||
cycAudio.await(); // Wait for resume() or stop()
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Timer has stopped
|
||||
if (state == STOPPED) {
|
||||
try { line.close(); } catch (Exception e) { }
|
||||
try { cycAudio.await(); } catch (Exception e) { }
|
||||
audio = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume playback
|
||||
line.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Timer thread entry point
|
||||
private int timer() {
|
||||
|
||||
// Synchronize with other threads
|
||||
try {
|
||||
if (source == AUDIO)
|
||||
cycAudio.await(); // Synchronize with audio thread
|
||||
cycTimer.await(); // Synchronize with parent thread
|
||||
} catch (Exception e) { }
|
||||
|
||||
// Initialize working variables
|
||||
byte[] empty = new byte[frames * channels * 2];
|
||||
long reference = source == AUDIO ? 0 : System.nanoTime();
|
||||
long target = source == AUDIO ? audioFrames(rate) :
|
||||
Math.max(1, (long) Math.round(rate * 1000000000L));
|
||||
|
||||
// Timer processing
|
||||
for (boolean first = true;; first = false) {
|
||||
long current = source == CLOCK ? System.nanoTime() :
|
||||
line.getLongFramePosition();
|
||||
long remain = target - current + reference;
|
||||
|
||||
// Processing on all frames but the first
|
||||
if (!first) {
|
||||
|
||||
// Wait until the next frame interval
|
||||
if (remain > 0) try {
|
||||
if (source == AUDIO)
|
||||
remain = remain * 1000000000L / sampleRate;
|
||||
remain = Math.max(1000000, remain);
|
||||
Thread.sleep(remain / 1000000, (int) (remain % 1000000));
|
||||
continue;
|
||||
} catch (Exception e) { timer.interrupt(); }
|
||||
|
||||
// Another thread configured the timer state
|
||||
if (Thread.interrupted()) {
|
||||
if (state == PAUSED)
|
||||
reference = onPause(reference, false);
|
||||
if (state == STOPPED)
|
||||
return onStop(false, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Invoke the event callback
|
||||
written = false;
|
||||
callback.onFrame(this);
|
||||
if (source == AUDIO && !written)
|
||||
queue.offer(empty);
|
||||
|
||||
// The callback configured the timer state
|
||||
if (lock.isHeldByCurrentThread()) {
|
||||
boolean paused = state == PAUSED;
|
||||
if (state == PAUSED)
|
||||
reference = onPause(reference, true);
|
||||
if (state == STOPPED)
|
||||
return onStop(paused, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track processed frame time
|
||||
reference = current + remain;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.io.*;
|
||||
|
||||
// Little-endian implementation of DataInputStream
|
||||
public class LEDataInputStream extends FilterInputStream {
|
||||
|
||||
// Instance fields
|
||||
private DataInputStream data; // Data-decoding stream
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public LEDataInputStream(InputStream stream) {
|
||||
super(stream = stream instanceof LEDataInputStream ?
|
||||
((LEDataInputStream) stream).in : stream);
|
||||
data = stream instanceof DataInputStream ?
|
||||
(DataInputStream) stream : new DataInputStream(stream);
|
||||
}
|
||||
|
||||
// Byte array constructor
|
||||
public LEDataInputStream(byte[] data) {
|
||||
this(new ByteArrayInputStream(data));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Reads a boolean from the underlying input stream
|
||||
public boolean readBoolean() throws IOException {
|
||||
return data.readByte() != 0;
|
||||
}
|
||||
|
||||
// Reads an array of booleans from the underlying input stream
|
||||
public boolean[] readBooleans(int count) throws IOException {
|
||||
return readBooleans(new boolean[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of booleans from the underlying input stream
|
||||
public boolean[] readBooleans(boolean[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readBoolean();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a byte from the underlying input stream
|
||||
public byte readByte() throws IOException {
|
||||
return data.readByte();
|
||||
}
|
||||
|
||||
// Reads an array of bytes from the underlying input stream
|
||||
public byte[] readBytes(int count) throws IOException {
|
||||
return readBytes(new byte[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of bytes from the underlying input stream
|
||||
public byte[] readBytes(byte[] v, int offset, int length)
|
||||
throws IOException {
|
||||
data.read(v, offset, length);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a character from the underlying input stream
|
||||
public char readChar() throws IOException {
|
||||
return (char) Short.reverseBytes(data.readShort());
|
||||
}
|
||||
|
||||
// Reads an array of characters from the underlying input stream
|
||||
public char[] readChars(int count) throws IOException {
|
||||
return readChars(new char[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of characters from the underlying input stream
|
||||
public char[] readChars(char[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readChar();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a double from the underlying input stream
|
||||
public double readDouble() throws IOException {
|
||||
return readDouble(false);
|
||||
}
|
||||
|
||||
// Reads a double from the underlying input stream
|
||||
public double readDouble(boolean finite) throws IOException {
|
||||
long bits = readLong();
|
||||
double ret = Double.longBitsToDouble(bits);
|
||||
if (finite && !Double.isFinite(ret)) throw new RuntimeException(
|
||||
String.format("Non-finite double value 0x%016X", bits));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reads an array of doubles from the underlying input stream
|
||||
public double[] readDoubles(int count) throws IOException {
|
||||
return readDoubles(new double[count], 0, count, false);
|
||||
}
|
||||
|
||||
// Reads an array of doubles from the underlying input stream
|
||||
public double[] readDoubles(int count, boolean finite) throws IOException {
|
||||
return readDoubles(new double[count], 0, count, finite);
|
||||
}
|
||||
|
||||
// Reads an array of doubles from the underlying input stream
|
||||
public double[] readDoubles(double[] v, int offset, int length)
|
||||
throws IOException {
|
||||
return readDoubles(v, offset, length, false);
|
||||
}
|
||||
|
||||
// Reads an array of doubles from the underlying input stream
|
||||
public double[] readDoubles(double[] v, int offset, int length,
|
||||
boolean finite) throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readDouble(finite);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a float from the underlying input stream
|
||||
public float readFloat() throws IOException {
|
||||
return readFloat(false);
|
||||
}
|
||||
|
||||
// Reads a float from the underlying input stream
|
||||
public float readFloat(boolean finite) throws IOException {
|
||||
int bits = readInt();
|
||||
float ret = Float.intBitsToFloat(bits);
|
||||
if (finite && !Float.isFinite(ret)) throw new RuntimeException(
|
||||
String.format("Non-finite float value 0x%08X", bits));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reads an array of floats from the underlying input stream
|
||||
public float[] readFloats(int count) throws IOException {
|
||||
return readFloats(new float[count], 0, count, false);
|
||||
}
|
||||
|
||||
// Reads an array of floats from the underlying input stream
|
||||
public float[] readFloats(int count, boolean finite) throws IOException {
|
||||
return readFloats(new float[count], 0, count, finite);
|
||||
}
|
||||
|
||||
// Reads an array of floats from the underlying input stream
|
||||
public float[] readFloats(float[] v, int offset, int length)
|
||||
throws IOException {
|
||||
return readFloats(v, offset, length, false);
|
||||
}
|
||||
|
||||
// Reads an array of floats from the underlying input stream
|
||||
public float[] readFloats(float[] v, int offset, int length,
|
||||
boolean finite) throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readFloat(finite);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads an int from the underlying input stream
|
||||
public int readInt() throws IOException {
|
||||
return Integer.reverseBytes(data.readInt());
|
||||
}
|
||||
|
||||
// Reads an array of ints from the underlying input stream
|
||||
public int[] readInts(int count) throws IOException {
|
||||
return readInts(new int[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of ints from the underlying input stream
|
||||
public int[] readInts(int[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readInt();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a long from the underlying input stream
|
||||
public long readLong() throws IOException {
|
||||
return Long.reverseBytes(data.readLong());
|
||||
}
|
||||
|
||||
// Reads an array of longs from the underlying input stream
|
||||
public long[] readLongs(int count) throws IOException {
|
||||
return readLongs(new long[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of longs from the underlying input stream
|
||||
public long[] readLongs(long[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readLong();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a 24-bit integer from the underlying input stream
|
||||
public int readMiddle() throws IOException {
|
||||
int v = readShort() & 0xFFFF;
|
||||
return readByte() << 16 | v;
|
||||
}
|
||||
|
||||
// Reads an array of 24-bit integers from the underlying input stream
|
||||
public int[] readMiddles(int count) throws IOException {
|
||||
return readMiddles(new int[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of 24-bit integers from the underlying input stream
|
||||
public int[] readMiddles(int[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readMiddle();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads a short from the underlying input stream
|
||||
public short readShort() throws IOException {
|
||||
return Short.reverseBytes(data.readShort());
|
||||
}
|
||||
|
||||
// Reads an array of shorts from the underlying input stream
|
||||
public short[] readShorts(int count) throws IOException {
|
||||
return readShorts(new short[count], 0, count);
|
||||
}
|
||||
|
||||
// Reads an array of shorts from the underlying input stream
|
||||
public short[] readShorts(short[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
v[offset + x] = readShort();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Reads from the stream in a modified UTF-8 string
|
||||
public String readUTF() throws IOException {
|
||||
return data.readUTF();
|
||||
}
|
||||
|
||||
// Advances forward in the stream some number of bytes
|
||||
public int skipBytes(int n) throws IOException {
|
||||
return data.skipBytes(n);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.io.*;
|
||||
import java.nio.charset.*;
|
||||
|
||||
// Little-endian implementation of DataOutputStream
|
||||
public class LEDataOutputStream extends FilterOutputStream {
|
||||
|
||||
// Instance fields
|
||||
private DataOutputStream data; // Data-encoding stream
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public LEDataOutputStream() {
|
||||
this(new ByteArrayOutputStream());
|
||||
}
|
||||
|
||||
// Stream constructor
|
||||
public LEDataOutputStream(OutputStream stream) {
|
||||
super(stream = stream instanceof LEDataOutputStream ?
|
||||
((LEDataOutputStream) stream).out : stream);
|
||||
data = stream instanceof DataOutputStream ?
|
||||
(DataOutputStream) stream : new DataOutputStream(stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Returns the current value of the underlying buffer
|
||||
public int size() {
|
||||
return out instanceof ByteArrayOutputStream ?
|
||||
((ByteArrayOutputStream) out).size() : -1;
|
||||
}
|
||||
|
||||
// Produce a byte array containing this stream's data
|
||||
public byte[] toByteArray() throws IOException {
|
||||
return out instanceof ByteArrayOutputStream ?
|
||||
((ByteArrayOutputStream) out).toByteArray() : null;
|
||||
}
|
||||
|
||||
// Writes a boolean to the underlying output stream as a 1-byte value
|
||||
public void writeBoolean(boolean v) throws IOException {
|
||||
data.writeBoolean(v);
|
||||
}
|
||||
|
||||
// Writes an array of booleans to the underlying output stream
|
||||
public void writeBooleans(boolean[] v) throws IOException {
|
||||
writeBooleans(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of booleans to the underlying output stream
|
||||
public void writeBooleans(boolean[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeBoolean(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes out a byte to the underlying output stream as a 1-byte value
|
||||
public void writeByte(int v) throws IOException {
|
||||
data.writeByte(v);
|
||||
}
|
||||
|
||||
// Writes out the string to the underlying output stream
|
||||
public void writeBytes(String s) throws IOException {
|
||||
data.writeBytes(s);
|
||||
}
|
||||
|
||||
// Writes an array of bytes to the underlying output stream
|
||||
public void writeBytes(byte[] v) throws IOException {
|
||||
writeBytes(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of bytes to the underlying output stream
|
||||
public void writeBytes(byte[] v, int offset, int length)
|
||||
throws IOException {
|
||||
data.write(v, offset, length);
|
||||
}
|
||||
|
||||
// Writes a character to the underlying output stream as a 2-byte value
|
||||
public void writeChar(int v) throws IOException {
|
||||
data.writeChar(Short.reverseBytes((short) v));
|
||||
}
|
||||
|
||||
// Writes a string to the underlying output stream
|
||||
public void writeChars(String s) throws IOException {
|
||||
writeChars(s.toCharArray());
|
||||
}
|
||||
|
||||
// Writes an array of characters to the underlying output stream
|
||||
public void writeChars(char[] v) throws IOException {
|
||||
writeChars(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of characters to the underlying output stream
|
||||
public void writeChars(char[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeChar(v[offset + x]);
|
||||
}
|
||||
|
||||
// Converts the double argument to a long using doubleToLongBits
|
||||
public void writeDouble(double v) throws IOException {
|
||||
data.writeLong(Long.reverseBytes(Double.doubleToLongBits(v)));
|
||||
}
|
||||
|
||||
// Writes an array of doubles to the underlying output stream
|
||||
public void writeDoubles(double[] v) throws IOException {
|
||||
writeDoubles(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of doubles to the underlying output stream
|
||||
public void writeDoubles(double[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeDouble(v[offset + x]);
|
||||
}
|
||||
|
||||
// Converts the float argument to an int using floatToIntBits
|
||||
public void writeFloat(float v) throws IOException {
|
||||
data.writeInt(Integer.reverseBytes(Float.floatToIntBits(v)));
|
||||
}
|
||||
|
||||
// Writes an array of floats to the underlying output stream
|
||||
public void writeFloats(float[] v) throws IOException {
|
||||
writeFloats(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of floats to the underlying output stream
|
||||
public void writeFloats(float[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeFloat(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes an int to the underlying output stream as four bytes
|
||||
public void writeInt(int v) throws IOException {
|
||||
data.writeInt(Integer.reverseBytes(v));
|
||||
}
|
||||
|
||||
// Writes an array of ints to the underlying output stream
|
||||
public void writeInts(int[] v) throws IOException {
|
||||
writeInts(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of ints to the underlying output stream
|
||||
public void writeInts(int[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeInt(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes an int to the underlying output stream as three bytes
|
||||
public void writeMiddle(int v) throws IOException {
|
||||
writeShort((short) v );
|
||||
writeByte ((byte ) (v >> 16));
|
||||
}
|
||||
|
||||
// Writes an array of 24-bit integers to the underlying output stream
|
||||
public void writeMiddles(int[] v) throws IOException {
|
||||
writeMiddles(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of 24-bit integers to the underlying output stream
|
||||
public void writeMiddles(int[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeMiddle(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes a long to the underlying output stream as eight bytes
|
||||
public void writeLong(long v) throws IOException {
|
||||
data.writeLong(Long.reverseBytes(v));
|
||||
}
|
||||
|
||||
// Writes an array of longs to the underlying output stream
|
||||
public void writeLong(long[] v) throws IOException {
|
||||
writeLongs(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of longs to the underlying output stream
|
||||
public void writeLongs(long[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeLong(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes a short to the underlying output stream as two bytes
|
||||
public void writeShort(int v) throws IOException {
|
||||
data.writeShort(Short.reverseBytes((short) v));
|
||||
}
|
||||
|
||||
// Writes an array of shorts to the underlying output stream
|
||||
public void writeShorts(short[] v) throws IOException {
|
||||
writeShorts(v, 0, v.length);
|
||||
}
|
||||
|
||||
// Writes an array of shorts to the underlying output stream
|
||||
public void writeShorts(short[] v, int offset, int length)
|
||||
throws IOException {
|
||||
for (int x = 0; x < length; x++)
|
||||
writeShort(v[offset + x]);
|
||||
}
|
||||
|
||||
// Writes a string to the underlying output stream using modified UTF-8
|
||||
public void writeUTF(String str) throws IOException {
|
||||
data.writeUTF(str);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,538 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
// UI display text manager
|
||||
public class Localizer {
|
||||
|
||||
// Instance fields
|
||||
private HashMap<Object, Control> controls; // Control mapping
|
||||
private Locale locale; // Current message store
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Class reference for JComboBox<String>
|
||||
JComboBox<String> JCOMBOBOX = new JComboBox<String>();
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Control settings
|
||||
private static class Control {
|
||||
String key; // Single-string dictionary key
|
||||
String[] keys; // Multiple-string dictionary key
|
||||
String tipKey; // Tooltip dictionary key
|
||||
HashMap<String, String> tags; // Message overrides
|
||||
Control(String tipKey) {
|
||||
tags = new HashMap<String, String>();
|
||||
this.tipKey = tipKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Locale container
|
||||
public static class Locale implements Comparable<Locale> {
|
||||
|
||||
// Public fields
|
||||
public final String id; // Unique identifier
|
||||
public final String name; // Display name
|
||||
|
||||
// Private fields
|
||||
private HashMap<String, String> messages; // Message dictionary
|
||||
|
||||
// Constructor
|
||||
private Locale(HashMap<String, String> messages) {
|
||||
id = messages.get("locale.id");
|
||||
this.messages = messages;
|
||||
name = messages.get("locale.name");
|
||||
}
|
||||
|
||||
// Comparator
|
||||
public int compareTo(Locale o) {
|
||||
return id.compareTo(o.id);
|
||||
}
|
||||
|
||||
// Represent this object as a string
|
||||
public String toString() {
|
||||
return id + " - " + name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Static Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Parse a text file to produce a Locale object
|
||||
public static Locale parse(String text) {
|
||||
var chars = text.replaceAll("\\r\\n?", "\n").toCharArray();
|
||||
var ret = new HashMap<String, String>(); // Output dictionary
|
||||
int start = 0; // Position of first character in key or value
|
||||
var stack = new Stack<String>(); // Nested key chain
|
||||
int state = 0; // Current parsing context
|
||||
|
||||
// Process all characters
|
||||
for (int x = 0, line = 1, col = 1; x < chars.length; x++, col++) {
|
||||
char c = chars[x];
|
||||
boolean end = x == chars.length - 1;
|
||||
String pos = line + ":" + col + ": ";
|
||||
boolean white = c == ' ' || c == '\t';
|
||||
|
||||
// Processing for newline
|
||||
if (c == '\n') {
|
||||
col = 0;
|
||||
line++;
|
||||
}
|
||||
|
||||
// Comment
|
||||
if (state == -1) {
|
||||
if (c == '\n' || end)
|
||||
state = 0;
|
||||
}
|
||||
|
||||
// Pre-key
|
||||
else if (state == 0) {
|
||||
|
||||
// Ignore leading whitespace
|
||||
if (white)
|
||||
continue;
|
||||
|
||||
// The line is a comment
|
||||
if (c == '#') {
|
||||
state = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// End of input has been reached
|
||||
if (end)
|
||||
break;
|
||||
|
||||
// The line is empty
|
||||
if (c == '\n')
|
||||
continue;
|
||||
|
||||
// Proceed to key
|
||||
start = x;
|
||||
state = 1;
|
||||
}
|
||||
|
||||
// Key
|
||||
else if (state == 1) {
|
||||
|
||||
// Any non-whitespace character is valid in a key
|
||||
if (!white && c != '\n' && !end)
|
||||
continue;
|
||||
|
||||
// Produce the key as a string
|
||||
String key = new String(chars, start, x - start).trim();
|
||||
|
||||
// Close a key group
|
||||
if (key.equals("}")) {
|
||||
|
||||
// Nesting error
|
||||
if (stack.size() == 0) throw new RuntimeException(
|
||||
pos + "Unexpected '}'");
|
||||
|
||||
// Syntax error
|
||||
outer: for (int y = x; y < chars.length; y++) {
|
||||
char h = chars[y];
|
||||
if (h == '\n') break;
|
||||
if (h != ' ' && h != '\t') throw new RuntimeException(
|
||||
pos + "Newline expected");
|
||||
}
|
||||
|
||||
// Proceed to key
|
||||
state = 0;
|
||||
stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Curly braces are not valid in keys
|
||||
if (key.contains("{") || key.contains("}"))
|
||||
throw new RuntimeException(
|
||||
line + ": Curly braces are not allowed in keys");
|
||||
|
||||
// Proceed to interim
|
||||
stack.push(key);
|
||||
state = 2;
|
||||
}
|
||||
|
||||
// Post-key, pre-value
|
||||
if (state == 2) {
|
||||
|
||||
// Ignore any whitespace between key and value
|
||||
if (white)
|
||||
continue;
|
||||
|
||||
// No value is present
|
||||
if (c == '\n') throw new RuntimeException(
|
||||
pos + "Unexpected newline");
|
||||
if (end) throw new RuntimeException(
|
||||
pos + "Unexpected end of input");
|
||||
|
||||
// Proceed to value
|
||||
start = x;
|
||||
state = 3;
|
||||
}
|
||||
|
||||
// Value
|
||||
if (state == 3) {
|
||||
|
||||
// Escape sequence
|
||||
if (c == '\\') {
|
||||
|
||||
// EOF
|
||||
if (x == chars.length - 1)
|
||||
throw new RuntimeException(
|
||||
pos + "Unexpected end of input");
|
||||
|
||||
// Cannot escape a newline
|
||||
if (chars[x + 1] == '\n')
|
||||
throw new RuntimeException(
|
||||
pos + "Unexpected newline");
|
||||
|
||||
// Skip the next character
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The end of the value has not yet been reached
|
||||
if (c != '\n' && !end)
|
||||
continue;
|
||||
|
||||
// Produce the value as a string
|
||||
if (end)
|
||||
x++;
|
||||
String value = new String(chars, start, x - start).trim();
|
||||
state = 0;
|
||||
|
||||
// Open a key group
|
||||
if (value.equals("{")) {
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for nesting errors
|
||||
int depth = 0;
|
||||
for (int y = start; y < x; y++) {
|
||||
switch (chars[y]) {
|
||||
case '\\': y++; continue;
|
||||
case '{' : depth++; continue;
|
||||
case '}' : if (--depth == -1)
|
||||
throw new RuntimeException(
|
||||
(line - 1) + ": Unexpected '}'"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (depth != 0) throw new RuntimeException(
|
||||
pos + "'}' expected");
|
||||
|
||||
// Produce the full key as a string
|
||||
var pkey = new StringBuilder();
|
||||
for (String item : stack)
|
||||
pkey.append((pkey.length() != 0 ? "." : "") + item);
|
||||
String key = pkey.toString();
|
||||
|
||||
// Check for duplicate keys
|
||||
String lkey = key.toLowerCase();
|
||||
if (ret.get(lkey) != null) throw new RuntimeException(
|
||||
(line-1)+ ": Key '" + key + "' has already been defined.");
|
||||
|
||||
// Add the pair to the dictionary
|
||||
ret.put(lkey, value);
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Perform post-processing error checks
|
||||
if (state != 0 || !stack.empty()) throw new RuntimeException(
|
||||
"Unexpected end of input");
|
||||
if (!ret.containsKey("locale.id")) throw new RuntimeException(
|
||||
"Required key not found: 'locale.id'");
|
||||
if (!ret.containsKey("locale.name")) throw new RuntimeException(
|
||||
"Required key not found: 'locale.name'");
|
||||
ret.put("null", "");
|
||||
return new Locale(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public Localizer() {
|
||||
controls = new HashMap<Object, Control>();
|
||||
}
|
||||
|
||||
// Parsing constructor
|
||||
public Localizer(String text) {
|
||||
this();
|
||||
setLocale(parse(text));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a control to the collection
|
||||
public boolean add(Object control, Object key) {
|
||||
return add(control, key, null);
|
||||
}
|
||||
|
||||
// Add a control with a tooltip to the collection
|
||||
public boolean add(Object control, Object key, String tipKey) {
|
||||
var ctrl = controls.get(control);
|
||||
if (ctrl == null)
|
||||
ctrl = new Control(tipKey);
|
||||
|
||||
// Error checking
|
||||
if (control == null || key == null)
|
||||
return false;
|
||||
|
||||
// Control takes a single string
|
||||
if (key instanceof String) {
|
||||
|
||||
// Type validation
|
||||
if (!(
|
||||
control instanceof AbstractButton ||
|
||||
control instanceof JFrame ||
|
||||
control instanceof JInternalFrame ||
|
||||
control instanceof JLabel ||
|
||||
control instanceof JPanel || // TitledBorder
|
||||
control instanceof JTextComponent
|
||||
)) return false;
|
||||
|
||||
// Configure key
|
||||
ctrl.key = (String) key;
|
||||
}
|
||||
|
||||
// Control takes an array of strings
|
||||
else if (key instanceof String[]) {
|
||||
|
||||
// Type validation
|
||||
if (!(
|
||||
JCOMBOBOX.getClass().isAssignableFrom(control.getClass())
|
||||
)) return false;
|
||||
|
||||
// Configure keys
|
||||
String[] keys = (String[]) key;
|
||||
ctrl.keys = new String[keys.length];
|
||||
System.arraycopy(keys, 0, ctrl.keys, 0, keys.length);
|
||||
}
|
||||
|
||||
// Invalid control type
|
||||
else return false;
|
||||
|
||||
// Add the control to the collection
|
||||
controls.put(control, ctrl);
|
||||
update(control);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove all controls from being managed
|
||||
public void clearControls() {
|
||||
controls.clear();
|
||||
}
|
||||
|
||||
// Evaluate the message for a given key
|
||||
public String get(String key) {
|
||||
return key == null ? null : evaluate(null, key);
|
||||
}
|
||||
|
||||
// Retrieve the currently loaded locale
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
// Configure a control tag
|
||||
public String put(Object control, String key, String value) {
|
||||
var ctrl = controls.get(control);
|
||||
|
||||
// Error checking
|
||||
if (ctrl == null || key == null)
|
||||
return null;
|
||||
|
||||
// Update the control's tags
|
||||
key = key.toLowerCase();
|
||||
String ret = value == null ?
|
||||
ctrl.tags.remove(key) :
|
||||
ctrl.tags.put(key, value)
|
||||
;
|
||||
|
||||
// Refresh the text on the control
|
||||
update(control);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Remove a control from the collection
|
||||
public boolean remove(Object control) {
|
||||
return controls.remove(control) != null;
|
||||
}
|
||||
|
||||
// Specify a message dictionary
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Process substitutions and escapes on a message for a given key
|
||||
private String evaluate(Control control, String key) {
|
||||
|
||||
// No locale is loaded
|
||||
if (locale == null)
|
||||
return key;
|
||||
|
||||
// Check that the key exists
|
||||
String ret = locale.messages.get(key.toLowerCase());
|
||||
if (ret == null)
|
||||
return key;
|
||||
|
||||
// Perform all substitutions
|
||||
outer: for (;;) {
|
||||
var chars = ret.toCharArray();
|
||||
int start = 0;
|
||||
|
||||
// Look for the first complete substitution
|
||||
for (int x = 0; x < chars.length; x++) {
|
||||
char c = chars[x];
|
||||
|
||||
// Escape sequence
|
||||
if (c == '\\') {
|
||||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Begin substitution
|
||||
if (c == '{')
|
||||
start = x + 1;
|
||||
|
||||
// Substitution has not yet ended
|
||||
if (c != '}')
|
||||
continue;
|
||||
|
||||
// Determine the substitution
|
||||
key = new String(chars, start, x - start);
|
||||
String lkey = key.toLowerCase();
|
||||
String value = control == null ? null : control.tags.get(lkey);
|
||||
if (value == null)
|
||||
value = locale.messages.get(lkey);
|
||||
if (value == null)
|
||||
value = "\\{" + key + "\\}";
|
||||
|
||||
// Apply the substitution to the message
|
||||
ret =
|
||||
new String(chars, 0, start - 1) +
|
||||
value +
|
||||
new String(chars, x + 1, chars.length - x - 1)
|
||||
;
|
||||
continue outer;
|
||||
}
|
||||
|
||||
// No further substitutions
|
||||
break;
|
||||
}
|
||||
|
||||
// Unescape all escape sequences
|
||||
var chars = ret.toCharArray();
|
||||
int length = 0;
|
||||
for (int x = 0; x < chars.length; x++, length++) {
|
||||
char c = chars[x];
|
||||
if (c == '\\') switch (c = chars[++x]) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 't': c = '\t'; break;
|
||||
}
|
||||
chars[length] = c;
|
||||
}
|
||||
return new String(chars, 0, length);
|
||||
}
|
||||
|
||||
// Update the text for all controls
|
||||
private void update() {
|
||||
for (var control : controls.keySet())
|
||||
update(control);
|
||||
}
|
||||
|
||||
// Update the text for a control
|
||||
private void update(Object control) {
|
||||
var ctrl = controls.get(control);
|
||||
String[] keys = ctrl.key==null ? ctrl.keys : new String[]{ctrl.key};
|
||||
String[] values = new String[keys.length];
|
||||
|
||||
// Evaluate all messages
|
||||
for (int x = 0; x < keys.length; x++)
|
||||
values[x] = evaluate(ctrl, keys[x]);
|
||||
|
||||
// Update the control's text
|
||||
if (control instanceof AbstractButton)
|
||||
((AbstractButton) control).setText (values[0]);
|
||||
if (control instanceof JFrame)
|
||||
((JFrame ) control).setTitle(values[0]);
|
||||
if (control instanceof JInternalFrame)
|
||||
((JInternalFrame) control).setTitle(values[0]);
|
||||
if (control instanceof JLabel )
|
||||
((JLabel ) control).setText (values[0]);
|
||||
if (control instanceof JTextComponent)
|
||||
((JTextComponent) control).setText (values[0]);
|
||||
|
||||
// JPanel must be wrapped in a TitledBorder
|
||||
if (control instanceof JPanel) {
|
||||
var border = ((JPanel) control).getBorder();
|
||||
if (border instanceof TitledBorder)
|
||||
((TitledBorder) border).setTitle(values[0]);
|
||||
}
|
||||
|
||||
// Replace the contents of a JComboBox without firing events
|
||||
if (JCOMBOBOX.getClass().isAssignableFrom(control.getClass())) {
|
||||
|
||||
// The type is explicitly verified above
|
||||
@SuppressWarnings("unchecked")
|
||||
var box = (JComboBox<String>) control;
|
||||
|
||||
// Configure working variables
|
||||
var action = box.getActionListeners();
|
||||
int index = box.getSelectedIndex();
|
||||
var item = box.getItemListeners();
|
||||
|
||||
// Remove event listeners
|
||||
for (var lst : action) box.removeActionListener(lst);
|
||||
for (var lst : item ) box.removeItemListener (lst);
|
||||
|
||||
// Update contents
|
||||
box.setModel(new DefaultComboBoxModel<String>(values));
|
||||
box.setSelectedIndex(index);
|
||||
|
||||
// Restore event listeners
|
||||
for (var lst : action) box.addActionListener(lst);
|
||||
for (var lst : item ) box.addItemListener (lst);
|
||||
}
|
||||
|
||||
// Update the control's tooltip text
|
||||
if (control instanceof JComponent)
|
||||
((JComponent) control).setToolTipText(
|
||||
ctrl.tipKey == null ? null : evaluate(ctrl, ctrl.tipKey));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Wrapper around JComboBox<String> to enforce desired behaviors
|
||||
public class UComboBox extends JComboBox<String> {
|
||||
|
||||
// Instance fields
|
||||
private HashMap<ActionListener, ActionListener> actionListeners;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Empty item set
|
||||
private static final String[] EMPTY = new String[0];
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public UComboBox() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
// List constructor
|
||||
public UComboBox(String[] items) {
|
||||
super();
|
||||
actionListeners = new HashMap<ActionListener, ActionListener>();
|
||||
setItems(items);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a listener to receive action events
|
||||
public void addActionListener(ActionListener l) {
|
||||
|
||||
// Error checking
|
||||
if (l == null)
|
||||
return;
|
||||
|
||||
// Wrap the event listener in a guard
|
||||
ActionListener L = e->{
|
||||
|
||||
// Search the stack trace for an invocation not from Java itself
|
||||
var trace = Thread.currentThread().getStackTrace();
|
||||
for (int x = 2; x < trace.length; x++)
|
||||
if (trace[x].getModuleName() == null)
|
||||
return;
|
||||
|
||||
// Call the event handler
|
||||
l.actionPerformed(e);
|
||||
};
|
||||
|
||||
// Manage the collections
|
||||
actionListeners.put(l, L);
|
||||
super.addActionListener(L);
|
||||
}
|
||||
|
||||
// Retrieve a list of the current action listeners
|
||||
public ActionListener[] getActionListeners() {
|
||||
return actionListeners.keySet().toArray(
|
||||
new ActionListener[actionListeners.size()]);
|
||||
}
|
||||
|
||||
// Remove an action listener from the collection
|
||||
public void removeActionListener(ActionListener l) {
|
||||
var L = actionListeners.get(l);
|
||||
if (L == null)
|
||||
return;
|
||||
actionListeners.remove(l);
|
||||
super.removeActionListener(L);
|
||||
}
|
||||
|
||||
// Update the items in the list
|
||||
public void setItems(String[] items) {
|
||||
int index = getSelectedIndex();
|
||||
setModel(new DefaultComboBoxModel<String>(
|
||||
items != null ? items : EMPTY));
|
||||
setSelectedIndex(index);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
// Wrapper around JPanel to work around a bug in GridBagLayout
|
||||
public class UPanel extends JPanel {
|
||||
|
||||
// Instance fields
|
||||
private HashSet<PaintListener> paintListeners;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Functional interface for paint events
|
||||
public interface PaintListener {
|
||||
void paint(Graphics2D g, int width, int height);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public UPanel() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
// Layout manager constructor
|
||||
public UPanel(LayoutManager layout) {
|
||||
super(layout);
|
||||
paintListeners = new HashSet<PaintListener>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a listener to receive paint events
|
||||
public void addPaintListener(PaintListener l) {
|
||||
if (l != null)
|
||||
paintListeners.add(l);
|
||||
}
|
||||
|
||||
// Retrieve a list of the current paint listeners
|
||||
public PaintListener[] getPaintListeners() {
|
||||
return paintListeners.toArray(
|
||||
new PaintListener[paintListeners.size()]);
|
||||
}
|
||||
|
||||
// Retrieve the maximum size of the component
|
||||
public Dimension getMaximumSize() {
|
||||
var ret = super.getMaximumSize();
|
||||
return ret != null ? ret : new Dimension();
|
||||
}
|
||||
|
||||
// Retrieve the minimum size of the component
|
||||
public Dimension getMinimumSize() {
|
||||
var ret = super.getMinimumSize();
|
||||
return ret != null ? ret : new Dimension();
|
||||
}
|
||||
|
||||
// Retrieve the preferred size of the component
|
||||
public Dimension getPreferredSize() {
|
||||
var ret = super.getPreferredSize();
|
||||
return ret != null ? ret : new Dimension();
|
||||
}
|
||||
|
||||
// Draw the component
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
var G = (Graphics2D) g;
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
for (var lst : paintListeners)
|
||||
lst.paint(G, width, height);
|
||||
}
|
||||
|
||||
// Remove a paint listener from the collection
|
||||
public void removePaintListener(PaintListener l) {
|
||||
paintListeners.remove(l);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,395 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import javax.imageio.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.plaf.basic.*;
|
||||
|
||||
// General utility methods
|
||||
public final class Util {
|
||||
|
||||
// This class cannot be instantiated
|
||||
private Util() { }
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Classes //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Event listener interfaces
|
||||
public interface OnClose { void call(WindowEvent e); }
|
||||
public interface OnClose2 { void call(InternalFrameEvent e); }
|
||||
public interface OnFocus { void call(FocusEvent e); }
|
||||
public interface OnKey { void call(KeyEvent e); }
|
||||
public interface OnMouse { void call(MouseEvent e); }
|
||||
public interface OnResize { void call(ComponentEvent e); }
|
||||
public interface OnVisible { void call(AncestorEvent e); }
|
||||
|
||||
// Data class for byte-order marks
|
||||
private static class BOM {
|
||||
byte[] mark;
|
||||
Charset set;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Directory from which the JVM was initialized
|
||||
public static final File PWD = new File(System.getProperty("user.dir"));
|
||||
|
||||
// Text file byte-order marks
|
||||
private static final BOM[] BOMS = {
|
||||
new BOM(new byte[] { -17, -69, -65 }, StandardCharsets.UTF_8 ),
|
||||
new BOM(new byte[] { - 2, - 1 }, StandardCharsets.UTF_16BE),
|
||||
new BOM(new byte[] { - 1, - 2 }, StandardCharsets.UTF_16LE),
|
||||
};
|
||||
|
||||
// Available font families
|
||||
private static final String[] FONTS;
|
||||
|
||||
// Filesystem state manager for the current .jar (if any)
|
||||
private static final FileSystem JARFS;
|
||||
|
||||
// Static initializer
|
||||
static {
|
||||
|
||||
// Font families
|
||||
var fonts = GraphicsEnvironment
|
||||
.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
|
||||
Arrays.sort(fonts, String.CASE_INSENSITIVE_ORDER);
|
||||
FONTS = fonts;
|
||||
|
||||
// .jar filesystem
|
||||
FileSystem fs = null;
|
||||
try {
|
||||
fs = FileSystems.newFileSystem(
|
||||
new URI("jar:" + Util.class.getProtectionDomain()
|
||||
.getCodeSource().getLocation().toString()),
|
||||
new HashMap<String, String>(),
|
||||
Util.class.getClassLoader()
|
||||
);
|
||||
} catch (Exception e) { }
|
||||
JARFS = fs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Static Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Apply an alpha value to a Color object
|
||||
public static Color alpha(Color color, float alpha) {
|
||||
int a = (int) Math.round(alpha * 255);
|
||||
return color == null || a < 0 || a > 255 ? null :
|
||||
new Color(color.getRGB() & 0x00FFFFFF | a << 24, true);
|
||||
}
|
||||
|
||||
// Blend one color into another
|
||||
public static Color blend(Color front, Color back, float alpha) {
|
||||
|
||||
// Error checking
|
||||
if (front == null || back == null ||
|
||||
!Float.isFinite(alpha) || alpha < 0 || alpha > 1)
|
||||
return null;
|
||||
|
||||
// Decompose components
|
||||
var colors = new Color[] { front, back };
|
||||
var argb = new float[3][4];
|
||||
for (int x = 0; x < 2; x++) {
|
||||
int bits = colors[x].getRGB();
|
||||
for (int y = 3; y >= 0; y++, bits >>= 8)
|
||||
argb[x][y] = (bits & 0xFF) / 255.0f;
|
||||
}
|
||||
|
||||
// Combine the colors
|
||||
argb[2][0] = argb[0][0] + argb[1][0] * (1 - argb[0][0]);
|
||||
for (int x = 1; x < 4; x++)
|
||||
argb[2][x] = (
|
||||
argb[0][x] * argb[0][0] +
|
||||
argb[1][x] * argb[1][0] * (1 - argb[0][0])
|
||||
) / argb[2][0];
|
||||
|
||||
// Produce the resulting Color object
|
||||
int bits = 0;
|
||||
for (int x = 0; x < 4; x++)
|
||||
bits = bits << 8 | (int) Math.round(argb[2][x] * 255);
|
||||
return new Color(bits, true);
|
||||
}
|
||||
|
||||
// Read a file from disk
|
||||
public static byte[] fileRead(File file) {
|
||||
FileInputStream stream = null;
|
||||
byte[] data = null;
|
||||
try {
|
||||
stream = new FileInputStream(file);
|
||||
data = stream.readAllBytes();
|
||||
} catch (Exception e) { }
|
||||
try { stream.close(); } catch (Exception e) { }
|
||||
return data;
|
||||
}
|
||||
|
||||
// Read a file, first from disk, then from .jar
|
||||
public static byte[] fileRead(String filename) {
|
||||
InputStream stream = null;
|
||||
|
||||
// Open the file on disk
|
||||
try { stream = new FileInputStream(filename); }
|
||||
|
||||
// File on disk could not be opened, so get resource from .jar
|
||||
catch (Exception e) {
|
||||
stream = Util.class.getResourceAsStream("/" + filename);
|
||||
if (stream == null)
|
||||
return null; // Resource in .jar could not be found
|
||||
}
|
||||
|
||||
// Read the file data into memory
|
||||
byte[] data = null;
|
||||
try { data = stream.readAllBytes(); } catch (Exception e) { }
|
||||
try { stream.close(); } catch (Exception e) { }
|
||||
return data;
|
||||
}
|
||||
|
||||
// Select a font family from a list, if avaiable
|
||||
public static String fontFamily(String[] families) {
|
||||
for (String family : families)
|
||||
if (Arrays.binarySearch(FONTS, family,
|
||||
String.CASE_INSENSITIVE_ORDER) >= 0)
|
||||
return family;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read an image file as an icon
|
||||
public static Icon iconRead(String filename) {
|
||||
BufferedImage img = imageRead(filename);
|
||||
return img == null ? null : new ImageIcon(img);
|
||||
}
|
||||
|
||||
// Read an image file by filename
|
||||
public static BufferedImage imageRead(String filename) {
|
||||
try { return
|
||||
ImageIO.read(new ByteArrayInputStream(fileRead(filename)));
|
||||
} catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
// Read an image file by handle
|
||||
public static BufferedImage imageRead(File file) {
|
||||
try { return imageRead(file.getAbsolutePath()); }
|
||||
catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
// Produce a list of files contained in a directory
|
||||
// If the directory is not found on disk, looks for it in the .jar
|
||||
public static String[] listFiles(String path) {
|
||||
|
||||
// Check for the directory on disk
|
||||
var file = new File(path);
|
||||
if (file.exists() && file.isDirectory())
|
||||
return file.list();
|
||||
|
||||
// Not executing out of a .jar
|
||||
if (JARFS == null)
|
||||
return null;
|
||||
|
||||
// Check for the directory in the .jar
|
||||
try {
|
||||
var list = Files.list(JARFS.getPath("/" + path)).toArray();
|
||||
var ret = new String[list.length];
|
||||
for (int x = 0; x < list.length; x++) {
|
||||
path = "/" + ((Path) list[x]).toString();
|
||||
ret[x] = path.substring(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
return ret;
|
||||
} catch (Exception e) { }
|
||||
|
||||
// The directory was not found
|
||||
return null;
|
||||
}
|
||||
|
||||
// Produce a list of available font family names
|
||||
public static String[] listFonts() {
|
||||
var ret = new String[FONTS.length];
|
||||
System.arraycopy(FONTS, 0, ret, 0, FONTS.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Produce a WindowListener with a functional interface
|
||||
public static WindowListener onClose(OnClose close) {
|
||||
return new WindowListener() {
|
||||
public void windowActivated (WindowEvent e) { }
|
||||
public void windowClosed (WindowEvent e) { }
|
||||
public void windowDeactivated(WindowEvent e) { }
|
||||
public void windowDeiconified(WindowEvent e) { }
|
||||
public void windowIconified (WindowEvent e) { }
|
||||
public void windowOpened (WindowEvent e) { }
|
||||
public void windowClosing (WindowEvent e)
|
||||
{ if (close != null) close.call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce an InternalFrameListener with a functional interface
|
||||
public static InternalFrameListener onClose2(OnClose2 close) {
|
||||
return new InternalFrameListener() {
|
||||
public void internalFrameActivated (InternalFrameEvent e) { }
|
||||
public void internalFrameClosed (InternalFrameEvent e) { }
|
||||
public void internalFrameDeactivated(InternalFrameEvent e) { }
|
||||
public void internalFrameDeiconified(InternalFrameEvent e) { }
|
||||
public void internalFrameIconified (InternalFrameEvent e) { }
|
||||
public void internalFrameOpened (InternalFrameEvent e) { }
|
||||
public void internalFrameClosing (InternalFrameEvent e)
|
||||
{ if (close != null) close.call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a FocusListener with functional interfaces
|
||||
public static FocusListener onFocus(OnFocus focus, OnFocus blur) {
|
||||
return new FocusListener() {
|
||||
public void focusGained(FocusEvent e)
|
||||
{ if (focus != null) focus.call(e); }
|
||||
public void focusLost (FocusEvent e)
|
||||
{ if (blur != null) blur .call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a KeyListener with functional interfaces
|
||||
public static KeyListener onKey(OnKey down, OnKey up) {
|
||||
return new KeyListener() {
|
||||
public void keyTyped (KeyEvent e) { }
|
||||
public void keyPressed (KeyEvent e)
|
||||
{ if (down != null) down.call(e); }
|
||||
public void keyReleased(KeyEvent e)
|
||||
{ if (up != null) up .call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a MouseMotionListener with a functional interface
|
||||
public static MouseMotionListener onMouseMove(OnMouse move, OnMouse drag) {
|
||||
return new MouseMotionListener() {
|
||||
public void mouseMoved (MouseEvent e)
|
||||
{ if (move != null) move.call(e); }
|
||||
public void mouseDragged(MouseEvent e)
|
||||
{ if (drag != null) drag.call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a MouseListener with functional interfaces
|
||||
public static MouseListener onMouse(OnMouse down, OnMouse up) {
|
||||
return new MouseListener() {
|
||||
public void mouseClicked (MouseEvent e) { }
|
||||
public void mouseEntered (MouseEvent e) { }
|
||||
public void mouseExited (MouseEvent e) { }
|
||||
public void mousePressed (MouseEvent e)
|
||||
{ if (down != null) down.call(e); }
|
||||
public void mouseReleased(MouseEvent e)
|
||||
{ if (up != null) up .call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a ComponentListener with a functional interface
|
||||
public static ComponentListener onResize(OnResize resize) {
|
||||
return new ComponentListener() {
|
||||
public void componentHidden (ComponentEvent e) { }
|
||||
public void componentMoved (ComponentEvent e) { }
|
||||
public void componentShown (ComponentEvent e) { }
|
||||
public void componentResized(ComponentEvent e)
|
||||
{ if (resize != null) resize.call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Produce an AncestorListener using functional interfaces
|
||||
public static AncestorListener onVisible(OnVisible show, OnVisible hide) {
|
||||
return new AncestorListener() {
|
||||
public void ancestorMoved (AncestorEvent e) { }
|
||||
public void ancestorAdded (AncestorEvent e)
|
||||
{ if (show != null) show.call(e); }
|
||||
public void ancestorRemoved(AncestorEvent e)
|
||||
{ if (hide != null) hide.call(e); }
|
||||
};
|
||||
}
|
||||
|
||||
// Configure the Swing look-and-feel with the system theme
|
||||
public static boolean setSystemLAF() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(
|
||||
UIManager.getSystemLookAndFeelClassName());
|
||||
return true;
|
||||
} catch (Exception e) { return false; }
|
||||
}
|
||||
|
||||
// Produce a JSplitPane with an un-styled divider
|
||||
public static JSplitPane splitPane(int orientation) {
|
||||
var split = new JSplitPane(orientation, null, null);
|
||||
split.setContinuousLayout(true);
|
||||
split.setDividerSize(2);
|
||||
split.setUI(new BasicSplitPaneUI() {
|
||||
public BasicSplitPaneDivider createDefaultDivider() {
|
||||
return new BasicSplitPaneDivider(this) {
|
||||
public void setBorder(Border b) { }
|
||||
};
|
||||
}
|
||||
});
|
||||
split.setBorder(null);
|
||||
return split;
|
||||
}
|
||||
|
||||
// Read a text file, accounting for BOMs
|
||||
public static String textRead(String filename) {
|
||||
|
||||
// Read the file
|
||||
byte[] data = fileRead(filename);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// Configure working variables
|
||||
byte[] mark = null;
|
||||
Charset set = StandardCharsets.ISO_8859_1; // Default charset
|
||||
|
||||
// Check for a byte-order mark
|
||||
outer: for (BOM bom : BOMS) {
|
||||
if (data.length < bom.mark.length)
|
||||
continue;
|
||||
for (int x = 0; x < bom.mark.length; x++)
|
||||
if (data[x] != bom.mark[x])
|
||||
continue outer;
|
||||
mark = bom.mark;
|
||||
set = bom.set;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove the byte-order mark from the data
|
||||
if (mark != null) {
|
||||
byte[] temp = new byte[data.length - mark.length];
|
||||
System.arraycopy(data, mark.length, temp, 0, temp.length);
|
||||
data = temp;
|
||||
}
|
||||
|
||||
// Produce a string
|
||||
return new String(data, set);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package util;
|
||||
|
||||
// Java imports
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import javax.xml.parsers.*;
|
||||
import org.w3c.dom.*;
|
||||
|
||||
// Utility methods for managing XML documents
|
||||
public class XML {
|
||||
|
||||
// This class cannot be instantiated
|
||||
private XML() { }
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Parsing instance
|
||||
private static final DocumentBuilder XML_PARSER;
|
||||
|
||||
// Static initializer
|
||||
static {
|
||||
DocumentBuilder parser = null;
|
||||
try { parser =
|
||||
DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
} catch (Exception e) { }
|
||||
XML_PARSER = parser;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Static Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Retrieve an attribute value from an XML element
|
||||
public static String attribute(Element element, String attribute) {
|
||||
return element.hasAttribute(attribute) ?
|
||||
element.getAttribute(attribute) : null;
|
||||
}
|
||||
|
||||
// Retrieve the child elements of an XML node
|
||||
public static Element[] children(Node node) {
|
||||
var nodes = node.getChildNodes();
|
||||
int count = nodes.getLength();
|
||||
var ret = new ArrayList<Element>();
|
||||
for (int x = 0; x < count; x++) {
|
||||
node = nodes.item(x);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE)
|
||||
ret.add((Element) node);
|
||||
}
|
||||
return ret.toArray(new Element[ret.size()]);
|
||||
}
|
||||
|
||||
// Retrieve the first node matching the given hierarchy
|
||||
public static Element element(Node node, String hierarchy) {
|
||||
|
||||
// Error checking
|
||||
if (node == null || hierarchy == null)
|
||||
return null;
|
||||
|
||||
// Propagate down the hierarchy
|
||||
outer: for (String name : hierarchy.split("\\.")) {
|
||||
var nodes = node.getChildNodes();
|
||||
int count = nodes.getLength();
|
||||
for (int x = 0; x < count; x++) {
|
||||
node = nodes.item(x);
|
||||
if (
|
||||
node.getNodeType() == Node.ELEMENT_NODE &&
|
||||
node.getNodeName().equals(name)
|
||||
) continue outer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Element) node;
|
||||
}
|
||||
|
||||
// Retrieve all nodes matching the endpoint of a given hierarchy
|
||||
public static Element[] elements(Node node, String hierarchy) {
|
||||
|
||||
// Find the first matching node
|
||||
node = element(node, hierarchy);
|
||||
if (node == null)
|
||||
return new Element[0];
|
||||
|
||||
// Working variables
|
||||
var names = hierarchy.split("\\.");
|
||||
String name = names[names.length - 1];
|
||||
var ret = new ArrayList<Element>();
|
||||
|
||||
// Include all siblings with the same tag name
|
||||
ret.add((Element) node);
|
||||
for (;;) {
|
||||
node = node.getNextSibling();
|
||||
if (node == null)
|
||||
break;
|
||||
if (
|
||||
node.getNodeType() == Node.ELEMENT_NODE &&
|
||||
node.getNodeName().equals(name)
|
||||
) ret.add((Element) node);
|
||||
}
|
||||
|
||||
return ret.toArray(new Element[ret.size()]);
|
||||
}
|
||||
|
||||
// Read and parse an XML file
|
||||
public static Node read(String filename) {
|
||||
try { return XML_PARSER.parse(
|
||||
new ByteArrayInputStream(Util.fileRead(filename)));
|
||||
} catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
// Retrieve the text content of an element
|
||||
public static String text(Node node) {
|
||||
|
||||
// Error checking
|
||||
if (node == null)
|
||||
return null;
|
||||
|
||||
// Find the child node called #text
|
||||
var nodes = node.getChildNodes();
|
||||
int count = nodes.getLength();
|
||||
for (int x = 0; x < count; x++) {
|
||||
node = nodes.item(x);
|
||||
if (node.getNodeType() != Node.TEXT_NODE)
|
||||
continue;
|
||||
String text = node.getNodeValue();
|
||||
return text == null ? "" : text.trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package vue;
|
||||
|
||||
// Access state
|
||||
public class Access {
|
||||
|
||||
// Instance fields
|
||||
public int address; // CPU bus address
|
||||
public int fetch; // Index of machine code unit
|
||||
public int type; // Data type
|
||||
public int value; // Value read/to write
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
Access() { }
|
||||
|
||||
}
|
||||
@@ -1,576 +0,0 @@
|
||||
// This file is included through NativeVue.c and cannot be built directly. */
|
||||
#ifdef NATIVEVUE
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Hook flags
|
||||
#define BRK_EXCEPTION 0x00000001
|
||||
#define BRK_EXECUTE 0x00000002
|
||||
#define BRK_FRAME 0x00000004
|
||||
#define BRK_READ 0x00000008
|
||||
#define BRK_WRITE 0x00000010
|
||||
|
||||
// Token types
|
||||
#define BRK_BOOL 0 /* Value types */
|
||||
#define BRK_SIGNED 1
|
||||
#define BRK_UNSIGNED 2
|
||||
#define BRK_FLOAT 3
|
||||
#define BRK_SYMBOL 4 /* Symbol types */
|
||||
#define BRK_PROREG 5
|
||||
#define BRK_SYSREG 6
|
||||
#define BRK_UNARY 7 /* Operator types */
|
||||
#define BRK_BINARY 8
|
||||
|
||||
|
||||
// Functional symbol IDs
|
||||
#define BRK_ADDRESS 0
|
||||
#define BRK_BREAK 1
|
||||
#define BRK_CODE 2
|
||||
#define BRK_COND 3
|
||||
#define BRK_DISP 4
|
||||
#define BRK_FETCH 5
|
||||
#define BRK_FORMAT 6
|
||||
#define BRK_ID 7
|
||||
#define BRK_IMM 8
|
||||
#define BRK_OPCODE 9
|
||||
#define BRK_REG1 10
|
||||
#define BRK_REG2 11
|
||||
#define BRK_REGID 12
|
||||
#define BRK_SIZE 13
|
||||
#define BRK_SUBOPCODE 14
|
||||
#define BRK_TYPE 15
|
||||
#define BRK_VALUE 16
|
||||
#define BRK_VECTOR 17
|
||||
|
||||
// Evaluation operator IDs
|
||||
#define BRK_XS32 1 /* xs32, xword */
|
||||
#define BRK_XU32 2 /* xu32, xuword */
|
||||
#define BRK_XFLOAT 3 /* xfloat */
|
||||
#define BRK_READ8 4 /* [] */
|
||||
#define BRK_READ16 5 /* [] */
|
||||
#define BRK_READ32 6 /* [] */
|
||||
#define BRK_BOOL_ - 2 /* bool */
|
||||
#define BRK_S32 - 3 /* s32, word */
|
||||
#define BRK_U32 - 4 /* u32, uword */
|
||||
#define BRK_FLOAT_ - 5 /* float */
|
||||
#define BRK_ADD - 6 /* + */
|
||||
#define BRK_AND_B - 7 /* & */
|
||||
#define BRK_AND_L - 8 /* && */
|
||||
#define BRK_CEIL - 9 /* ceil */
|
||||
#define BRK_DIVIDE -10 /* / */
|
||||
#define BRK_EQUAL -11 /* == */
|
||||
#define BRK_FLOOR -12 /* floor */
|
||||
#define BRK_GREATER -13 /* > */
|
||||
#define BRK_GREQUAL -14 /* >= */
|
||||
#define BRK_LEFT_L -15 /* << */
|
||||
#define BRK_LEQUAL -16 /* <= */
|
||||
#define BRK_LESS -17 /* < */
|
||||
#define BRK_MULTIPLY -18 /* * */
|
||||
#define BRK_NEQUAL -19 /* != */
|
||||
#define BRK_NOT_B -20 /* ~ */
|
||||
#define BRK_NOT_L -21 /* ! */
|
||||
#define BRK_NEGATE -22 /* - */
|
||||
#define BRK_OR_B -23 /* | */
|
||||
#define BRK_OR_L -24 /* || */
|
||||
#define BRK_REMAINDER -25 /* % */
|
||||
#define BRK_RIGHT_A -26 /* >> */
|
||||
#define BRK_RIGHT_L -27 /* >>> */
|
||||
#define BRK_ROUND -28 /* round */
|
||||
#define BRK_S8 -29 /* s8, byte */
|
||||
#define BRK_S16 -30 /* s16, halfword */
|
||||
#define BRK_SUBTRACT -31 /* - */
|
||||
#define BRK_TRUNC -32 /* trunc */
|
||||
#define BRK_U8 -33 /* u8, ubyte */
|
||||
#define BRK_U16 -34 /* u16, uhalfword */
|
||||
#define BRK_XOR_B -36 /* ^ */
|
||||
#define BRK_XOR_L -37 /* ^^ */
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Internal Functions //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Adjust a float value as needed
|
||||
static int brkAdjust(float value) {
|
||||
int32_t bits = *(int32_t *)&value;
|
||||
int exp = bits & 0x7F800000;
|
||||
return
|
||||
(bits & 0x7FFFFFFF) == 0 || // Zero
|
||||
exp == 0x7F800000 || // Indefinite
|
||||
exp == 0 // Denormal
|
||||
? 0 : bits;
|
||||
}
|
||||
|
||||
// Evaluate a binary operator
|
||||
static int32_t brkEvalBinary(int32_t id, int32_t left, int32_t right) {
|
||||
|
||||
// Processing by ID
|
||||
switch (id) {
|
||||
|
||||
// Add
|
||||
case -BRK_ADD * 4 + BRK_BOOL:
|
||||
case -BRK_ADD * 4 + BRK_SIGNED:
|
||||
case -BRK_ADD * 4 + BRK_UNSIGNED:
|
||||
return left + right;
|
||||
case -BRK_ADD * 4 + BRK_FLOAT:
|
||||
return brkAdjust(*(float *)&left + *(float *)&right);
|
||||
|
||||
// And Bitwise
|
||||
case -BRK_AND_B * 4 + BRK_BOOL:
|
||||
case -BRK_AND_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_AND_B * 4 + BRK_SIGNED:
|
||||
case -BRK_AND_B * 4 + BRK_UNSIGNED:
|
||||
return left & right;
|
||||
|
||||
// And Logical
|
||||
case -BRK_AND_L * 4 + BRK_BOOL:
|
||||
case -BRK_AND_L * 4 + BRK_FLOAT:
|
||||
case -BRK_AND_L * 4 + BRK_SIGNED:
|
||||
case -BRK_AND_L * 4 + BRK_UNSIGNED:
|
||||
return left == 0 ? left : right;
|
||||
|
||||
// Divide
|
||||
case -BRK_DIVIDE * 4 + BRK_BOOL:
|
||||
case -BRK_DIVIDE * 4 + BRK_SIGNED:
|
||||
return right == 0 ? 0 : left / right;
|
||||
case -BRK_DIVIDE * 4 + BRK_UNSIGNED:
|
||||
return right == 0 ? 0 : (uint32_t) left / right;
|
||||
case -BRK_DIVIDE * 4 + BRK_FLOAT:
|
||||
return right == 0 ? 0 :
|
||||
brkAdjust(*(float *)&left / *(float *)&right);
|
||||
|
||||
// Equal
|
||||
case -BRK_EQUAL * 4 + BRK_BOOL:
|
||||
case -BRK_EQUAL * 4 + BRK_FLOAT:
|
||||
case -BRK_EQUAL * 4 + BRK_SIGNED:
|
||||
case -BRK_EQUAL * 4 + BRK_UNSIGNED:
|
||||
return left == right ? 1 : 0;
|
||||
|
||||
// Greater
|
||||
case -BRK_GREATER * 4 + BRK_BOOL:
|
||||
case -BRK_GREATER * 4 + BRK_SIGNED:
|
||||
return left > right ? 1 : 0;
|
||||
case -BRK_GREATER * 4 + BRK_UNSIGNED:
|
||||
return (uint32_t) left > right ? 1 : 0;
|
||||
case -BRK_GREATER * 4 + BRK_FLOAT:
|
||||
return *(float *)&left > *(float *)&right ? 1 : 0;
|
||||
|
||||
// Greater or Equal
|
||||
case -BRK_GREQUAL * 4 + BRK_BOOL:
|
||||
case -BRK_GREQUAL * 4 + BRK_SIGNED:
|
||||
return left >= right ? 1 : 0;
|
||||
case -BRK_GREQUAL * 4 + BRK_UNSIGNED:
|
||||
return (uint32_t) left >= right ? 1 : 0;
|
||||
case -BRK_GREQUAL * 4 + BRK_FLOAT:
|
||||
return *(float *)&left >= *(float *)&right ? 1 : 0;
|
||||
|
||||
// Shift Left
|
||||
case -BRK_LEFT_L * 4 + BRK_BOOL:
|
||||
case -BRK_LEFT_L * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_LEFT_L * 4 + BRK_SIGNED:
|
||||
case -BRK_LEFT_L * 4 + BRK_UNSIGNED:
|
||||
return left << (right & 31);
|
||||
|
||||
// Less or Equal
|
||||
case -BRK_LEQUAL * 4 + BRK_BOOL:
|
||||
case -BRK_LEQUAL * 4 + BRK_SIGNED:
|
||||
return left <= right ? 1 : 0;
|
||||
case -BRK_LEQUAL * 4 + BRK_UNSIGNED:
|
||||
return (uint32_t) left <= right ? 1 : 0;
|
||||
case -BRK_LEQUAL * 4 + BRK_FLOAT:
|
||||
return *(float *)&left <= *(float *)&right ? 1 : 0;
|
||||
|
||||
// Less
|
||||
case -BRK_LESS * 4 + BRK_BOOL:
|
||||
case -BRK_LESS * 4 + BRK_SIGNED:
|
||||
return left < right ? 1 : 0;
|
||||
case -BRK_LESS * 4 + BRK_UNSIGNED:
|
||||
return (uint32_t) left < right ? 1 : 0;
|
||||
case -BRK_LESS * 4 + BRK_FLOAT:
|
||||
return *(float *)&left < *(float *)&right ? 1 : 0;
|
||||
|
||||
// Multiply
|
||||
case -BRK_MULTIPLY * 4 + BRK_BOOL:
|
||||
case -BRK_MULTIPLY * 4 + BRK_SIGNED:
|
||||
return left * right;
|
||||
case -BRK_MULTIPLY * 4 + BRK_UNSIGNED:
|
||||
return (uint32_t) left * right;
|
||||
case -BRK_MULTIPLY * 4 + BRK_FLOAT:
|
||||
return brkAdjust(*(float *)&left * *(float *)&right);
|
||||
|
||||
// Not Equal
|
||||
case -BRK_NEQUAL * 4 + BRK_BOOL:
|
||||
case -BRK_NEQUAL * 4 + BRK_FLOAT:
|
||||
case -BRK_NEQUAL * 4 + BRK_SIGNED:
|
||||
case -BRK_NEQUAL * 4 + BRK_UNSIGNED:
|
||||
return left != right ? 1 : 0;
|
||||
|
||||
// Or Bitwise
|
||||
case -BRK_OR_B * 4 + BRK_BOOL:
|
||||
case -BRK_OR_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_OR_B * 4 + BRK_SIGNED:
|
||||
case -BRK_OR_B * 4 + BRK_UNSIGNED:
|
||||
return left | right;
|
||||
|
||||
// Or Logical
|
||||
case -BRK_OR_L * 4 + BRK_BOOL:
|
||||
case -BRK_OR_L * 4 + BRK_FLOAT:
|
||||
case -BRK_OR_L * 4 + BRK_SIGNED:
|
||||
case -BRK_OR_L * 4 + BRK_UNSIGNED:
|
||||
return left != 0 ? left : right;
|
||||
|
||||
// Remainder
|
||||
case -BRK_REMAINDER * 4 + BRK_BOOL:
|
||||
case -BRK_REMAINDER * 4 + BRK_SIGNED:
|
||||
return right == 0 ? 0 : left % right;
|
||||
case -BRK_REMAINDER * 4 + BRK_UNSIGNED:
|
||||
return right == 0 ? 0 : (uint32_t) left % right;
|
||||
case -BRK_REMAINDER * 4 + BRK_FLOAT:
|
||||
return right == 0 ? 0 :
|
||||
brkAdjust(fmodf(*(float *)&left, *(float *)&right));
|
||||
|
||||
// Shift Right Arithmetic
|
||||
case -BRK_RIGHT_A * 4 + BRK_BOOL:
|
||||
case -BRK_RIGHT_A * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_RIGHT_A * 4 + BRK_SIGNED:
|
||||
case -BRK_RIGHT_A * 4 + BRK_UNSIGNED:
|
||||
return SIGN_EXTEND(left >> (right & 31), 32 - (right & 31));
|
||||
|
||||
// Shift Right Logical
|
||||
case -BRK_RIGHT_L * 4 + BRK_BOOL:
|
||||
case -BRK_RIGHT_L * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_RIGHT_L * 4 + BRK_SIGNED:
|
||||
case -BRK_RIGHT_L * 4 + BRK_UNSIGNED:
|
||||
return left >> (right & 31) & ((int32_t) -1 << (right & 31));
|
||||
|
||||
// Subtract
|
||||
case -BRK_SUBTRACT * 4 + BRK_BOOL:
|
||||
case -BRK_SUBTRACT * 4 + BRK_SIGNED:
|
||||
case -BRK_SUBTRACT * 4 + BRK_UNSIGNED:
|
||||
return left - right;
|
||||
case -BRK_SUBTRACT * 4 + BRK_FLOAT:
|
||||
return brkAdjust(*(float *)&left - *(float *)&right);
|
||||
|
||||
// Exclusive Or Bitwise
|
||||
case -BRK_XOR_B * 4 + BRK_BOOL:
|
||||
case -BRK_XOR_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_XOR_B * 4 + BRK_SIGNED:
|
||||
case -BRK_XOR_B * 4 + BRK_UNSIGNED:
|
||||
return left ^ right;
|
||||
|
||||
// Exclusive Or Logical
|
||||
case -BRK_XOR_L * 4 + BRK_BOOL:
|
||||
case -BRK_XOR_L * 4 + BRK_FLOAT:
|
||||
case -BRK_XOR_L * 4 + BRK_SIGNED:
|
||||
case -BRK_XOR_L * 4 + BRK_UNSIGNED:
|
||||
return left != 0 && right != 0 ? 0 : left != 0 ? left : right;
|
||||
|
||||
}
|
||||
|
||||
// Unreachable
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evaluate a unary operator
|
||||
static int32_t brkEvalUnary(Vue *vue, int32_t id, int32_t operand) {
|
||||
float val; // Working variable
|
||||
|
||||
// Processing by ID
|
||||
switch (id) {
|
||||
|
||||
// Cast to Boolean
|
||||
case -BRK_BOOL_ * 4 + BRK_BOOL: // Removed by parser
|
||||
case -BRK_BOOL_ * 4 + BRK_FLOAT:
|
||||
case -BRK_BOOL_ * 4 + BRK_SIGNED:
|
||||
case -BRK_BOOL_ * 4 + BRK_UNSIGNED:
|
||||
return operand == 0 ? 0 : 1;
|
||||
|
||||
// Round up
|
||||
case -BRK_CEIL * 4 + BRK_BOOL: // Removed by parser
|
||||
case -BRK_CEIL * 4 + BRK_FLOAT:
|
||||
case -BRK_CEIL * 4 + BRK_SIGNED: // Removed by parser
|
||||
case -BRK_CEIL * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return brkAdjust(ceilf(*(float *)&operand));
|
||||
|
||||
// Cast to Float
|
||||
case -BRK_FLOAT_ * 4 + BRK_BOOL:
|
||||
case -BRK_FLOAT_ * 4 + BRK_FLOAT: // Removed by parser
|
||||
case -BRK_FLOAT_ * 4 + BRK_SIGNED:
|
||||
return brkAdjust(operand);
|
||||
case -BRK_FLOAT_ * 4 + BRK_UNSIGNED:
|
||||
return brkAdjust((uint32_t) operand);
|
||||
|
||||
// Round down
|
||||
case -BRK_FLOOR * 4 + BRK_BOOL: // Removed by parser
|
||||
case -BRK_FLOOR * 4 + BRK_FLOAT:
|
||||
case -BRK_FLOOR * 4 + BRK_SIGNED: // Removed by parser
|
||||
case -BRK_FLOOR * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return brkAdjust(floorf(*(float *)&operand));
|
||||
|
||||
// Bitwise Not
|
||||
case -BRK_NOT_B * 4 + BRK_BOOL:
|
||||
case -BRK_NOT_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||
case -BRK_NOT_B * 4 + BRK_SIGNED:
|
||||
case -BRK_NOT_B * 4 + BRK_UNSIGNED:
|
||||
return ~operand;
|
||||
|
||||
// Logical Not
|
||||
case -BRK_NOT_L * 4 + BRK_BOOL:
|
||||
case -BRK_NOT_L * 4 + BRK_FLOAT:
|
||||
case -BRK_NOT_L * 4 + BRK_SIGNED:
|
||||
case -BRK_NOT_L * 4 + BRK_UNSIGNED:
|
||||
return operand == 0 ? 1 : 0;
|
||||
|
||||
// Negate
|
||||
case -BRK_NEGATE * 4 + BRK_BOOL:
|
||||
case -BRK_NEGATE * 4 + BRK_SIGNED:
|
||||
case -BRK_NEGATE * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return -operand;
|
||||
case -BRK_NEGATE * 4 + BRK_FLOAT:
|
||||
return brkAdjust(-*(float *)&operand);
|
||||
|
||||
// Read Byte
|
||||
case BRK_READ8:
|
||||
return vueRead(vue, operand, VUE_S8);
|
||||
|
||||
// Read Halfword
|
||||
case BRK_READ16:
|
||||
return vueRead(vue, operand, VUE_S16);
|
||||
|
||||
// Read Word
|
||||
case BRK_READ32:
|
||||
return vueRead(vue, operand, VUE_S32);
|
||||
|
||||
// Round to Nearest
|
||||
case -BRK_ROUND * 4 + BRK_BOOL: // Removed by parser
|
||||
case -BRK_ROUND * 4 + BRK_FLOAT:
|
||||
case -BRK_ROUND * 4 + BRK_SIGNED: // Removed by parser
|
||||
case -BRK_ROUND * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return brkAdjust(roundf(*(float *)&operand));
|
||||
|
||||
// Cast to Signed Byte
|
||||
case -BRK_S8 * 4 + BRK_FLOAT:
|
||||
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||
// Fallthrough
|
||||
case -BRK_S8 * 4 + BRK_BOOL:
|
||||
case -BRK_S8 * 4 + BRK_SIGNED:
|
||||
case -BRK_S8 * 4 + BRK_UNSIGNED:
|
||||
return SIGN_EXTEND(operand, 8);
|
||||
|
||||
// Cast to Signed Halfword
|
||||
case -BRK_S16 * 4 + BRK_FLOAT:
|
||||
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||
// Fallthrough
|
||||
case -BRK_S16 * 4 + BRK_BOOL:
|
||||
case -BRK_S16 * 4 + BRK_SIGNED:
|
||||
case -BRK_S16 * 4 + BRK_UNSIGNED:
|
||||
return SIGN_EXTEND(operand, 16);
|
||||
|
||||
// Cast to Signed Word
|
||||
case -BRK_S32 * 4 + BRK_BOOL:
|
||||
case -BRK_S32 * 4 + BRK_SIGNED: // Removed by parser
|
||||
case -BRK_S32 * 4 + BRK_UNSIGNED:
|
||||
return operand;
|
||||
case -BRK_S32 * 4 + BRK_FLOAT:
|
||||
val = *(float *)&operand;
|
||||
return val >= -MAX_WORD && val < MAX_WORD ? (int32_t) val : 0;
|
||||
|
||||
// Truncate
|
||||
case -BRK_TRUNC * 4 + BRK_BOOL: // Removed by parser
|
||||
case -BRK_TRUNC * 4 + BRK_FLOAT:
|
||||
case -BRK_TRUNC * 4 + BRK_SIGNED: // Removed by parser
|
||||
case -BRK_TRUNC * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return brkAdjust(truncf(*(float *)&operand));
|
||||
|
||||
// Cast to Unsigned Byte
|
||||
case -BRK_U8 * 4 + BRK_FLOAT:
|
||||
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||
case -BRK_U8 * 4 + BRK_BOOL:
|
||||
case -BRK_U8 * 4 + BRK_SIGNED:
|
||||
case -BRK_U8 * 4 + BRK_UNSIGNED:
|
||||
return operand & 0xFF;
|
||||
|
||||
// Cast to Unsigned Halfword
|
||||
case -BRK_U16 * 4 + BRK_FLOAT:
|
||||
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||
case -BRK_U16 * 4 + BRK_BOOL:
|
||||
case -BRK_U16 * 4 + BRK_SIGNED:
|
||||
case -BRK_U16 * 4 + BRK_UNSIGNED:
|
||||
return operand & 0xFFFF;
|
||||
|
||||
// Cast to Unsigned Word
|
||||
case -BRK_U32 * 4 + BRK_BOOL:
|
||||
case -BRK_U32 * 4 + BRK_SIGNED:
|
||||
case -BRK_U32 * 4 + BRK_UNSIGNED: // Removed by parser
|
||||
return operand;
|
||||
case -BRK_U32 * 4 + BRK_FLOAT:
|
||||
val = *(float *)&operand;
|
||||
return val >= 0 && val < MAX_UWORD ? (uint32_t) val : 0;
|
||||
|
||||
// Reinterpret as Float
|
||||
case BRK_XFLOAT:
|
||||
return brkAdjust(*(float *)&operand);
|
||||
|
||||
// Reinterpret as Signed Word, Reinterpret as Unsigned Word
|
||||
//case BRK_XS32: // Removed by parser
|
||||
//case BRK_XU32: // Removed by parser
|
||||
}
|
||||
|
||||
// Unreachable
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evaluate the condition, returning only the computed value
|
||||
static int32_t brkEvaluate(Core *core, Breakpoint *brk) {
|
||||
int32_t *stack = core->stack;
|
||||
Vue *vue = &core->vue;
|
||||
|
||||
// Process all tokens
|
||||
int size = 0;
|
||||
Token *tok;
|
||||
int32_t x, sym; // Working variables
|
||||
for (tok = &brk->tokens[x=0]; x < brk->numTokens; tok = &brk->tokens[++x])
|
||||
switch (tok->type) {
|
||||
|
||||
// Binary operator
|
||||
case BRK_BINARY:
|
||||
stack[size - 2] =
|
||||
brkEvalBinary(tok->id, stack[size - 2], stack[size - 1]);
|
||||
size--;
|
||||
break;
|
||||
|
||||
// Literal
|
||||
case BRK_BOOL:
|
||||
case BRK_FLOAT:
|
||||
case BRK_SIGNED:
|
||||
case BRK_UNSIGNED:
|
||||
stack[size++] = tok->id;
|
||||
break;
|
||||
|
||||
// CPU register
|
||||
case BRK_PROREG:
|
||||
case BRK_SYSREG:
|
||||
stack[size++] = vueGetRegister(vue,tok->id,tok->type==BRK_SYSREG);
|
||||
break;
|
||||
|
||||
// Unary operator
|
||||
case BRK_UNARY:
|
||||
stack[size - 1] = brkEvalUnary(vue, tok->id, stack[size - 1]);
|
||||
break;
|
||||
|
||||
// Symbol
|
||||
case BRK_SYMBOL:
|
||||
sym = vue->cpu.inst.imm; // IMM, REGID, VECTOR
|
||||
switch (tok->id) {
|
||||
case BRK_ADDRESS : sym = vue->cpu.access.address; break;
|
||||
case BRK_BREAK : sym = core->breakType ; break;
|
||||
case BRK_CODE : sym = vue->cpu.exception ; break;
|
||||
case BRK_COND : sym = vue->cpu.inst.cond ; break;
|
||||
case BRK_DISP : sym = vue->cpu.inst.disp ; break;
|
||||
case BRK_FETCH : sym = vue->cpu.fetch ; break;
|
||||
case BRK_FORMAT : sym = vue->cpu.inst.format ; break;
|
||||
case BRK_ID : sym = vue->cpu.inst.id ; break;
|
||||
case BRK_OPCODE : sym = vue->cpu.inst.opcode ; break;
|
||||
case BRK_REG1 : sym = vue->cpu.inst.reg1 ; break;
|
||||
case BRK_REG2 : sym = vue->cpu.inst.reg2 ; break;
|
||||
case BRK_SIZE : sym = vue->cpu.inst.size ; break;
|
||||
case BRK_SUBOPCODE: sym = vue->cpu.inst.subopcode; break;
|
||||
case BRK_TYPE : sym = vue->cpu.access.type ; break;
|
||||
case BRK_VALUE : sym = vue->cpu.access.value ; break;
|
||||
}
|
||||
stack[size++] = sym;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Return the remaining stack value
|
||||
return stack[0];
|
||||
}
|
||||
|
||||
// Determine whether the breakpoint applies to a given address range
|
||||
static vbool brkInRange(Breakpoint *brk, uint32_t start, uint32_t end) {
|
||||
|
||||
// Implicit inclusion
|
||||
if (brk->numRanges == 0)
|
||||
return VUE_TRUE;
|
||||
|
||||
// Check all ranges
|
||||
for (int x = 0; x < brk->numRanges; x++) {
|
||||
uint32_t *range = &brk->ranges[x * 2];
|
||||
if (
|
||||
start - range[0] <= range[1] - range[0] ||
|
||||
range[0] - start <= end - start
|
||||
) return VUE_TRUE;
|
||||
}
|
||||
return VUE_FALSE;
|
||||
}
|
||||
|
||||
// Check for breakpoints
|
||||
static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) {
|
||||
Core *core = (Core *) vue->tag;
|
||||
uint32_t end = 0;
|
||||
vbool ranged = VUE_FALSE;
|
||||
uint32_t start = 0;
|
||||
core->breakType = breakType;
|
||||
|
||||
// Processing for Execute
|
||||
if (breakType == BRK_EXECUTE) {
|
||||
ranged = VUE_TRUE;
|
||||
start = vue->cpu.pc;
|
||||
end = start + vue->cpu.inst.size - 1;
|
||||
}
|
||||
|
||||
// Processing for Read and Write
|
||||
else if (breakType == BRK_READ || breakType == BRK_WRITE) {
|
||||
ranged = VUE_TRUE;
|
||||
start = vue->cpu.access.address;
|
||||
end = start + TYPE_SIZES[vue->cpu.access.type] - 1;
|
||||
}
|
||||
|
||||
// Check all breakpoints
|
||||
int32_t fetch = breakType == BRK_READ && vue->cpu.fetch != -1 ? 1 : 0;
|
||||
for (int x = 0; x < core->numBreakpoints; x++) {
|
||||
Breakpoint *brk = &core->breakpoints[x];
|
||||
if (
|
||||
brk->enabled && (brk->fetch || !fetch) &&
|
||||
(breakType == 0 || (breakType & brk->hooks) != 0) &&
|
||||
(!ranged || brkInRange(brk, start, end)) &&
|
||||
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
|
||||
) return x + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for exception breakpoints
|
||||
static int32_t brkOnException(Vue *vue, uint16_t code) {
|
||||
return brkOnBreakpoint(vue, BRK_EXCEPTION);
|
||||
}
|
||||
|
||||
// Check for execute breakpoints
|
||||
static int32_t brkOnExecute(Vue *vue, VueInstruction *inst) {
|
||||
return brkOnBreakpoint(vue, BRK_EXECUTE);
|
||||
}
|
||||
|
||||
// Check for frame breakpoints
|
||||
static int32_t brkOnFrame(Vue *vue) {
|
||||
return brkOnBreakpoint(vue, BRK_FRAME);
|
||||
}
|
||||
|
||||
// Check for read breakpoints
|
||||
static int32_t brkOnRead(Vue *vue, VueAccess *acc) {
|
||||
return brkOnBreakpoint(vue, BRK_READ);
|
||||
}
|
||||
|
||||
// Check for write breakpoints
|
||||
static int32_t brkOnWrite(Vue *vue, VueAccess *acc) {
|
||||
return brkOnBreakpoint(vue, BRK_WRITE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // NATIVEVUE
|
||||
@@ -1,38 +0,0 @@
|
||||
package vue;
|
||||
|
||||
// Simulation of the Game Pak
|
||||
class GamePak {
|
||||
|
||||
// Package fields
|
||||
byte[] ram; // Cartridge SRAM
|
||||
byte[] rom; // Cartridge ROM
|
||||
int wcr_exp1w; // Expansion one-wait mode
|
||||
int wcr_rom1w; // ROM one-wait mode
|
||||
|
||||
// Private fields
|
||||
private JavaVue vue; // Emulation state
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
GamePak(JavaVue vue) {
|
||||
this.vue = vue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// System reset
|
||||
void reset() {
|
||||
wcr_exp1w = 0;
|
||||
wcr_rom1w = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
package vue;
|
||||
|
||||
// Instruction state and decoder
|
||||
public class Instruction {
|
||||
|
||||
// Instruction fields
|
||||
public int bits; // Binary encoding
|
||||
public int cond; // Condition for Bcond
|
||||
public int disp; // Displacement for jumps and branches
|
||||
public int format; // Binary format
|
||||
public int id; // Library-specific identifier
|
||||
public int imm; // Immediate operand
|
||||
public int opcode; // Instruction opcode
|
||||
public int reg1; // Source/right register
|
||||
public int reg2; // Destination/left register
|
||||
public int size; // Number of bytes in encoding
|
||||
public int subopcode; // Instruction subopcode
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Intermediate instruction IDs
|
||||
private static final int BITSTRING = -2;
|
||||
private static final int FLOATENDO = -3;
|
||||
|
||||
// Opcode lookup table
|
||||
private static final byte[] LOOKUP_OPCODE = {
|
||||
Vue.MOV_REG, 1, Vue.ADD_REG, 1, Vue.SUB , 1, Vue.CMP_REG, 1,
|
||||
Vue.SHL_REG, 1, Vue.SHR_REG, 1, Vue.JMP , 1, Vue.SAR_REG, 1,
|
||||
Vue.MUL , 1, Vue.DIV , 1, Vue.MULU , 1, Vue.DIVU , 1,
|
||||
Vue.OR , 1, Vue.AND , 1, Vue.XOR , 1, Vue.NOT , 1,
|
||||
Vue.MOV_IMM,-2, Vue.ADD_IMM,-2, Vue.SETF , 2, Vue.CMP_IMM,-2,
|
||||
Vue.SHL_IMM, 2, Vue.SHR_IMM, 2, Vue.CLI , 2, Vue.SAR_IMM, 2,
|
||||
Vue.TRAP , 2, Vue.RETI , 2, Vue.HALT , 2, Vue.ILLEGAL, 0,
|
||||
Vue.LDSR , 2, Vue.STSR , 2, Vue.SEI , 2, BITSTRING , 2,
|
||||
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
|
||||
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
|
||||
Vue.MOVEA ,-5, Vue.ADDI ,-5, Vue.JR , 4, Vue.JAL , 4,
|
||||
Vue.ORI , 5, Vue.ANDI , 5, Vue.XORI , 5, Vue.MOVHI , 5,
|
||||
Vue.LD_B , 6, Vue.LD_H , 6, Vue.ILLEGAL, 0, Vue.LD_W , 6,
|
||||
Vue.ST_B , 6, Vue.ST_H , 6, Vue.ILLEGAL, 0, Vue.ST_W , 6,
|
||||
Vue.IN_B , 6, Vue.IN_H , 6, Vue.CAXI , 6, Vue.IN_W , 6,
|
||||
Vue.OUT_B , 6, Vue.OUT_H , 6, FLOATENDO , 7, Vue.OUT_W , 6
|
||||
};
|
||||
|
||||
// Bit string lookup table
|
||||
private static final byte[] LOOKUP_BITSTRING = {
|
||||
Vue.SCH0BSU, Vue.SCH0BSD, Vue.SCH1BSU, Vue.SCH1BSD,
|
||||
Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL,
|
||||
Vue.ORBSU , Vue.ANDBSU , Vue.XORBSU , Vue.MOVBSU ,
|
||||
Vue.ORNBSU , Vue.ANDNBSU, Vue.XORNBSU, Vue.XORNBSU
|
||||
};
|
||||
|
||||
// Floating-point and Nintendo lookup table
|
||||
private static final byte[] LOOKUP_FLOATENDO = {
|
||||
Vue.CMPF_S , Vue.ILLEGAL, Vue.CVT_WS , Vue.CVT_SW ,
|
||||
Vue.ADDF_S , Vue.SUBF_S , Vue.MULF_S , Vue.DIVF_S ,
|
||||
Vue.XB , Vue.XH , Vue.REV , Vue.TRNC_SW,
|
||||
Vue.MPYHW , Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL
|
||||
};
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Static Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Determine the size of an instruction given its opcode
|
||||
public static int size(int opcode) {
|
||||
return Math.abs(LOOKUP_OPCODE[opcode << 1 | 1]) < 4 ? 2 : 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Default constructor
|
||||
public Instruction() { }
|
||||
|
||||
// Decoding constructor
|
||||
public Instruction(int bits) {
|
||||
decode(bits);
|
||||
}
|
||||
|
||||
// Cloning constructor
|
||||
Instruction(Instruction o) {
|
||||
this();
|
||||
bits = o.bits;
|
||||
cond = o.cond;
|
||||
disp = o.disp;
|
||||
format = o.format;
|
||||
id = o.id;
|
||||
imm = o.imm;
|
||||
opcode = o.opcode;
|
||||
reg1 = o.reg1;
|
||||
reg2 = o.reg2;
|
||||
size = o.size;
|
||||
subopcode = o.subopcode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Produce an access descriptor from the current instruction state
|
||||
public Access access(Vue vue) {
|
||||
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
|
||||
switch (id) {
|
||||
case Vue.CAXI : read = true; break;
|
||||
case Vue.IN_B : ret.type = Vue.U8 ; read = true; break;
|
||||
case Vue.IN_H : ret.type = Vue.U16; read = true; break;
|
||||
case Vue.IN_W : read = true; break;
|
||||
case Vue.LD_B : ret.type = Vue.S8 ; read = true; break;
|
||||
case Vue.LD_H : ret.type = Vue.S16; read = true; break;
|
||||
case Vue.LD_W : read = true; break;
|
||||
case Vue.OUT_B: ret.type = Vue.U8 ; break;
|
||||
case Vue.OUT_H: ret.type = Vue.U16; break;
|
||||
case Vue.OUT_W: break;
|
||||
case Vue.ST_B : ret.type = Vue.S8 ; break;
|
||||
case Vue.ST_H : ret.type = Vue.S16; break;
|
||||
case Vue.ST_W : break;
|
||||
// TODO: Bit strings
|
||||
default: return null;
|
||||
}
|
||||
|
||||
// Read the value currently on the bus
|
||||
if (read)
|
||||
ret.value = vue.read(ret.address, ret.type);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Decode an instruction from a byte array
|
||||
public Instruction decode(byte[] data, int offset) {
|
||||
return decode(
|
||||
(data[offset + 0] & 0xFF) |
|
||||
(data[offset + 1] & 0xFF) << 8 |
|
||||
(data[offset + 2] & 0xFF) << 16 |
|
||||
(data[offset + 3] & 0xFF) << 24
|
||||
);
|
||||
}
|
||||
|
||||
// Decode an instruction from its binary encoding (swapped halfwords)
|
||||
public Instruction decode(int bits) {
|
||||
byte extend; // Sign-extend the immediate operand
|
||||
int x; // Working variable
|
||||
|
||||
// Configure instance fields
|
||||
this.bits = bits = bits << 16 | bits >>> 16;
|
||||
opcode = bits >> 26 & 63;
|
||||
x = opcode << 1;
|
||||
id = LOOKUP_OPCODE[x ];
|
||||
extend = LOOKUP_OPCODE[x + 1];
|
||||
format = extend < 0 ? -extend : extend;
|
||||
size = format < 4 ? 2 : 4;
|
||||
|
||||
// Decode by format
|
||||
switch (format) {
|
||||
case 1:
|
||||
reg2 = bits >> 21 & 31;
|
||||
reg1 = bits >> 16 & 31;
|
||||
break;
|
||||
case 2:
|
||||
reg2 = bits >> 21 & 31;
|
||||
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
|
||||
if (id == BITSTRING) {
|
||||
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
|
||||
subopcode = imm;
|
||||
}
|
||||
if (id == Vue.SETF)
|
||||
cond = imm & 15;
|
||||
break;
|
||||
case 3:
|
||||
opcode = 0x20;
|
||||
cond = bits >> 25 & 15;
|
||||
disp = bits << 7 >> 23;
|
||||
break;
|
||||
case 4:
|
||||
disp = bits << 6 >> 6;
|
||||
break;
|
||||
case 5:
|
||||
reg2 = bits >> 21 & 31;
|
||||
reg1 = bits >> 16 & 31;
|
||||
imm = extend < 0 ? bits << 16 >> 16 : bits & 0xFFFF;
|
||||
break;
|
||||
case 6:
|
||||
reg2 = bits >> 21 & 31;
|
||||
reg1 = bits >> 16 & 31;
|
||||
disp = bits << 16 >> 16;
|
||||
break;
|
||||
case 7:
|
||||
reg2 = bits >> 21 & 31;
|
||||
reg1 = bits >> 16 & 31;
|
||||
subopcode = bits >> 10 & 63;
|
||||
id = subopcode >= 16 ? Vue.ILLEGAL :
|
||||
LOOKUP_FLOATENDO[subopcode];
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||