diff --git a/core/cpu.c b/core/cpu.c index d560b32..48135c9 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -483,7 +483,7 @@ static int exaFloating1(VB *sim) { /* Reserved operand */ reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31]; if (cpuFRO(reg1)) { - sim->cpu.fp_flags = 0x00000200; /* FRO */ + sim->cpu.fpFlags = 0x00000200; /* FRO */ sim->cpu.exception = 0xFF60; return 0; } @@ -506,7 +506,7 @@ static int exaFloating1(VB *sim) { /* Invalid operation */ if (bits > 7) { - sim->cpu.fp_flags = 0x00000100; /* FIV */ + sim->cpu.fpFlags = 0x00000100; /* FIV */ sim->cpu.exception = 0xFF70; return 0; } @@ -529,7 +529,7 @@ static int exaFloating1(VB *sim) { result = -result; /* Stage updates */ - sim->cpu.fp_flags = result==*(float *)®1 ? 0 : 0x00000010; /* FPR */ + sim->cpu.fpFlags = result==*(float *)®1 ? 0 : 0x00000010; /* FPR */ sim->cpu.inst.aux[0] = result; sim->cpu.inst.aux[1] = subop; 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]; reg2 = sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31]; if (cpuFRO(reg1) || cpuFRO(reg2)) { - sim->cpu.fp_flags = 0x00000200; /* FRO */ + sim->cpu.fpFlags = 0x00000200; /* FRO */ sim->cpu.exception = 0xFF60; return 0; } @@ -564,14 +564,14 @@ static int exaFloating2(VB *sim) { bits = 0x7F7FFFFF; /* Maximum value */ test = *(float *)&bits; if (aux->f64 > test || aux->f64 < -test) { - sim->cpu.fp_flags = 0x00000040; /* FOV */ + sim->cpu.fpFlags = 0x00000040; /* FOV */ sim->cpu.exception = 0xFF64; return 0; } /* Process result */ - bits = *(int32_t *)&aux->f32; - sim->cpu.fp_flags = 0; + bits = *(int32_t *)&aux->f32; + sim->cpu.fpFlags = 0; /* Zero */ if ((bits & 0x7FFFFFFF) == 0x00000000) @@ -579,13 +579,13 @@ static int exaFloating2(VB *sim) { /* Underflow */ else if ((bits & 0x7F800000) == 0x00000000) { - sim->cpu.fp_flags = 0x00000020; /* FUD */ - aux->f32 = bits = 0; + sim->cpu.fpFlags = 0x00000020; /* FUD */ + aux->f32 = bits = 0; } /* Precision degradation */ if (aux->f32 != aux->f64) - sim->cpu.fp_flags |= 0x00000010; /* FPR */ + sim->cpu.fpFlags |= 0x00000010; /* FPR */ /* Other state */ sim->cpu.inst.aux[0] = bits; @@ -770,10 +770,10 @@ static void exbException(VB *sim) { int x; /* Iterator */ /* Apply staged floating-point flags */ - if (sim->cpu.fp_flags != 0) { - cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fp_flags | + if (sim->cpu.fpFlags != 0) { + cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags | cpuGetSystemRegister(sim, VB_PSW), 0); - sim->cpu.fp_flags = 0; + sim->cpu.fpFlags = 0; } /* Fatal exception */ @@ -826,10 +826,10 @@ static void exbFloating(VB *sim) { int32_t subop = sim->cpu.inst.aux[1]; /* Sub-opcode */ /* Apply staged floating-point flags */ - if (sim->cpu.fp_flags != 0) { - cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fp_flags | + if (sim->cpu.fpFlags != 0) { + cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags | cpuGetSystemRegister(sim, VB_PSW), 0); - sim->cpu.fp_flags = 0; + sim->cpu.fpFlags = 0; } /* Update state */ @@ -985,13 +985,13 @@ static void opDIVF_S(VB *sim, int32_t *dest, int32_t src) { /* Invalid operation */ if (src == 0) { - sim->cpu.fp_flags = 0x00000100; /* FIV */ + sim->cpu.fpFlags = 0x00000100; /* FIV */ sim->cpu.exception = 0xFF70; } /* Zero division */ else { - sim->cpu.fp_flags = 0x00000080; /* FZD */ + sim->cpu.fpFlags = 0x00000080; /* FZD */ sim->cpu.exception = 0xFF68; } diff --git a/core/vb.h b/core/vb.h index 0c47276..aeedea6 100644 --- a/core/vb.h +++ b/core/vb.h @@ -172,7 +172,7 @@ struct VB { /* Other state */ 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 */ uint8_t irq[5]; /* Interrupt requests */ uint8_t bitstring; /* Processing a bit string instruction */ diff --git a/web/core/Disassembler.js b/web/core/Disassembler.js index 9c2a459..8149762 100644 --- a/web/core/Disassembler.js +++ b/web/core/Disassembler.js @@ -139,7 +139,7 @@ class Disassembler { ]; // Program register names - static PRONAMES = [ + static REG_PROGRAM = [ "r0" , "r1" , "hp" , "sp" , "gp" , "tp" , "r6" , "r7" , "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", @@ -147,13 +147,16 @@ class Disassembler { ]; // System register names - static SYSNAMES = [ + static REG_SYSTEM = [ "EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW", "8" , "9" , "10" , "11" , "12" , "13" , "14" , "15" , "16" , "17" , "18" , "19" , "20" , "21" , "22" , "23" , "CHCW", "ADTRE", "26" , "27" , "28" , "29" , "30" , "31" ]; + // Other register names + static REG_OTHER = [ "PC", "PSW" ]; + ///////////////////////////// Static Methods ////////////////////////////// @@ -522,7 +525,8 @@ class Disassembler { // Format a program register programRegister(index) { - let ret = this.proNames ? Disassembler.PRONAMES[index] : "r" + index; + let ret = this.proNames ? + Disassembler.REG_PROGRAM[index] : "r" + index; if (this.proUppercase) ret = ret.toUpperCase(); return ret; @@ -531,7 +535,7 @@ class Disassembler { // Format a system register systemRegister(index) { let ret = this.sysNames ? - Disassembler.SYSNAMES[index] : index.toString(); + Disassembler.REG_SYSTEM[index] : index.toString(); if (!this.sysUppercase && this.sysNames) ret = ret.toLowerCase(); return ret; diff --git a/web/debugger/CPU.js b/web/debugger/CPU.js index a15d9f3..a51ea0e 100644 --- a/web/debugger/CPU.js +++ b/web/debugger/CPU.js @@ -72,7 +72,8 @@ class CPU extends Toolkit.Window { break; case "g": case "G": this.disassembler.goto(); - return; + break; + default: return; } // Processing by key: CTRL up @@ -112,6 +113,20 @@ class CPU extends Toolkit.Window { ///////////////////////////// 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 firstShow() { this.disassembler.firstShow(); @@ -139,7 +154,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Configure instance fields this._bytesColumn = true; - this.columnWidths = [ 0, 0, 0, 0, 0, 0 ]; + this.columnWidths = new Array(6); this.cpu = cpu; this.delta = 0; this.dasm = null; @@ -149,6 +164,10 @@ class DisassemblerPane extends Toolkit.ScrollPane { this.viewAddress = 0xFFFFFFF0; this.viewLine = 10; + // Initialize column widths + for (let x = 0; x < this.columnWidths.length; x++) + this.columnWidths[x] = 0; + // Client area let view = this.view = new Toolkit.Component(cpu.debug.app, { class : "tk mono disassembler", @@ -251,19 +270,19 @@ class DisassemblerPane extends Toolkit.ScrollPane { let line = { lblAddress : document.createElement("div"), - lblBytes : [], + lblBytes : new Array(4), lblMnemonic: document.createElement("div"), lblOperands: document.createElement("div") }; // Address label - line.lblAddress.className = "addr"; + line.lblAddress.className = "address"; if (y == 0) resizer.observe(line.lblAddress); this.view.append(line.lblAddress); // 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"); lbl.className = "byte" + (x == 0 ? " b0" : ""); if (y == 0) { @@ -274,13 +293,13 @@ class DisassemblerPane extends Toolkit.ScrollPane { } // Mnemonic label - line.lblMnemonic.className = "inst"; + line.lblMnemonic.className = "mnemonic"; if (y == 0) resizer.observe(line.lblMnemonic); this.view.append(line.lblMnemonic); // Operand label - line.lblOperands.className = "ops"; + line.lblOperands.className = "operands"; this.view.append(line.lblOperands); // All elements @@ -428,10 +447,11 @@ class DisassemblerPane extends Toolkit.ScrollPane { for (let x = 0; x < this.columnWidths.length; x++) this.columnWidths[x] = 0; this.refresh(); + this.colResize(); } // Ensure PC is visible in the view - followPC(pc = null) { + followPC(pc) { let tall = this.tall(true); let count = !this.dasm ? 0 : Math.min(this.dasm.length - 10, tall); @@ -498,15 +518,17 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Determine an initial number of visible byte columns let showBytes = 0; - if (this.bytesColumn) - for (let x = 0; x<4 && this.columnWidths[x]!=0; x++, showBytes++); + if (this.bytesColumn) { + for (let x = 0; x < 4 && + this.columnWidths[x] != 0; x++, showBytes++); + } // Working variables let foundPC = false; let lineHeight = this.pc.element.getBoundingClientRect().height; // Process all lines - let index = 20 - this.viewLine; + let index = 20 - this.viewLine; for (let y = 0; y < this.lines.length; y++, index++) { let line = this.lines[y]; let isPC = false; @@ -517,7 +539,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { line.lblBytes[0].innerText = "--"; line.lblMnemonic.innerText = "---"; line.lblOperands.innerText = ""; - for (let x = 1; x < 4; x++) + for (let x = 1; x < line.lblBytes.length; x++) line.lblBytes[x].innerText = ""; if (this.bytesColumn) showBytes = Math.max(showBytes, 1); @@ -529,7 +551,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { line.lblAddress .innerText = dasm.address; line.lblMnemonic.innerText = dasm.mnemonic; 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] || ""; isPC = this.cpu.registers.pc == dasm.rawAddress; if (this.bytesColumn) @@ -554,7 +576,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Configure which byte columns are visible 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 [x < showBytes ? "removeProperty" : "setProperty"] ("display", "none") @@ -599,8 +621,8 @@ class Register { //////////////////////////////// Constants //////////////////////////////// // Register types - static PROGRAM = 0; - static PLAIN = 1; + static PLAIN = 0; + static PROGRAM = 1; static CHCW = 2; static ECR = 3; static PSW = 4; @@ -626,31 +648,20 @@ class Register { [ "texth", "PT", 0, 16 ] ], [this.PSW]: [ - [ "check", "CY" , 3 ], - [ "check", "FRO", 9 ], - [ "check", "OV" , 2 ], - [ "check", "FIV", 8 ], - [ "check", "S" , 1 ], - [ "check", "FZD", 7 ], - [ "check", "Z" , 0 ], - [ "check", "FOV", 6 ], - [ "check", "NP" , 15 ], - [ "check", "FUD", 5 ], - [ "check", "EP" , 14 ], - [ "check", "FPR", 4 ], - [ "check", "ID" , 12 ], - [ "textd", "I" , 16, 4 ], + [ "check", "CY" , 3 ], [ "check", "FRO", 9 ], + [ "check", "OV" , 2 ], [ "check", "FIV", 8 ], + [ "check", "S" , 1 ], [ "check", "FZD", 7 ], + [ "check", "Z" , 0 ], [ "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 ] ], [this.TKCW]: [ - [ "check", "FIT", 7 ], - [ "check", "FUT", 4 ], - [ "check", "FZT", 6 ], - [ "check", "FPT", 3 ], - [ "check", "FVT", 5 ], - [ "check", "OTM", 8 ], - [ "check", "RDI", 2 ], - [ "textd", "RD" , 0, 2 ] + [ "check", "FIT", 7 ], [ "check", "FUT", 4 ], + [ "check", "FZT", 6 ], [ "check", "FPT", 3 ], + [ "check", "FVT", 5 ], [ "check", "OTM", 8 ], + [ "check", "RDI", 2 ], [ "textd", "RD" , 0, 2 ] ] }; @@ -668,47 +679,37 @@ class Register { this.dasm = registers.cpu.debug.app.dasm; this.debug = registers.cpu.debug; this.expansion = null; + this.format = Register.HEX; + this.key = key; this.registers = registers; - this.target = registers; this.type = type; - // Resolve the register reference - key = key.slice(); - while (key.length != 1) - this.target = this.target[key.shift()]; - this.key = key[0]; + // Resolve the target object + switch (apiType) { + case Core.VB_PROGRAM: this.target = registers.program; break; + case Core.VB_SYSTEM : this.target = registers.system ; break; + case Core.VB_OTHER : this.target = registers ; break; + } // Main controls this.main = new Toolkit.Component(app, { - class: "main", - style: { + class : "main", + visibility: true, + style : { alignItems : "center", display : "grid", gridTemplateColumns: "max-content auto" } }); - // Expand/collapse button - this.btnExpand = new Toolkit.Component(app, { - class: "tk expand", - style: { - alignItems : "center", - display : "grid", - gridTemplateColumns: "max-content auto" - } + // Expand/collapse check box + this.chkExpand = new Toolkit.Checkbox(app, { + class : "tk expand", + disabled: true, + instant : true, + role : "" }); - this.main.add(this.btnExpand); - - // 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); + this.main.add(this.chkExpand); // Value text box this.txtValue = new Toolkit.TextBox(app, { @@ -728,34 +729,29 @@ class Register { else this.initSystem(app, Register.FIELDS[type]); if (this.expansion != null) { - // Expand/collapse button - this.btnExpand.element.setAttribute("aria-expanded", "false" ); - this.btnExpand.element.setAttribute("tabindex" , "0" ); - this.btnExpand.element.setAttribute("role" , "button"); - this.btnExpand.element + // Expand/collapse check box + this.chkExpand.addEventListener("input", e=>this.chkInput(e)); + this.chkExpand.disabled = false; + this.chkExpand.element.setAttribute("role", "checkbox"); + this.chkExpand.element.setAttribute("tabindex", "0"); + this.chkExpand.element .setAttribute("aria-controls", this.expansion.element.id); // Expansion area - this.btnExpand.addEventListener("keydown", - e=>this.expKeyDown (e)); - this.btnExpand.addEventListener("pointerdown", - e=>this.expPointerDown(e)); this.expansion.visible = false; } // Update controls + this.dasmConfigured(); this.refresh(); // PSW is initially expanded - if (type == Register.PSW && key == 5) + if (apiType == Core.VB_SYSTEM && apiId == Core.VB_PSW) this.expanded = true; // System registers after PSW are initially hidden - else if (key != "pc" && key != 5 && type != Register.PROGRAM) { - this.main.style.position = "absolute"; - this.main.style.visibility = "hidden"; - } - + else if (apiType == Core.VB_SYSTEM) + this.visible = false; } // Expansion controls for program registers @@ -774,8 +770,7 @@ class Register { exp.setLabel("{debug.cpu.format}", true); // Radio group - let group = new Toolkit.RadioGroup(app); - this.format = Register.HEX; + let group = new Toolkit.RadioGroup(app); // Hex radio button let opt = new Toolkit.Radio(app, { @@ -934,21 +929,9 @@ class Register { ///////////////////////////// Event Handlers ////////////////////////////// - // Expand/collapse button key press - expKeyDown(e) { - if ( - !(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; + // Expand/collapse check box input + chkInput(e) { + this.expanded = this.chkExpand.checked; } // Program register format changed @@ -956,7 +939,7 @@ class Register { this.format = format; this.txtValue.element.classList [format == Register.HEX ? "add" : "remove"]("mono"); - this.formatValue(); + this.refresh(); } // Bit check box input @@ -975,14 +958,16 @@ class Register { // Text box commit 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 if (e.disabled) 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 if (!Number.isInteger(newValue)) { this.refresh(); @@ -996,54 +981,10 @@ class Register { // Value text box commit valAction(e) { - let text = this.txtValue.value; - let value = null; - Toolkit.handle(e); - - // 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 + let value = this.parseValue(this.txtValue.value); if (value === null) - this.formatValue(); + this.refresh(); else this.setValue(value); } @@ -1052,8 +993,7 @@ class Register { if (e.altKey || e.ctrlKey || e.shiftKey || e.key != "Escape") return; Toolkit.handle(e); - this.txtValue.value = - this.debug.dasm.hex(this.target[this.key], 8, false); + this.refresh(); } @@ -1061,34 +1001,45 @@ class Register { ///////////////////////////// Public Methods ////////////////////////////// // The expansion area is visible - get expanded() { - return this.btnExpand.element.getAttribute("aria-expanded") == "true"; - } + get expanded() { return this.chkExpand.checked; } set expanded(expanded) { - expanded = !!expanded; - if (this.expansion == null || expanded == this.expanded) + this.chkExpand.checked = 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; - this.btnExpand.element.setAttribute("aria-expanded", expanded); - this.expansion.visible = expanded; + this.main.element.style[visible ? "removeProperty" : "setProperty"] + ("position", "absolute"); + this.main.visible = visible; + this.setVisible(visible); } ///////////////////////////// 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 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 let value = this.target[this.key]; - this.formatValue(); + this.txtValue.value = this.formatValue(value); // Expansion controls for (let ctrl of this.controls) { @@ -1117,45 +1068,74 @@ class Register { ///////////////////////////// Private Methods ///////////////////////////// - // Format the value as a string in the text box - formatValue() { - 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) { - case Register.SIGNED: - text = (value >> 0).toString(); - break; - case Register.UNSIGNED: - text = (value >>> 0).toString(); - break; - case Register.FLOAT: - value = Debugger.ixf(value); - if (Number.isFinite(value)) { - text = value.toFixed(100); - if (/[^0-9\-\.]/.test(text)) - text = value.toFixed(6); - if (text.indexOf(".") != -1) { - text = text.replace(/0+$/, "") - .replace(/\.$/, ".0"); - } else text += ".0"; - } else if (!Number.isNaN(value)) { - text = - (value == Number.NEGATIVE_INFINITY ? "-" : "") + - this.debug.app.localize("{debug.cpu.infinity}") - ; - } else text = "NaN"; - break; - } + // Format a register value as text + formatValue(value) { + switch (this.format) { + case Register.HEX: + return this.debug.hex(value >>> 0, 8, false); + case Register.SIGNED: + return (value >> 0).toString(); + case Register.UNSIGNED: + return (value >>> 0).toString(); + case Register.FLOAT: + value = Debugger.ixf(value); + if (Number.isFinite(value)) { + let text = value.toFixed(100); + if (/[^0-9\-\.]/.test(text)) + text = value.toFixed(6); + if (text.indexOf(".") != -1) { + text = text.replace(/0+$/, "") + .replace(/\.$/, ".0"); + } else text += ".0"; + return text; + } + if (!Number.isNaN(value)) { + return ( + (value == Number.NEGATIVE_INFINITY ? "-" : "") + + this.debug.app.localize("{debug.cpu.infinity}") + ); + } + return "NaN"; } + return null; + } - // Default hexadecimal format - else text = this.dasm.hex(value >>> 0, 8, false); - - // Update the text - this.txtValue.value = text; + // Parse text as a register value + parseValue(value) { + switch (this.format) { + case Register.HEX: + 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 @@ -1175,8 +1155,13 @@ class Register { 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 static SYSTEMS = [ - [["pc" ],Register.PLAIN,Core.VB_OTHER ,Core.VB_PC ], - [["system",Core.VB_PSW ],Register.PSW ,Core.VB_SYSTEM,Core.VB_PSW ], - [["system",Core.VB_ADTRE],Register.PLAIN,Core.VB_SYSTEM,Core.VB_ADTRE], - [["system",Core.VB_CHCW ],Register.CHCW ,Core.VB_SYSTEM,Core.VB_CHCW ], - [["system",Core.VB_ECR ],Register.ECR ,Core.VB_SYSTEM,Core.VB_ECR ], - [["system",Core.VB_EIPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_EIPC ], - [["system",Core.VB_EIPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_EIPSW], - [["system",Core.VB_FEPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_FEPC ], - [["system",Core.VB_FEPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_FEPSW], - [["system",Core.VB_PIR ],Register.PIR ,Core.VB_SYSTEM,Core.VB_PIR ], - [["system",Core.VB_TKCW ],Register.TKCW ,Core.VB_SYSTEM,Core.VB_TKCW ], - [["system",29 ],Register.PLAIN,Core.VB_SYSTEM,29 ], - [["system",30 ],Register.PLAIN,Core.VB_SYSTEM,30 ], - [["system",31 ],Register.PLAIN,Core.VB_SYSTEM,31 ] + [ "pc" , Register.PLAIN, Core.VB_OTHER , Core.VB_PC ], + [ Core.VB_PSW , Register.PSW , Core.VB_SYSTEM, Core.VB_PSW ], + [ Core.VB_ADTRE, Register.PLAIN, Core.VB_SYSTEM, Core.VB_ADTRE ], + [ Core.VB_CHCW , Register.CHCW , Core.VB_SYSTEM, Core.VB_CHCW ], + [ Core.VB_ECR , Register.ECR , Core.VB_SYSTEM, Core.VB_ECR ], + [ Core.VB_EIPC , Register.PLAIN, Core.VB_SYSTEM, Core.VB_EIPC ], + [ Core.VB_EIPSW, Register.PSW , Core.VB_SYSTEM, Core.VB_EIPSW ], + [ Core.VB_FEPC , Register.PLAIN, Core.VB_SYSTEM, Core.VB_FEPC ], + [ Core.VB_FEPSW, Register.PSW , Core.VB_SYSTEM, Core.VB_FEPSW ], + [ Core.VB_PIR , Register.PIR , Core.VB_SYSTEM, Core.VB_PIR ], + [ Core.VB_TKCW , Register.TKCW , Core.VB_SYSTEM, Core.VB_TKCW ], + [ 29 , Register.PLAIN, Core.VB_SYSTEM, 29 ], + [ 30 , Register.PLAIN, Core.VB_SYSTEM, 30 ], + [ 31 , Register.PLAIN, Core.VB_SYSTEM, 31 ] ]; @@ -1222,13 +1207,13 @@ class RegisterPane extends Toolkit.SplitPane { // Configure instance fields this.cpu = cpu; this.list = []; - this.pc = 0xFFFFFFF0; - this.program = new Array(32); this.pending = false; this.subscription = [ 0, cpu.index, "cpu", "registers", "refresh" ], - this.system = new Array(32); // Initialize regsiters + this.pc = 0xFFFFFFF0; + this.program = new Array(32); + this.system = new Array(32); for (let x = 0; x < 32; x++) this.program[x] = this.system[x] = 0; @@ -1245,8 +1230,8 @@ class RegisterPane extends Toolkit.SplitPane { // System registers scroll pane this.scrSystem = new Toolkit.ScrollPane(cpu.debug.app, { class : "tk scroll-pane scr-system", - overflowX: "auto", - overflowY: "auto", + overflowX: "hidden", + overflowY: "scroll", view : this.lstSystem, style : { position: "relative" @@ -1267,8 +1252,8 @@ class RegisterPane extends Toolkit.SplitPane { // Program registers scroll pane this.scrProgram = new Toolkit.ScrollPane(cpu.debug.app, { class : "tk scroll-pane scr-program", - overflowX: "auto", - overflowY: "auto", + overflowX: "hidden", + overflowY: "scroll", view : this.lstProgram }); this.secondary = this.scrProgram; @@ -1278,7 +1263,7 @@ class RegisterPane extends Toolkit.SplitPane { this.addRegister(new Register(this, ... sys)); for (let x = 0; x < 32; x++) { this.addRegister(new Register(this, - [ "program", x ], Register.PROGRAM, Core.VB_PROGRAM, x)); + x, Register.PROGRAM, Core.VB_PROGRAM, x)); } // Value text box measurer @@ -1298,7 +1283,7 @@ class RegisterPane extends Toolkit.SplitPane { // Monitor the bounds of the register names column let resizer = new ResizeObserver(()=>this.regResize()); for (let reg of this.list) - resizer.observe(reg.label.element); + resizer.observe(reg.chkExpand.element); // Monitor the bounds of the value text boxes 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 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); max = Math.max(max, widths[x]); } @@ -1338,7 +1323,7 @@ class RegisterPane extends Toolkit.SplitPane { // Ensure all labels share the same maximum width for (let x = 0; x < this.list.length; x++) { if (widths[x] < max) - this.list[x].label.element.style.minWidth = max + "px"; + this.list[x].chkExpand.element.style.minWidth = max + "px"; } } @@ -1380,9 +1365,9 @@ class RegisterPane extends Toolkit.SplitPane { // Wait for a transaction to complete if (promise != null) { - this.pending= true; - data = await promise; - promise = null; + this.pending = true; + data = await promise; + promise = null; } // Initiate a new transaction @@ -1410,17 +1395,19 @@ class RegisterPane extends Toolkit.SplitPane { // Retrieve the desired dimensions of the system registers list let bounds = this.scrSystem.element.getBoundingClientRect(); - for (let reg of this.list) { - let style = reg.main.style; - if (style.visibility) { - style.removeProperty("position"); - style.removeProperty("visibility"); - } - } + + // Show all hidden system registers + for (let reg of this.list) + reg.visible = true; // Prepare the initial dimensions of the register lists - this .element.style.width = Math.ceil(bounds.width ) + "px"; - this.scrSystem.element.style.height = Math.ceil(bounds.height) + "px"; + this .element.style.width = Math.ceil(bounds.width ) + "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(); } diff --git a/web/debugger/Debugger.js b/web/debugger/Debugger.js index 1ea7d7d..be029a8 100644 --- a/web/debugger/Debugger.js +++ b/web/debugger/Debugger.js @@ -79,6 +79,12 @@ class Debugger { ///////////////////////////// Package Methods ///////////////////////////// + // Disassembler configuration has changed + dasmConfigured() { + this.cpu .dasmConfigured(); + this.memory.dasmConfigured(); + } + // Ensure PC is visible in the disassembler followPC(pc = null) { this.cpu.disassembler.followPC(pc); diff --git a/web/debugger/Memory.js b/web/debugger/Memory.js index 36cafc3..5012a72 100644 --- a/web/debugger/Memory.js +++ b/web/debugger/Memory.js @@ -130,17 +130,7 @@ class Memory extends Toolkit.Window { hexKeyDown(e) { // Error checking - if (e.altKey) - return; - - // Processing by key, Ctrl pressed - if (e.ctrlKey) switch (e.key) { - case "g": case "G": - Toolkit.handle(e); - this.goto(); - return; - default: return; - } + if (e.altKey || e.ctrlKey) // Processing by key, scroll lock off if (!e.getModifierState("ScrollLock")) switch (e.key) { @@ -352,6 +342,23 @@ class Memory extends Toolkit.Window { 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 onVisibility(e) { this.shown = this.shown || e.visible; @@ -364,6 +371,11 @@ class Memory extends Toolkit.Window { ///////////////////////////// Package Methods ///////////////////////////// + // Disassembler configuration has changed + dasmConfigured() { + this.refresh(); + } + // Prompt the user to navigate to a new editing address goto() { @@ -383,6 +395,7 @@ class Memory extends Toolkit.Window { this.commit(); // Navigate to the given address + this.hexEditor.focus(); this.setEditAddress(addr, 1/3); } diff --git a/web/theme/check2.svg b/web/theme/check2.svg new file mode 100644 index 0000000..f0c4196 --- /dev/null +++ b/web/theme/check2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web/theme/expand2.svg b/web/theme/expand2.svg new file mode 100644 index 0000000..9673e56 --- /dev/null +++ b/web/theme/expand2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web/theme/kiosk.css b/web/theme/kiosk.css index 1684037..c166be4 100644 --- a/web/theme/kiosk.css +++ b/web/theme/kiosk.css @@ -130,6 +130,12 @@ table.tk { 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 { background: var(--tk-control-shadow); } diff --git a/web/theme/vbemu.css b/web/theme/vbemu.css index 71fcf0e..69410ea 100644 --- a/web/theme/vbemu.css +++ b/web/theme/vbemu.css @@ -31,7 +31,7 @@ user-select: none; } -.tk.window.cpu .disassembler .addr { +.tk.window.cpu .disassembler .address { margin-left: 2px; } @@ -40,13 +40,13 @@ text-align : center; } -.tk.window.cpu .disassembler .ops { +.tk.window.cpu .disassembler .operands { margin-right: 2px; } .tk.window.cpu .disassembler .byte.b0, -.tk.window.cpu .disassembler .inst, -.tk.window.cpu .disassembler .ops { +.tk.window.cpu .disassembler .mnemonic, +.tk.window.cpu .disassembler .operands { margin-left: 1em; } @@ -81,33 +81,35 @@ padding-bottom: 1px; } -.tk.window.cpu .registers .icon { +.tk.window.cpu .registers .expand .box { + border : none; border-radius: 2px; - margin : 0 1px 1px 0; + margin : 0 1px 0 0; +} +.tk.window.cpu .registers .expand .box:before { + background: transparent; + content : ""; + display : block; + height : 11px; + width : 11px; } -.tk.window.cpu .registers .expand:focus .icon { - background: var(--tk-control-active); -} - -.tk.window.cpu .registers .expand .icon:before { - content: ""; - display: block; - height : 11px; - width : 11px; -} - -.tk.window.cpu .registers .expand[aria-expanded="false"] .icon:before { +.tk.window.cpu .registers .expand[role="checkbox"] .box:before { background : currentcolor; mask : /**/url("./expand.svg") center no-repeat; -webkit-mask: /**/url("./expand.svg") center no-repeat; } - -.tk.window.cpu .registers .expand[aria-expanded="true"] .icon:before { +.tk.window.cpu .registers .expand[aria-checked="true"] .box:before { background : currentcolor; 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 { gap : 1px 1em; diff --git a/web/toolkit/Checkbox.js b/web/toolkit/Checkbox.js index 5c5c933..d718447 100644 --- a/web/toolkit/Checkbox.js +++ b/web/toolkit/Checkbox.js @@ -33,7 +33,9 @@ class Checkbox extends Toolkit.Component { this.add(this.uiLabel); // 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 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"); Toolkit.handle(e); } @@ -95,7 +103,7 @@ class Checkbox extends Toolkit.Component { // Activate the check box if applicable if (this.isWithin(e)) - this.setChecked(!this.checked); + this.setChecked(this.checked !== true); } @@ -103,9 +111,12 @@ class Checkbox extends Toolkit.Component { ///////////////////////////// Public Methods ////////////////////////////// // 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) { - checked = !!checked; + checked = checked == "mixed" ? checked : !!checked; if (checked == this.checked) return; this.element.setAttribute("aria-checked", checked); @@ -135,8 +146,10 @@ class Checkbox extends Toolkit.Component { checked = !!checked; if (checked == this.checked) return; + let previous = this.checked this.checked = checked; - this.element.dispatchEvent(new Event("input")); + this.element.dispatchEvent( + Object.assign(new Event("input"), { previous: previous })); } } diff --git a/web/toolkit/TextBox.js b/web/toolkit/TextBox.js index e30095b..90a833d 100644 --- a/web/toolkit/TextBox.js +++ b/web/toolkit/TextBox.js @@ -24,7 +24,8 @@ class TextBox extends Toolkit.Component { onKeyDown(e) { if (e.altKey || e.ctrlKey || e.shiftKey || this.disabled) return; - e.stopPropagation(); + if (e.key != "Tab") + e.stopPropagation(); if (e.key == "Enter") this.commit(); }