From b77f2a5ab1f3384d0b74540a68f3df28ddee1ec4 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Sat, 11 Mar 2023 14:11:57 -0600 Subject: [PATCH] Adjusting presentation of Memory and CPU windows --- makefile | 2 +- web/App.js | 2 + web/core/wasm.c | 46 +++++++++---------- web/debugger/CPU.js | 101 +++++++++++++++++++++++------------------ web/debugger/Memory.js | 7 +-- web/theme/vbemu.css | 53 +++++---------------- 6 files changed, 98 insertions(+), 113 deletions(-) diff --git a/makefile b/makefile index 53771b8..9246f3e 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,7 @@ .PHONY: help help: @echo - @echo "Virtual Boy Emulator - March 10, 2023" + @echo "Virtual Boy Emulator - March 11, 2023" @echo @echo "Target build environment is any Debian with the following packages:" @echo " emscripten" diff --git a/web/App.js b/web/App.js index 915e120..5f728b1 100644 --- a/web/App.js +++ b/web/App.js @@ -640,6 +640,8 @@ class App extends Toolkit.App { // The window is not visible else { adjust = !wnd.shown; + if (adjust && wnd.firstShow) + wnd.firstShow(); wnd.visible = true; this.desktop.bringToFront(wnd); } diff --git a/web/core/wasm.c b/web/core/wasm.c index 362306e..8f2bbdc 100644 --- a/web/core/wasm.c +++ b/web/core/wasm.c @@ -9,16 +9,16 @@ // Create and initialize a new simulation EMSCRIPTEN_KEEPALIVE VB* Create() { - VB *vb = malloc(sizeof (VB)); - vbInit(vb); - return vb; + VB *sim = malloc(sizeof (VB)); + vbInit(sim); + return sim; } // Delete all memory used by a simulation -EMSCRIPTEN_KEEPALIVE void Delete(VB *vb) { - free(vb->cart.ram); - free(vb->cart.rom); - free(vb); +EMSCRIPTEN_KEEPALIVE void Delete(VB *sim) { + free(sim->cart.ram); + free(sim->cart.rom); + free(sim); } // Proxy for free() @@ -42,26 +42,26 @@ EMSCRIPTEN_KEEPALIVE int PointerSize() { // Execute until the following instruction static uint32_t RunNextAddress; -static int RunNextFetch(VB *vb, int fetch, VBAccess *access) { +static int RunNextFetch(VB *sim, int fetch, VBAccess *access) { return access->address == RunNextAddress; } -static int RunNextExecute(VB *vb, VBInstruction *inst) { +static int RunNextExecute(VB *sim, VBInstruction *inst) { RunNextAddress = inst->address + inst->size; - vbSetCallback(vb, VB_ONEXECUTE, NULL); - vbSetCallback(vb, VB_ONFETCH, &RunNextFetch); + vbSetCallback(sim, VB_ONEXECUTE, NULL); + vbSetCallback(sim, VB_ONFETCH, &RunNextFetch); return 0; } -EMSCRIPTEN_KEEPALIVE void RunNext(VB **vbs, int count) { +EMSCRIPTEN_KEEPALIVE void RunNext(VB **sims, int count) { uint32_t clocks = 20000000; // 1s - vbSetCallback(vbs[0], VB_ONEXECUTE, &RunNextExecute); - vbEmulateEx (vbs, count, &clocks); - vbSetCallback(vbs[0], VB_ONEXECUTE, NULL); - vbSetCallback(vbs[0], VB_ONFETCH , NULL); + vbSetCallback(sims[0], VB_ONEXECUTE, &RunNextExecute); + vbEmulateEx (sims, count, &clocks); + vbSetCallback(sims[0], VB_ONEXECUTE, NULL); + vbSetCallback(sims[0], VB_ONFETCH , NULL); } // Execute the current instruction static int SingleStepBreak; -static int SingleStepFetch(VB *vb, int fetch, VBAccess *access) { +static int SingleStepFetch(VB *sim, int fetch, VBAccess *access) { if (fetch != 0) return 0; if (SingleStepBreak == 1) @@ -69,11 +69,11 @@ static int SingleStepFetch(VB *vb, int fetch, VBAccess *access) { SingleStepBreak = 1; return 0; } -EMSCRIPTEN_KEEPALIVE void SingleStep(VB **vbs, int count) { +EMSCRIPTEN_KEEPALIVE void SingleStep(VB **sims, int count) { uint32_t clocks = 20000000; // 1s - SingleStepBreak = vbs[0]->cpu.stage == 0 ? 0 : 1; - vbSetCallback(vbs[0], VB_ONFETCH, &SingleStepFetch); - vbEmulateEx (vbs, count, &clocks); - vbSetCallback(vbs[0], VB_ONEXECUTE, NULL); - vbSetCallback(vbs[0], VB_ONFETCH , NULL); + SingleStepBreak = sims[0]->cpu.stage == 0 ? 0 : 1; + vbSetCallback(sims[0], VB_ONFETCH, &SingleStepFetch); + vbEmulateEx (sims, count, &clocks); + vbSetCallback(sims[0], VB_ONEXECUTE, NULL); + vbSetCallback(sims[0], VB_ONFETCH , NULL); } diff --git a/web/debugger/CPU.js b/web/debugger/CPU.js index f95d009..38a4ab7 100644 --- a/web/debugger/CPU.js +++ b/web/debugger/CPU.js @@ -90,17 +90,10 @@ class CPU extends Toolkit.Window { // Window visibility onVisibility(e) { - let firstShow = !this.shown && e.visible; // Configure instance fields this.shown = this.shown || e.visible; - // Window visible for the first time - if (firstShow) { - this.disassembler.firstShow(); - this.registers .firstShow(); - } - // Configure subscriptions if (!e.visible) { if (this.registers) @@ -114,6 +107,16 @@ class CPU extends Toolkit.Window { } + + + ///////////////////////////// Package Methods ///////////////////////////// + + // Window is being shown for the first time + firstShow() { + this.disassembler.firstShow(); + this.registers .firstShow(); + } + } @@ -166,16 +169,16 @@ class DisassemblerPane extends Toolkit.ScrollPane { view.addEventListener("wheel" , e=>this.viewWheel (e)); // Label for measuring text dimensions in the disassembler - this.sizer = new Toolkit.Label(cpu.debug.app, { - class : "tk label mono", + this.pc = new Toolkit.Label(cpu.debug.app, { + class : "tk label mono pc", visible : false, visibility: true, - style: { + style : { position: "absolute" } }); - this.sizer.setText("\u00a0", false); //   - view.append(this.sizer); + this.pc.setText("\u00a0", false); //   + view.append(this.pc); } @@ -233,16 +236,15 @@ class DisassemblerPane extends Toolkit.ScrollPane { viewResize() { // Error checking - if (!this.sizer) + if (!this.pc) return; // Working variables - let tall = this.tall(false) + 1; + let tall = this.tall(false); let grew = this.lines.length < tall; // Process all new lines for (let y = this.lines.length; y < tall; y++) { - let first = y == 0 ? " first" : ""; let resizer = y != 0 ? null : new ResizeObserver(()=>this.colResize()); @@ -250,12 +252,11 @@ class DisassemblerPane extends Toolkit.ScrollPane { lblAddress : document.createElement("div"), lblBytes : [], lblMnemonic: document.createElement("div"), - lblOperands: document.createElement("div"), - spacer : document.createElement("div") + lblOperands: document.createElement("div") }; // Address label - line.lblAddress.className = "addr" + first; + line.lblAddress.className = "addr"; if (y == 0) resizer.observe(line.lblAddress); this.view.append(line.lblAddress); @@ -263,7 +264,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Byte labels for (let x = 0; x < 4; x++) { let lbl = line.lblBytes[x] = document.createElement("div"); - lbl.className = "byte" + first + (x == 0 ? " b0" : ""); + lbl.className = "byte" + (x == 0 ? " b0" : ""); if (y == 0) { lbl.style.minWidth = "0px"; resizer.observe(lbl); @@ -272,25 +273,20 @@ class DisassemblerPane extends Toolkit.ScrollPane { } // Mnemonic label - line.lblMnemonic.className = "inst" + first; + line.lblMnemonic.className = "inst"; if (y == 0) resizer.observe(line.lblMnemonic); this.view.append(line.lblMnemonic); // Operand label - line.lblOperands.className = "ops" + first; + line.lblOperands.className = "ops"; this.view.append(line.lblOperands); - // Growing spacer - line.spacer.className = "spacer" + first; - this.view.append(line.spacer); - // All elements line.all = line.lblBytes.concat([ line.lblAddress, line.lblMnemonic, - line.lblOperands, - line.spacer + line.lblOperands ]); this.lines.push(line); @@ -302,15 +298,15 @@ class DisassemblerPane extends Toolkit.ScrollPane { line.lblAddress .remove(); line.lblMnemonic.remove(); line.lblOperands.remove(); - line.spacer .remove(); for (let lbl of line.lblBytes) lbl.remove(); this.lines.splice(tall, 1); } - // Configure scroll bar - this.hscroll.unitIncrement = - this.sizer.element.getBoundingClientRect().height; + // Configure elements + let lineHeight = this.pc.element.getBoundingClientRect().height; + this.hscroll.unitIncrement = lineHeight; + this.view.element.style.gridAutoRows = lineHeight + "px"; // Update components if (grew) @@ -330,7 +326,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Determine how many full lines were scrolled let scr = Debugger.linesScrolled(e, - this.sizer.element.getBoundingClientRect().height, + this.pc.element.getBoundingClientRect().height, this.tall(true), this.delta ); @@ -419,6 +415,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Component is being displayed for the first time firstShow() { this.viewLine = Math.floor(this.tall(true) / 3) + 10; + this.viewResize(); } // Shrink all columns to fit the current view @@ -503,11 +500,15 @@ class DisassemblerPane extends Toolkit.ScrollPane { 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 method = "remove"; + let line = this.lines[y]; + let isPC = false; // There is no data for this line if (index < 0 || this.dasm == null || index >= this.dasm.length) { @@ -529,17 +530,27 @@ class DisassemblerPane extends Toolkit.ScrollPane { line.lblOperands.innerText = dasm.operands.join(", "); for (let x = 0; x < 4; x++) line.lblBytes[x].innerText = dasm.bytes[x] || ""; - if (this.cpu.registers.pc == dasm.rawAddress) - method = "add"; + isPC = this.cpu.registers.pc == dasm.rawAddress; if (this.bytesColumn) showBytes = Math.max(showBytes, dasm.bytes.length); } // Configure whether PC is on this line - for (let elm of line.all) - elm.classList[method]("pc"); + if (isPC) { + foundPC = true; + this.pc.element.style.top = lineHeight * y + "px"; + for (let elm of line.all) + elm.classList.add("is-pc"); + } else { + for (let elm of line.all) + elm.classList.remove("is-pc"); + } + } + // Show or hide the PC background + this.pc.visible = foundPC; + // Configure which byte columns are visible for (let line of this.lines) { for (let x = 0; x < 4; x++) { @@ -552,7 +563,7 @@ class DisassemblerPane extends Toolkit.ScrollPane { // Configure layout this.view.element.style.gridTemplateColumns = - "repeat(" + (showBytes + 3) + ", max-content) auto"; + "repeat(" + (showBytes + 3) + ", max-content)";// auto"; } // Stop receiving updates from the simulation @@ -565,10 +576,11 @@ class DisassemblerPane extends Toolkit.ScrollPane { ///////////////////////////// Private Methods ///////////////////////////// // Measure the number of lines visible in the view - tall(fully = null) { - return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"]( - this.view .element.getBoundingClientRect().height / - this.sizer.element.getBoundingClientRect().height + tall(fully) { + return Math.max(1, Math[fully ? "floor" : "ceil"]( + (fully ? this.view : this).element + .getBoundingClientRect().height / + this.pc.element.getBoundingClientRect().height )); } @@ -1412,6 +1424,7 @@ class RegisterPane extends Toolkit.SplitPane { // 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.fetch(); } // Update register lists diff --git a/web/debugger/Memory.js b/web/debugger/Memory.js index 7834b81..d3947e6 100644 --- a/web/debugger/Memory.js +++ b/web/debugger/Memory.js @@ -321,9 +321,10 @@ class Memory extends Toolkit.Window { this.lines.splice(tall, 1); } - // Configure scroll bar - this.scrHex.hscroll.unitIncrement = - this.sizer.element.getBoundingClientRect().height; + // Configure components + let lineHeight = this.sizer.element.getBoundingClientRect().height; + this.scrHex.hscroll.unitIncrement = lineHeight; + this.hexEditor.element.style.gridAutoRows = lineHeight + "px"; // Update components if (grew) diff --git a/web/theme/vbemu.css b/web/theme/vbemu.css index eed45e5..71fcf0e 100644 --- a/web/theme/vbemu.css +++ b/web/theme/vbemu.css @@ -27,6 +27,7 @@ .tk.window.cpu .disassembler div { cursor : default; line-height: calc(1em + 2px); + isolation : isolate; user-select: none; } @@ -34,67 +35,35 @@ margin-left: 2px; } -.tk.window.cpu .disassembler .spacer { - margin-right: 2px; -} - .tk.window.cpu .disassembler .byte { margin-left: 0.5em; text-align : center; } +.tk.window.cpu .disassembler .ops { + margin-right: 2px; +} + .tk.window.cpu .disassembler .byte.b0, .tk.window.cpu .disassembler .inst, .tk.window.cpu .disassembler .ops { margin-left: 1em; } - .tk.window.cpu .disassembler .pc { background: var(--tk-selected-blur); - box-shadow: 0 1px 0 var(--tk-selected-blur), - 0 -1px 0 var(--tk-selected-blur); - color : var(--tk-selected-blur-text); + left : 1px; + right : 1px; } .tk.window.cpu .disassembler:focus-within .pc { background: var(--tk-selected); - box-shadow: 0 1px 0 var(--tk-selected), - 0 -1px 0 var(--tk-selected); - color : var(--tk-selected-text); } -.tk.window.cpu .disassembler .pc.addr { - box-shadow: 0 0 0 1px var(--tk-selected-blur); +.tk.window.cpu .disassembler .is-pc { + color: var(--tk-selected-blur-text); } -.tk.window.cpu .disassembler:focus-within .pc.addr { - box-shadow: 0 0 0 1px var(--tk-selected); -} - -.tk.window.cpu .disassembler .pc:is(.byte) { - box-shadow: 0 0 0 1px var(--tk-selected-blur), - calc(-0.5em + 1px) 0 0 1px var(--tk-selected-blur); -} -.tk.window.cpu .disassembler:focus-within .pc:is(.byte) { - box-shadow: 0 0 0 1px var(--tk-selected), - calc(-0.5em + 1px) 0 0 1px var(--tk-selected); -} - -.tk.window.cpu .disassembler .pc:is(.byte.b0, .inst, .ops) { - box-shadow: 0 0 0 1px var(--tk-selected-blur), - calc(-1em + 1px) 0 0 1px var(--tk-selected-blur); -} -.tk.window.cpu .disassembler:focus-within .pc:is(.byte.b0, .inst, .ops) { - box-shadow: 0 0 0 1px var(--tk-selected), - calc(-1em + 1px) 0 0 1px var(--tk-selected); -} - -.tk.window.cpu .disassembler .spacer.pc { - box-shadow: 1px 1px 0 var(--tk-selected-blur), - 1px -1px 0 var(--tk-selected-blur); -} -.tk.window.cpu .disassembler:focus-within .spacer.pc { - box-shadow: 1px 1px 0 var(--tk-selected), - 1px -1px 0 var(--tk-selected); +.tk.window.cpu .disassembler:focus-within .is-pc { + color: var(--tk-selected-text); } .tk.window.cpu .registers {