Backporting from concurrent project

This commit is contained in:
Guy Perfect 2023-03-19 18:15:55 -05:00
parent 386f4a904f
commit 77c256f2e0
12 changed files with 342 additions and 300 deletions

View File

@ -483,7 +483,7 @@ static int exaFloating1(VB *sim) {
/* Reserved operand */ /* Reserved operand */
reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31]; reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31];
if (cpuFRO(reg1)) { if (cpuFRO(reg1)) {
sim->cpu.fp_flags = 0x00000200; /* FRO */ sim->cpu.fpFlags = 0x00000200; /* FRO */
sim->cpu.exception = 0xFF60; sim->cpu.exception = 0xFF60;
return 0; return 0;
} }
@ -506,7 +506,7 @@ static int exaFloating1(VB *sim) {
/* Invalid operation */ /* Invalid operation */
if (bits > 7) { if (bits > 7) {
sim->cpu.fp_flags = 0x00000100; /* FIV */ sim->cpu.fpFlags = 0x00000100; /* FIV */
sim->cpu.exception = 0xFF70; sim->cpu.exception = 0xFF70;
return 0; return 0;
} }
@ -529,7 +529,7 @@ static int exaFloating1(VB *sim) {
result = -result; result = -result;
/* Stage updates */ /* Stage updates */
sim->cpu.fp_flags = result==*(float *)&reg1 ? 0 : 0x00000010; /* FPR */ sim->cpu.fpFlags = result==*(float *)&reg1 ? 0 : 0x00000010; /* FPR */
sim->cpu.inst.aux[0] = result; sim->cpu.inst.aux[0] = result;
sim->cpu.inst.aux[1] = subop; sim->cpu.inst.aux[1] = subop;
sim->cpu.clocks += ((OpDef *) sim->cpu.inst.def)->aux; sim->cpu.clocks += ((OpDef *) sim->cpu.inst.def)->aux;
@ -549,7 +549,7 @@ static int exaFloating2(VB *sim) {
reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31]; reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31];
reg2 = sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31]; reg2 = sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31];
if (cpuFRO(reg1) || cpuFRO(reg2)) { if (cpuFRO(reg1) || cpuFRO(reg2)) {
sim->cpu.fp_flags = 0x00000200; /* FRO */ sim->cpu.fpFlags = 0x00000200; /* FRO */
sim->cpu.exception = 0xFF60; sim->cpu.exception = 0xFF60;
return 0; return 0;
} }
@ -564,14 +564,14 @@ static int exaFloating2(VB *sim) {
bits = 0x7F7FFFFF; /* Maximum value */ bits = 0x7F7FFFFF; /* Maximum value */
test = *(float *)&bits; test = *(float *)&bits;
if (aux->f64 > test || aux->f64 < -test) { if (aux->f64 > test || aux->f64 < -test) {
sim->cpu.fp_flags = 0x00000040; /* FOV */ sim->cpu.fpFlags = 0x00000040; /* FOV */
sim->cpu.exception = 0xFF64; sim->cpu.exception = 0xFF64;
return 0; return 0;
} }
/* Process result */ /* Process result */
bits = *(int32_t *)&aux->f32; bits = *(int32_t *)&aux->f32;
sim->cpu.fp_flags = 0; sim->cpu.fpFlags = 0;
/* Zero */ /* Zero */
if ((bits & 0x7FFFFFFF) == 0x00000000) if ((bits & 0x7FFFFFFF) == 0x00000000)
@ -579,13 +579,13 @@ static int exaFloating2(VB *sim) {
/* Underflow */ /* Underflow */
else if ((bits & 0x7F800000) == 0x00000000) { else if ((bits & 0x7F800000) == 0x00000000) {
sim->cpu.fp_flags = 0x00000020; /* FUD */ sim->cpu.fpFlags = 0x00000020; /* FUD */
aux->f32 = bits = 0; aux->f32 = bits = 0;
} }
/* Precision degradation */ /* Precision degradation */
if (aux->f32 != aux->f64) if (aux->f32 != aux->f64)
sim->cpu.fp_flags |= 0x00000010; /* FPR */ sim->cpu.fpFlags |= 0x00000010; /* FPR */
/* Other state */ /* Other state */
sim->cpu.inst.aux[0] = bits; sim->cpu.inst.aux[0] = bits;
@ -770,10 +770,10 @@ static void exbException(VB *sim) {
int x; /* Iterator */ int x; /* Iterator */
/* Apply staged floating-point flags */ /* Apply staged floating-point flags */
if (sim->cpu.fp_flags != 0) { if (sim->cpu.fpFlags != 0) {
cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fp_flags | cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags |
cpuGetSystemRegister(sim, VB_PSW), 0); cpuGetSystemRegister(sim, VB_PSW), 0);
sim->cpu.fp_flags = 0; sim->cpu.fpFlags = 0;
} }
/* Fatal exception */ /* Fatal exception */
@ -826,10 +826,10 @@ static void exbFloating(VB *sim) {
int32_t subop = sim->cpu.inst.aux[1]; /* Sub-opcode */ int32_t subop = sim->cpu.inst.aux[1]; /* Sub-opcode */
/* Apply staged floating-point flags */ /* Apply staged floating-point flags */
if (sim->cpu.fp_flags != 0) { if (sim->cpu.fpFlags != 0) {
cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fp_flags | cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags |
cpuGetSystemRegister(sim, VB_PSW), 0); cpuGetSystemRegister(sim, VB_PSW), 0);
sim->cpu.fp_flags = 0; sim->cpu.fpFlags = 0;
} }
/* Update state */ /* Update state */
@ -985,13 +985,13 @@ static void opDIVF_S(VB *sim, int32_t *dest, int32_t src) {
/* Invalid operation */ /* Invalid operation */
if (src == 0) { if (src == 0) {
sim->cpu.fp_flags = 0x00000100; /* FIV */ sim->cpu.fpFlags = 0x00000100; /* FIV */
sim->cpu.exception = 0xFF70; sim->cpu.exception = 0xFF70;
} }
/* Zero division */ /* Zero division */
else { else {
sim->cpu.fp_flags = 0x00000080; /* FZD */ sim->cpu.fpFlags = 0x00000080; /* FZD */
sim->cpu.exception = 0xFF68; sim->cpu.exception = 0xFF68;
} }

View File

@ -172,7 +172,7 @@ struct VB {
/* Other state */ /* Other state */
uint32_t clocks; /* Clocks until next activity */ uint32_t clocks; /* Clocks until next activity */
uint32_t fp_flags; /* Floating-point exception flags */ uint32_t fpFlags; /* Floating-point exception flags */
uint16_t exception; /* Current exception cause code */ uint16_t exception; /* Current exception cause code */
uint8_t irq[5]; /* Interrupt requests */ uint8_t irq[5]; /* Interrupt requests */
uint8_t bitstring; /* Processing a bit string instruction */ uint8_t bitstring; /* Processing a bit string instruction */

View File

@ -139,7 +139,7 @@ class Disassembler {
]; ];
// Program register names // Program register names
static PRONAMES = [ static REG_PROGRAM = [
"r0" , "r1" , "hp" , "sp" , "gp" , "tp" , "r6" , "r7" , "r0" , "r1" , "hp" , "sp" , "gp" , "tp" , "r6" , "r7" ,
"r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
@ -147,13 +147,16 @@ class Disassembler {
]; ];
// System register names // System register names
static SYSNAMES = [ static REG_SYSTEM = [
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW", "EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
"8" , "9" , "10" , "11" , "12" , "13" , "14" , "15" , "8" , "9" , "10" , "11" , "12" , "13" , "14" , "15" ,
"16" , "17" , "18" , "19" , "20" , "21" , "22" , "23" , "16" , "17" , "18" , "19" , "20" , "21" , "22" , "23" ,
"CHCW", "ADTRE", "26" , "27" , "28" , "29" , "30" , "31" "CHCW", "ADTRE", "26" , "27" , "28" , "29" , "30" , "31"
]; ];
// Other register names
static REG_OTHER = [ "PC", "PSW" ];
///////////////////////////// Static Methods ////////////////////////////// ///////////////////////////// Static Methods //////////////////////////////
@ -522,7 +525,8 @@ class Disassembler {
// Format a program register // Format a program register
programRegister(index) { programRegister(index) {
let ret = this.proNames ? Disassembler.PRONAMES[index] : "r" + index; let ret = this.proNames ?
Disassembler.REG_PROGRAM[index] : "r" + index;
if (this.proUppercase) if (this.proUppercase)
ret = ret.toUpperCase(); ret = ret.toUpperCase();
return ret; return ret;
@ -531,7 +535,7 @@ class Disassembler {
// Format a system register // Format a system register
systemRegister(index) { systemRegister(index) {
let ret = this.sysNames ? let ret = this.sysNames ?
Disassembler.SYSNAMES[index] : index.toString(); Disassembler.REG_SYSTEM[index] : index.toString();
if (!this.sysUppercase && this.sysNames) if (!this.sysUppercase && this.sysNames)
ret = ret.toLowerCase(); ret = ret.toLowerCase();
return ret; return ret;

View File

@ -72,7 +72,8 @@ class CPU extends Toolkit.Window {
break; break;
case "g": case "G": case "g": case "G":
this.disassembler.goto(); this.disassembler.goto();
return; break;
default: return;
} }
// Processing by key: CTRL up // Processing by key: CTRL up
@ -112,6 +113,20 @@ class CPU extends Toolkit.Window {
///////////////////////////// Package Methods ///////////////////////////// ///////////////////////////// Package Methods /////////////////////////////
// Disassembler configuration has changed
dasmConfigured() {
// Disassembler
this.disassembler.fetch();
// Registers
for (let reg of this.registers.list) {
reg.chkExpand.element.style.removeProperty("min-width");
reg.dasmConfigured();
}
this.registers.regResize();
}
// Window is being shown for the first time // Window is being shown for the first time
firstShow() { firstShow() {
this.disassembler.firstShow(); this.disassembler.firstShow();
@ -139,7 +154,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Configure instance fields // Configure instance fields
this._bytesColumn = true; this._bytesColumn = true;
this.columnWidths = [ 0, 0, 0, 0, 0, 0 ]; this.columnWidths = new Array(6);
this.cpu = cpu; this.cpu = cpu;
this.delta = 0; this.delta = 0;
this.dasm = null; this.dasm = null;
@ -149,6 +164,10 @@ class DisassemblerPane extends Toolkit.ScrollPane {
this.viewAddress = 0xFFFFFFF0; this.viewAddress = 0xFFFFFFF0;
this.viewLine = 10; this.viewLine = 10;
// Initialize column widths
for (let x = 0; x < this.columnWidths.length; x++)
this.columnWidths[x] = 0;
// Client area // Client area
let view = this.view = new Toolkit.Component(cpu.debug.app, { let view = this.view = new Toolkit.Component(cpu.debug.app, {
class : "tk mono disassembler", class : "tk mono disassembler",
@ -251,19 +270,19 @@ class DisassemblerPane extends Toolkit.ScrollPane {
let line = { let line = {
lblAddress : document.createElement("div"), lblAddress : document.createElement("div"),
lblBytes : [], lblBytes : new Array(4),
lblMnemonic: document.createElement("div"), lblMnemonic: document.createElement("div"),
lblOperands: document.createElement("div") lblOperands: document.createElement("div")
}; };
// Address label // Address label
line.lblAddress.className = "addr"; line.lblAddress.className = "address";
if (y == 0) if (y == 0)
resizer.observe(line.lblAddress); resizer.observe(line.lblAddress);
this.view.append(line.lblAddress); this.view.append(line.lblAddress);
// Byte labels // Byte labels
for (let x = 0; x < 4; x++) { for (let x = 0; x < line.lblBytes.length; x++) {
let lbl = line.lblBytes[x] = document.createElement("div"); let lbl = line.lblBytes[x] = document.createElement("div");
lbl.className = "byte" + (x == 0 ? " b0" : ""); lbl.className = "byte" + (x == 0 ? " b0" : "");
if (y == 0) { if (y == 0) {
@ -274,13 +293,13 @@ class DisassemblerPane extends Toolkit.ScrollPane {
} }
// Mnemonic label // Mnemonic label
line.lblMnemonic.className = "inst"; line.lblMnemonic.className = "mnemonic";
if (y == 0) if (y == 0)
resizer.observe(line.lblMnemonic); resizer.observe(line.lblMnemonic);
this.view.append(line.lblMnemonic); this.view.append(line.lblMnemonic);
// Operand label // Operand label
line.lblOperands.className = "ops"; line.lblOperands.className = "operands";
this.view.append(line.lblOperands); this.view.append(line.lblOperands);
// All elements // All elements
@ -428,10 +447,11 @@ class DisassemblerPane extends Toolkit.ScrollPane {
for (let x = 0; x < this.columnWidths.length; x++) for (let x = 0; x < this.columnWidths.length; x++)
this.columnWidths[x] = 0; this.columnWidths[x] = 0;
this.refresh(); this.refresh();
this.colResize();
} }
// Ensure PC is visible in the view // Ensure PC is visible in the view
followPC(pc = null) { followPC(pc) {
let tall = this.tall(true); let tall = this.tall(true);
let count = !this.dasm ? 0 : Math.min(this.dasm.length - 10, tall); let count = !this.dasm ? 0 : Math.min(this.dasm.length - 10, tall);
@ -498,8 +518,10 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Determine an initial number of visible byte columns // Determine an initial number of visible byte columns
let showBytes = 0; let showBytes = 0;
if (this.bytesColumn) if (this.bytesColumn) {
for (let x = 0; x<4 && this.columnWidths[x]!=0; x++, showBytes++); for (let x = 0; x < 4 &&
this.columnWidths[x] != 0; x++, showBytes++);
}
// Working variables // Working variables
let foundPC = false; let foundPC = false;
@ -517,7 +539,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
line.lblBytes[0].innerText = "--"; line.lblBytes[0].innerText = "--";
line.lblMnemonic.innerText = "---"; line.lblMnemonic.innerText = "---";
line.lblOperands.innerText = ""; line.lblOperands.innerText = "";
for (let x = 1; x < 4; x++) for (let x = 1; x < line.lblBytes.length; x++)
line.lblBytes[x].innerText = ""; line.lblBytes[x].innerText = "";
if (this.bytesColumn) if (this.bytesColumn)
showBytes = Math.max(showBytes, 1); showBytes = Math.max(showBytes, 1);
@ -529,7 +551,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
line.lblAddress .innerText = dasm.address; line.lblAddress .innerText = dasm.address;
line.lblMnemonic.innerText = dasm.mnemonic; line.lblMnemonic.innerText = dasm.mnemonic;
line.lblOperands.innerText = dasm.operands.join(", "); line.lblOperands.innerText = dasm.operands.join(", ");
for (let x = 0; x < 4; x++) for (let x = 0; x < line.lblBytes.length; x++)
line.lblBytes[x].innerText = dasm.bytes[x] || ""; line.lblBytes[x].innerText = dasm.bytes[x] || "";
isPC = this.cpu.registers.pc == dasm.rawAddress; isPC = this.cpu.registers.pc == dasm.rawAddress;
if (this.bytesColumn) if (this.bytesColumn)
@ -554,7 +576,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Configure which byte columns are visible // Configure which byte columns are visible
for (let line of this.lines) { for (let line of this.lines) {
for (let x = 0; x < 4; x++) { for (let x = 0; x < line.lblBytes.length; x++) {
line.lblBytes[x].style line.lblBytes[x].style
[x < showBytes ? "removeProperty" : "setProperty"] [x < showBytes ? "removeProperty" : "setProperty"]
("display", "none") ("display", "none")
@ -599,8 +621,8 @@ class Register {
//////////////////////////////// Constants //////////////////////////////// //////////////////////////////// Constants ////////////////////////////////
// Register types // Register types
static PROGRAM = 0; static PLAIN = 0;
static PLAIN = 1; static PROGRAM = 1;
static CHCW = 2; static CHCW = 2;
static ECR = 3; static ECR = 3;
static PSW = 4; static PSW = 4;
@ -626,31 +648,20 @@ class Register {
[ "texth", "PT", 0, 16 ] [ "texth", "PT", 0, 16 ]
], ],
[this.PSW]: [ [this.PSW]: [
[ "check", "CY" , 3 ], [ "check", "CY" , 3 ], [ "check", "FRO", 9 ],
[ "check", "FRO", 9 ], [ "check", "OV" , 2 ], [ "check", "FIV", 8 ],
[ "check", "OV" , 2 ], [ "check", "S" , 1 ], [ "check", "FZD", 7 ],
[ "check", "FIV", 8 ], [ "check", "Z" , 0 ], [ "check", "FOV", 6 ],
[ "check", "S" , 1 ], [ "check", "NP" , 15 ], [ "check", "FUD", 5 ],
[ "check", "FZD", 7 ], [ "check", "EP" , 14 ], [ "check", "FPR", 4 ],
[ "check", "Z" , 0 ], [ "check", "ID" , 12 ], [ "textd", "I" , 16, 4 ],
[ "check", "FOV", 6 ],
[ "check", "NP" , 15 ],
[ "check", "FUD", 5 ],
[ "check", "EP" , 14 ],
[ "check", "FPR", 4 ],
[ "check", "ID" , 12 ],
[ "textd", "I" , 16, 4 ],
[ "check", "AE" , 13 ] [ "check", "AE" , 13 ]
], ],
[this.TKCW]: [ [this.TKCW]: [
[ "check", "FIT", 7 ], [ "check", "FIT", 7 ], [ "check", "FUT", 4 ],
[ "check", "FUT", 4 ], [ "check", "FZT", 6 ], [ "check", "FPT", 3 ],
[ "check", "FZT", 6 ], [ "check", "FVT", 5 ], [ "check", "OTM", 8 ],
[ "check", "FPT", 3 ], [ "check", "RDI", 2 ], [ "textd", "RD" , 0, 2 ]
[ "check", "FVT", 5 ],
[ "check", "OTM", 8 ],
[ "check", "RDI", 2 ],
[ "textd", "RD" , 0, 2 ]
] ]
}; };
@ -668,19 +679,22 @@ class Register {
this.dasm = registers.cpu.debug.app.dasm; this.dasm = registers.cpu.debug.app.dasm;
this.debug = registers.cpu.debug; this.debug = registers.cpu.debug;
this.expansion = null; this.expansion = null;
this.format = Register.HEX;
this.key = key;
this.registers = registers; this.registers = registers;
this.target = registers;
this.type = type; this.type = type;
// Resolve the register reference // Resolve the target object
key = key.slice(); switch (apiType) {
while (key.length != 1) case Core.VB_PROGRAM: this.target = registers.program; break;
this.target = this.target[key.shift()]; case Core.VB_SYSTEM : this.target = registers.system ; break;
this.key = key[0]; case Core.VB_OTHER : this.target = registers ; break;
}
// Main controls // Main controls
this.main = new Toolkit.Component(app, { this.main = new Toolkit.Component(app, {
class : "main", class : "main",
visibility: true,
style : { style : {
alignItems : "center", alignItems : "center",
display : "grid", display : "grid",
@ -688,27 +702,14 @@ class Register {
} }
}); });
// Expand/collapse button // Expand/collapse check box
this.btnExpand = new Toolkit.Component(app, { this.chkExpand = new Toolkit.Checkbox(app, {
class : "tk expand", class : "tk expand",
style: { disabled: true,
alignItems : "center", instant : true,
display : "grid", role : ""
gridTemplateColumns: "max-content auto"
}
}); });
this.main.add(this.btnExpand); this.main.add(this.chkExpand);
// Expand/collapse icon
this.icon = new Toolkit.Component(app, { class: "icon" });
this.btnExpand.add(this.icon);
// Register name
this.label = new Toolkit.Label(app, {
class: "label",
id : Toolkit.id()
});
this.btnExpand.add(this.label);
// Value text box // Value text box
this.txtValue = new Toolkit.TextBox(app, { this.txtValue = new Toolkit.TextBox(app, {
@ -728,34 +729,29 @@ class Register {
else this.initSystem(app, Register.FIELDS[type]); else this.initSystem(app, Register.FIELDS[type]);
if (this.expansion != null) { if (this.expansion != null) {
// Expand/collapse button // Expand/collapse check box
this.btnExpand.element.setAttribute("aria-expanded", "false" ); this.chkExpand.addEventListener("input", e=>this.chkInput(e));
this.btnExpand.element.setAttribute("tabindex" , "0" ); this.chkExpand.disabled = false;
this.btnExpand.element.setAttribute("role" , "button"); this.chkExpand.element.setAttribute("role", "checkbox");
this.btnExpand.element this.chkExpand.element.setAttribute("tabindex", "0");
this.chkExpand.element
.setAttribute("aria-controls", this.expansion.element.id); .setAttribute("aria-controls", this.expansion.element.id);
// Expansion area // Expansion area
this.btnExpand.addEventListener("keydown",
e=>this.expKeyDown (e));
this.btnExpand.addEventListener("pointerdown",
e=>this.expPointerDown(e));
this.expansion.visible = false; this.expansion.visible = false;
} }
// Update controls // Update controls
this.dasmConfigured();
this.refresh(); this.refresh();
// PSW is initially expanded // PSW is initially expanded
if (type == Register.PSW && key == 5) if (apiType == Core.VB_SYSTEM && apiId == Core.VB_PSW)
this.expanded = true; this.expanded = true;
// System registers after PSW are initially hidden // System registers after PSW are initially hidden
else if (key != "pc" && key != 5 && type != Register.PROGRAM) { else if (apiType == Core.VB_SYSTEM)
this.main.style.position = "absolute"; this.visible = false;
this.main.style.visibility = "hidden";
}
} }
// Expansion controls for program registers // Expansion controls for program registers
@ -775,7 +771,6 @@ class Register {
// Radio group // Radio group
let group = new Toolkit.RadioGroup(app); let group = new Toolkit.RadioGroup(app);
this.format = Register.HEX;
// Hex radio button // Hex radio button
let opt = new Toolkit.Radio(app, { let opt = new Toolkit.Radio(app, {
@ -934,21 +929,9 @@ class Register {
///////////////////////////// Event Handlers ////////////////////////////// ///////////////////////////// Event Handlers //////////////////////////////
// Expand/collapse button key press // Expand/collapse check box input
expKeyDown(e) { chkInput(e) {
if ( this.expanded = this.chkExpand.checked;
!(e.altKey || e.ctrlKey || e.shiftKey) &&
(e.key == " " || e.key == "Enter")
) this.expanded = !this.expanded;
}
// Expand/collapse button pointer down
expPointerDown(e) {
this.btnExpand.element.focus();
if (e.button != 0)
return;
Toolkit.handle(e);
this.expanded = !this.expanded;
} }
// Program register format changed // Program register format changed
@ -956,7 +939,7 @@ class Register {
this.format = format; this.format = format;
this.txtValue.element.classList this.txtValue.element.classList
[format == Register.HEX ? "add" : "remove"]("mono"); [format == Register.HEX ? "add" : "remove"]("mono");
this.formatValue(); this.refresh();
} }
// Bit check box input // Bit check box input
@ -975,14 +958,16 @@ class Register {
// Text box commit // Text box commit
onText(e) { onText(e) {
let oldValue = this.target[this.key];
let target = e.target.component;
let newValue = parseInt(target.value, target.isHex ? 16 : 10);
// Cannot change the value // Cannot change the value
if (e.disabled) if (e.disabled)
return; return;
// Working variables
let oldValue = this.target[this.key];
let target = e.target.component;
let newValue = parseInt(target.value, target.isHex ? 16 : 10);
// The provided value is invalid // The provided value is invalid
if (!Number.isInteger(newValue)) { if (!Number.isInteger(newValue)) {
this.refresh(); this.refresh();
@ -996,54 +981,10 @@ class Register {
// Value text box commit // Value text box commit
valAction(e) { valAction(e) {
let text = this.txtValue.value;
let value = null;
Toolkit.handle(e); Toolkit.handle(e);
let value = this.parseValue(this.txtValue.value);
// Program register with non-default format
if (this.type == Register.PROGRAM && this.format != Register.HEX) {
switch (this.format) {
case Register.SIGNED:
value = parseInt(text);
if (
!Number.isInteger(value) ||
value < -2147483648 ||
value > +2147483647
) value = null;
break;
case Register.UNSIGNED:
value = parseInt(text);
if (
!Number.isInteger(value) ||
value < 0 ||
value > 4294967295
) value = null;
break;
case Register.FLOAT:
value = parseFloat(text);
value =
!Number.isFinite(value) ||
value < Debugger.ixf(0xFF7FFFFF) ||
value > Debugger.ixf(0x7F7FFFFF)
? null : Debugger.fxi(value) >>> 0;
break;
}
}
// Default hexadecimal format
else {
value = parseInt(text, 16);
if (
!Number.isInteger(value) ||
value < 0 ||
value > 4294967295
) value = null;
}
// Apply the new value
if (value === null) if (value === null)
this.formatValue(); this.refresh();
else this.setValue(value); else this.setValue(value);
} }
@ -1052,8 +993,7 @@ class Register {
if (e.altKey || e.ctrlKey || e.shiftKey || e.key != "Escape") if (e.altKey || e.ctrlKey || e.shiftKey || e.key != "Escape")
return; return;
Toolkit.handle(e); Toolkit.handle(e);
this.txtValue.value = this.refresh();
this.debug.dasm.hex(this.target[this.key], 8, false);
} }
@ -1061,34 +1001,45 @@ class Register {
///////////////////////////// Public Methods ////////////////////////////// ///////////////////////////// Public Methods //////////////////////////////
// The expansion area is visible // The expansion area is visible
get expanded() { get expanded() { return this.chkExpand.checked; }
return this.btnExpand.element.getAttribute("aria-expanded") == "true";
}
set expanded(expanded) { set expanded(expanded) {
expanded = !!expanded; this.chkExpand.checked = expanded;
if (this.expansion == null || expanded == this.expanded) this.setVisible(this.main.visible);
}
// Specify whether the element is visible
get visible() { return this.main.visible; }
set visible(visible) {
visible = !!visible;
if (visible == this.main.visible)
return; return;
this.btnExpand.element.setAttribute("aria-expanded", expanded); this.main.element.style[visible ? "removeProperty" : "setProperty"]
this.expansion.visible = expanded; ("position", "absolute");
this.main.visible = visible;
this.setVisible(visible);
} }
///////////////////////////// Package Methods ///////////////////////////// ///////////////////////////// Package Methods /////////////////////////////
// Disassembler configuration has changed
dasmConfigured() {
let names;
switch (this.apiType) {
case Core.VB_SYSTEM: names = Disassembler.REG_SYSTEM; break;
case Core.VB_OTHER : names = Disassembler.REG_OTHER ; break;
}
this.chkExpand.uiLabel.setText(this.apiType != Core.VB_PROGRAM ?
names[this.apiId] : this.dasm.programRegister(this.key));
}
// Update controls from simulation state // Update controls from simulation state
refresh() { refresh() {
// Name label
this.label.setText(
this.key == "pc" ? "PC" :
this.type != Register.PROGRAM ? Disassembler.SYSNAMES[this.key] :
this.dasm.programRegister(this.key)
);
// Value text box // Value text box
let value = this.target[this.key]; let value = this.target[this.key];
this.formatValue(); this.txtValue.value = this.formatValue(value);
// Expansion controls // Expansion controls
for (let ctrl of this.controls) { for (let ctrl of this.controls) {
@ -1117,45 +1068,74 @@ class Register {
///////////////////////////// Private Methods ///////////////////////////// ///////////////////////////// Private Methods /////////////////////////////
// Format the value as a string in the text box // Format a register value as text
formatValue() { formatValue(value) {
let text = "";
let value = this.target[this.key];
// Program register with non-default format
if (this.type == Register.PROGRAM && this.format != Register.HEX) {
switch (this.format) { switch (this.format) {
case Register.HEX:
return this.debug.hex(value >>> 0, 8, false);
case Register.SIGNED: case Register.SIGNED:
text = (value >> 0).toString(); return (value >> 0).toString();
break;
case Register.UNSIGNED: case Register.UNSIGNED:
text = (value >>> 0).toString(); return (value >>> 0).toString();
break;
case Register.FLOAT: case Register.FLOAT:
value = Debugger.ixf(value); value = Debugger.ixf(value);
if (Number.isFinite(value)) { if (Number.isFinite(value)) {
text = value.toFixed(100); let text = value.toFixed(100);
if (/[^0-9\-\.]/.test(text)) if (/[^0-9\-\.]/.test(text))
text = value.toFixed(6); text = value.toFixed(6);
if (text.indexOf(".") != -1) { if (text.indexOf(".") != -1) {
text = text.replace(/0+$/, "") text = text.replace(/0+$/, "")
.replace(/\.$/, ".0"); .replace(/\.$/, ".0");
} else text += ".0"; } else text += ".0";
} else if (!Number.isNaN(value)) { return text;
text = }
if (!Number.isNaN(value)) {
return (
(value == Number.NEGATIVE_INFINITY ? "-" : "") + (value == Number.NEGATIVE_INFINITY ? "-" : "") +
this.debug.app.localize("{debug.cpu.infinity}") this.debug.app.localize("{debug.cpu.infinity}")
; );
} else text = "NaN";
break;
} }
return "NaN";
}
return null;
} }
// Default hexadecimal format // Parse text as a register value
else text = this.dasm.hex(value >>> 0, 8, false); parseValue(value) {
switch (this.format) {
// Update the text case Register.HEX:
this.txtValue.value = text; value = parseInt(value, 16);
return (
!Number.isInteger(value) ||
value < 0 ||
value > 0xFFFFFFFF ?
null : value
);
case Register.SIGNED:
value = parseInt(value);
return (
!Number.isInteger(value) ||
value < -0x80000000 ||
value > 0x7FFFFFFF ?
null : value
);
case Register.UNSIGNED:
value = parseInt(value);
return (
!Number.isInteger(value) ||
value < 0 ||
value > 0xFFFFFFFF ?
null : value
);
case Register.FLOAT:
value = parseFloat(value);
return (
!Number.isFinite(value) ||
value < Debugger.ixf(0xFF7FFFFF) ||
value > Debugger.ixf(0x7F7FFFFF)
? null : Debugger.fxi(value) >>> 0);
}
return null;
} }
// Specify a new register value // Specify a new register value
@ -1175,8 +1155,13 @@ class Register {
this.refresh(); this.refresh();
} }
// Update visibility for expansion controls
setVisible(visible) {
if (this.expansion)
this.expansion.visible = visible && !!this.expanded;
} }
}
@ -1191,20 +1176,20 @@ class RegisterPane extends Toolkit.SplitPane {
// System register templates // System register templates
static SYSTEMS = [ static SYSTEMS = [
[["pc" ],Register.PLAIN,Core.VB_OTHER ,Core.VB_PC ], [ "pc" , Register.PLAIN, Core.VB_OTHER , Core.VB_PC ],
[["system",Core.VB_PSW ],Register.PSW ,Core.VB_SYSTEM,Core.VB_PSW ], [ Core.VB_PSW , Register.PSW , Core.VB_SYSTEM, Core.VB_PSW ],
[["system",Core.VB_ADTRE],Register.PLAIN,Core.VB_SYSTEM,Core.VB_ADTRE], [ Core.VB_ADTRE, Register.PLAIN, Core.VB_SYSTEM, Core.VB_ADTRE ],
[["system",Core.VB_CHCW ],Register.CHCW ,Core.VB_SYSTEM,Core.VB_CHCW ], [ Core.VB_CHCW , Register.CHCW , Core.VB_SYSTEM, Core.VB_CHCW ],
[["system",Core.VB_ECR ],Register.ECR ,Core.VB_SYSTEM,Core.VB_ECR ], [ Core.VB_ECR , Register.ECR , Core.VB_SYSTEM, Core.VB_ECR ],
[["system",Core.VB_EIPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_EIPC ], [ Core.VB_EIPC , Register.PLAIN, Core.VB_SYSTEM, Core.VB_EIPC ],
[["system",Core.VB_EIPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_EIPSW], [ Core.VB_EIPSW, Register.PSW , Core.VB_SYSTEM, Core.VB_EIPSW ],
[["system",Core.VB_FEPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_FEPC ], [ Core.VB_FEPC , Register.PLAIN, Core.VB_SYSTEM, Core.VB_FEPC ],
[["system",Core.VB_FEPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_FEPSW], [ Core.VB_FEPSW, Register.PSW , Core.VB_SYSTEM, Core.VB_FEPSW ],
[["system",Core.VB_PIR ],Register.PIR ,Core.VB_SYSTEM,Core.VB_PIR ], [ Core.VB_PIR , Register.PIR , Core.VB_SYSTEM, Core.VB_PIR ],
[["system",Core.VB_TKCW ],Register.TKCW ,Core.VB_SYSTEM,Core.VB_TKCW ], [ Core.VB_TKCW , Register.TKCW , Core.VB_SYSTEM, Core.VB_TKCW ],
[["system",29 ],Register.PLAIN,Core.VB_SYSTEM,29 ], [ 29 , Register.PLAIN, Core.VB_SYSTEM, 29 ],
[["system",30 ],Register.PLAIN,Core.VB_SYSTEM,30 ], [ 30 , Register.PLAIN, Core.VB_SYSTEM, 30 ],
[["system",31 ],Register.PLAIN,Core.VB_SYSTEM,31 ] [ 31 , Register.PLAIN, Core.VB_SYSTEM, 31 ]
]; ];
@ -1222,13 +1207,13 @@ class RegisterPane extends Toolkit.SplitPane {
// Configure instance fields // Configure instance fields
this.cpu = cpu; this.cpu = cpu;
this.list = []; this.list = [];
this.pc = 0xFFFFFFF0;
this.program = new Array(32);
this.pending = false; this.pending = false;
this.subscription = [ 0, cpu.index, "cpu", "registers", "refresh" ], this.subscription = [ 0, cpu.index, "cpu", "registers", "refresh" ],
this.system = new Array(32);
// Initialize regsiters // Initialize regsiters
this.pc = 0xFFFFFFF0;
this.program = new Array(32);
this.system = new Array(32);
for (let x = 0; x < 32; x++) for (let x = 0; x < 32; x++)
this.program[x] = this.system[x] = 0; this.program[x] = this.system[x] = 0;
@ -1245,8 +1230,8 @@ class RegisterPane extends Toolkit.SplitPane {
// System registers scroll pane // System registers scroll pane
this.scrSystem = new Toolkit.ScrollPane(cpu.debug.app, { this.scrSystem = new Toolkit.ScrollPane(cpu.debug.app, {
class : "tk scroll-pane scr-system", class : "tk scroll-pane scr-system",
overflowX: "auto", overflowX: "hidden",
overflowY: "auto", overflowY: "scroll",
view : this.lstSystem, view : this.lstSystem,
style : { style : {
position: "relative" position: "relative"
@ -1267,8 +1252,8 @@ class RegisterPane extends Toolkit.SplitPane {
// Program registers scroll pane // Program registers scroll pane
this.scrProgram = new Toolkit.ScrollPane(cpu.debug.app, { this.scrProgram = new Toolkit.ScrollPane(cpu.debug.app, {
class : "tk scroll-pane scr-program", class : "tk scroll-pane scr-program",
overflowX: "auto", overflowX: "hidden",
overflowY: "auto", overflowY: "scroll",
view : this.lstProgram view : this.lstProgram
}); });
this.secondary = this.scrProgram; this.secondary = this.scrProgram;
@ -1278,7 +1263,7 @@ class RegisterPane extends Toolkit.SplitPane {
this.addRegister(new Register(this, ... sys)); this.addRegister(new Register(this, ... sys));
for (let x = 0; x < 32; x++) { for (let x = 0; x < 32; x++) {
this.addRegister(new Register(this, this.addRegister(new Register(this,
[ "program", x ], Register.PROGRAM, Core.VB_PROGRAM, x)); x, Register.PROGRAM, Core.VB_PROGRAM, x));
} }
// Value text box measurer // Value text box measurer
@ -1298,7 +1283,7 @@ class RegisterPane extends Toolkit.SplitPane {
// Monitor the bounds of the register names column // Monitor the bounds of the register names column
let resizer = new ResizeObserver(()=>this.regResize()); let resizer = new ResizeObserver(()=>this.regResize());
for (let reg of this.list) for (let reg of this.list)
resizer.observe(reg.label.element); resizer.observe(reg.chkExpand.element);
// Monitor the bounds of the value text boxes // Monitor the bounds of the value text boxes
this.list[0].txtValue.addEventListener("resize", e=>this.valResize(e)); this.list[0].txtValue.addEventListener("resize", e=>this.valResize(e));
@ -1330,7 +1315,7 @@ class RegisterPane extends Toolkit.SplitPane {
// Measure the widths of all labels // Measure the widths of all labels
for (let x = 0; x < this.list.length; x++) { for (let x = 0; x < this.list.length; x++) {
widths[x] = Math.ceil(this.list[x].label.element widths[x] = Math.ceil(this.list[x].chkExpand.element
.getBoundingClientRect().width); .getBoundingClientRect().width);
max = Math.max(max, widths[x]); max = Math.max(max, widths[x]);
} }
@ -1338,7 +1323,7 @@ class RegisterPane extends Toolkit.SplitPane {
// Ensure all labels share the same maximum width // Ensure all labels share the same maximum width
for (let x = 0; x < this.list.length; x++) { for (let x = 0; x < this.list.length; x++) {
if (widths[x] < max) if (widths[x] < max)
this.list[x].label.element.style.minWidth = max + "px"; this.list[x].chkExpand.element.style.minWidth = max + "px";
} }
} }
@ -1410,17 +1395,19 @@ class RegisterPane extends Toolkit.SplitPane {
// Retrieve the desired dimensions of the system registers list // Retrieve the desired dimensions of the system registers list
let bounds = this.scrSystem.element.getBoundingClientRect(); let bounds = this.scrSystem.element.getBoundingClientRect();
for (let reg of this.list) {
let style = reg.main.style; // Show all hidden system registers
if (style.visibility) { for (let reg of this.list)
style.removeProperty("position"); reg.visible = true;
style.removeProperty("visibility");
}
}
// Prepare the initial dimensions of the register lists // Prepare the initial dimensions of the register lists
this .element.style.width = Math.ceil(bounds.width ) + "px"; this .element.style.width = Math.ceil(bounds.width ) + "px";
this.scrSystem .element.style.height = Math.ceil(bounds.height) + "px"; this.scrSystem .element.style.height = Math.ceil(bounds.height) + "px";
this.scrSystem .overflowX = "auto";
this.scrSystem .overflowY = "auto";
this.scrProgram.overflowX = "auto";
this.scrProgram.overflowY = "auto";
this.fetch(); this.fetch();
} }

View File

@ -79,6 +79,12 @@ class Debugger {
///////////////////////////// Package Methods ///////////////////////////// ///////////////////////////// Package Methods /////////////////////////////
// Disassembler configuration has changed
dasmConfigured() {
this.cpu .dasmConfigured();
this.memory.dasmConfigured();
}
// Ensure PC is visible in the disassembler // Ensure PC is visible in the disassembler
followPC(pc = null) { followPC(pc = null) {
this.cpu.disassembler.followPC(pc); this.cpu.disassembler.followPC(pc);

View File

@ -130,17 +130,7 @@ class Memory extends Toolkit.Window {
hexKeyDown(e) { hexKeyDown(e) {
// Error checking // Error checking
if (e.altKey) if (e.altKey || e.ctrlKey)
return;
// Processing by key, Ctrl pressed
if (e.ctrlKey) switch (e.key) {
case "g": case "G":
Toolkit.handle(e);
this.goto();
return;
default: return;
}
// Processing by key, scroll lock off // Processing by key, scroll lock off
if (!e.getModifierState("ScrollLock")) switch (e.key) { if (!e.getModifierState("ScrollLock")) switch (e.key) {
@ -352,6 +342,23 @@ class Memory extends Toolkit.Window {
this.fetch(); this.fetch();
} }
// Window key press
onKeyDown(e) {
super.onKeyDown(e);
// Error checking
if (e.altKey || !e.ctrlKey || e.shiftKey)
return;
// Processing by key
switch (e.key) {
case "g": case "G": this.goto(); break;
default: return;
}
Toolkit.handle(e);
}
// Window visibility // Window visibility
onVisibility(e) { onVisibility(e) {
this.shown = this.shown || e.visible; this.shown = this.shown || e.visible;
@ -364,6 +371,11 @@ class Memory extends Toolkit.Window {
///////////////////////////// Package Methods ///////////////////////////// ///////////////////////////// Package Methods /////////////////////////////
// Disassembler configuration has changed
dasmConfigured() {
this.refresh();
}
// Prompt the user to navigate to a new editing address // Prompt the user to navigate to a new editing address
goto() { goto() {
@ -383,6 +395,7 @@ class Memory extends Toolkit.Window {
this.commit(); this.commit();
// Navigate to the given address // Navigate to the given address
this.hexEditor.focus();
this.setEditAddress(addr, 1/3); this.setEditAddress(addr, 1/3);
} }

4
web/theme/check2.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458 2.6458" version="1.1">
<path style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.13229;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" d="m 1.05832,1.653625 1.3229,-1.3229 v 0.66145 l -1.3229,1.3229 -0.79374,-0.79374 v -0.66145 z" />
</svg>

After

Width:  |  Height:  |  Size: 484 B

6
web/theme/expand2.svg Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168" version="1.1">
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="1.8555599" y="-1.1941016" transform="rotate(60)" />
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="1.3263932" y="0.40035158" transform="rotate(30)" />
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="-1.5875" y="0.79374999" transform="scale(-1,1)" />
</svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@ -130,6 +130,12 @@ table.tk {
background: currentcolor; background: currentcolor;
} }
.tk.checkbox[aria-checked="mixed"] .box:before {
background : currentcolor;
mask : /**/url("./check2.svg") center no-repeat;
-webkit-mask: /**/url("./check2.svg") center no-repeat;
}
.tk.checkbox.pushed .box:before { .tk.checkbox.pushed .box:before {
background: var(--tk-control-shadow); background: var(--tk-control-shadow);
} }

View File

@ -31,7 +31,7 @@
user-select: none; user-select: none;
} }
.tk.window.cpu .disassembler .addr { .tk.window.cpu .disassembler .address {
margin-left: 2px; margin-left: 2px;
} }
@ -40,13 +40,13 @@
text-align : center; text-align : center;
} }
.tk.window.cpu .disassembler .ops { .tk.window.cpu .disassembler .operands {
margin-right: 2px; margin-right: 2px;
} }
.tk.window.cpu .disassembler .byte.b0, .tk.window.cpu .disassembler .byte.b0,
.tk.window.cpu .disassembler .inst, .tk.window.cpu .disassembler .mnemonic,
.tk.window.cpu .disassembler .ops { .tk.window.cpu .disassembler .operands {
margin-left: 1em; margin-left: 1em;
} }
@ -81,33 +81,35 @@
padding-bottom: 1px; padding-bottom: 1px;
} }
.tk.window.cpu .registers .icon { .tk.window.cpu .registers .expand .box {
border : none;
border-radius: 2px; border-radius: 2px;
margin : 0 1px 1px 0; margin : 0 1px 0 0;
} }
.tk.window.cpu .registers .expand .box:before {
.tk.window.cpu .registers .expand:focus .icon { background: transparent;
background: var(--tk-control-active);
}
.tk.window.cpu .registers .expand .icon:before {
content : ""; content : "";
display : block; display : block;
height : 11px; height : 11px;
width : 11px; width : 11px;
} }
.tk.window.cpu .registers .expand[aria-expanded="false"] .icon:before { .tk.window.cpu .registers .expand[role="checkbox"] .box:before {
background : currentcolor; background : currentcolor;
mask : /**/url("./expand.svg") center no-repeat; mask : /**/url("./expand.svg") center no-repeat;
-webkit-mask: /**/url("./expand.svg") center no-repeat; -webkit-mask: /**/url("./expand.svg") center no-repeat;
} }
.tk.window.cpu .registers .expand[aria-checked="true"] .box:before {
.tk.window.cpu .registers .expand[aria-expanded="true"] .icon:before {
background : currentcolor; background : currentcolor;
mask : /**/url("./collapse.svg") center no-repeat; mask : /**/url("./collapse.svg") center no-repeat;
-webkit-mask: /**/url("./collapse.svg") center no-repeat; -webkit-mask: /**/url("./collapse.svg") center no-repeat;
} }
.tk.window.cpu .registers .expand:focus .box {
background: var(--tk-control-active);
}
.tk.window.cpu .registers .main {
column-gap: 0.5em;
}
.tk.window.cpu .registers .expansion { .tk.window.cpu .registers .expansion {
gap : 1px 1em; gap : 1px 1em;

View File

@ -34,6 +34,8 @@ class Checkbox extends Toolkit.Component {
// Configure options // Configure options
this.checked = options.checked; this.checked = options.checked;
this.disabled = !!options.disabled;
this.instant = !!options.instant;
} }
@ -65,6 +67,12 @@ class Checkbox extends Toolkit.Component {
// Begin dragging // Begin dragging
this.element.setPointerCapture(e.pointerId); this.element.setPointerCapture(e.pointerId);
// Use instant activation
if (this.instant)
return this.onPointerUp(e);
// Do not use instant activation
this.element.classList.add("pushed"); this.element.classList.add("pushed");
Toolkit.handle(e); Toolkit.handle(e);
} }
@ -95,7 +103,7 @@ class Checkbox extends Toolkit.Component {
// Activate the check box if applicable // Activate the check box if applicable
if (this.isWithin(e)) if (this.isWithin(e))
this.setChecked(!this.checked); this.setChecked(this.checked !== true);
} }
@ -103,9 +111,12 @@ class Checkbox extends Toolkit.Component {
///////////////////////////// Public Methods ////////////////////////////// ///////////////////////////// Public Methods //////////////////////////////
// The check box is checked // The check box is checked
get checked() { return this.element.getAttribute("aria-checked")=="true"; } get checked() {
let ret = this.element.getAttribute("aria-checked");
return ret == "mixed" ? ret : ret == "true";
}
set checked(checked) { set checked(checked) {
checked = !!checked; checked = checked == "mixed" ? checked : !!checked;
if (checked == this.checked) if (checked == this.checked)
return; return;
this.element.setAttribute("aria-checked", checked); this.element.setAttribute("aria-checked", checked);
@ -135,8 +146,10 @@ class Checkbox extends Toolkit.Component {
checked = !!checked; checked = !!checked;
if (checked == this.checked) if (checked == this.checked)
return; return;
let previous = this.checked
this.checked = checked; this.checked = checked;
this.element.dispatchEvent(new Event("input")); this.element.dispatchEvent(
Object.assign(new Event("input"), { previous: previous }));
} }
} }

View File

@ -24,6 +24,7 @@ class TextBox extends Toolkit.Component {
onKeyDown(e) { onKeyDown(e) {
if (e.altKey || e.ctrlKey || e.shiftKey || this.disabled) if (e.altKey || e.ctrlKey || e.shiftKey || this.disabled)
return; return;
if (e.key != "Tab")
e.stopPropagation(); e.stopPropagation();
if (e.key == "Enter") if (e.key == "Enter")
this.commit(); this.commit();