890 lines
25 KiB
JavaScript
890 lines
25 KiB
JavaScript
import { Util } from /**/"./Util.js";
|
|
|
|
|
|
|
|
// Value types
|
|
const HEX = 0;
|
|
const SIGNED = 1;
|
|
const UNSIGNED = 2;
|
|
const FLOAT = 3;
|
|
|
|
// System register indexes
|
|
const ADTRE = 25;
|
|
const CHCW = 24;
|
|
const ECR = 4;
|
|
const EIPC = 0;
|
|
const EIPSW = 1;
|
|
const FEPC = 2;
|
|
const FEPSW = 3;
|
|
const PC = -1;
|
|
const PIR = 6;
|
|
const PSW = 5;
|
|
const TKCW = 7;
|
|
|
|
// Program register names
|
|
const PROREGS = {
|
|
[ 2]: "hp",
|
|
[ 3]: "sp",
|
|
[ 4]: "gp",
|
|
[ 5]: "tp",
|
|
[31]: "lp"
|
|
};
|
|
|
|
// System register names
|
|
const SYSREGS = {
|
|
[ADTRE]: "ADTRE",
|
|
[CHCW ]: "CHCW",
|
|
[ECR ]: "ECR",
|
|
[EIPC ]: "EIPC",
|
|
[EIPSW]: "EIPSW",
|
|
[FEPC ]: "FEPC",
|
|
[FEPSW]: "FEPSW",
|
|
[PC ]: "PC",
|
|
[PIR ]: "PIR",
|
|
[PSW ]: "PSW",
|
|
[TKCW ]: "TKCW",
|
|
[29 ]: "29",
|
|
[30 ]: "30",
|
|
[31 ]: "31"
|
|
};
|
|
|
|
// Expansion control types
|
|
const BIT = 0;
|
|
const INT = 1;
|
|
|
|
// Produce a template object for register expansion controls
|
|
function ctrl(name, shift, size, disabled) {
|
|
return {
|
|
disabled: !!disabled,
|
|
name : name,
|
|
shift : shift,
|
|
size : size
|
|
};
|
|
}
|
|
|
|
// Program register epansion controls
|
|
const EXP_PROGRAM = [
|
|
ctrl("cpu.hex" , true , HEX ),
|
|
ctrl("cpu.signed" , false, SIGNED ),
|
|
ctrl("cpu.unsigned", false, UNSIGNED),
|
|
ctrl("cpu.float" , false, FLOAT )
|
|
];
|
|
|
|
// CHCW expansion controls
|
|
const EXP_CHCW = [
|
|
ctrl("ICE", 1, 1)
|
|
];
|
|
|
|
// ECR expansion controls
|
|
const EXP_ECR = [
|
|
ctrl("FECC", 16, 16),
|
|
ctrl("EICC", 0, 16)
|
|
];
|
|
|
|
// PIR expansion controls
|
|
const EXP_PIR = [
|
|
ctrl("PT", 0, 16, true)
|
|
];
|
|
|
|
// PSW expansion controls
|
|
const EXP_PSW = [
|
|
ctrl("CY", 3, 1), ctrl("FRO", 9, 1),
|
|
ctrl("OV", 2, 1), ctrl("FIV", 8, 1),
|
|
ctrl("S" , 1, 1), ctrl("FZD", 7, 1),
|
|
ctrl("Z" , 0, 1), ctrl("FOV", 6, 1),
|
|
ctrl("NP", 15, 1), ctrl("FUD", 5, 1),
|
|
ctrl("EP", 14, 1), ctrl("FPR", 4, 1),
|
|
ctrl("ID", 12, 1), ctrl("I" , 16, 4),
|
|
ctrl("AE", 13, 1)
|
|
];
|
|
|
|
// TKCW expansion controls
|
|
const EXP_TKCW = [
|
|
ctrl("FIT", 7, 1, true), ctrl("FUT", 4, 1, true),
|
|
ctrl("FZT", 6, 1, true), ctrl("FPT", 3, 1, true),
|
|
ctrl("FVT", 5, 1, true), ctrl("OTM", 8, 1, true),
|
|
ctrl("RDI", 2, 1, true), ctrl("RD" , 0, 2, true)
|
|
];
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Register //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// One register within a register list
|
|
class Register {
|
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
constructor(list, index, andMask, orMask) {
|
|
|
|
// Configure instance fields
|
|
this.andMask = andMask;
|
|
this.app = list.app;
|
|
this.controls = [];
|
|
this.dasm = list.dasm;
|
|
this.format = HEX;
|
|
this.index = index;
|
|
this.isExpanded = null;
|
|
this.list = list;
|
|
this.metrics = { width: 0, height: 0 };
|
|
this.orMask = orMask;
|
|
this.sim = list.sim;
|
|
this.system = list.system;
|
|
this.value = 0x00000000;
|
|
|
|
// Establish elements
|
|
let row = document.createElement("tr");
|
|
let cell;
|
|
list.view.append(row);
|
|
|
|
// Processing by type
|
|
this[this.system ? "initSystem" : "initProgram"]();
|
|
|
|
// Expansion button
|
|
this.btnExpand = new Toolkit.Component(this.app, {
|
|
className: "tk tk-expand tk-mono",
|
|
tagName : "div"
|
|
});
|
|
row .append(cell = document.createElement("td"));
|
|
cell.className = "tk";
|
|
cell.style.width = "1px";
|
|
cell.append(this.btnExpand.element);
|
|
|
|
// Name label
|
|
this.lblName = document.createElement("div");
|
|
Object.assign(this.lblName, {
|
|
className: "tk tk-name",
|
|
id : Toolkit.id(),
|
|
innerText: this.dasm.sysregCaps?this.name:this.name.toLowerCase()
|
|
});
|
|
this.lblName.style.userSelect = "none";
|
|
row .append(cell = document.createElement("td"));
|
|
cell.className = "tk";
|
|
cell.append(this.lblName);
|
|
|
|
// Value text box
|
|
this.txtValue = new Toolkit.TextBox(this.app, {
|
|
className: "tk tk-textbox tk-mono",
|
|
maxLength: 8
|
|
});
|
|
this.txtValue.setAttribute("aria-labelledby", this.lblName.id);
|
|
this.txtValue.setAttribute("digits", "8");
|
|
this.txtValue.addEventListener("action", e=>this.onValue());
|
|
row .append(cell = document.createElement("td"));
|
|
Object.assign(cell.style, {
|
|
textAlign: "right",
|
|
width : "1px"
|
|
});
|
|
cell.className = "tk";
|
|
cell.append(this.txtValue.element);
|
|
|
|
// Expansion area
|
|
if (this.expansion != null)
|
|
this.list.view.append(this.expansion);
|
|
|
|
// Enable expansion function
|
|
if (this.expansion != null) {
|
|
let key = e=>this.expandKeyDown (e);
|
|
let pointer = e=>this.expandPointerDown(e);
|
|
this.btnExpand.setAttribute("aria-controls", this.expansion.id);
|
|
this.btnExpand.setAttribute("aria-labelledby", this.lblName.id);
|
|
this.btnExpand.setAttribute("role", "button");
|
|
this.btnExpand.setAttribute("tabindex", "0");
|
|
this.btnExpand.addEventListener("keydown" , key );
|
|
this.btnExpand.addEventListener("pointerdown", pointer);
|
|
this.lblName .addEventListener("pointerdown", pointer);
|
|
this.setExpanded(this.system && this.index == PSW);
|
|
}
|
|
|
|
// Expansion function is unavailable
|
|
else this.btnExpand.setAttribute("aria-hidden", "true");
|
|
|
|
}
|
|
|
|
// Set up a program register
|
|
initProgram() {
|
|
this.name = PROREGS[this.index] || "r" + this.index.toString();
|
|
this.initExpansion(EXP_PROGRAM);
|
|
}
|
|
|
|
// Set up a system register
|
|
initSystem() {
|
|
this.name = SYSREGS[this.index] || this.index.toString();
|
|
|
|
switch (this.index) {
|
|
case CHCW :
|
|
this.initExpansion(EXP_CHCW); break;
|
|
case ECR :
|
|
this.initExpansion(EXP_ECR ); break;
|
|
case EIPSW: case FEPSW: case PSW:
|
|
this.initExpansion(EXP_PSW ); break;
|
|
case PIR :
|
|
this.initExpansion(EXP_PIR ); break;
|
|
case TKCW :
|
|
this.initExpansion(EXP_TKCW); break;
|
|
}
|
|
|
|
}
|
|
|
|
// Initialize expansion controls
|
|
initExpansion(controls) {
|
|
let two = this.index == ECR || this.index == PIR;
|
|
|
|
// Establish expansion element
|
|
let exp = this.expansion = document.createElement("tr");
|
|
exp.contents = new Toolkit.Component(this.app, {
|
|
className: "tk tk-expansion",
|
|
id : Toolkit.id(),
|
|
tagName : "div",
|
|
style : {
|
|
display : "grid",
|
|
gridTemplateColumns:
|
|
this.system ? "repeat(2, max-content)" : "max-content"
|
|
}
|
|
});
|
|
let cell = document.createElement("td");
|
|
cell.className = "tk";
|
|
cell.colSpan = "3";
|
|
cell.append(exp.contents.element);
|
|
exp.append(cell);
|
|
exp = exp.contents;
|
|
|
|
// Produce program register controls
|
|
if (!this.system) {
|
|
let group = new Toolkit.Group();
|
|
exp.append(group);
|
|
|
|
// Process all controls
|
|
for (let template of controls) {
|
|
|
|
// Create control
|
|
let ctrl = new Toolkit.Radio(this.app, {
|
|
group : group,
|
|
selected: template.shift,
|
|
text : template.name
|
|
});
|
|
ctrl.format = template.size;
|
|
|
|
// Configure event handler
|
|
ctrl.addEventListener("action",
|
|
e=>this.setFormat(e.component.format));
|
|
|
|
// Add the control to the element
|
|
let box = document.createElement("div");
|
|
box.append(ctrl.element);
|
|
exp.append(box);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Process all control templates
|
|
for (let template of controls) {
|
|
let box, ctrl;
|
|
|
|
// Not using an inner two-column layout
|
|
if (!two)
|
|
exp.append(box = document.createElement("div"));
|
|
|
|
// Bit check box
|
|
if (template.size == 1) {
|
|
box.classList.add("tk-bit");
|
|
|
|
// Create control
|
|
ctrl = new Toolkit.CheckBox(this.app, {
|
|
text : "name",
|
|
substitutions: { name: template.name }
|
|
});
|
|
ctrl.mask = 1 << template.shift;
|
|
box.append(ctrl.element);
|
|
|
|
// Disable control
|
|
if (template.disabled)
|
|
ctrl.setEnabled(false);
|
|
|
|
// Configure event handler
|
|
ctrl.addEventListener("action", e=>this.onBit(e.component));
|
|
}
|
|
|
|
// Number text box
|
|
else {
|
|
if (!two)
|
|
box.classList.add("tk-number");
|
|
|
|
// Create label
|
|
let label = document.createElement("label");
|
|
Object.assign(label, {
|
|
className: "tk tk-label",
|
|
innerText: template.name,
|
|
});
|
|
if (!two) Object.assign(box.style, {
|
|
columnGap : "2px",
|
|
display : "grid",
|
|
gridTemplateColumns: "max-content auto"
|
|
});
|
|
(two ? exp : box).append(label);
|
|
|
|
// Create control
|
|
ctrl = new Toolkit.TextBox(this.app, {
|
|
id : Toolkit.id(),
|
|
style: { height: "1em" }
|
|
});
|
|
label.htmlFor = ctrl.id;
|
|
(two ? exp : box).append(ctrl.element);
|
|
|
|
// Control is a hex field
|
|
if (template.size == 16) {
|
|
ctrl.element.classList.add("tk-mono");
|
|
ctrl.setAttribute("digits", 4);
|
|
ctrl.setMaxLength(4);
|
|
}
|
|
|
|
// Disable control
|
|
if (template.disabled) {
|
|
ctrl.setEnabled(false);
|
|
(two ? label : box).setAttribute("disabled", "true");
|
|
}
|
|
|
|
// Configure event handler
|
|
ctrl.addEventListener("action", e=>this.onNumber(e.component));
|
|
}
|
|
|
|
Object.assign(ctrl, template);
|
|
this.controls.push(ctrl);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Event Handlers //////////////////////////////
|
|
|
|
// Expand button key press
|
|
expandKeyDown(e) {
|
|
|
|
// Processing by key
|
|
switch (e.key) {
|
|
case "Enter":
|
|
case " ":
|
|
this.setExpanded(!this.isExpanded);
|
|
break;
|
|
default: return;
|
|
}
|
|
|
|
// Configure event
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
// Expand button pointer down
|
|
expandPointerDown(e) {
|
|
|
|
// Focus management
|
|
this.btnExpand.focus();
|
|
|
|
// Error checking
|
|
if (e.button != 0)
|
|
return;
|
|
|
|
// Configure event
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
|
|
// Configure expansion area
|
|
this.setExpanded(!this.isExpanded);
|
|
}
|
|
|
|
// Expansion bit check box
|
|
onBit(ctrl) {
|
|
this.setValue(ctrl.isSelected ?
|
|
this.value | ctrl.mask :
|
|
this.value & Util.u32(~ctrl.mask)
|
|
);
|
|
}
|
|
|
|
// Expansion number text box
|
|
onNumber(ctrl) {
|
|
let mask = (1 << ctrl.size) - 1 << ctrl.shift;
|
|
let value = parseInt(ctrl.getText(), ctrl.size == 16 ? 16 : 10);
|
|
this.setValue(isNaN(value) ? this.value :
|
|
this.value & Util.u32(~mask) | value << ctrl.shift & mask);
|
|
}
|
|
|
|
// Register value
|
|
onValue() {
|
|
let text = this.txtValue.getText();
|
|
let value;
|
|
|
|
// Processing by type
|
|
switch (this.format) {
|
|
|
|
// Unsigned hexadecimal
|
|
case HEX:
|
|
value = parseInt(text, 16);
|
|
break;
|
|
|
|
// Decimal
|
|
case SIGNED:
|
|
case UNSIGNED:
|
|
value = parseInt(text);
|
|
break;
|
|
|
|
// Float
|
|
case FLOAT:
|
|
value = parseFloat(text);
|
|
if (isNaN(value))
|
|
break;
|
|
value = Util.fromF32(value);
|
|
break;
|
|
}
|
|
|
|
// Assign the new value
|
|
this.setValue(isNaN(value) ? this.value : value);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Package Methods /////////////////////////////
|
|
|
|
// Disassembler settings have been updated
|
|
dasmChanged() {
|
|
let dasm = this.list.dasm;
|
|
let name = this.name;
|
|
|
|
// Program register name
|
|
if (!this.system) {
|
|
if (!dasm.proregNames)
|
|
name = "r" + this.index.toString();
|
|
if (dasm.proregCaps)
|
|
name = name.toUpperCase();
|
|
}
|
|
|
|
// System register name
|
|
else {
|
|
if (!dasm.sysregCaps)
|
|
name = name.toLowerCase();
|
|
}
|
|
|
|
// Common processing
|
|
this.lblName.innerText = name;
|
|
this.refresh(this.value);
|
|
}
|
|
|
|
// Update the value returned from the core
|
|
refresh(value) {
|
|
let text;
|
|
|
|
// Configure instance fields
|
|
this.value = value = Util.u32(value);
|
|
|
|
// Value text box
|
|
switch (this.format) {
|
|
|
|
// Unsigned hexadecimal
|
|
case HEX:
|
|
text = value.toString(16).padStart(8, "0");
|
|
if (this.dasm.hexCaps)
|
|
text = text.toUpperCase();
|
|
break;
|
|
|
|
// Signed decimal
|
|
case SIGNED:
|
|
text = Util.s32(value).toString();
|
|
break;
|
|
|
|
// Unsigned decial
|
|
case UNSIGNED:
|
|
text = Util.u32(value).toString();
|
|
break;
|
|
|
|
// Float
|
|
case FLOAT:
|
|
if ((value & 0x7F800000) != 0x7F800000) {
|
|
text = Util.toF32(value).toFixed(5).replace(/0+$/, "");
|
|
if (text.endsWith("."))
|
|
text += "0";
|
|
} else text = "NaN";
|
|
break;
|
|
}
|
|
this.txtValue.setText(text);
|
|
|
|
// No further processing for program registers
|
|
if (!this.system)
|
|
return;
|
|
|
|
// Process all expansion controls
|
|
for (let ctrl of this.controls) {
|
|
|
|
// Bit check box
|
|
if (ctrl.size == 1) {
|
|
ctrl.setSelected(value & ctrl.mask);
|
|
continue;
|
|
}
|
|
|
|
// Integer text box
|
|
text = value >> ctrl.shift & (1 << ctrl.size) - 1;
|
|
text = ctrl.size != 16 ? text.toString() :
|
|
text.toString(16).padStart(4, "0");
|
|
if (this.dasm.hexCaps)
|
|
text = text.toUpperCase();
|
|
ctrl.setText(text);
|
|
}
|
|
|
|
}
|
|
|
|
// Specify whether the expansion area is visible
|
|
setExpanded(expanded) {
|
|
expanded = !!expanded;
|
|
|
|
// Error checking
|
|
if (this.expansion == null || expanded === this.isExpanded)
|
|
return;
|
|
|
|
// Configure instance fields
|
|
this.isExpanded = expanded;
|
|
|
|
// Configure elements
|
|
let key = expanded ? "common.collapse" : "common.expand";
|
|
this.btnExpand.setAttribute("aria-expanded", expanded);
|
|
this.btnExpand.setToolTip(key);
|
|
this.expansion.style.display =
|
|
expanded ? "table-row" : "none";
|
|
}
|
|
|
|
// Specify the font metrics
|
|
setMetrics(width, height) {
|
|
|
|
// Configure instance fields
|
|
this.metrics = { width: width, height: height };
|
|
|
|
// Height
|
|
height += "px";
|
|
this.txtValue.element.style.height = height;
|
|
for (let ctrl of this.controls.filter(c=>c.size > 1))
|
|
ctrl.element.style.height = height;
|
|
|
|
// Hexadecimal formatting
|
|
if (this.format == HEX) {
|
|
this.txtValue.element.style.width = (width * 8) + "px";
|
|
this.txtValue.setMaxLength(8);
|
|
}
|
|
|
|
// Decimal formatting
|
|
else {
|
|
this.txtValue.element.style.removeProperty("width");
|
|
this.txtValue.setMaxLength(null);
|
|
}
|
|
|
|
// Expansion text boxes
|
|
for (let box of this.controls.filter(c=>c.size > 1)) {
|
|
box.element.style.height = height;
|
|
if (box.size == 16)
|
|
box.element.style.width = (width * 4) + "px";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Private Methods /////////////////////////////
|
|
|
|
// Specify the formatting type of the register value
|
|
setFormat(format) {
|
|
if (format == this.format)
|
|
return;
|
|
this.format = format;
|
|
this.txtValue.element
|
|
.classList[format == HEX ? "add" : "remove"]("tk-mono");
|
|
this.setMetrics(this.metrics.width, this.metrics.height);
|
|
this.refresh(this.value);
|
|
}
|
|
|
|
// Specify a new value for the register
|
|
async setValue(value) {
|
|
|
|
// Update the display with the new value immediately
|
|
value = Util.u32(value & this.andMask | this.orMask);
|
|
let matched = value == this.value;
|
|
this.refresh(value);
|
|
if (matched)
|
|
return;
|
|
|
|
// Update the new value in the core
|
|
let options = { refresh: true };
|
|
this.refresh(await (
|
|
!this.system ?
|
|
this.sim.setProgramRegister(this.index, value, options) :
|
|
this.index == PC ?
|
|
this.sim.setProgramCounter ( value, options) :
|
|
this.sim.setSystemRegister (this.index, value, options)
|
|
));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RegisterList //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Scrolling list of registers
|
|
class RegisterList extends Toolkit.ScrollPane {
|
|
|
|
///////////////////////// Initialization Methods //////////////////////////
|
|
|
|
constructor(debug, system) {
|
|
super(debug.app, {
|
|
className: "tk tk-scrollpane tk-reglist " +
|
|
(system ? "tk-system" : "tk-program"),
|
|
vertical : Toolkit.ScrollPane.ALWAYS
|
|
});
|
|
|
|
// Configure instance fields
|
|
this.app = debug.app;
|
|
this.dasm = debug.disassembler;
|
|
this.method = system?"getSystemRegisters":"getProgramRegisters";
|
|
this.registers = [];
|
|
this.sim = debug.sim;
|
|
this.subscription = system ? "sysregs" : "proregs";
|
|
this.system = system;
|
|
|
|
// Configure view element
|
|
this.setView(new Toolkit.Component(debug.app, {
|
|
className: "tk tk-list",
|
|
tagName : "table",
|
|
style : {
|
|
width: "100%"
|
|
}
|
|
}));
|
|
|
|
// Font-measuring element
|
|
let text = "";
|
|
for (let x = 0; x < 16; x++) {
|
|
if (x != 0) text += "\n";
|
|
let digit = x.toString(16);
|
|
text += digit + "\n" + digit.toUpperCase();
|
|
}
|
|
this.metrics = new Toolkit.Component(this.app, {
|
|
className: "tk tk-mono",
|
|
tagName : "div",
|
|
style : {
|
|
position : "absolute",
|
|
visibility: "hidden"
|
|
}
|
|
});
|
|
this.metrics.element.innerText = text;
|
|
this.metrics.addEventListener("resize", e=>this.onMetrics());
|
|
this.viewport.append(this.metrics.element);
|
|
|
|
// Processing by type
|
|
this[system ? "initSystem" : "initProgram"]();
|
|
|
|
// Configure component
|
|
this.addEventListener("keydown", e=>this.onKeyDown (e));
|
|
this.addEventListener("wheel" , e=>this.onMouseWheel(e));
|
|
}
|
|
|
|
// Initialize a list of program registers
|
|
initProgram() {
|
|
this.add(new Register(this, 0, 0x00000000, 0x00000000));
|
|
for (let x = 1; x < 32; x++)
|
|
this.add(new Register(this, x, 0xFFFFFFFF, 0x00000000));
|
|
}
|
|
|
|
// Initialie a list of system registers
|
|
initSystem() {
|
|
this.add(new Register(this, PC , 0xFFFFFFFE, 0x00000000));
|
|
this.add(new Register(this, PSW , 0x000FF3FF, 0x00000000));
|
|
this.add(new Register(this, ADTRE, 0xFFFFFFFE, 0x00000000));
|
|
this.add(new Register(this, CHCW , 0x00000002, 0x00000000));
|
|
this.add(new Register(this, ECR , 0xFFFFFFFF, 0x00000000));
|
|
this.add(new Register(this, EIPC , 0xFFFFFFFE, 0x00000000));
|
|
this.add(new Register(this, EIPSW, 0x000FF3FF, 0x00000000));
|
|
this.add(new Register(this, FEPC , 0xFFFFFFFE, 0x00000000));
|
|
this.add(new Register(this, FEPSW, 0x000FF3FF, 0x00000000));
|
|
this.add(new Register(this, PIR , 0x00000000, 0x00005346));
|
|
this.add(new Register(this, TKCW , 0x00000000, 0x000000E0));
|
|
this.add(new Register(this, 29 , 0xFFFFFFFF, 0x00000000));
|
|
this.add(new Register(this, 30 , 0x00000000, 0x00000004));
|
|
this.add(new Register(this, 31 , 0xFFFFFFFF, 0x00000000));
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Event Handlers //////////////////////////////
|
|
|
|
// Key press
|
|
onKeyDown(e) {
|
|
|
|
// Processing by key
|
|
switch (e.key) {
|
|
case "ArrowDown":
|
|
this.vertical.setValue(this.vertical.value +
|
|
this.vertical.increment);
|
|
break;
|
|
case "ArrowLeft":
|
|
this.horizontal.setValue(this.horizontal.value -
|
|
this.horizontal.increment);
|
|
break;
|
|
case "ArrowRight":
|
|
this.horizontal.setValue(this.horizontal.value +
|
|
this.horizontal.increment);
|
|
break;
|
|
case "ArrowUp":
|
|
this.vertical.setValue(this.vertical.value -
|
|
this.vertical.increment);
|
|
break;
|
|
case "PageDown":
|
|
this.vertical.setValue(this.vertical.value +
|
|
this.vertical.extent);
|
|
break;
|
|
case "PageUp":
|
|
this.vertical.setValue(this.vertical.value -
|
|
this.vertical.extent);
|
|
break;
|
|
default: return;
|
|
}
|
|
|
|
// Configure event
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}
|
|
|
|
// Metrics element resized
|
|
onMetrics() {
|
|
|
|
// Error checking
|
|
if (!this.metrics)
|
|
return;
|
|
|
|
// Measure the dimensions of one hex character
|
|
let bounds = this.metrics.getBounds();
|
|
if (bounds.height <= 0)
|
|
return;
|
|
let width = Math.ceil(bounds.width);
|
|
let height = Math.ceil(bounds.height / 32);
|
|
|
|
// Resize all text boxes
|
|
for (let reg of this.registers)
|
|
reg.setMetrics(width, height);
|
|
|
|
// Update scroll bars
|
|
this.horizontal.setIncrement(height);
|
|
this.vertical .setIncrement(height);
|
|
}
|
|
|
|
// Mouse wheel
|
|
onMouseWheel(e) {
|
|
|
|
// User agent scaling action
|
|
if (e.ctrlKey)
|
|
return;
|
|
|
|
// No rotation has occurred
|
|
let offset = Math.sign(e.deltaY) * 3;
|
|
if (offset == 0)
|
|
return;
|
|
|
|
// Update the display address
|
|
this.vertical.setValue(this.vertical.value +
|
|
this.vertical.increment * offset);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Public Methods //////////////////////////////
|
|
|
|
// Update with CPU state from the core
|
|
refresh(registers) {
|
|
|
|
// System registers
|
|
if (this.system) {
|
|
for (let reg of Object.entries(SYSREGS))
|
|
this[reg[0]].refresh(registers[reg[1].toLowerCase()]);
|
|
}
|
|
|
|
// Program registers
|
|
else for (let x = 0; x < 32; x++)
|
|
this[x].refresh(registers[x]);
|
|
}
|
|
|
|
// Subscribe to or unsubscribe from core updates
|
|
setSubscribed(subscribed) {
|
|
subscribed = !!subscribed;
|
|
|
|
// Nothing to change
|
|
if (subscribed == this.isSubscribed)
|
|
return;
|
|
|
|
// Configure instance fields
|
|
this.isSubscribed = subscribed;
|
|
|
|
// Subscribe to core updates
|
|
if (subscribed)
|
|
this.fetch();
|
|
|
|
// Unsubscribe from core updates
|
|
else this.sim.unsubscribe(this.subscription);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Package Methods /////////////////////////////
|
|
|
|
// Disassembler settings have been updated
|
|
dasmChanged() {
|
|
for (let reg of this.registers)
|
|
reg.dasmChanged();
|
|
}
|
|
|
|
// Determine the initial size of the register list
|
|
getPreferredSize() {
|
|
let ret = {
|
|
height: 0,
|
|
width : 0
|
|
};
|
|
|
|
// Error checking
|
|
if (!this.view)
|
|
return ret;
|
|
|
|
// Measure the view element
|
|
ret.width = this.view.element.scrollWidth;
|
|
|
|
// Locate the bottom of PSW
|
|
if (this.system && this[PSW].expansion) {
|
|
ret.height = this[PSW].expansion.getBoundingClientRect().bottom -
|
|
this.view.getBounds().top;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////// Private Methods /////////////////////////////
|
|
|
|
// Add a register to the list
|
|
add(reg) {
|
|
this[reg.index] = reg;
|
|
this.registers.push(reg);
|
|
}
|
|
|
|
// Retrieve CPU state from the core
|
|
async fetch() {
|
|
this.refresh(
|
|
await this.sim[this.method]({
|
|
subscribe: this.isSubscribed && this.subscription
|
|
})
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export { RegisterList };
|