2021-09-02 00:16:22 +00:00
|
|
|
"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
|
2021-09-06 00:09:15 +00:00
|
|
|
this.txtValue = this.add(this.newTextBox({ text: "\u00a0" }));
|
2021-09-02 00:16:22 +00:00
|
|
|
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":
|
2021-09-20 21:29:00 +00:00
|
|
|
let e = value >> 23 & 0xFF;
|
|
|
|
let s = value & 0x007FFFFF;
|
|
|
|
|
|
|
|
// Check for denormal number
|
|
|
|
if (e == 0x00 & s != 0) {
|
|
|
|
text = "Denormal";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for reserved operand
|
|
|
|
if (e == 0xFF) {
|
|
|
|
text = s!=0 ? "NaN" : value<0 ? "-Infinity" : "Infinity";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for negative zero
|
|
|
|
if ((value & 0xFFFFFFFF) >>> 0 == 0x80000000) {
|
|
|
|
text = "-0.0";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format the number
|
|
|
|
text = CPUWindow.Register.intBitsToFloat(value).toFixed(6);
|
2021-09-02 00:16:22 +00:00
|
|
|
if (text.indexOf(".") == -1)
|
|
|
|
text += ".0";
|
2021-09-20 21:29:00 +00:00
|
|
|
while (text.endsWith("0"))
|
|
|
|
text = text.substring(0, text.length - 1);
|
|
|
|
if (text.endsWith("."))
|
|
|
|
text += "0";
|
2021-09-02 00:16:22 +00:00
|
|
|
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}"
|
|
|
|
}));
|
2021-09-03 17:38:48 +00:00
|
|
|
this.txtFrom = this.expansion.add(this.newTextBox({ enabled: false }));
|
2021-09-02 00:16:22 +00:00
|
|
|
this.txtFrom.element.setAttribute("name", "value");
|
|
|
|
|
|
|
|
// To text box
|
|
|
|
lbl = this.expansion.add(this.newLabel({
|
|
|
|
localized: true,
|
|
|
|
text : "{cpu.pcTo}"
|
|
|
|
}));
|
2021-09-03 17:38:48 +00:00
|
|
|
this.txtTo = this.expansion.add(this.newTextBox({ enabled: false }));
|
2021-09-02 00:16:22 +00:00
|
|
|
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",
|
2021-09-20 12:51:12 +00:00
|
|
|
dbgwnd : "CPU",
|
2021-09-02 00:16:22 +00:00
|
|
|
id : this.id,
|
|
|
|
sim : this.debug.sim,
|
|
|
|
type : this.system ? this.id == -1 ? "pc" : "system" : "program",
|
|
|
|
value : value
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}).initializer();
|