Enabling CPU trace and hex edit
This commit is contained in:
+2
-1
@@ -109,7 +109,7 @@ globalThis.App = class App {
|
||||
|
||||
// Message received
|
||||
onmessage(msg) {
|
||||
if ("debug" in msg) {
|
||||
if ("dbgwnd" in msg) {
|
||||
this.debuggers[msg.sim].message(msg);
|
||||
return;
|
||||
}
|
||||
@@ -164,6 +164,7 @@ globalThis.App = class App {
|
||||
// Send the ROM to the WebAssembly core module
|
||||
this.core.postMessage({
|
||||
command: "SetROM",
|
||||
reset : true,
|
||||
rom : file,
|
||||
sim : 0
|
||||
}, file);
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ globalThis.Debugger = class Debugger {
|
||||
|
||||
// Message received from emulation thread
|
||||
message(msg) {
|
||||
switch (msg.debug) {
|
||||
switch (msg.dbgwnd) {
|
||||
case "CPU" : this.cpu .message(msg); break;
|
||||
case "Memory": this.memory.message(msg); break;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@
|
||||
case "GetRegisters": this.getRegisters(msg); break;
|
||||
case "Init" : this.init (msg); break;
|
||||
case "ReadBuffer" : this.readBuffer (msg); break;
|
||||
case "RunNext" : this.runNext (msg); break;
|
||||
case "SetRegister" : this.setRegister (msg); break;
|
||||
case "SetROM" : this.setROM (msg); break;
|
||||
case "SingleStep" : this.singleStep (msg); break;
|
||||
case "Write" : this.write (msg); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +86,12 @@
|
||||
postMessage(msg, msg.buffer);
|
||||
}
|
||||
|
||||
// Attempt to advance to the next instruction
|
||||
runNext(msg) {
|
||||
this.core.RunNext(msg.sim);
|
||||
postMessage(msg);
|
||||
}
|
||||
|
||||
// Specify a new value for a register
|
||||
setRegister(msg) {
|
||||
switch (msg.type) {
|
||||
@@ -106,6 +115,21 @@
|
||||
buffer.data[x] = rom[x];
|
||||
msg.success = !!this.core.SetROM(msg.sim, buffer.pointer, rom.length);
|
||||
delete msg.rom;
|
||||
if (msg.reset)
|
||||
this.core.Reset(msg.sim);
|
||||
postMessage(msg);
|
||||
}
|
||||
|
||||
// Execute the current instruction
|
||||
singleStep(msg) {
|
||||
this.core.SingleStep(msg.sim);
|
||||
postMessage(msg);
|
||||
}
|
||||
|
||||
// Write a single value into the bus
|
||||
write(msg) {
|
||||
this.core.Write(msg.sim, msg.address, msg.type, msg.value,
|
||||
msg.debug ? 1 : 0);
|
||||
postMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
--control-shadow : #999999;
|
||||
--control-text : #cccccc;
|
||||
--desktop : #111111;
|
||||
--selected : #008542;
|
||||
--selectedBlur : #57665d;
|
||||
--selectedText : #ffffff;
|
||||
--selectedTextBlur : #ffffff;
|
||||
--splitter-focus : #0099ff99;
|
||||
--title : #007ACC;
|
||||
--title-blur : #555555;
|
||||
|
||||
@@ -363,6 +363,25 @@ input[type="text"] {
|
||||
margin-left: calc(var(--font-size) / 2);
|
||||
}
|
||||
|
||||
[role="dialog"][window="memory"] [name="hex"] [name="byte"][selected] {
|
||||
background: var(--selected);
|
||||
box-shadow:
|
||||
-0.5px 0.5px 0 0.5px var(--selected),
|
||||
0.5px 0.5px 0 0.5px var(--selected)
|
||||
;
|
||||
color : var(--selectedText);
|
||||
}
|
||||
|
||||
[role="dialog"][window="memory"][focus="false"]
|
||||
[name="hex"] [name="byte"][selected] {
|
||||
background: var(--selectedBlur);
|
||||
box-shadow:
|
||||
-0.5px 0.5px 0 0.5px var(--selectedBlur),
|
||||
0.5px 0.5px 0 0.5px var(--selectedBlur)
|
||||
;
|
||||
color : var(--selectedTextBlur);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -507,3 +526,16 @@ input[type="text"] {
|
||||
[role="dialog"][window="cpu"] [name="disassembler"] [name="row"] {
|
||||
column-gap: calc(var(--font-size) * 1.5);
|
||||
}
|
||||
|
||||
[role="dialog"][window="cpu"] [name="disassembler"] [name="row"][pc] {
|
||||
background: var(--selected);
|
||||
box-shadow: 0 1px 0 var(--selected);
|
||||
color : var(--selectedText);
|
||||
}
|
||||
|
||||
[role="dialog"][window="cpu"][focus="false"]
|
||||
[name="disassembler"] [name="row"][pc] {
|
||||
background: var(--selectedBlur);
|
||||
box-shadow: 0 1px 0 var(--selectedBlur);
|
||||
color : var(--selectedTextBlur);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
--control-shadow : #999999;
|
||||
--control-text : #000000;
|
||||
--desktop : #cccccc;
|
||||
--selected : #008542;
|
||||
--selectedBlur : #57665d;
|
||||
--selectedText : #ffffff;
|
||||
--selectedTextBlur : #ffffff;
|
||||
--splitter-focus : #0099ff99;
|
||||
--title : #80ccff;
|
||||
--title-blur : #cccccc;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
--control-shadow : #aa0000;
|
||||
--control-text : #ff0000;
|
||||
--desktop : #000000;
|
||||
--selected : #ff0000;
|
||||
--selectedBlur : #aa0000;
|
||||
--selectedText : #550000;
|
||||
--selectedTextBlur : #000000;
|
||||
--splitter-focus : #ff000099;
|
||||
--title : #550000;
|
||||
--title-blur : #000000;
|
||||
|
||||
+92
-24
@@ -207,9 +207,11 @@
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Update the display with current emulation data
|
||||
refresh() {
|
||||
this.refreshDasm();
|
||||
this.refreshRegs();
|
||||
refresh(seekToPC, dasm, regs) {
|
||||
if (dasm || dasm === undefined)
|
||||
this.refreshDasm(this.address, 0, !!seekToPC);
|
||||
if (regs || regs === undefined)
|
||||
this.refreshRegs();
|
||||
}
|
||||
|
||||
// Specify whether the component is visible
|
||||
@@ -231,6 +233,8 @@
|
||||
case "GetRegisters": this.getRegisters(msg); break;
|
||||
case "ReadBuffer" : this.readBuffer (msg); break;
|
||||
case "SetRegister" : this.setRegister (msg); break;
|
||||
case "RunNext": case "SingleStep":
|
||||
this.refresh(true); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,14 +282,38 @@
|
||||
new Uint8Array(msg.buffer), 0, msg.address, msg.target,
|
||||
msg.pc, msg.line, lines);
|
||||
|
||||
// Configure instance fields
|
||||
this.address = dasm[0].address;
|
||||
// Ensure PC is visible if requested
|
||||
let reseeking = false;
|
||||
if (msg.seekToPC) {
|
||||
let visible = this.lines(true);
|
||||
let count = Math.min(msg.lines, visible);
|
||||
let x;
|
||||
|
||||
// Configure elements
|
||||
for (let x = 0; x < lines; x++)
|
||||
this.rows[x].update(dasm[x], this.columns);
|
||||
for (let x = 0; x < lines; x++)
|
||||
this.rows[x].setWidths(this.columns);
|
||||
// Ensure PC is visible in the disassembly
|
||||
for (x = 0; x < count; x++)
|
||||
if (dasm[x].address == msg.pc)
|
||||
break;
|
||||
|
||||
// Seek to display PC in the view
|
||||
if (x == count) {
|
||||
reseeking = true;
|
||||
this.seek(msg.pc, Math.floor(visible / 3));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Not seeking to PC
|
||||
if (!reseeking) {
|
||||
|
||||
// Configure instance fields
|
||||
this.address = dasm[0].address;
|
||||
|
||||
// Configure elements
|
||||
for (let x = 0; x < lines; x++)
|
||||
this.rows[x].update(dasm[x], this.columns, msg.pc);
|
||||
for (let x = 0; x < lines; x++)
|
||||
this.rows[x].setWidths(this.columns);
|
||||
}
|
||||
|
||||
// Check for pending display updates
|
||||
let address = this.pendingDasm.address === null ?
|
||||
@@ -310,6 +338,7 @@
|
||||
setRegister(msg) {
|
||||
(msg.type == "program" ? this.proRegs : this.sysRegs)
|
||||
.registers[msg.id].setValue(msg.value);
|
||||
this.refreshDasm(this.address, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -383,11 +412,45 @@
|
||||
|
||||
// Processing by key
|
||||
else switch (e.key) {
|
||||
case "ArrowDown": this.scroll( 1 ); break;
|
||||
case "ArrowUp" : this.scroll(-1 ); break;
|
||||
case "ArrowDown": this.scroll( 1); break;
|
||||
case "ArrowUp" : this.scroll(-1); break;
|
||||
case "PageDown" : this.scroll( this.lines(true)); break;
|
||||
case "PageUp" : this.scroll(-this.lines(true)); break;
|
||||
default : return;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
// Key down event handler
|
||||
onkeydown(e) {
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
|
||||
// Run next
|
||||
case "F10":
|
||||
this.debug.core.postMessage({
|
||||
command : "RunNext",
|
||||
dbgwnd : "CPU",
|
||||
sim : this.debug.sim,
|
||||
seekToPC: true
|
||||
});
|
||||
break;
|
||||
|
||||
// Single step
|
||||
case "F11":
|
||||
this.debug.core.postMessage({
|
||||
command : "SingleStep",
|
||||
dbgwnd : "CPU",
|
||||
sim : this.debug.sim,
|
||||
seekToPC: true
|
||||
});
|
||||
break;
|
||||
|
||||
default: return super.onkeydown(e);
|
||||
}
|
||||
|
||||
// Configure event
|
||||
@@ -429,7 +492,7 @@
|
||||
}
|
||||
|
||||
// Update the disassembler with current emulation data
|
||||
refreshDasm(address, line) {
|
||||
refreshDasm(address, line, seekToPC) {
|
||||
|
||||
// Do nothing while closed or already waiting to refresh
|
||||
if (!this.isVisible() || this.pendingDasm.mode != null)
|
||||
@@ -450,14 +513,15 @@
|
||||
|
||||
// Request bus data from the WebAssembly core
|
||||
this.debug.core.postMessage({
|
||||
command: "ReadBuffer",
|
||||
sim : this.debug.sim,
|
||||
debug : "CPU",
|
||||
address: (address + start * 4 & 0xFFFFFFFE) >>> 0,
|
||||
line : line,
|
||||
lines : lines,
|
||||
target : address,
|
||||
size : (end - start + 1) * 4
|
||||
command : "ReadBuffer",
|
||||
sim : this.debug.sim,
|
||||
dbgwnd : "CPU",
|
||||
address : (address + start * 4 & 0xFFFFFFFE) >>> 0,
|
||||
line : line,
|
||||
lines : lines,
|
||||
target : address,
|
||||
seekToPC: seekToPC,
|
||||
size : (end - start + 1) * 4
|
||||
});
|
||||
}
|
||||
|
||||
@@ -480,7 +544,7 @@
|
||||
// Request bus data from the WebAssembly core
|
||||
this.debug.core.postMessage({
|
||||
command: "GetRegisters",
|
||||
debug : "CPU",
|
||||
dbgwnd : "CPU",
|
||||
sim : this.debug.sim
|
||||
});
|
||||
}
|
||||
@@ -580,7 +644,11 @@ CPUWindow.Row = class Row extends Toolkit.Panel {
|
||||
}
|
||||
|
||||
// Update the output labels with emulation state content
|
||||
update(line, columns) {
|
||||
update(line, columns, pc) {
|
||||
if (pc == line.address)
|
||||
this.element.setAttribute("pc", "");
|
||||
else this.element.removeAttribute("pc");
|
||||
|
||||
this.address.setText(
|
||||
("0000000" + line.address.toString(16).toUpperCase()).slice(-8));
|
||||
|
||||
|
||||
+216
-42
@@ -8,10 +8,12 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
super(debug.gui, options);
|
||||
|
||||
// Configure instance fields
|
||||
this.address = 0x05000000;
|
||||
this.debug = debug;
|
||||
this.pending = { mode: null };
|
||||
this.rows = [];
|
||||
this.address = 0x05000000;
|
||||
this.debug = debug;
|
||||
this.editDigit = null;
|
||||
this.pending = { mode: null };
|
||||
this.rows = [];
|
||||
this.selected = this.address;
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("window", "memory");
|
||||
@@ -46,7 +48,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
// Configure properties
|
||||
this.setProperty("sim", "");
|
||||
this.rows.push(this.hex.add(new MemoryWindow.Row(this.hex)));
|
||||
this.rows.push(this.hex.add(new MemoryWindow.Row(this, this.hex, 0)));
|
||||
this.application.addComponent(this);
|
||||
}
|
||||
|
||||
@@ -74,7 +76,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
this.debug.core.postMessage({
|
||||
command: "ReadBuffer",
|
||||
sim : this.debug.sim,
|
||||
debug : "Memory",
|
||||
dbgwnd : "Memory",
|
||||
address: address,
|
||||
lines : lines,
|
||||
size : lines * 16
|
||||
@@ -98,6 +100,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
message(msg) {
|
||||
switch (msg.command) {
|
||||
case "ReadBuffer": this.readBuffer(msg); break;
|
||||
case "Write" : this.debug.refresh(); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +117,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
let x = 0, address = msg.address, offset = 0;
|
||||
x < lines && offset < buffer.length;
|
||||
x++, address = (address + 16 & 0xFFFFFFF0) >>> 0, offset += 16
|
||||
) this.rows[x].update(address, buffer, offset);
|
||||
) this.rows[x].update(buffer, offset);
|
||||
|
||||
// Check for pending display updates
|
||||
let address = this.pending.address === null ?
|
||||
@@ -139,6 +142,19 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Write the current edited value to the bus
|
||||
commit(value) {
|
||||
this.editDigit = null;
|
||||
this.debug.core.postMessage({
|
||||
command: "Write",
|
||||
sim : this.debug.sim,
|
||||
dbgwnd : "Memory",
|
||||
address: this.selected,
|
||||
type : 0,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// The window is being displayed for the first time
|
||||
firstShow() {
|
||||
super.firstShow();
|
||||
@@ -147,7 +163,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
|
||||
// Determine the height in pixels of one row of output
|
||||
lineHeight() {
|
||||
return Math.max(10, this.rows[0].address.getBounds().height);
|
||||
return Math.max(10, this.rows[0].addr.getBounds().height);
|
||||
}
|
||||
|
||||
// Determine the number of rows of output
|
||||
@@ -158,32 +174,81 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
ret = fullyVisible ? Math.floor(ret) : Math.ceil(ret);
|
||||
return Math.max(1, ret);
|
||||
}
|
||||
|
||||
// Focus lost event capture
|
||||
onblur(e) {
|
||||
super.onblur(e);
|
||||
if (this.editDigit !== null && !this.contains(e.relatedTarget))
|
||||
this.commit(this.editDigit);
|
||||
}
|
||||
|
||||
// Key down event handler
|
||||
onkeydown(e) {
|
||||
let change = null;
|
||||
let digit = null;
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "ArrowDown" : change = 16 ; break;
|
||||
case "ArrowLeft" : change = - 1 ; break;
|
||||
case "ArrowRight": change = 1 ; break;
|
||||
case "ArrowUp" : change = -16 ; break;
|
||||
case "PageDown" : change = visible; break;
|
||||
case "PageUp" : change = -visible; break;
|
||||
case "0": case "1": case "2": case "3": case "4":
|
||||
case "5": case "6": case "7": case "8": case "9":
|
||||
digit = e.key.codePointAt(0) - "0".codePointAt(0);
|
||||
break;
|
||||
case "a": case "b": case "c": case "d": case "e": case "f":
|
||||
digit = e.key.codePointAt(0) - "a".codePointAt(0) + 10;
|
||||
break;
|
||||
case "A": case "B": case "C": case "D": case "E": case "F":
|
||||
digit = e.key.codePointAt(0) - "A".codePointAt(0) + 10;
|
||||
break;
|
||||
default: return super.onkeydown(e);
|
||||
}
|
||||
|
||||
// Moving the selection
|
||||
if (change !== null) {
|
||||
if (this.editDigit !== null)
|
||||
this.commit(this.editDigit);
|
||||
this.setSelected((this.selected + change & 0xFFFFFFFF) >>> 0);
|
||||
}
|
||||
|
||||
// Entering a digit
|
||||
if (digit !== null) {
|
||||
let selected = this.selected;
|
||||
if (this.editDigit !== null) {
|
||||
this.commit(this.editDigit << 4 | digit);
|
||||
selected++;
|
||||
} else this.editDigit = digit;
|
||||
if (!this.setSelected(selected))
|
||||
for (let row of this.rows)
|
||||
row.update();
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
// Key down event handler
|
||||
onkeyhex(e) {
|
||||
|
||||
// Control is pressed
|
||||
if (e.ctrlKey) switch (e.key) {
|
||||
// Control is not pressed
|
||||
if (!e.ctrlKey)
|
||||
return;
|
||||
|
||||
// Processing by key
|
||||
switch (e.key) {
|
||||
case "g": case "G":
|
||||
let addr = prompt(this.application.translate("{app.goto_}"));
|
||||
if (addr === null)
|
||||
break;
|
||||
this.seek(
|
||||
(parseInt(addr, 16) & 0xFFFFFFF0) >>> 0,
|
||||
Math.floor(this.lines(true) / 3)
|
||||
);
|
||||
this.setSelected((parseInt(addr, 16) & 0xFFFFFFFF) >>> 0);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Processing by key
|
||||
else switch (e.key) {
|
||||
case "ArrowDown": this.scroll( 1 ); break;
|
||||
case "ArrowUp" : this.scroll(-1 ); break;
|
||||
case "PageDown" : this.scroll( this.lines(true)); break;
|
||||
case "PageUp" : this.scroll(-this.lines(true)); break;
|
||||
default : return;
|
||||
}
|
||||
|
||||
// Configure event
|
||||
e.preventDefault();
|
||||
@@ -194,7 +259,8 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
onresize() {
|
||||
let lines = this.lines(false);
|
||||
for (let y = this.rows.length; y < lines; y++)
|
||||
this.rows[y] = this.hex.add(new MemoryWindow.Row(this.hex));
|
||||
this.rows[y] =
|
||||
this.hex.add(new MemoryWindow.Row(this, this.hex, y * 16));
|
||||
for (let y = lines; y < this.rows.length; y++)
|
||||
this.hex.remove(this.rows[y]);
|
||||
if (this.rows.length > lines)
|
||||
@@ -255,13 +321,49 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||
}
|
||||
}
|
||||
|
||||
// Specify which byte value is selected
|
||||
setSelected(selected) {
|
||||
|
||||
// The selected cell is not changing
|
||||
if (selected == this.selected)
|
||||
return false;
|
||||
|
||||
// An edit was in progress
|
||||
if (this.editDigit !== null)
|
||||
this.commit(this.editDigit);
|
||||
|
||||
// Working variables
|
||||
let pos = (selected - this.address & 0xFFFFFFFF) >>> 0;
|
||||
let visible = this.lines(true) * 16;
|
||||
|
||||
// The selected cell is visible
|
||||
if (pos >= 0 && pos < visible) {
|
||||
this.selected = selected;
|
||||
for (let y = 0; y < this.rows.length; y++)
|
||||
this.rows[y].checkSelected();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Working variables
|
||||
let down = (selected - this.address & 0xFFFFFFF0) >>> 0;
|
||||
let up = (this.address - selected + 15 & 0xFFFFFFF0) >>> 0;
|
||||
|
||||
// Seek to show the new selection in the view
|
||||
this.selected = selected;
|
||||
if (down <= up) {
|
||||
this.seek((this.address + down & 0xFFFFFFFF) >>> 0,
|
||||
visible / 16 - 1);
|
||||
} else this.seek((this.address - up & 0xFFFFFFFF) >>> 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// One row of output
|
||||
MemoryWindow.Row = class Row extends Toolkit.Panel {
|
||||
|
||||
// Object constructor
|
||||
constructor(parent) {
|
||||
constructor(wnd, parent, offset) {
|
||||
super(parent.application, {
|
||||
layout : "grid",
|
||||
columns : "repeat(17, max-content)",
|
||||
@@ -271,37 +373,109 @@ MemoryWindow.Row = class Row extends Toolkit.Panel {
|
||||
});
|
||||
|
||||
// Configure instance fields
|
||||
this.bytes = new Array(16);
|
||||
this.cells = new Array(16);
|
||||
this.offset = offset;
|
||||
this.wnd = wnd;
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("role", "row");
|
||||
|
||||
// Address label
|
||||
this.address = this.add(parent.newLabel({ text: "\u00a0" }));
|
||||
this.address.element.setAttribute("role", "gridcell");
|
||||
this.address.element.setAttribute("name", "address");
|
||||
this.addr = this.add(parent.newLabel({ text: "\u00a0" }));
|
||||
this.addr.element.setAttribute("role", "gridcell");
|
||||
this.addr.element.setAttribute("name", "address");
|
||||
|
||||
// Byte labels
|
||||
for (let x = 0; x < 16; x++) {
|
||||
let lbl = this.bytes[x] =
|
||||
this.add(parent.newLabel({ text: "\u00a0" }));
|
||||
lbl.element.setAttribute("role", "gridcell");
|
||||
lbl.element.setAttribute("name", "byte");
|
||||
}
|
||||
|
||||
for (let x = 0; x < 16; x++)
|
||||
this.cells[x] = new MemoryWindow.Cell(wnd, this, offset + x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Check whether any byte label is the selected byte
|
||||
checkSelected() {
|
||||
for (let cell of this.cells)
|
||||
cell.checkSelected();
|
||||
}
|
||||
|
||||
// Update the output labels with emulation state content
|
||||
update(address, bytes, offset) {
|
||||
this.address.setText(
|
||||
("0000000" + address.toString(16).toUpperCase()).slice(-8));
|
||||
for (let x = 0; x < 16; x++, offset++)
|
||||
this.bytes[x].setText(
|
||||
("0" + bytes[offset].toString(16).toUpperCase()).slice(-2));
|
||||
update(bytes, offset) {
|
||||
this.addr.setText(
|
||||
("0000000" + this.address().toString(16).toUpperCase()).slice(-8));
|
||||
for (let cell of this.cells)
|
||||
cell.update(bytes ? bytes[offset++] : cell.value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Compute the current address of the row
|
||||
address() {
|
||||
return (this.wnd.address + this.offset & 0xFFFFFFFF) >>> 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// One cell of output
|
||||
MemoryWindow.Cell = class Cell extends Toolkit.Label {
|
||||
|
||||
// Object constructor
|
||||
constructor(wnd, parent, offset) {
|
||||
super(wnd.application, { text: "\u00a0" });
|
||||
|
||||
// Configure instance fields
|
||||
this.offset = offset;
|
||||
this.wnd = wnd;
|
||||
this.value = 0x00;
|
||||
|
||||
// Configure element
|
||||
this.element.setAttribute("role", "gridcell");
|
||||
this.element.setAttribute("name", "byte");
|
||||
this.element.addEventListener("pointerdown", e=>this.onpointerdown(e));
|
||||
|
||||
parent.add(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// Check whether this cell is the selected cell
|
||||
checkSelected() {
|
||||
let selected = this.address() == this.wnd.selected;
|
||||
if (selected)
|
||||
this.element.setAttribute("selected", "");
|
||||
else this.element.removeAttribute("selected");
|
||||
return selected;
|
||||
}
|
||||
|
||||
// Update the output with emulation state content
|
||||
update(value) {
|
||||
if (value === undefined)
|
||||
value = this.value;
|
||||
else this.value = value;
|
||||
if (this.checkSelected() && this.wnd.editDigit !== null) {
|
||||
this.setText("\u00a0" +
|
||||
this.wnd.editDigit.toString(16).toUpperCase());
|
||||
} else this.setText(("0"+value.toString(16).toUpperCase()).slice(-2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Compute the current address of the cell
|
||||
address() {
|
||||
return (this.wnd.address + this.offset & 0xFFFFFFFF) >>> 0;
|
||||
}
|
||||
|
||||
// Pointer down event handler
|
||||
onpointerdown(e) {
|
||||
if (e.button == 0)
|
||||
this.wnd.setSelected(this.address());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user