Tweaks from concurrent project
This commit is contained in:
parent
3cf006ba13
commit
7d31c55391
|
@ -79,8 +79,12 @@ class App extends Toolkit {
|
|||
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+G: Goto (also works in Memory window)");
|
||||
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 " +
|
||||
|
|
|
@ -100,6 +100,7 @@ class Line {
|
|||
constructor(parent, first) {
|
||||
|
||||
// Configure instance fields
|
||||
this.first = first;
|
||||
this.parent = parent;
|
||||
|
||||
// Configure labels
|
||||
|
@ -133,7 +134,7 @@ class Line {
|
|||
}
|
||||
|
||||
// Update style according to selection
|
||||
let method = isPC ? "add" : "remove";
|
||||
let method = row && isPC ? "add" : "remove";
|
||||
this.lblAddress .classList[method]("tk-selected");
|
||||
this.lblBytes .classList[method]("tk-selected");
|
||||
this.lblMnemonic.classList[method]("tk-selected");
|
||||
|
@ -142,11 +143,33 @@ class Line {
|
|||
|
||||
// Specify whether the elements on this line are visible
|
||||
setVisible(visible) {
|
||||
visible = visible ? "block" : "none";
|
||||
this.lblAddress .style.display = visible;
|
||||
this.lblBytes .style.display = visible;
|
||||
this.lblMnemonic.style.display = visible;
|
||||
this.lblOperands.style.display = 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";
|
||||
}
|
||||
|
||||
|
||||
|
@ -214,6 +237,7 @@ class Disassembler extends Toolkit.ScrollPane {
|
|||
this.columns = [ 0, 0, 0, 0 ];
|
||||
this.data = [];
|
||||
this.debug = debug;
|
||||
this.hasBytes = true;
|
||||
this.isSubscribed = false;
|
||||
this.lines = null;
|
||||
this.pc = this.address;
|
||||
|
@ -294,8 +318,30 @@ class Disassembler extends Toolkit.ScrollPane {
|
|||
onKeyDown(e) {
|
||||
let tall = this.tall(false);
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
|
||||
// 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;
|
||||
|
@ -309,13 +355,6 @@ class Disassembler extends Toolkit.ScrollPane {
|
|||
case "ArrowRight": this.horizontal.setValue(
|
||||
this.horizontal.value + this.horizontal.increment); break;
|
||||
|
||||
// Goto
|
||||
case "g": case "G":
|
||||
if (!e.ctrlKey)
|
||||
return;
|
||||
this.promptGoto();
|
||||
break;
|
||||
|
||||
// Single step
|
||||
case "F10":
|
||||
this.debug.runNext();
|
||||
|
@ -593,6 +632,16 @@ class Disassembler extends Toolkit.ScrollPane {
|
|||
);
|
||||
}
|
||||
|
||||
// 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 ? "-" : "";
|
||||
|
@ -629,12 +678,29 @@ class Disassembler extends Toolkit.ScrollPane {
|
|||
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.viewport.getBoundingClientRect().height / lineHeight));
|
||||
this.getBounds().height / lineHeight));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@ 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,
|
||||
|
@ -46,11 +49,13 @@ class Line {
|
|||
|
||||
// Update the elements' display
|
||||
refresh() {
|
||||
let address = Util.u32(this.parent.address + this.index * 16);
|
||||
let data = this.parent.data;
|
||||
let dataAddress = this.parent.dataAddress;
|
||||
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 = Util.s32(address - dataAddress);
|
||||
let offset =
|
||||
(this.parent.row(address) - this.parent.row(dataAddress)) * 16;
|
||||
|
||||
// Format the line's address
|
||||
let text = address.toString(16).padStart(8, "0");
|
||||
|
@ -59,9 +64,12 @@ class Line {
|
|||
this.lblAddress.innerText = text;
|
||||
|
||||
// The line's data is not available
|
||||
if (offset < 0 || offset >= data.length)
|
||||
for (let lbl of this.lblBytes)
|
||||
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++) {
|
||||
|
@ -69,14 +77,14 @@ class Line {
|
|||
text = data[offset].toString(16).padStart(2, "0");
|
||||
|
||||
// The byte is the current selection
|
||||
if (Util.u32(address + x) == this.parent.selection) {
|
||||
lbl.classList.add("selected");
|
||||
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("selected");
|
||||
else lbl.classList.remove("tk-selected");
|
||||
|
||||
// Update the label's text
|
||||
if (hexCaps)
|
||||
|
@ -103,41 +111,59 @@ class Line {
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Memory hex editor
|
||||
class Memory extends Toolkit.ScrollPane {
|
||||
class Memory extends Toolkit.Component {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(debug) {
|
||||
super(debug.app, {
|
||||
className : "tk tk-scrollpane tk-memory",
|
||||
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
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.address = 0x05000000;
|
||||
this.app = debug.app;
|
||||
this.dasm = debug.disassembler;
|
||||
this.data = [];
|
||||
this.dataAddress = this.address;
|
||||
this.debug = debug;
|
||||
this.digit = null;
|
||||
this.isSubscribed = false;
|
||||
this.lines = [];
|
||||
this.selection = this.address;
|
||||
this.sim = debug.sim;
|
||||
this.append(this.editor);
|
||||
|
||||
// Configure view
|
||||
let view = document.createElement("div");
|
||||
view.className = "tk tk-view";
|
||||
Object.assign(view.style, {
|
||||
this.view = document.createElement("div");
|
||||
this.view.className = "tk tk-view";
|
||||
Object.assign(this.view.style, {
|
||||
display : "grid",
|
||||
gridTemplateColumns: "repeat(17, max-content)"
|
||||
});
|
||||
this.setView(view);
|
||||
this.editor.setView(this.view);
|
||||
|
||||
// Font-measuring element
|
||||
this.metrics = new Toolkit.Component(this.app, {
|
||||
|
@ -152,7 +178,7 @@ class Memory extends Toolkit.ScrollPane {
|
|||
this.append(this.metrics.element);
|
||||
|
||||
// Configure event handlers
|
||||
Toolkit.addResizeListener(this.viewport, e=>this.onResize(e));
|
||||
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));
|
||||
|
@ -164,23 +190,25 @@ class Memory extends Toolkit.ScrollPane {
|
|||
|
||||
// Typed a digit
|
||||
onDigit(digit) {
|
||||
let bus = this[this.bus];
|
||||
|
||||
// Begin an edit
|
||||
if (this.digit === null) {
|
||||
this.digit = digit;
|
||||
this.setSelection(this.selection, true);
|
||||
this.setSelection(bus.selection, true);
|
||||
}
|
||||
|
||||
// Complete an edit
|
||||
else {
|
||||
this.digit = this.digit << 4 | digit;
|
||||
this.setSelection(this.selection + 1);
|
||||
this.setSelection(bus.selection + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Key press
|
||||
onKeyDown(e) {
|
||||
let bus = this[this.bus];
|
||||
let key = e.key;
|
||||
|
||||
// A hex digit was entered
|
||||
|
@ -189,39 +217,44 @@ class Memory extends Toolkit.ScrollPane {
|
|||
key = "digit";
|
||||
}
|
||||
|
||||
// Processing by key
|
||||
switch (key) {
|
||||
// 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(this.selection + 16); break;
|
||||
case "ArrowLeft" : this.setSelection(this.selection - 1); break;
|
||||
case "ArrowRight": this.setSelection(this.selection + 1); break;
|
||||
case "ArrowUp" : this.setSelection(this.selection - 16); break;
|
||||
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(this.selection);
|
||||
break;
|
||||
|
||||
// Goto
|
||||
case "g": case "G":
|
||||
if (!e.ctrlKey)
|
||||
return;
|
||||
this.promptGoto();
|
||||
this.setSelection(bus.selection);
|
||||
break;
|
||||
|
||||
// Page key navigation
|
||||
case "PageDown":
|
||||
this.setSelection(this.selection + this.tall(false) * 16);
|
||||
this.setSelection(bus.selection + this.tall(false) * 16);
|
||||
break;
|
||||
case "PageUp":
|
||||
this.setSelection(this.selection - this.tall(false) * 16);
|
||||
this.setSelection(bus.selection - this.tall(false) * 16);
|
||||
break;
|
||||
|
||||
// Hex digit: already processed
|
||||
case "digit": break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
|
||||
|
@ -243,15 +276,14 @@ class Memory extends Toolkit.ScrollPane {
|
|||
return;
|
||||
|
||||
// Update the display address
|
||||
this.address = Util.u32(this.address + offset);
|
||||
this.fetch(this.address, true);
|
||||
this.fetch(this[this.bus].address + offset, true);
|
||||
}
|
||||
|
||||
// Pointer down
|
||||
onPointerDown(e) {
|
||||
|
||||
// Common handling
|
||||
this.focus();
|
||||
this.editor.focus();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -261,8 +293,11 @@ class Memory extends Toolkit.ScrollPane {
|
|||
|
||||
// Determine the row that was clicked on
|
||||
let lineHeight = !this.metrics ? 0 :
|
||||
Math.max(1, Math.ceil(this.metrics.getBounds().height));
|
||||
let y = Math.floor((e.y - this.getBounds().top) / lineHeight);
|
||||
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;
|
||||
|
@ -274,7 +309,7 @@ class Memory extends Toolkit.ScrollPane {
|
|||
// 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.address + y * 16 + x);
|
||||
this.setSelection(this[this.bus].address + y * 16 + x);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -303,12 +338,12 @@ class Memory extends Toolkit.ScrollPane {
|
|||
this.lines[x].setVisible(false);
|
||||
|
||||
// Configure horizontal scroll bar
|
||||
if (this.metrics)
|
||||
this.horizontal.setIncrement(this.metrics.getBounds().width);
|
||||
if (this.metrics) this.editor.horizontal
|
||||
.setIncrement(this.metrics.getBounds().width);
|
||||
|
||||
// Update the display
|
||||
if (fetch)
|
||||
this.fetch(this.address, true);
|
||||
this.fetch(this[this.bus].address, true);
|
||||
else this.refresh();
|
||||
}
|
||||
|
||||
|
@ -318,11 +353,12 @@ class Memory extends Toolkit.ScrollPane {
|
|||
|
||||
// Update with memory state from the core
|
||||
refresh(data) {
|
||||
let bus = this[this.bus];
|
||||
|
||||
// Update with data from the core thread
|
||||
if (data) {
|
||||
this.data = data.bytes;
|
||||
this.dataAddress = data.address;
|
||||
bus.data = data.bytes;
|
||||
bus.dataAddress = data.address;
|
||||
}
|
||||
|
||||
// Update elements
|
||||
|
@ -343,7 +379,7 @@ class Memory extends Toolkit.ScrollPane {
|
|||
|
||||
// Subscribe to core updates
|
||||
if (subscribed)
|
||||
this.fetch(this.address);
|
||||
this.fetch(this[this.bus].address);
|
||||
|
||||
// Unsubscribe from core updates
|
||||
else this.sim.unsubscribe("memory");
|
||||
|
@ -366,7 +402,7 @@ class Memory extends Toolkit.ScrollPane {
|
|||
async fetch(address, prefresh) {
|
||||
|
||||
// Configure instance fields
|
||||
this.address = address;
|
||||
this[this.bus].address = address = this.mask(address);
|
||||
|
||||
// Update the view immediately
|
||||
if (prefresh)
|
||||
|
@ -382,6 +418,11 @@ class Memory extends Toolkit.ScrollPane {
|
|||
);
|
||||
}
|
||||
|
||||
// Mask an address according to the current bus
|
||||
mask(address) {
|
||||
return Util.u32(address);
|
||||
}
|
||||
|
||||
// Prompt the user to specify a new address
|
||||
promptGoto() {
|
||||
|
||||
|
@ -397,17 +438,23 @@ class Memory extends Toolkit.ScrollPane {
|
|||
|
||||
// The address is not currently visible in the output
|
||||
let tall = this.tall(false);
|
||||
if (Util.u32(address - this.address) >= tall * 16) {
|
||||
this.fetch(Util.u32(
|
||||
(address & 0xFFFFFFF0) - Math.floor(tall / 3) * 16));
|
||||
}
|
||||
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(Util.u32(address));
|
||||
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
|
||||
|
@ -418,27 +465,27 @@ class Memory extends Toolkit.ScrollPane {
|
|||
}
|
||||
|
||||
// Configure instance fields
|
||||
this.selection = address = Util.u32(address);
|
||||
bus.selection = address = this.mask(address);
|
||||
|
||||
// Working variables
|
||||
let row = Util.s32(address - this.address & 0xFFFFFFF0) / 16;
|
||||
let row = this.row(address);
|
||||
|
||||
// The new address is above the top line of output
|
||||
if (row < 0) {
|
||||
this.fetch(Util.u32(this.address + row * 16), true);
|
||||
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(Util.u32(address - tall * 16 + 16 & 0xFFFFFFF0), true);
|
||||
this.fetch(address - tall * 16 + 16 & 0xFFFFFFF0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the display
|
||||
if (fetch)
|
||||
this.fetch(this.address, true);
|
||||
this.fetch(bus.address, true);
|
||||
else this.refresh();
|
||||
}
|
||||
|
||||
|
@ -447,14 +494,17 @@ class Memory extends Toolkit.ScrollPane {
|
|||
let lineHeight = !this.metrics ? 0 :
|
||||
Math.ceil(this.metrics.getBounds().height);
|
||||
return lineHeight <= 0 ? 1 : Math.max(1, Math[partial?"ceil":"floor"](
|
||||
this.viewport.getBoundingClientRect().height / lineHeight));
|
||||
this.editor.getBounds().height / lineHeight));
|
||||
}
|
||||
|
||||
// Write a value to the core thread
|
||||
write(value) {
|
||||
this.data[Util.s32(this.selection - this.dataAddress)] = 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(
|
||||
this.selection,
|
||||
bus.selection,
|
||||
Uint8Array.from([ value ]), {
|
||||
refresh: true
|
||||
});
|
||||
|
|
|
@ -124,24 +124,20 @@ class Register {
|
|||
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.type = HEX;
|
||||
this.value = 0x00000000;
|
||||
|
||||
// Configure main controls
|
||||
this.contents = new Toolkit.Component(this.app, {
|
||||
className: "tk tk-contents",
|
||||
tagName : "div",
|
||||
style : {
|
||||
display : "grid",
|
||||
gridTemplateColumns: "max-content auto max-content"
|
||||
}
|
||||
});
|
||||
// Establish elements
|
||||
let row = document.createElement("tr");
|
||||
let cell;
|
||||
list.view.append(row);
|
||||
|
||||
// Processing by type
|
||||
this[this.system ? "initSystem" : "initProgram"]();
|
||||
|
@ -151,7 +147,10 @@ class Register {
|
|||
className: "tk tk-expand tk-mono",
|
||||
tagName : "div"
|
||||
});
|
||||
this.list.view.append(this.btnExpand);
|
||||
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");
|
||||
|
@ -161,7 +160,9 @@ class Register {
|
|||
innerText: this.dasm.sysregCaps?this.name:this.name.toLowerCase()
|
||||
});
|
||||
this.lblName.style.userSelect = "none";
|
||||
this.list.view.append(this.lblName);
|
||||
row .append(cell = document.createElement("td"));
|
||||
cell.className = "tk";
|
||||
cell.append(this.lblName);
|
||||
|
||||
// Value text box
|
||||
this.txtValue = new Toolkit.TextBox(this.app, {
|
||||
|
@ -171,7 +172,13 @@ class Register {
|
|||
this.txtValue.setAttribute("aria-labelledby", this.lblName.id);
|
||||
this.txtValue.setAttribute("digits", "8");
|
||||
this.txtValue.addEventListener("action", e=>this.onValue());
|
||||
this.list.view.append(this.txtValue);
|
||||
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)
|
||||
|
@ -226,17 +233,23 @@ class Register {
|
|||
let two = this.index == ECR || this.index == PIR;
|
||||
|
||||
// Establish expansion element
|
||||
let exp = this.expansion = new Toolkit.Component(this.app, {
|
||||
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 : "none",
|
||||
gridColumnEnd : "span 3",
|
||||
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) {
|
||||
|
@ -256,7 +269,7 @@ class Register {
|
|||
|
||||
// Configure event handler
|
||||
ctrl.addEventListener("action",
|
||||
e=>this.setType(e.component.format));
|
||||
e=>this.setFormat(e.component.format));
|
||||
|
||||
// Add the control to the element
|
||||
let box = document.createElement("div");
|
||||
|
@ -405,7 +418,7 @@ class Register {
|
|||
let value;
|
||||
|
||||
// Processing by type
|
||||
switch (this.type) {
|
||||
switch (this.format) {
|
||||
|
||||
// Unsigned hexadecimal
|
||||
case HEX:
|
||||
|
@ -431,25 +444,6 @@ class Register {
|
|||
this.setValue(isNaN(value) ? this.value : value);
|
||||
}
|
||||
|
||||
// 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.element.style.display =
|
||||
expanded ? "inline-grid" : "none";
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
@ -483,17 +477,10 @@ class Register {
|
|||
let text;
|
||||
|
||||
// Configure instance fields
|
||||
this.value = value;
|
||||
this.value = value = Util.u32(value);
|
||||
|
||||
// Value text box
|
||||
if (this.type == HEX) {
|
||||
this.txtValue.element.classList.add("tk-mono");
|
||||
this.txtValue.setMaxLength(8);
|
||||
} else {
|
||||
this.txtValue.element.classList.remove("tk-mono");
|
||||
this.txtValue.setMaxLength(null);
|
||||
}
|
||||
switch (this.type) {
|
||||
switch (this.format) {
|
||||
|
||||
// Unsigned hexadecimal
|
||||
case HEX:
|
||||
|
@ -547,15 +534,70 @@ class Register {
|
|||
|
||||
}
|
||||
|
||||
// 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
|
||||
setType(type) {
|
||||
if (type == this.type)
|
||||
setFormat(format) {
|
||||
if (format == this.format)
|
||||
return;
|
||||
this.type = type;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -604,6 +646,7 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
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;
|
||||
|
@ -611,10 +654,9 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
// Configure view element
|
||||
this.setView(new Toolkit.Component(debug.app, {
|
||||
className: "tk tk-list",
|
||||
tagName : "div",
|
||||
tagName : "table",
|
||||
style : {
|
||||
display : "grid",
|
||||
gridTemplateColumns: "max-content auto max-content"
|
||||
width: "100%"
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -647,27 +689,27 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
|
||||
// Initialize a list of program registers
|
||||
initProgram() {
|
||||
this[0] = new Register(this, 0, 0x00000000, 0x00000000);
|
||||
this.add(new Register(this, 0, 0x00000000, 0x00000000));
|
||||
for (let x = 1; x < 32; x++)
|
||||
this[x] = new Register(this, x, 0xFFFFFFFF, 0x00000000);
|
||||
this.add(new Register(this, x, 0xFFFFFFFF, 0x00000000));
|
||||
}
|
||||
|
||||
// Initialie a list of system registers
|
||||
initSystem() {
|
||||
this[PC ] = new Register(this, PC , 0xFFFFFFFE, 0x00000000);
|
||||
this[PSW ] = new Register(this, PSW , 0x000FF3FF, 0x00000000);
|
||||
this[ADTRE] = new Register(this, ADTRE, 0xFFFFFFFE, 0x00000000);
|
||||
this[CHCW ] = new Register(this, CHCW , 0x00000002, 0x00000000);
|
||||
this[ECR ] = new Register(this, ECR , 0xFFFFFFFF, 0x00000000);
|
||||
this[EIPC ] = new Register(this, EIPC , 0xFFFFFFFE, 0x00000000);
|
||||
this[EIPSW] = new Register(this, EIPSW, 0x000FF3FF, 0x00000000);
|
||||
this[FEPC ] = new Register(this, FEPC , 0xFFFFFFFE, 0x00000000);
|
||||
this[FEPSW] = new Register(this, FEPSW, 0x000FF3FF, 0x00000000);
|
||||
this[PIR ] = new Register(this, PIR , 0x00000000, 0x00005346);
|
||||
this[TKCW ] = new Register(this, TKCW , 0x00000000, 0x000000E0);
|
||||
this[29 ] = new Register(this, 29 , 0xFFFFFFFF, 0x00000000);
|
||||
this[30 ] = new Register(this, 30 , 0x00000000, 0x00000004);
|
||||
this[31 ] = new Register(this, 31 , 0xFFFFFFFF, 0x00000000);
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -725,14 +767,9 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
let width = Math.ceil(bounds.width);
|
||||
let height = Math.ceil(bounds.height / 32);
|
||||
|
||||
// Resize all monospaced text boxes
|
||||
for (let box of this.element
|
||||
.querySelectorAll(".tk-textbox[digits]")) {
|
||||
Object.assign(box.style, {
|
||||
height: height + "px",
|
||||
width : (parseInt(box.getAttribute("digits")) * width) + "px"
|
||||
});
|
||||
}
|
||||
// Resize all text boxes
|
||||
for (let reg of this.registers)
|
||||
reg.setMetrics(width, height);
|
||||
|
||||
// Update scroll bars
|
||||
this.horizontal.setIncrement(height);
|
||||
|
@ -765,20 +802,8 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
|
||||
// System registers
|
||||
if (this.system) {
|
||||
this[ADTRE].refresh(registers.adtre);
|
||||
this[CHCW ].refresh(registers.chcw );
|
||||
this[ECR ].refresh(registers.ecr );
|
||||
this[EIPC ].refresh(registers.eipc );
|
||||
this[EIPSW].refresh(registers.eipsw);
|
||||
this[FEPC ].refresh(registers.fepc );
|
||||
this[FEPSW].refresh(registers.fepsw);
|
||||
this[PC ].refresh(registers.pc );
|
||||
this[PIR ].refresh(registers.pir );
|
||||
this[PSW ].refresh(registers.psw );
|
||||
this[TKCW ].refresh(registers.tkcw );
|
||||
this[29 ].refresh(registers[29] );
|
||||
this[30 ].refresh(registers[30] );
|
||||
this[31 ].refresh(registers[31] );
|
||||
for (let reg of Object.entries(SYSREGS))
|
||||
this[reg[0]].refresh(registers[reg[1].toLowerCase()]);
|
||||
}
|
||||
|
||||
// Program registers
|
||||
|
@ -811,13 +836,8 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
|
||||
// Disassembler settings have been updated
|
||||
dasmChanged() {
|
||||
if (this.system) {
|
||||
for (let key of Object.keys(SYSREGS))
|
||||
this[key].dasmChanged();
|
||||
} else {
|
||||
for (let key = 0; key < 32; key++)
|
||||
this[key].dasmChanged();
|
||||
}
|
||||
for (let reg of this.registers)
|
||||
reg.dasmChanged();
|
||||
}
|
||||
|
||||
// Determine the initial size of the register list
|
||||
|
@ -836,7 +856,7 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
|
||||
// Locate the bottom of PSW
|
||||
if (this.system && this[PSW].expansion) {
|
||||
ret.height = this[PSW].expansion.getBounds().bottom -
|
||||
ret.height = this[PSW].expansion.getBoundingClientRect().bottom -
|
||||
this.view.getBounds().top;
|
||||
}
|
||||
|
||||
|
@ -847,6 +867,12 @@ class RegisterList extends Toolkit.ScrollPane {
|
|||
|
||||
///////////////////////////// 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(
|
||||
|
|
|
@ -321,7 +321,6 @@ new class CoreWorker {
|
|||
address: address,
|
||||
bytes : [ bits & 0xFF, bits >> 8 ],
|
||||
size : u32(address + 2) == pc ? 2 : size
|
||||
//size : Math.min(u32(pc - address), size)
|
||||
};
|
||||
|
||||
// Read additional bytes
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
padding : 0;
|
||||
}
|
||||
|
||||
table.tk {
|
||||
border : none;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.tk-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -106,6 +111,19 @@
|
|||
|
||||
|
||||
|
||||
/****************************** 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 {
|
||||
|
@ -526,7 +544,7 @@
|
|||
|
||||
|
||||
|
||||
/******************************* Disassembler ********************************/
|
||||
/************************************ CPU ************************************/
|
||||
|
||||
.tk-cpu .tk-main {
|
||||
height: 100%;
|
||||
|
@ -654,18 +672,21 @@
|
|||
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 {
|
||||
column-gap: 0.8em;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-number .tk-label {
|
||||
align-items : center;
|
||||
display : flex;
|
||||
|
@ -673,10 +694,6 @@
|
|||
min-width : 12px;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-textbox {
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
.tk-reglist .tk-expansion .tk-checkbox[aria-disabled="true"][aria-checked="true"]
|
||||
.tk-icon:before {
|
||||
background: var(--tk-control-shadow);
|
||||
|
@ -694,6 +711,11 @@
|
|||
/********************************** 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;
|
||||
|
@ -714,23 +736,26 @@
|
|||
}
|
||||
|
||||
.tk-memory .tk-byte {
|
||||
border : 0 solid transparent;
|
||||
margin-left: calc(0.6em - 1px);
|
||||
padding : 0 1px;
|
||||
text-align : center;
|
||||
border : 0 solid transparent;
|
||||
padding : 0 1px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tk-memory .tk-byte.tk-0,
|
||||
.tk-memory .tk-byte.tk-8 {
|
||||
margin-left: calc(1.2em - 1px);
|
||||
.tk-memory .tk-byte:not(.tk-15) {
|
||||
margin-right: calc(0.6em - 1px);
|
||||
}
|
||||
|
||||
.tk-memory .tk-byte.selected {
|
||||
.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:focus-within .tk-byte.selected {
|
||||
.tk-memory .tk-editor:focus-within .tk-byte.tk-selected {
|
||||
background: var(--tk-selected);
|
||||
color : var(--tk-selected-text);
|
||||
}
|
||||
|
|
|
@ -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 };
|
|
@ -1,5 +1,6 @@
|
|||
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" ;
|
||||
|
@ -26,6 +27,7 @@ let Toolkit = globalThis.Toolkit = (class GUI extends Component {
|
|||
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);
|
||||
|
@ -34,6 +36,7 @@ let Toolkit = globalThis.Toolkit = (class GUI extends Component {
|
|||
this.CheckBox = CheckBox;
|
||||
this.Component = Component;
|
||||
this.Desktop = Desktop;
|
||||
this.DropDown = DropDown;
|
||||
this.Group = Group;
|
||||
this.Menu = Menu;
|
||||
this.MenuBar = MenuBar;
|
||||
|
|
18
core/bus.c
18
core/bus.c
|
@ -6,7 +6,7 @@
|
|||
/***************************** Utility Functions *****************************/
|
||||
|
||||
/* Read a data unit from a memory buffer */
|
||||
static int32_t busReadMemory(uint8_t *mem, int type) {
|
||||
static int32_t busReadBuffer(uint8_t *mem, int type) {
|
||||
|
||||
/* Little-endian implementation */
|
||||
#ifdef VB_LITTLEENDIAN
|
||||
|
@ -33,7 +33,7 @@ static int32_t busReadMemory(uint8_t *mem, int type) {
|
|||
}
|
||||
|
||||
/* Write a data unit to a memory buffer */
|
||||
static void busWriteMemory(uint8_t *mem, int type, int32_t value) {
|
||||
static void busWriteBuffer(uint8_t *mem, int type, int32_t value) {
|
||||
|
||||
/* Little-endian implementation */
|
||||
#ifdef VB_LITTLEENDIAN
|
||||
|
@ -72,17 +72,17 @@ static int32_t busRead(VB *sim, uint32_t address, int type, int debug) {
|
|||
/* Process by address range */
|
||||
switch (address >> 24 & 7) {
|
||||
case 0 : return 0; /* VIP */
|
||||
case 1 : debug = debug; return 0; /* VSU */
|
||||
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 */
|
||||
busReadMemory(&sim->wram[address & 0xFFFF], type);
|
||||
busReadBuffer(&sim->wram[address & 0xFFFF], type);
|
||||
case 6 : return sim->cart.sram == NULL ? 0 : /* Game pak RAM */
|
||||
busReadMemory(&sim->cart.sram
|
||||
busReadBuffer(&sim->cart.sram
|
||||
[address & (sim->cart.sramSize - 1)], type);
|
||||
default: return sim->cart.rom == NULL ? 0 : /* Game pak ROM */
|
||||
busReadMemory(&sim->cart.rom
|
||||
busReadBuffer(&sim->cart.rom
|
||||
[address & (sim->cart.romSize - 1)], type);
|
||||
}
|
||||
|
||||
|
@ -103,16 +103,16 @@ static void busWrite(
|
|||
case 3 : return; /* Unmapped */
|
||||
case 4 : return; /* Game pak expansion */
|
||||
case 5 : /* WRAM */
|
||||
busWriteMemory(&sim->wram[address & 0xFFFF], type, value);
|
||||
busWriteBuffer(&sim->wram[address & 0xFFFF], type, value);
|
||||
return;
|
||||
case 6 : /* Cartridge RAM */
|
||||
if (sim->cart.sram != NULL)
|
||||
busWriteMemory(&sim->cart.sram
|
||||
busWriteBuffer(&sim->cart.sram
|
||||
[address & (sim->cart.sramSize - 1)], type, value);
|
||||
return;
|
||||
default: /* Cartridge ROM */
|
||||
if (debug && sim->cart.rom != NULL)
|
||||
busWriteMemory(&sim->cart.rom
|
||||
busWriteBuffer(&sim->cart.rom
|
||||
[address & (sim->cart.romSize - 1)], type, value);
|
||||
}
|
||||
|
||||
|
|
13
core/cpu.c
13
core/cpu.c
|
@ -646,12 +646,11 @@ static uint32_t cpuSetSystemRegister(VB *sim,int id,uint32_t value,int debug) {
|
|||
/* TODO: Perform dump/restore operations */
|
||||
return value & 0x00000002;
|
||||
case VB_ECR :
|
||||
if (debug) {
|
||||
sim->cpu.ecr.fecc = value >> 16;
|
||||
sim->cpu.ecr.eicc = value;
|
||||
return value;
|
||||
}
|
||||
return vbGetSystemRegister(sim, id);
|
||||
if (!debug)
|
||||
return (uint32_t) sim->cpu.ecr.fecc << 16 | sim->cpu.ecr.eicc;
|
||||
sim->cpu.ecr.fecc = value >> 16;
|
||||
sim->cpu.ecr.eicc = value;
|
||||
return value;
|
||||
case VB_PSW :
|
||||
sim->cpu.psw.i = value >> 16 & 15;
|
||||
sim->cpu.psw.np = value >> 15 & 1;
|
||||
|
@ -690,7 +689,7 @@ static int cpuStore(VB *sim,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks){
|
|||
/* Initiate the write */
|
||||
if (sim->cpu.busWait == 0) {
|
||||
|
||||
/* Read the data unit from the bus */
|
||||
/* Write the data unit to the bus */
|
||||
if (cpuWrite(
|
||||
sim,
|
||||
sim->cpu.program[inst->bits[0] & 0x1F] +
|
||||
|
|
81
core/vb.c
81
core/vb.c
|
@ -1,4 +1,8 @@
|
|||
#define VBAPI VB_EXPORT
|
||||
#ifdef VB_EXPORT
|
||||
#define VBAPI VB_EXPORT
|
||||
#else
|
||||
#define VBAPI
|
||||
#endif
|
||||
|
||||
/* Header includes */
|
||||
#include <float.h>
|
||||
|
@ -50,36 +54,31 @@ static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
|||
/******************************* API Functions *******************************/
|
||||
|
||||
/* Associate two simulations as peers, or remove an association */
|
||||
void vbConnect(VB *a, VB *b) {
|
||||
void vbConnect(VB *sim1, VB *sim2) {
|
||||
|
||||
/* Disconnect */
|
||||
if (b == NULL) {
|
||||
if (a->peer != NULL)
|
||||
a->peer->peer = NULL;
|
||||
a->peer = NULL;
|
||||
if (sim2 == NULL) {
|
||||
if (sim1->peer != NULL)
|
||||
sim1->peer->peer = NULL;
|
||||
sim1->peer = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The simulations are already linked */
|
||||
if (a->peer == b && b->peer == a)
|
||||
return;
|
||||
|
||||
/* Disconnect any existing link associations */
|
||||
if (a->peer != NULL && a->peer != b)
|
||||
a->peer->peer = NULL;
|
||||
if (b->peer != NULL && b->peer != a)
|
||||
b->peer->peer = NULL;
|
||||
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 */
|
||||
a->peer = b;
|
||||
b->peer = a;
|
||||
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 */
|
||||
int x; /* Iterator */
|
||||
|
||||
/* Process the simulation until a break condition occurs */
|
||||
do {
|
||||
|
@ -114,18 +113,20 @@ int vbEmulateMulti(VB **sims, int count, uint32_t *clocks) {
|
|||
|
||||
/* Retrieve a current breakpoint callback */
|
||||
void* vbGetCallback(VB *sim, int type) {
|
||||
/* -Wpedantic ignored for pointer conversion because no alternative */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
void **field; /* Pointer to field within simulation */
|
||||
|
||||
/* Select the field to update */
|
||||
switch (type) {
|
||||
case VB_ONEXCEPTION: return sim->onException;
|
||||
case VB_ONEXECUTE : return sim->onExecute;
|
||||
case VB_ONFETCH : return sim->onFetch;
|
||||
case VB_ONREAD : return sim->onRead;
|
||||
case VB_ONWRITE : return sim->onWrite;
|
||||
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;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
return NULL;
|
||||
|
||||
/* Retrieve the simulation field */
|
||||
return *field;
|
||||
}
|
||||
|
||||
/* Retrieve the value of PC */
|
||||
|
@ -231,18 +232,24 @@ void vbReset(VB *sim) {
|
|||
}
|
||||
|
||||
/* Specify a breakpoint callback */
|
||||
void vbSetCallback(VB *sim, int type, void *callback) {
|
||||
/* -Wpedantic ignored for pointer conversion because no alternative */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
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: sim->onException=(VB_EXCEPTIONPROC)callback;break;
|
||||
case VB_ONEXECUTE : sim->onExecute =(VB_EXECUTEPROC )callback;break;
|
||||
case VB_ONFETCH : sim->onFetch =(VB_FETCHPROC )callback;break;
|
||||
case VB_ONREAD : sim->onRead =(VB_READPROC )callback;break;
|
||||
case VB_ONWRITE : sim->onWrite =(VB_WRITEPROC )callback;break;
|
||||
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;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/* Update the simulation field */
|
||||
prev = *field;
|
||||
*field = callback;
|
||||
return prev;
|
||||
}
|
||||
|
||||
/* Specify a new value for PC */
|
||||
|
|
|
@ -181,7 +181,7 @@ 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 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);
|
||||
|
|
6
makefile
6
makefile
|
@ -1,7 +1,7 @@
|
|||
.PHONY: help
|
||||
help:
|
||||
@echo
|
||||
@echo "Virtual Boy Emulator - April 14, 2022"
|
||||
@echo "Virtual Boy Emulator - April 20, 2022"
|
||||
@echo
|
||||
@echo "Target build environment is any Debian with the following packages:"
|
||||
@echo " emscripten"
|
||||
|
@ -39,6 +39,10 @@ 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
|
||||
|
||||
.PHONY: wasm
|
||||
wasm:
|
||||
|
|
|
@ -124,7 +124,3 @@ EMSCRIPTEN_KEEPALIVE void SingleStep(VB *sim0, VB *sim1) {
|
|||
|
||||
vbSetCallback(sim0, VB_ONFETCH, NULL);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE uint32_t Clocks(VB *sim) {
|
||||
return sim->cpu.clocks;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue