Adjusting presentation of Memory and CPU windows

This commit is contained in:
Guy Perfect 2023-03-11 14:11:57 -06:00
parent 265aeed8cd
commit b77f2a5ab1
6 changed files with 98 additions and 113 deletions

View File

@ -1,7 +1,7 @@
.PHONY: help .PHONY: help
help: help:
@echo @echo
@echo "Virtual Boy Emulator - March 10, 2023" @echo "Virtual Boy Emulator - March 11, 2023"
@echo @echo
@echo "Target build environment is any Debian with the following packages:" @echo "Target build environment is any Debian with the following packages:"
@echo " emscripten" @echo " emscripten"

View File

@ -640,6 +640,8 @@ class App extends Toolkit.App {
// The window is not visible // The window is not visible
else { else {
adjust = !wnd.shown; adjust = !wnd.shown;
if (adjust && wnd.firstShow)
wnd.firstShow();
wnd.visible = true; wnd.visible = true;
this.desktop.bringToFront(wnd); this.desktop.bringToFront(wnd);
} }

View File

@ -9,16 +9,16 @@
// Create and initialize a new simulation // Create and initialize a new simulation
EMSCRIPTEN_KEEPALIVE VB* Create() { EMSCRIPTEN_KEEPALIVE VB* Create() {
VB *vb = malloc(sizeof (VB)); VB *sim = malloc(sizeof (VB));
vbInit(vb); vbInit(sim);
return vb; return sim;
} }
// Delete all memory used by a simulation // Delete all memory used by a simulation
EMSCRIPTEN_KEEPALIVE void Delete(VB *vb) { EMSCRIPTEN_KEEPALIVE void Delete(VB *sim) {
free(vb->cart.ram); free(sim->cart.ram);
free(vb->cart.rom); free(sim->cart.rom);
free(vb); free(sim);
} }
// Proxy for free() // Proxy for free()
@ -42,26 +42,26 @@ EMSCRIPTEN_KEEPALIVE int PointerSize() {
// Execute until the following instruction // Execute until the following instruction
static uint32_t RunNextAddress; 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; return access->address == RunNextAddress;
} }
static int RunNextExecute(VB *vb, VBInstruction *inst) { static int RunNextExecute(VB *sim, VBInstruction *inst) {
RunNextAddress = inst->address + inst->size; RunNextAddress = inst->address + inst->size;
vbSetCallback(vb, VB_ONEXECUTE, NULL); vbSetCallback(sim, VB_ONEXECUTE, NULL);
vbSetCallback(vb, VB_ONFETCH, &RunNextFetch); vbSetCallback(sim, VB_ONFETCH, &RunNextFetch);
return 0; return 0;
} }
EMSCRIPTEN_KEEPALIVE void RunNext(VB **vbs, int count) { EMSCRIPTEN_KEEPALIVE void RunNext(VB **sims, int count) {
uint32_t clocks = 20000000; // 1s uint32_t clocks = 20000000; // 1s
vbSetCallback(vbs[0], VB_ONEXECUTE, &RunNextExecute); vbSetCallback(sims[0], VB_ONEXECUTE, &RunNextExecute);
vbEmulateEx (vbs, count, &clocks); vbEmulateEx (sims, count, &clocks);
vbSetCallback(vbs[0], VB_ONEXECUTE, NULL); vbSetCallback(sims[0], VB_ONEXECUTE, NULL);
vbSetCallback(vbs[0], VB_ONFETCH , NULL); vbSetCallback(sims[0], VB_ONFETCH , NULL);
} }
// Execute the current instruction // Execute the current instruction
static int SingleStepBreak; static int SingleStepBreak;
static int SingleStepFetch(VB *vb, int fetch, VBAccess *access) { static int SingleStepFetch(VB *sim, int fetch, VBAccess *access) {
if (fetch != 0) if (fetch != 0)
return 0; return 0;
if (SingleStepBreak == 1) if (SingleStepBreak == 1)
@ -69,11 +69,11 @@ static int SingleStepFetch(VB *vb, int fetch, VBAccess *access) {
SingleStepBreak = 1; SingleStepBreak = 1;
return 0; return 0;
} }
EMSCRIPTEN_KEEPALIVE void SingleStep(VB **vbs, int count) { EMSCRIPTEN_KEEPALIVE void SingleStep(VB **sims, int count) {
uint32_t clocks = 20000000; // 1s uint32_t clocks = 20000000; // 1s
SingleStepBreak = vbs[0]->cpu.stage == 0 ? 0 : 1; SingleStepBreak = sims[0]->cpu.stage == 0 ? 0 : 1;
vbSetCallback(vbs[0], VB_ONFETCH, &SingleStepFetch); vbSetCallback(sims[0], VB_ONFETCH, &SingleStepFetch);
vbEmulateEx (vbs, count, &clocks); vbEmulateEx (sims, count, &clocks);
vbSetCallback(vbs[0], VB_ONEXECUTE, NULL); vbSetCallback(sims[0], VB_ONEXECUTE, NULL);
vbSetCallback(vbs[0], VB_ONFETCH , NULL); vbSetCallback(sims[0], VB_ONFETCH , NULL);
} }

View File

@ -90,17 +90,10 @@ class CPU extends Toolkit.Window {
// Window visibility // Window visibility
onVisibility(e) { onVisibility(e) {
let firstShow = !this.shown && e.visible;
// Configure instance fields // Configure instance fields
this.shown = this.shown || e.visible; this.shown = this.shown || e.visible;
// Window visible for the first time
if (firstShow) {
this.disassembler.firstShow();
this.registers .firstShow();
}
// Configure subscriptions // Configure subscriptions
if (!e.visible) { if (!e.visible) {
if (this.registers) 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)); view.addEventListener("wheel" , e=>this.viewWheel (e));
// Label for measuring text dimensions in the disassembler // Label for measuring text dimensions in the disassembler
this.sizer = new Toolkit.Label(cpu.debug.app, { this.pc = new Toolkit.Label(cpu.debug.app, {
class : "tk label mono", class : "tk label mono pc",
visible : false, visible : false,
visibility: true, visibility: true,
style: { style : {
position: "absolute" position: "absolute"
} }
}); });
this.sizer.setText("\u00a0", false); //   this.pc.setText("\u00a0", false); //  
view.append(this.sizer); view.append(this.pc);
} }
@ -233,16 +236,15 @@ class DisassemblerPane extends Toolkit.ScrollPane {
viewResize() { viewResize() {
// Error checking // Error checking
if (!this.sizer) if (!this.pc)
return; return;
// Working variables // Working variables
let tall = this.tall(false) + 1; let tall = this.tall(false);
let grew = this.lines.length < tall; let grew = this.lines.length < tall;
// Process all new lines // Process all new lines
for (let y = this.lines.length; y < tall; y++) { for (let y = this.lines.length; y < tall; y++) {
let first = y == 0 ? " first" : "";
let resizer = y != 0 ? null : let resizer = y != 0 ? null :
new ResizeObserver(()=>this.colResize()); new ResizeObserver(()=>this.colResize());
@ -250,12 +252,11 @@ class DisassemblerPane extends Toolkit.ScrollPane {
lblAddress : document.createElement("div"), lblAddress : document.createElement("div"),
lblBytes : [], lblBytes : [],
lblMnemonic: document.createElement("div"), lblMnemonic: document.createElement("div"),
lblOperands: document.createElement("div"), lblOperands: document.createElement("div")
spacer : document.createElement("div")
}; };
// Address label // Address label
line.lblAddress.className = "addr" + first; line.lblAddress.className = "addr";
if (y == 0) if (y == 0)
resizer.observe(line.lblAddress); resizer.observe(line.lblAddress);
this.view.append(line.lblAddress); this.view.append(line.lblAddress);
@ -263,7 +264,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Byte labels // Byte labels
for (let x = 0; x < 4; x++) { for (let x = 0; x < 4; x++) {
let lbl = line.lblBytes[x] = document.createElement("div"); let lbl = line.lblBytes[x] = document.createElement("div");
lbl.className = "byte" + first + (x == 0 ? " b0" : ""); lbl.className = "byte" + (x == 0 ? " b0" : "");
if (y == 0) { if (y == 0) {
lbl.style.minWidth = "0px"; lbl.style.minWidth = "0px";
resizer.observe(lbl); resizer.observe(lbl);
@ -272,25 +273,20 @@ class DisassemblerPane extends Toolkit.ScrollPane {
} }
// Mnemonic label // Mnemonic label
line.lblMnemonic.className = "inst" + first; line.lblMnemonic.className = "inst";
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" + first; line.lblOperands.className = "ops";
this.view.append(line.lblOperands); this.view.append(line.lblOperands);
// Growing spacer
line.spacer.className = "spacer" + first;
this.view.append(line.spacer);
// All elements // All elements
line.all = line.lblBytes.concat([ line.all = line.lblBytes.concat([
line.lblAddress, line.lblAddress,
line.lblMnemonic, line.lblMnemonic,
line.lblOperands, line.lblOperands
line.spacer
]); ]);
this.lines.push(line); this.lines.push(line);
@ -302,15 +298,15 @@ class DisassemblerPane extends Toolkit.ScrollPane {
line.lblAddress .remove(); line.lblAddress .remove();
line.lblMnemonic.remove(); line.lblMnemonic.remove();
line.lblOperands.remove(); line.lblOperands.remove();
line.spacer .remove();
for (let lbl of line.lblBytes) for (let lbl of line.lblBytes)
lbl.remove(); lbl.remove();
this.lines.splice(tall, 1); this.lines.splice(tall, 1);
} }
// Configure scroll bar // Configure elements
this.hscroll.unitIncrement = let lineHeight = this.pc.element.getBoundingClientRect().height;
this.sizer.element.getBoundingClientRect().height; this.hscroll.unitIncrement = lineHeight;
this.view.element.style.gridAutoRows = lineHeight + "px";
// Update components // Update components
if (grew) if (grew)
@ -330,7 +326,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Determine how many full lines were scrolled // Determine how many full lines were scrolled
let scr = Debugger.linesScrolled(e, let scr = Debugger.linesScrolled(e,
this.sizer.element.getBoundingClientRect().height, this.pc.element.getBoundingClientRect().height,
this.tall(true), this.tall(true),
this.delta this.delta
); );
@ -419,6 +415,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Component is being displayed for the first time // Component is being displayed for the first time
firstShow() { firstShow() {
this.viewLine = Math.floor(this.tall(true) / 3) + 10; this.viewLine = Math.floor(this.tall(true) / 3) + 10;
this.viewResize();
} }
// Shrink all columns to fit the current view // Shrink all columns to fit the current view
@ -503,11 +500,15 @@ class DisassemblerPane extends Toolkit.ScrollPane {
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
let foundPC = false;
let lineHeight = this.pc.element.getBoundingClientRect().height;
// Process all lines // Process all lines
let index = 20 - this.viewLine; let index = 20 - this.viewLine;
for (let y = 0; y < this.lines.length; y++, index++) { for (let y = 0; y < this.lines.length; y++, index++) {
let line = this.lines[y]; let line = this.lines[y];
let method = "remove"; let isPC = false;
// There is no data for this line // There is no data for this line
if (index < 0 || this.dasm == null || index >= this.dasm.length) { 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(", "); line.lblOperands.innerText = dasm.operands.join(", ");
for (let x = 0; x < 4; x++) for (let x = 0; x < 4; x++)
line.lblBytes[x].innerText = dasm.bytes[x] || ""; line.lblBytes[x].innerText = dasm.bytes[x] || "";
if (this.cpu.registers.pc == dasm.rawAddress) isPC = this.cpu.registers.pc == dasm.rawAddress;
method = "add";
if (this.bytesColumn) if (this.bytesColumn)
showBytes = Math.max(showBytes, dasm.bytes.length); showBytes = Math.max(showBytes, dasm.bytes.length);
} }
// Configure whether PC is on this line // Configure whether PC is on this line
for (let elm of line.all) if (isPC) {
elm.classList[method]("pc"); 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 // 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 < 4; x++) {
@ -552,7 +563,7 @@ class DisassemblerPane extends Toolkit.ScrollPane {
// Configure layout // Configure layout
this.view.element.style.gridTemplateColumns = this.view.element.style.gridTemplateColumns =
"repeat(" + (showBytes + 3) + ", max-content) auto"; "repeat(" + (showBytes + 3) + ", max-content)";// auto";
} }
// Stop receiving updates from the simulation // Stop receiving updates from the simulation
@ -565,10 +576,11 @@ class DisassemblerPane extends Toolkit.ScrollPane {
///////////////////////////// Private Methods ///////////////////////////// ///////////////////////////// Private Methods /////////////////////////////
// Measure the number of lines visible in the view // Measure the number of lines visible in the view
tall(fully = null) { tall(fully) {
return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"]( return Math.max(1, Math[fully ? "floor" : "ceil"](
this.view .element.getBoundingClientRect().height / (fully ? this.view : this).element
this.sizer.element.getBoundingClientRect().height .getBoundingClientRect().height /
this.pc.element.getBoundingClientRect().height
)); ));
} }
@ -1412,6 +1424,7 @@ class RegisterPane extends Toolkit.SplitPane {
// 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.fetch();
} }
// Update register lists // Update register lists

View File

@ -321,9 +321,10 @@ class Memory extends Toolkit.Window {
this.lines.splice(tall, 1); this.lines.splice(tall, 1);
} }
// Configure scroll bar // Configure components
this.scrHex.hscroll.unitIncrement = let lineHeight = this.sizer.element.getBoundingClientRect().height;
this.sizer.element.getBoundingClientRect().height; this.scrHex.hscroll.unitIncrement = lineHeight;
this.hexEditor.element.style.gridAutoRows = lineHeight + "px";
// Update components // Update components
if (grew) if (grew)

View File

@ -27,6 +27,7 @@
.tk.window.cpu .disassembler div { .tk.window.cpu .disassembler div {
cursor : default; cursor : default;
line-height: calc(1em + 2px); line-height: calc(1em + 2px);
isolation : isolate;
user-select: none; user-select: none;
} }
@ -34,67 +35,35 @@
margin-left: 2px; margin-left: 2px;
} }
.tk.window.cpu .disassembler .spacer {
margin-right: 2px;
}
.tk.window.cpu .disassembler .byte { .tk.window.cpu .disassembler .byte {
margin-left: 0.5em; margin-left: 0.5em;
text-align : center; text-align : center;
} }
.tk.window.cpu .disassembler .ops {
margin-right: 2px;
}
.tk.window.cpu .disassembler .byte.b0, .tk.window.cpu .disassembler .byte.b0,
.tk.window.cpu .disassembler .inst, .tk.window.cpu .disassembler .inst,
.tk.window.cpu .disassembler .ops { .tk.window.cpu .disassembler .ops {
margin-left: 1em; margin-left: 1em;
} }
.tk.window.cpu .disassembler .pc { .tk.window.cpu .disassembler .pc {
background: var(--tk-selected-blur); background: var(--tk-selected-blur);
box-shadow: 0 1px 0 var(--tk-selected-blur), left : 1px;
0 -1px 0 var(--tk-selected-blur); right : 1px;
color : var(--tk-selected-blur-text);
} }
.tk.window.cpu .disassembler:focus-within .pc { .tk.window.cpu .disassembler:focus-within .pc {
background: var(--tk-selected); 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 { .tk.window.cpu .disassembler .is-pc {
box-shadow: 0 0 0 1px var(--tk-selected-blur); color: var(--tk-selected-blur-text);
} }
.tk.window.cpu .disassembler:focus-within .pc.addr { .tk.window.cpu .disassembler:focus-within .is-pc {
box-shadow: 0 0 0 1px var(--tk-selected); color: var(--tk-selected-text);
}
.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 .registers { .tk.window.cpu .registers {