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