Adding registers panes of CPU window
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
"use strict";
|
||||
|
||||
// CPU register and disassembler display
|
||||
(globalThis.CPUWindow = class CPUWindow extends Toolkit.Window {
|
||||
|
||||
// Static initializer
|
||||
static initializer() {
|
||||
|
||||
// System register IDs
|
||||
this.ADTRE = 25;
|
||||
this.CHCW = 24;
|
||||
this.ECR = 4;
|
||||
this.EIPC = 0;
|
||||
this.EIPSW = 1;
|
||||
this.FEPC = 2;
|
||||
this.FEPSW = 3;
|
||||
this.PC = -1;
|
||||
this.PIR = 6;
|
||||
this.PSW = 5;
|
||||
this.TKCW = 7;
|
||||
|
||||
// Program register names
|
||||
this.PROGRAM = {
|
||||
[ 2]: "hp",
|
||||
[ 3]: "sp",
|
||||
[ 4]: "gp",
|
||||
[ 5]: "tp",
|
||||
[31]: "lp"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Object constructor
|
||||
constructor(debug, options) {
|
||||
super(debug.gui, options);
|
||||
|
||||
// Configure instance fields
|
||||
this.address = 0xFFFFFFF0;
|
||||
this.debug = debug;
|
||||
|
||||
// Configure properties
|
||||
this.setProperty("sim", "");
|
||||
|
||||
// Configure elements
|
||||
|
||||
this.initDisassembler();
|
||||
this.initSystemRegisters();
|
||||
this.initProgramRegisters();
|
||||
this.initWindow();
|
||||
|
||||
// Layout components
|
||||
|
||||
// Disassembler on the left
|
||||
this.mainWrap.add(this.dasmWrap);
|
||||
|
||||
// Registers on the right
|
||||
this.regs = this.newPanel({
|
||||
layout: "grid",
|
||||
rows : "max-content max-content auto"
|
||||
});
|
||||
this.regs.element.setAttribute("name", "wrap-registers");
|
||||
|
||||
// Splitter between disassembler and registers
|
||||
this.mainSplit = this.newSplitter({
|
||||
component : this.regs,
|
||||
orientation: "vertical",
|
||||
edge : "right",
|
||||
name : "{cpu.mainSplit}"
|
||||
});
|
||||
this.mainSplit.element.setAttribute("name", "split-main");
|
||||
this.mainSplit.element.style.width = "3px";
|
||||
this.mainSplit.element.style.minWidth = "3px";
|
||||
this.mainSplit.element.style.cursor = "ew-resize";
|
||||
this.mainWrap.add(this.mainSplit);
|
||||
|
||||
// Registers on the right
|
||||
this.mainWrap.add(this.regs);
|
||||
|
||||
// System registers on top
|
||||
this.regs.add(this.sysWrap);
|
||||
|
||||
// Splitter between system registers and program registers
|
||||
this.regsSplit = this.regs.add(this.newSplitter({
|
||||
component : this.sysWrap,
|
||||
orientation: "horizontal",
|
||||
edge : "top",
|
||||
name : "{cpu.regsSplit}"
|
||||
}));
|
||||
this.regsSplit.element.style.height = "3px";
|
||||
this.regsSplit.element.style.minHeight = "3px";
|
||||
this.regsSplit.element.style.cursor = "ns-resize";
|
||||
|
||||
// Program registers on the bottom
|
||||
this.regs.add(this.proWrap);
|
||||
}
|
||||
|
||||
// Initialize disassembler pane
|
||||
initDisassembler() {
|
||||
|
||||
// Wrapping element to hide overflowing scrollbar
|
||||
this.dasmWrap = this.newPanel({
|
||||
layout : "grid",
|
||||
overflowX: "hidden",
|
||||
overflowY: "hidden"
|
||||
});
|
||||
this.dasmWrap.element.setAttribute("name", "wrap-disassembler");
|
||||
|
||||
// Main element
|
||||
this.dasm = this.dasmWrap.add(this.newPanel({
|
||||
focusable: true,
|
||||
name : "{cpu.disassembler}",
|
||||
overflowX: "auto",
|
||||
overflowY: "hidden"
|
||||
}));
|
||||
this.dasm.element.setAttribute("name", "disassembler");
|
||||
}
|
||||
|
||||
// Initialize program registers pane
|
||||
initProgramRegisters() {
|
||||
|
||||
// Wrapping element to hide overflowing scrollbar
|
||||
this.proWrap = this.newPanel({
|
||||
layout : "grid",
|
||||
overflow: "hidden"
|
||||
});
|
||||
this.proWrap.element.setAttribute("name", "wrap-program-registers");
|
||||
this.proWrap.element.style.flexGrow = "1";
|
||||
|
||||
// Main element
|
||||
this.proRegs = this.proWrap.add(this.newPanel({
|
||||
overflowX: "auto",
|
||||
overflowY: "scroll"
|
||||
}));
|
||||
this.proRegs.element.setAttribute("name", "program-registers");
|
||||
|
||||
// List of registers
|
||||
this.proRegs.registers = {};
|
||||
for (let x = 0; x <= 31; x++)
|
||||
this.addRegister(false, x, CPUWindow.PROGRAM[x] || "r" + x);
|
||||
|
||||
}
|
||||
|
||||
// Initialize system registers pane
|
||||
initSystemRegisters() {
|
||||
|
||||
// Wrapping element to hide overflowing scrollbar
|
||||
this.sysWrap = this.newPanel({
|
||||
layout : "grid",
|
||||
overflow: "hidden"
|
||||
});
|
||||
this.sysWrap.element.setAttribute("name", "wrap-system-registers");
|
||||
|
||||
// Main element
|
||||
this.sysRegs = this.sysWrap.add(this.newPanel({
|
||||
overflowX: "auto",
|
||||
overflowY: "scroll"
|
||||
}));
|
||||
this.sysRegs.element.setAttribute("name", "system-registers");
|
||||
|
||||
// List of registers
|
||||
this.sysRegs.registers = {};
|
||||
this.addRegister(true, CPUWindow.PC , "PC" );
|
||||
this.addRegister(true, CPUWindow.PSW , "PSW" );
|
||||
this.addRegister(true, CPUWindow.ADTRE, "ADTRE");
|
||||
this.addRegister(true, CPUWindow.CHCW , "CHCW" );
|
||||
this.addRegister(true, CPUWindow.ECR , "ECR" );
|
||||
this.addRegister(true, CPUWindow.EIPC , "EIPC" );
|
||||
this.addRegister(true, CPUWindow.EIPSW, "EIPSW");
|
||||
this.addRegister(true, CPUWindow.FEPC , "FEPC" );
|
||||
this.addRegister(true, CPUWindow.FEPSW, "FEPSW");
|
||||
this.addRegister(true, CPUWindow.PIR , "PIR" );
|
||||
this.addRegister(true, CPUWindow.TKCW , "TKCW" );
|
||||
this.addRegister(true, 29 , "29" );
|
||||
this.addRegister(true, 30 , "30" );
|
||||
this.addRegister(true, 31 , "31" );
|
||||
this.sysRegs.registers[CPUWindow.PSW].setExpanded(true);
|
||||
}
|
||||
|
||||
// Initialize window and client
|
||||
initWindow() {
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("window", "cpu");
|
||||
|
||||
// Configure body
|
||||
this.body.element.setAttribute("filter", "");
|
||||
|
||||
// Configure client
|
||||
this.client.setLayout("grid", {
|
||||
columns: "auto"
|
||||
});
|
||||
this.client.addResizeListener(b=>this.onresize(b));
|
||||
|
||||
// Configure main wrapper
|
||||
this.mainWrap = this.client.add(this.newPanel({
|
||||
layout : "grid",
|
||||
columns: "auto max-content max-content"
|
||||
}));
|
||||
this.mainWrap.element.setAttribute("name", "wrap-main");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// The window is being displayed for the first time
|
||||
firstShow() {
|
||||
super.firstShow();
|
||||
this.center();
|
||||
this.mainSplit.measure();
|
||||
this.regsSplit.measure();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update the display with current emulation data
|
||||
refresh(clientHeight, lineHeight, registers) {
|
||||
if (!registers) {
|
||||
this.debug.core.postMessage({
|
||||
command: "GetRegisters",
|
||||
debug : "CPU",
|
||||
sim : this.debug.sim
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Message Methods /////////////////////////////
|
||||
|
||||
// Message received
|
||||
message(msg) {
|
||||
switch (msg.command) {
|
||||
case "GetRegisters": this.getRegisters(msg); break;
|
||||
case "SetRegister" : this.setRegister (msg); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieved all register values
|
||||
getRegisters(msg) {
|
||||
this.sysRegs.registers[CPUWindow.PC ]
|
||||
.setValue(msg.pc, msg.pcFrom, msg.pcTo);
|
||||
this.sysRegs.registers[CPUWindow.PSW ].setValue(msg.psw );
|
||||
this.sysRegs.registers[CPUWindow.ADTRE].setValue(msg.adtre);
|
||||
this.sysRegs.registers[CPUWindow.CHCW ].setValue(msg.chcw );
|
||||
this.sysRegs.registers[CPUWindow.ECR ].setValue(msg.ecr );
|
||||
this.sysRegs.registers[CPUWindow.EIPC ].setValue(msg.eipc );
|
||||
this.sysRegs.registers[CPUWindow.EIPSW].setValue(msg.eipsw);
|
||||
this.sysRegs.registers[CPUWindow.FEPC ].setValue(msg.fepc );
|
||||
this.sysRegs.registers[CPUWindow.FEPSW].setValue(msg.fepsw);
|
||||
this.sysRegs.registers[CPUWindow.PIR ].setValue(msg.pir );
|
||||
this.sysRegs.registers[CPUWindow.TKCW ].setValue(msg.tkcw );
|
||||
this.sysRegs.registers[29 ].setValue(msg.sr29 );
|
||||
this.sysRegs.registers[30 ].setValue(msg.sr30 );
|
||||
this.sysRegs.registers[31 ].setValue(msg.sr31 );
|
||||
for (let x = 0; x <= 31; x++)
|
||||
this.proRegs.registers[x].setValue(msg.program[x]);
|
||||
}
|
||||
|
||||
// Modified a register value
|
||||
setRegister(msg) {
|
||||
(msg.type == "program" ? this.proRegs : this.sysRegs)
|
||||
.registers[msg.id].setValue(msg.value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Insert a register control to a register list
|
||||
addRegister(system, id, name) {
|
||||
let list = system ? this.sysRegs : this.proRegs;
|
||||
let reg = new CPUWindow.Register(this.debug, list, system, id, name);
|
||||
list.registers[id] = reg;
|
||||
list.add(reg);
|
||||
if (reg.expands)
|
||||
list.add(reg.expansion);
|
||||
}
|
||||
|
||||
// Resize event handler
|
||||
onresize(bounds) {
|
||||
if (!this.isVisible())
|
||||
return;
|
||||
//this.regs.setHeight(bounds.height);
|
||||
this.mainSplit.measure();
|
||||
this.regsSplit.measure();
|
||||
}
|
||||
|
||||
}).initializer();
|
||||
+82
-50
@@ -8,47 +8,68 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
super(debug.gui, options);
|
||||
|
||||
// Configure instance fields
|
||||
this.address = 0xFFFFFFF0;
|
||||
this.address = 0x05000000;
|
||||
this.debug = debug;
|
||||
this.rows = [new MemoryWindow.Row(this.client)];
|
||||
this.rows = [];
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("window", "memory");
|
||||
this.element.addEventListener("wheel", e=>this.onwheel(e));
|
||||
|
||||
// Configure body
|
||||
this.body.element.setAttribute("filter", "");
|
||||
|
||||
// Configure client
|
||||
this.client.setLayout("grid", { columns: "repeat(17, max-content)" });
|
||||
this.client.element.style.gridAutoRows = "max-content";
|
||||
this.client.setOverflow("auto", "hidden");
|
||||
this.client.setLayout("grid");
|
||||
this.client.addResizeListener(b=>this.refresh(b.height));
|
||||
|
||||
// Wrapping element to hide overflowing scrollbar
|
||||
this.hexWrap = this.client.add(this.newPanel({
|
||||
layout : "grid",
|
||||
columns: "auto"
|
||||
}));
|
||||
this.hexWrap.element.setAttribute("name", "wrap-hex");
|
||||
|
||||
// Configure hex viewer
|
||||
this.hex = this.hexWrap.add(this.client.newPanel({
|
||||
focusable: true,
|
||||
layout : "block",
|
||||
hollow : false,
|
||||
overflowX: "auto",
|
||||
overflowY: "hidden"
|
||||
}));
|
||||
this.hex.element.setAttribute("role", "grid");
|
||||
this.hex.element.setAttribute("name", "hex");
|
||||
this.hex.element.addEventListener("wheel", e=>this.onwheel(e));
|
||||
|
||||
// Configure properties
|
||||
this.setProperty("sim", "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// The window is being displayed for the first time
|
||||
firstShow() {
|
||||
super.firstShow();
|
||||
this.seek(this.address, Math.floor(this.lines(true) / 3));
|
||||
this.rows.push(this.hex.add(new MemoryWindow.Row(this.hex)));
|
||||
this.application.addComponent(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Update display text with localized strings
|
||||
localize() {
|
||||
let hex = "";
|
||||
if (this.application)
|
||||
hex = this.application.translate("{memory.hexEditor}");
|
||||
this.hex.element.setAttribute("aria-label", hex);
|
||||
}
|
||||
|
||||
// Update the display with current emulation data
|
||||
refresh(clientHeight, lineHeight) {
|
||||
clientHeight = clientHeight || this.client.getBounds().height;
|
||||
lineHeight = lineHeight || this.lineHeight();
|
||||
let rowCount = this.lines(false, clientHeight, lineHeight);
|
||||
|
||||
// Showing for the first time
|
||||
if (this.address < 0)
|
||||
this.seek(-this.address, Math.floor(rowCount / 3));
|
||||
refresh(gridHeight, lineHeight) {
|
||||
|
||||
// Do nothing while closed
|
||||
if (!this.isVisible())
|
||||
return;
|
||||
|
||||
// Working variables
|
||||
gridHeight = gridHeight || this.hex.getBounds().height;
|
||||
lineHeight = lineHeight || this.lineHeight();
|
||||
let rowCount = this.lines(false, gridHeight, lineHeight);
|
||||
|
||||
// Request bus data from the WebAssembly core
|
||||
this.debug.core.postMessage({
|
||||
@@ -61,9 +82,9 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
// Configure elements
|
||||
for (let y = this.rows.length; y < rowCount; y++)
|
||||
this.rows[y] = new MemoryWindow.Row(this.client);
|
||||
this.rows[y] = this.hex.add(new MemoryWindow.Row(this.hex));
|
||||
for (let y = rowCount; y < this.rows.length; y++)
|
||||
this.rows[y].remove();
|
||||
this.hex.remove(this.rows[y]);
|
||||
if (this.rows.length > rowCount)
|
||||
this.rows.splice(rowCount, this.rows.length - rowCount);
|
||||
}
|
||||
@@ -93,17 +114,23 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// The window is being displayed for the first time
|
||||
firstShow() {
|
||||
super.firstShow();
|
||||
this.center();
|
||||
}
|
||||
|
||||
// Determine the height in pixels of one row of output
|
||||
lineHeight() {
|
||||
return Math.max(1, this.rows[0].address.getBounds().height);
|
||||
return Math.max(10, this.rows[0].address.getBounds().height);
|
||||
}
|
||||
|
||||
// Determine the number of rows of output
|
||||
lines(fullyVisible, clientHeight, lineHeight) {
|
||||
clientHeight = clientHeight || this.client.getBounds().height;
|
||||
lineHeight = lineHeight || this.lineHeight();
|
||||
let ret = clientHeight / lineHeight;
|
||||
ret = fullyVisible ? Math.floor(ret) : Math.ceil(ret);
|
||||
lines(fullyVisible, gridHeight, lineHeight) {
|
||||
gridHeight = gridHeight || this.client.getBounds().height;
|
||||
lineHeight = lineHeight || this.lineHeight();
|
||||
let ret = gridHeight / lineHeight;
|
||||
ret = fullyVisible ? Math.floor(ret) : Math.ceil(ret);
|
||||
return Math.max(1, ret);
|
||||
}
|
||||
|
||||
@@ -127,29 +154,24 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
// Processing by key
|
||||
else switch (e.key) {
|
||||
|
||||
case "ArrowDown":
|
||||
this.address = (this.address + 16 & 0xFFFFFFFF) >>> 0;
|
||||
this.refresh();
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
this.address = (this.address - 16 & 0xFFFFFFFF) >>> 0;
|
||||
this.refresh();
|
||||
break;
|
||||
|
||||
case "PageUp":
|
||||
this.address = (this.address - 16 * this.lines(true) &
|
||||
0xFFFFFFFF) >>> 0;
|
||||
this.refresh();
|
||||
break;
|
||||
|
||||
case "PageDown":
|
||||
this.address = (this.address + 16 * this.lines(true) &
|
||||
0xFFFFFFFF) >>> 0;
|
||||
this.refresh();
|
||||
break;
|
||||
|
||||
default: return super.onkeydown(e);
|
||||
}
|
||||
|
||||
@@ -165,6 +187,12 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
let mag = Math.abs (e.deltaY);
|
||||
if (e.deltaMode == WheelEvent.DOM_DELTA_PIXEL)
|
||||
mag = Math.max(1, Math.floor(mag / lineHeight));
|
||||
|
||||
// Configure element
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Specify the new address
|
||||
this.address = (this.address + sign * mag * 16 & 0xFFFFFFFF) >>> 0;
|
||||
this.refresh(null, lineHeight);
|
||||
}
|
||||
@@ -177,23 +205,34 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
};
|
||||
|
||||
// One row of output
|
||||
MemoryWindow.Row = class Row {
|
||||
MemoryWindow.Row = class Row extends Toolkit.Panel {
|
||||
|
||||
// Object constructor
|
||||
constructor(client) {
|
||||
constructor(parent) {
|
||||
super(parent.application, {
|
||||
layout : "grid",
|
||||
columns : "repeat(17, max-content)",
|
||||
hollow : false,
|
||||
overflowX: "visible",
|
||||
overflowY: "visible"
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.bytes = new Array(16);
|
||||
this.client = client;
|
||||
this.bytes = new Array(16);
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("role", "row");
|
||||
|
||||
// Address label
|
||||
this.address = client.add(client.newLabel({ text: "\u00a0" }));
|
||||
this.address = this.add(parent.newLabel({ text: "\u00a0" }));
|
||||
this.address.element.setAttribute("role", "gridcell");
|
||||
this.address.element.setAttribute("name", "address");
|
||||
|
||||
// Byte labels
|
||||
for (let x = 0; x < 16; x++) {
|
||||
let lbl = this.bytes[x] =
|
||||
client.add(client.newLabel({ text: "\u00a0" }));
|
||||
this.add(parent.newLabel({ text: "\u00a0" }));
|
||||
lbl.element.setAttribute("role", "gridcell");
|
||||
lbl.element.setAttribute("name", "byte");
|
||||
}
|
||||
|
||||
@@ -203,13 +242,6 @@ MemoryWindow.Row = class Row {
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Remove components from the memory window
|
||||
remove() {
|
||||
this.client.remove(this.address);
|
||||
for (let bytel of this.bytes)
|
||||
this.client.remove(bytel);
|
||||
}
|
||||
|
||||
// Update the output labels with emulation state content
|
||||
update(address, bytes, offset) {
|
||||
this.address.setText(
|
||||
|
||||
@@ -0,0 +1,496 @@
|
||||
"use strict";
|
||||
|
||||
// List item for CPU window register lists
|
||||
(CPUWindow.Register = class Register extends Toolkit.Panel {
|
||||
|
||||
// Static initializer
|
||||
static initializer() {
|
||||
let buffer = new ArrayBuffer(4);
|
||||
this.F32 = new Float32Array(buffer);
|
||||
this.S32 = new Int32Array (buffer);
|
||||
this.U32 = new Uint32Array (buffer);
|
||||
}
|
||||
|
||||
// Object constructor
|
||||
constructor(debug, parent, system, id, name) {
|
||||
super(parent.application, {
|
||||
layout : "grid",
|
||||
columns : "auto max-content",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible"
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.debug = debug;
|
||||
this.expanded = false;
|
||||
this.expansion = null;
|
||||
this.fields = {};
|
||||
this.format = "hex";
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.system = system;
|
||||
this.value = 0x00000000;
|
||||
|
||||
// Determine whether the register has expansion fields
|
||||
this.expands = !system;
|
||||
switch (id) {
|
||||
case CPUWindow.CHCW:
|
||||
case CPUWindow.ECR:
|
||||
case CPUWindow.EIPSW:
|
||||
case CPUWindow.FEPSW:
|
||||
case CPUWindow.PC:
|
||||
case CPUWindow.PIR:
|
||||
case CPUWindow.PSW:
|
||||
case CPUWindow.TKCW:
|
||||
this.expands = true;
|
||||
}
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("name", "register");
|
||||
this.element.setAttribute("format", this.format);
|
||||
|
||||
// Name/expansion "check box"
|
||||
this.chkExpand = this.add(this.newCheckBox({
|
||||
enabled: this.expands,
|
||||
text : name
|
||||
}));
|
||||
this.chkExpand.element.setAttribute("name", "expand");
|
||||
this.chkExpand.element.setAttribute("aria-expanded", "false");
|
||||
this.chkExpand.addChangeListener(e=>
|
||||
this.setExpanded(this.chkExpand.isChecked()));
|
||||
|
||||
// Value text box
|
||||
this.txtValue = this.add(this.newTextBox({ text: "00000000" }));
|
||||
this.txtValue.element.setAttribute("name", "value");
|
||||
this.txtValue.addCommitListener(e=>this.onvalue());
|
||||
|
||||
// Expansion controls
|
||||
if (!system)
|
||||
this.expansionProgram();
|
||||
else switch (id) {
|
||||
case CPUWindow.CHCW : this.expansionCHCW(); break;
|
||||
case CPUWindow.ECR : this.expansionECR (); break;
|
||||
case CPUWindow.EIPSW:
|
||||
case CPUWindow.FEPSW:
|
||||
case CPUWindow.PSW : this.expansionPSW (); break;
|
||||
case CPUWindow.PC : this.expansionPC (); break;
|
||||
case CPUWindow.PIR : this.expansionPIR (); break;
|
||||
case CPUWindow.TKCW : this.expansionTKCW(); break;
|
||||
}
|
||||
if (this.expands) {
|
||||
this.chkExpand.element.setAttribute(
|
||||
"aria-controls", this.expansion.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Static Methods //////////////////////////////
|
||||
|
||||
// Force a 32-bit integer to be signed
|
||||
static asSigned(value) {
|
||||
this.U32[0] = value >>> 0;
|
||||
return this.S32[0];
|
||||
}
|
||||
|
||||
// Interpret a 32-bit integer as a float
|
||||
static intBitsToFloat(value) {
|
||||
this.U32[0] = value;
|
||||
value = this.F32[0];
|
||||
return Number.isFinite(value) ? value : 0;
|
||||
}
|
||||
|
||||
// Interpret a float as a 32-bit integer
|
||||
static floatToIntBits(value) {
|
||||
if (!Number.isFinite(value))
|
||||
return 0;
|
||||
this.F32[0] = value;
|
||||
return this.U32[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Specify whether the expansion fields are visible
|
||||
setExpanded(expanded) {
|
||||
this.expanded = expanded = !!expanded;
|
||||
this.expansion.setVisible(expanded);
|
||||
this.chkExpand.setChecked(expanded);
|
||||
this.chkExpand.element.setAttribute("aria-expanded", expanded);
|
||||
}
|
||||
|
||||
// Specify the display mode of the register value
|
||||
setFormat(format) {
|
||||
this.format = format;
|
||||
this.setValue(this.value);
|
||||
this.element.setAttribute("format", format.replace("_", ""));
|
||||
}
|
||||
|
||||
// Update the value of the register
|
||||
setValue(value, pcFrom, pcTo) {
|
||||
this.value = value;
|
||||
|
||||
// Value text box
|
||||
let text;
|
||||
switch (this.format) {
|
||||
case "float":
|
||||
text = CPUWindow.Register.intBitsToFloat(value).toString();
|
||||
if (text.indexOf(".") == -1)
|
||||
text += ".0";
|
||||
break;
|
||||
case "hex":
|
||||
text = ("0000000" +
|
||||
(value >>> 0).toString(16).toUpperCase()).slice(-8);
|
||||
break;
|
||||
case "signed":
|
||||
text = CPUWindow.Register.asSigned(value).toString();
|
||||
break;
|
||||
case "unsigned":
|
||||
text = (value >>> 0).toString();
|
||||
}
|
||||
this.txtValue.setText(text);
|
||||
|
||||
// Expansion fields
|
||||
for (let field of Object.values(this.fields)) {
|
||||
switch (field.type) {
|
||||
case "bit":
|
||||
field.setChecked(value >> field.bit & 1);
|
||||
break;
|
||||
case "decimal":
|
||||
field.setText(value >> field.bit & field.mask);
|
||||
break;
|
||||
case "hex":
|
||||
let digits = Math.max(1, Math.ceil(field.width / 4));
|
||||
field.setText(("0".repeat(digits) +
|
||||
(value >> field.bit & field.mask)
|
||||
.toString(16).toUpperCase()
|
||||
).slice(-digits));
|
||||
}
|
||||
}
|
||||
|
||||
// Special fields for PC
|
||||
if (pcFrom === undefined)
|
||||
return;
|
||||
this.txtFrom.setText(("0000000" +
|
||||
(pcFrom >>> 0).toString(16).toUpperCase()).slice(-8));
|
||||
this.txtTo .setText(("0000000" +
|
||||
(pcTo >>> 0).toString(16).toUpperCase()).slice(-8));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Add a field component to the expansion area
|
||||
addField(type, name, bit, width, readonly) {
|
||||
let field, label, panel;
|
||||
|
||||
// Processing by type
|
||||
switch (type) {
|
||||
|
||||
// Bit
|
||||
case "bit":
|
||||
field = this.newCheckBox({
|
||||
enabled: !readonly,
|
||||
text : name
|
||||
});
|
||||
field.addChangeListener(e=>this.onbit(field));
|
||||
this.expansion.add(field);
|
||||
break;
|
||||
|
||||
// Decimal number
|
||||
case "decimal":
|
||||
|
||||
// Field
|
||||
field = this.newTextBox({
|
||||
enabled: !readonly,
|
||||
name : name
|
||||
});
|
||||
field.addCommitListener(e=>this.onnumber(field));
|
||||
label = this.newLabel({
|
||||
label: true,
|
||||
text : name
|
||||
});
|
||||
label.element.htmlFor = field.id;
|
||||
if (readonly)
|
||||
label.element.setAttribute("aria-disabled", "true");
|
||||
|
||||
// Enclose in a panel
|
||||
panel = this.newPanel({
|
||||
layout : "flex",
|
||||
alignCross: "center",
|
||||
alignMain : "start",
|
||||
direction : "row"
|
||||
});
|
||||
panel.element.setAttribute("name", name);
|
||||
panel.add(label);
|
||||
panel.add(field);
|
||||
this.expansion.add(panel);
|
||||
break;
|
||||
|
||||
// Hexadecimal number
|
||||
case "hex":
|
||||
field = this.newTextBox({
|
||||
enabled: !readonly,
|
||||
name : name
|
||||
});
|
||||
field.addCommitListener(e=>this.onnumber(field));
|
||||
label = this.newLabel({
|
||||
label: true,
|
||||
text : name
|
||||
});
|
||||
label.element.htmlFor = field.id;
|
||||
if (readonly)
|
||||
label.element.setAttribute("aria-disabled", "true");
|
||||
this.expansion.add(label);
|
||||
this.expansion.add(field);
|
||||
}
|
||||
|
||||
// Configure field
|
||||
field.bit = bit;
|
||||
field.mask = (1 << width) - 1;
|
||||
field.type = type;
|
||||
field.width = width;
|
||||
this.fields[name] = field;
|
||||
}
|
||||
|
||||
// Expansion controls for CHCW
|
||||
expansionCHCW() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "block",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "chcw");
|
||||
|
||||
// Fields
|
||||
this.addField("bit", "ICE", 1, 1, false);
|
||||
}
|
||||
|
||||
// Expansion controls for ECR
|
||||
expansionECR() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "max-content auto",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "ecr");
|
||||
this.expansion.element.style.justifyContent = "start";
|
||||
|
||||
// Fields
|
||||
this.addField("hex", "FECC", 16, 16, false);
|
||||
this.addField("hex", "EICC", 0, 16, false);
|
||||
}
|
||||
|
||||
// Expansion controls for PC
|
||||
expansionPC() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "auto max-content",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
|
||||
// From text box
|
||||
let lbl = this.expansion.add(this.newLabel({
|
||||
localized: true,
|
||||
text : "{cpu.pcFrom}"
|
||||
}));
|
||||
lbl.element.setAttribute("name", "indent");
|
||||
this.txtFrom = this.expansion.add(this.newTextBox());
|
||||
this.txtFrom.element.setAttribute("name", "value");
|
||||
|
||||
// To text box
|
||||
lbl = this.expansion.add(this.newLabel({
|
||||
localized: true,
|
||||
text : "{cpu.pcTo}"
|
||||
}));
|
||||
lbl.element.setAttribute("name", "indent");
|
||||
this.txtTo = this.expansion.add(this.newTextBox());
|
||||
this.txtTo.element.setAttribute("name", "value");
|
||||
}
|
||||
|
||||
// Expansion controls for PIR
|
||||
expansionPIR() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "max-content auto",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "pir");
|
||||
|
||||
// Fields
|
||||
this.addField("hex", "PT", 0, 16, true);
|
||||
}
|
||||
|
||||
// Expansion controls for program registers
|
||||
expansionProgram() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "auto",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("role", "radiogroup");
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "program");
|
||||
this.expansion.element.style.justifyContent = "start";
|
||||
|
||||
// Format selections
|
||||
let group = new Toolkit.ButtonGroup();
|
||||
for (let opt of [ "hex", "signed", "unsigned", "float_" ]) {
|
||||
let fmt = group.add(this.expansion.add(this.newRadioButton({
|
||||
checked: opt == "hex",
|
||||
text : "{cpu." + opt + "}",
|
||||
})));
|
||||
fmt.addChangeListener(
|
||||
(opt=>e=>this.setFormat(opt))
|
||||
(opt.replace("_", ""))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Expansion controls for EIPSW, FEPSW and PSW
|
||||
expansionPSW() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "max-content auto",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "psw");
|
||||
this.expansion.element.style.justifyContent = "start";
|
||||
|
||||
// Fields
|
||||
this.addField("bit" , "CY" , 3, 1, false);
|
||||
this.addField("bit" , "FRO", 9, 1, false);
|
||||
this.addField("bit" , "OV" , 2, 1, false);
|
||||
this.addField("bit" , "FIV", 8, 1, false);
|
||||
this.addField("bit" , "S" , 1, 1, false);
|
||||
this.addField("bit" , "FZD", 7, 1, false);
|
||||
this.addField("bit" , "Z" , 0, 1, false);
|
||||
this.addField("bit" , "FOV", 6, 1, false);
|
||||
this.addField("bit" , "NP" , 15, 1, false);
|
||||
this.addField("bit" , "FUD", 5, 1, false);
|
||||
this.addField("bit" , "EP" , 14, 1, false);
|
||||
this.addField("bit" , "FPR", 4, 1, false);
|
||||
this.addField("bit" , "ID" , 12, 1, false);
|
||||
this.addField("decimal", "I" , 16, 4, false);
|
||||
this.addField("bit" , "AE" , 13, 1, false);
|
||||
}
|
||||
|
||||
// Expansion controls for TKCW
|
||||
expansionTKCW() {
|
||||
|
||||
// Expansion area
|
||||
this.expansion = this.newPanel({
|
||||
layout : "grid",
|
||||
columns : "max-content auto",
|
||||
overflowX: "visible",
|
||||
overflowY: "visible",
|
||||
visible : false
|
||||
});
|
||||
this.expansion.element.setAttribute("name", "expansion");
|
||||
this.expansion.element.setAttribute("register", "tkcw");
|
||||
this.expansion.element.style.justifyContent = "start";
|
||||
|
||||
// Fields
|
||||
this.addField("bit" , "FIT", 7, 1, true);
|
||||
this.addField("bit" , "FUT", 4, 1, true);
|
||||
this.addField("bit" , "FZT", 6, 1, true);
|
||||
this.addField("bit" , "FPT", 3, 1, true);
|
||||
this.addField("bit" , "FVT", 5, 1, true);
|
||||
this.addField("bit" , "OTM", 8, 1, true);
|
||||
this.addField("bit" , "RDI", 2, 1, true);
|
||||
this.addField("decimal", "RD" , 0, 2, true);
|
||||
}
|
||||
|
||||
// Bit check box change event handler
|
||||
onbit(field) {
|
||||
let mask = 1 << field.bit;
|
||||
let value = this.value;
|
||||
if (field.isChecked())
|
||||
value = (value | mask & 0xFFFFFFFF) >>> 0;
|
||||
else value = (value & ~mask & 0xFFFFFFFF) >>> 0;
|
||||
this.setRegister(value);
|
||||
}
|
||||
|
||||
// Number text box commit event handler
|
||||
onnumber(field) {
|
||||
let value = parseInt(field.getText(),
|
||||
field.type == "decimal" ? 10 : 16);
|
||||
if (value == NaN)
|
||||
value = this.value;
|
||||
this.setRegister((
|
||||
this.value & ~(field.mask << field.bit) |
|
||||
(value & field.mask) << field.bit
|
||||
) >>> 0);
|
||||
}
|
||||
|
||||
// Value text box commit event handler
|
||||
onvalue() {
|
||||
|
||||
// Process the entered value
|
||||
let value = this.txtValue.getText();
|
||||
switch (this.format) {
|
||||
case "float":
|
||||
value = parseFloat(value);
|
||||
if (Number.isFinite(value))
|
||||
value = CPUWindow.Register.floatToIntBits(value);
|
||||
break;
|
||||
case "hex":
|
||||
value = parseInt(value, 16);
|
||||
break;
|
||||
case "signed":
|
||||
case "unsigned":
|
||||
value = parseInt(value);
|
||||
}
|
||||
|
||||
// Update the value
|
||||
if (!Number.isFinite(value))
|
||||
this.setValue(this.value);
|
||||
else this.setRegister((value & 0xFFFFFFFF) >>> 0);
|
||||
}
|
||||
|
||||
// Update the value of the register
|
||||
setRegister(value) {
|
||||
this.debug.core.postMessage({
|
||||
command: "SetRegister",
|
||||
debug : "CPU",
|
||||
id : this.id,
|
||||
sim : this.debug.sim,
|
||||
type : this.system ? this.id == -1 ? "pc" : "system" : "program",
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
}).initializer();
|
||||
Reference in New Issue
Block a user