Enabling CPU trace and hex edit
This commit is contained in:
parent
94486ecf02
commit
573f72e46f
|
@ -109,7 +109,7 @@ globalThis.App = class App {
|
||||||
|
|
||||||
// Message received
|
// Message received
|
||||||
onmessage(msg) {
|
onmessage(msg) {
|
||||||
if ("debug" in msg) {
|
if ("dbgwnd" in msg) {
|
||||||
this.debuggers[msg.sim].message(msg);
|
this.debuggers[msg.sim].message(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +164,7 @@ globalThis.App = class App {
|
||||||
// Send the ROM to the WebAssembly core module
|
// Send the ROM to the WebAssembly core module
|
||||||
this.core.postMessage({
|
this.core.postMessage({
|
||||||
command: "SetROM",
|
command: "SetROM",
|
||||||
|
reset : true,
|
||||||
rom : file,
|
rom : file,
|
||||||
sim : 0
|
sim : 0
|
||||||
}, file);
|
}, file);
|
||||||
|
|
|
@ -39,7 +39,7 @@ globalThis.Debugger = class Debugger {
|
||||||
|
|
||||||
// Message received from emulation thread
|
// Message received from emulation thread
|
||||||
message(msg) {
|
message(msg) {
|
||||||
switch (msg.debug) {
|
switch (msg.dbgwnd) {
|
||||||
case "CPU" : this.cpu .message(msg); break;
|
case "CPU" : this.cpu .message(msg); break;
|
||||||
case "Memory": this.memory.message(msg); break;
|
case "Memory": this.memory.message(msg); break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,11 @@
|
||||||
case "GetRegisters": this.getRegisters(msg); break;
|
case "GetRegisters": this.getRegisters(msg); break;
|
||||||
case "Init" : this.init (msg); break;
|
case "Init" : this.init (msg); break;
|
||||||
case "ReadBuffer" : this.readBuffer (msg); break;
|
case "ReadBuffer" : this.readBuffer (msg); break;
|
||||||
|
case "RunNext" : this.runNext (msg); break;
|
||||||
case "SetRegister" : this.setRegister (msg); break;
|
case "SetRegister" : this.setRegister (msg); break;
|
||||||
case "SetROM" : this.setROM (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);
|
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
|
// Specify a new value for a register
|
||||||
setRegister(msg) {
|
setRegister(msg) {
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
|
@ -106,6 +115,21 @@
|
||||||
buffer.data[x] = rom[x];
|
buffer.data[x] = rom[x];
|
||||||
msg.success = !!this.core.SetROM(msg.sim, buffer.pointer, rom.length);
|
msg.success = !!this.core.SetROM(msg.sim, buffer.pointer, rom.length);
|
||||||
delete msg.rom;
|
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);
|
postMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
--control-shadow : #999999;
|
--control-shadow : #999999;
|
||||||
--control-text : #cccccc;
|
--control-text : #cccccc;
|
||||||
--desktop : #111111;
|
--desktop : #111111;
|
||||||
|
--selected : #008542;
|
||||||
|
--selectedBlur : #57665d;
|
||||||
|
--selectedText : #ffffff;
|
||||||
|
--selectedTextBlur : #ffffff;
|
||||||
--splitter-focus : #0099ff99;
|
--splitter-focus : #0099ff99;
|
||||||
--title : #007ACC;
|
--title : #007ACC;
|
||||||
--title-blur : #555555;
|
--title-blur : #555555;
|
||||||
|
|
|
@ -363,6 +363,25 @@ input[type="text"] {
|
||||||
margin-left: calc(var(--font-size) / 2);
|
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"] {
|
[role="dialog"][window="cpu"] [name="disassembler"] [name="row"] {
|
||||||
column-gap: calc(var(--font-size) * 1.5);
|
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-shadow : #999999;
|
||||||
--control-text : #000000;
|
--control-text : #000000;
|
||||||
--desktop : #cccccc;
|
--desktop : #cccccc;
|
||||||
|
--selected : #008542;
|
||||||
|
--selectedBlur : #57665d;
|
||||||
|
--selectedText : #ffffff;
|
||||||
|
--selectedTextBlur : #ffffff;
|
||||||
--splitter-focus : #0099ff99;
|
--splitter-focus : #0099ff99;
|
||||||
--title : #80ccff;
|
--title : #80ccff;
|
||||||
--title-blur : #cccccc;
|
--title-blur : #cccccc;
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
--control-shadow : #aa0000;
|
--control-shadow : #aa0000;
|
||||||
--control-text : #ff0000;
|
--control-text : #ff0000;
|
||||||
--desktop : #000000;
|
--desktop : #000000;
|
||||||
|
--selected : #ff0000;
|
||||||
|
--selectedBlur : #aa0000;
|
||||||
|
--selectedText : #550000;
|
||||||
|
--selectedTextBlur : #000000;
|
||||||
--splitter-focus : #ff000099;
|
--splitter-focus : #ff000099;
|
||||||
--title : #550000;
|
--title : #550000;
|
||||||
--title-blur : #000000;
|
--title-blur : #000000;
|
||||||
|
|
|
@ -207,8 +207,10 @@
|
||||||
///////////////////////////// Public Methods //////////////////////////////
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
// Update the display with current emulation data
|
// Update the display with current emulation data
|
||||||
refresh() {
|
refresh(seekToPC, dasm, regs) {
|
||||||
this.refreshDasm();
|
if (dasm || dasm === undefined)
|
||||||
|
this.refreshDasm(this.address, 0, !!seekToPC);
|
||||||
|
if (regs || regs === undefined)
|
||||||
this.refreshRegs();
|
this.refreshRegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +233,8 @@
|
||||||
case "GetRegisters": this.getRegisters(msg); break;
|
case "GetRegisters": this.getRegisters(msg); break;
|
||||||
case "ReadBuffer" : this.readBuffer (msg); break;
|
case "ReadBuffer" : this.readBuffer (msg); break;
|
||||||
case "SetRegister" : this.setRegister (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,
|
new Uint8Array(msg.buffer), 0, msg.address, msg.target,
|
||||||
msg.pc, msg.line, lines);
|
msg.pc, msg.line, lines);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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
|
// Configure instance fields
|
||||||
this.address = dasm[0].address;
|
this.address = dasm[0].address;
|
||||||
|
|
||||||
// Configure elements
|
// Configure elements
|
||||||
for (let x = 0; x < lines; x++)
|
for (let x = 0; x < lines; x++)
|
||||||
this.rows[x].update(dasm[x], this.columns);
|
this.rows[x].update(dasm[x], this.columns, msg.pc);
|
||||||
for (let x = 0; x < lines; x++)
|
for (let x = 0; x < lines; x++)
|
||||||
this.rows[x].setWidths(this.columns);
|
this.rows[x].setWidths(this.columns);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for pending display updates
|
// Check for pending display updates
|
||||||
let address = this.pendingDasm.address === null ?
|
let address = this.pendingDasm.address === null ?
|
||||||
|
@ -310,6 +338,7 @@
|
||||||
setRegister(msg) {
|
setRegister(msg) {
|
||||||
(msg.type == "program" ? this.proRegs : this.sysRegs)
|
(msg.type == "program" ? this.proRegs : this.sysRegs)
|
||||||
.registers[msg.id].setValue(msg.value);
|
.registers[msg.id].setValue(msg.value);
|
||||||
|
this.refreshDasm(this.address, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,6 +424,40 @@
|
||||||
e.stopPropagation();
|
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
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
// Resize event handler
|
// Resize event handler
|
||||||
onresize(bounds) {
|
onresize(bounds) {
|
||||||
|
|
||||||
|
@ -429,7 +492,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the disassembler with current emulation data
|
// Update the disassembler with current emulation data
|
||||||
refreshDasm(address, line) {
|
refreshDasm(address, line, seekToPC) {
|
||||||
|
|
||||||
// Do nothing while closed or already waiting to refresh
|
// Do nothing while closed or already waiting to refresh
|
||||||
if (!this.isVisible() || this.pendingDasm.mode != null)
|
if (!this.isVisible() || this.pendingDasm.mode != null)
|
||||||
|
@ -452,11 +515,12 @@
|
||||||
this.debug.core.postMessage({
|
this.debug.core.postMessage({
|
||||||
command : "ReadBuffer",
|
command : "ReadBuffer",
|
||||||
sim : this.debug.sim,
|
sim : this.debug.sim,
|
||||||
debug : "CPU",
|
dbgwnd : "CPU",
|
||||||
address : (address + start * 4 & 0xFFFFFFFE) >>> 0,
|
address : (address + start * 4 & 0xFFFFFFFE) >>> 0,
|
||||||
line : line,
|
line : line,
|
||||||
lines : lines,
|
lines : lines,
|
||||||
target : address,
|
target : address,
|
||||||
|
seekToPC: seekToPC,
|
||||||
size : (end - start + 1) * 4
|
size : (end - start + 1) * 4
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -480,7 +544,7 @@
|
||||||
// Request bus data from the WebAssembly core
|
// Request bus data from the WebAssembly core
|
||||||
this.debug.core.postMessage({
|
this.debug.core.postMessage({
|
||||||
command: "GetRegisters",
|
command: "GetRegisters",
|
||||||
debug : "CPU",
|
dbgwnd : "CPU",
|
||||||
sim : this.debug.sim
|
sim : this.debug.sim
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -580,7 +644,11 @@ CPUWindow.Row = class Row extends Toolkit.Panel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the output labels with emulation state content
|
// 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(
|
this.address.setText(
|
||||||
("0000000" + line.address.toString(16).toUpperCase()).slice(-8));
|
("0000000" + line.address.toString(16).toUpperCase()).slice(-8));
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,10 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
// Configure instance fields
|
// Configure instance fields
|
||||||
this.address = 0x05000000;
|
this.address = 0x05000000;
|
||||||
this.debug = debug;
|
this.debug = debug;
|
||||||
|
this.editDigit = null;
|
||||||
this.pending = { mode: null };
|
this.pending = { mode: null };
|
||||||
this.rows = [];
|
this.rows = [];
|
||||||
|
this.selected = this.address;
|
||||||
|
|
||||||
// Configure element
|
// Configure element
|
||||||
this.element.setAttribute("window", "memory");
|
this.element.setAttribute("window", "memory");
|
||||||
|
@ -46,7 +48,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
|
|
||||||
// Configure properties
|
// Configure properties
|
||||||
this.setProperty("sim", "");
|
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);
|
this.application.addComponent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
this.debug.core.postMessage({
|
this.debug.core.postMessage({
|
||||||
command: "ReadBuffer",
|
command: "ReadBuffer",
|
||||||
sim : this.debug.sim,
|
sim : this.debug.sim,
|
||||||
debug : "Memory",
|
dbgwnd : "Memory",
|
||||||
address: address,
|
address: address,
|
||||||
lines : lines,
|
lines : lines,
|
||||||
size : lines * 16
|
size : lines * 16
|
||||||
|
@ -98,6 +100,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
message(msg) {
|
message(msg) {
|
||||||
switch (msg.command) {
|
switch (msg.command) {
|
||||||
case "ReadBuffer": this.readBuffer(msg); break;
|
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;
|
let x = 0, address = msg.address, offset = 0;
|
||||||
x < lines && offset < buffer.length;
|
x < lines && offset < buffer.length;
|
||||||
x++, address = (address + 16 & 0xFFFFFFF0) >>> 0, offset += 16
|
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
|
// Check for pending display updates
|
||||||
let address = this.pending.address === null ?
|
let address = this.pending.address === null ?
|
||||||
|
@ -139,6 +142,19 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
|
|
||||||
///////////////////////////// Private Methods /////////////////////////////
|
///////////////////////////// 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
|
// The window is being displayed for the first time
|
||||||
firstShow() {
|
firstShow() {
|
||||||
super.firstShow();
|
super.firstShow();
|
||||||
|
@ -147,7 +163,7 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
|
|
||||||
// Determine the height in pixels of one row of output
|
// Determine the height in pixels of one row of output
|
||||||
lineHeight() {
|
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
|
// Determine the number of rows of output
|
||||||
|
@ -159,32 +175,81 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
return Math.max(1, 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
|
// Key down event handler
|
||||||
onkeyhex(e) {
|
onkeyhex(e) {
|
||||||
|
|
||||||
// Control is pressed
|
// Control is not pressed
|
||||||
if (e.ctrlKey) switch (e.key) {
|
if (!e.ctrlKey)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Processing by key
|
||||||
|
switch (e.key) {
|
||||||
case "g": case "G":
|
case "g": case "G":
|
||||||
let addr = prompt(this.application.translate("{app.goto_}"));
|
let addr = prompt(this.application.translate("{app.goto_}"));
|
||||||
if (addr === null)
|
if (addr === null)
|
||||||
break;
|
break;
|
||||||
this.seek(
|
this.setSelected((parseInt(addr, 16) & 0xFFFFFFFF) >>> 0);
|
||||||
(parseInt(addr, 16) & 0xFFFFFFF0) >>> 0,
|
|
||||||
Math.floor(this.lines(true) / 3)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default: return;
|
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
|
// Configure event
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -194,7 +259,8 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
|
||||||
onresize() {
|
onresize() {
|
||||||
let lines = this.lines(false);
|
let lines = this.lines(false);
|
||||||
for (let y = this.rows.length; y < lines; y++)
|
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++)
|
for (let y = lines; y < this.rows.length; y++)
|
||||||
this.hex.remove(this.rows[y]);
|
this.hex.remove(this.rows[y]);
|
||||||
if (this.rows.length > lines)
|
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
|
// One row of output
|
||||||
MemoryWindow.Row = class Row extends Toolkit.Panel {
|
MemoryWindow.Row = class Row extends Toolkit.Panel {
|
||||||
|
|
||||||
// Object constructor
|
// Object constructor
|
||||||
constructor(parent) {
|
constructor(wnd, parent, offset) {
|
||||||
super(parent.application, {
|
super(parent.application, {
|
||||||
layout : "grid",
|
layout : "grid",
|
||||||
columns : "repeat(17, max-content)",
|
columns : "repeat(17, max-content)",
|
||||||
|
@ -271,37 +373,109 @@ MemoryWindow.Row = class Row extends Toolkit.Panel {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure instance fields
|
// Configure instance fields
|
||||||
this.bytes = new Array(16);
|
this.cells = new Array(16);
|
||||||
|
this.offset = offset;
|
||||||
|
this.wnd = wnd;
|
||||||
|
|
||||||
// Configure element
|
// Configure element
|
||||||
this.element.setAttribute("role", "row");
|
this.element.setAttribute("role", "row");
|
||||||
|
|
||||||
// Address label
|
// Address label
|
||||||
this.address = this.add(parent.newLabel({ text: "\u00a0" }));
|
this.addr = this.add(parent.newLabel({ text: "\u00a0" }));
|
||||||
this.address.element.setAttribute("role", "gridcell");
|
this.addr.element.setAttribute("role", "gridcell");
|
||||||
this.address.element.setAttribute("name", "address");
|
this.addr.element.setAttribute("name", "address");
|
||||||
|
|
||||||
// Byte labels
|
// Byte labels
|
||||||
for (let x = 0; x < 16; x++) {
|
for (let x = 0; x < 16; x++)
|
||||||
let lbl = this.bytes[x] =
|
this.cells[x] = new MemoryWindow.Cell(wnd, this, offset + x);
|
||||||
this.add(parent.newLabel({ text: "\u00a0" }));
|
|
||||||
lbl.element.setAttribute("role", "gridcell");
|
|
||||||
lbl.element.setAttribute("name", "byte");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////// Package Methods /////////////////////////////
|
///////////////////////////// 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 the output labels with emulation state content
|
||||||
update(address, bytes, offset) {
|
update(bytes, offset) {
|
||||||
this.address.setText(
|
this.addr.setText(
|
||||||
("0000000" + address.toString(16).toUpperCase()).slice(-8));
|
("0000000" + this.address().toString(16).toUpperCase()).slice(-8));
|
||||||
for (let x = 0; x < 16; x++, offset++)
|
for (let cell of this.cells)
|
||||||
this.bytes[x].setText(
|
cell.update(bytes ? bytes[offset++] : cell.value);
|
||||||
("0" + bytes[offset].toString(16).toUpperCase()).slice(-2));
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -96,7 +96,7 @@ static void busWrite(
|
||||||
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
|
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
|
||||||
|
|
||||||
/* Process by address range */
|
/* Process by address range */
|
||||||
switch (address >> 24 ^ 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 0 : return; /* VIP */
|
case 0 : return; /* VIP */
|
||||||
case 1 : return; /* VSU */
|
case 1 : return; /* VSU */
|
||||||
case 2 : return; /* Miscellaneous hardware */
|
case 2 : return; /* Miscellaneous hardware */
|
||||||
|
|
40
core/cpu.c
40
core/cpu.c
|
@ -756,15 +756,15 @@ static void cpuAND(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
/* And Bit String Upward */
|
/* And Bit String Upward */
|
||||||
#define cpuANDBSU(emu, inst) cpuBitString(emu, inst)
|
#define cpuANDBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
/* And Not Bit String Upward */
|
|
||||||
#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst)
|
|
||||||
|
|
||||||
/* And Immediate */
|
/* And Immediate */
|
||||||
static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) {
|
static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
||||||
emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]);
|
emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* And Not Bit String Upward */
|
||||||
|
#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
/* Conditional Branch */
|
/* Conditional Branch */
|
||||||
static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) {
|
static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
int32_t disp; /* Target address displacement */
|
int32_t disp; /* Target address displacement */
|
||||||
|
@ -1124,15 +1124,15 @@ static void cpuOR(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
/* Or Bit String Upward */
|
/* Or Bit String Upward */
|
||||||
#define cpuORBSU(emu, inst) cpuBitString(emu, inst)
|
#define cpuORBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
/* Or Not Bit String Upward */
|
|
||||||
#define cpuORNBSU(emu, inst) cpuBitString(emu, inst)
|
|
||||||
|
|
||||||
/* Or Immediate */
|
/* Or Immediate */
|
||||||
static void cpuORI(VB *emu, VB_INSTRUCTION *inst) {
|
static void cpuORI(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
||||||
emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]);
|
emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Or Not Bit String Upward */
|
||||||
|
#define cpuORNBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
/* Output Byte */
|
/* Output Byte */
|
||||||
#define cpuOUT_B(emu, inst) cpuStore(emu, inst, VB_U8, 4)
|
#define cpuOUT_B(emu, inst) cpuStore(emu, inst, VB_U8, 4)
|
||||||
|
|
||||||
|
@ -1334,15 +1334,15 @@ static void cpuXOR(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
/* Exclusive Or Bit String Upward */
|
/* Exclusive Or Bit String Upward */
|
||||||
#define cpuXORBSU(emu, inst) cpuBitString(emu, inst)
|
#define cpuXORBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
/* Exclusive Or Not Bit String Upward */
|
|
||||||
#define cpuXORNBSU(emu, inst) cpuBitString(emu, inst)
|
|
||||||
|
|
||||||
/* Exclusive Or Immediate */
|
/* Exclusive Or Immediate */
|
||||||
static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) {
|
static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
|
||||||
emu->cpu.program[inst->bits[0] & 0x1F] ^ inst->bits[1]);
|
emu->cpu.program[inst->bits[0] & 0x1F] ^ inst->bits[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Exclusive Or Not Bit String Upward */
|
||||||
|
#define cpuXORNBSU(emu, inst) cpuBitString(emu, inst)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************** Module Functions ******************************/
|
/***************************** Module Functions ******************************/
|
||||||
|
@ -1505,14 +1505,14 @@ static int cpuExecute(VB *emu) {
|
||||||
|
|
||||||
/* Enter an exception state */
|
/* Enter an exception state */
|
||||||
static int cpuException(VB *emu) {
|
static int cpuException(VB *emu) {
|
||||||
|
uint16_t causeCode = emu->cpu.causeCode;
|
||||||
|
|
||||||
/* Fatal exception */
|
/* Fatal exception */
|
||||||
if (emu->cpu.psw.np) {
|
if (emu->cpu.psw.np) {
|
||||||
|
|
||||||
/* Write the cause code for debugging */
|
/* Write the cause code for debugging */
|
||||||
if (emu->cpu.busWait == 0) {
|
if (emu->cpu.busWait == 0) {
|
||||||
if (cpuWrite(emu, 0x00000000, VB_S32,
|
if (cpuWrite(emu, 0x00000000, VB_S32, 0xFFFF0000 | causeCode))
|
||||||
0xFFFF0000 | emu->cpu.causeCode))
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Update state */
|
/* Update state */
|
||||||
|
@ -1562,7 +1562,7 @@ static int cpuException(VB *emu) {
|
||||||
|
|
||||||
/* Duplexed exception */
|
/* Duplexed exception */
|
||||||
if (emu->cpu.psw.ep) {
|
if (emu->cpu.psw.ep) {
|
||||||
emu->cpu.ecr.fecc = emu->cpu.causeCode;
|
emu->cpu.ecr.fecc = causeCode;
|
||||||
emu->cpu.fepsw = vbGetSystemRegister(emu, VB_PSW);
|
emu->cpu.fepsw = vbGetSystemRegister(emu, VB_PSW);
|
||||||
emu->cpu.fepc = emu->cpu.pc;
|
emu->cpu.fepc = emu->cpu.pc;
|
||||||
emu->cpu.fepcFrom = emu->cpu.pcFrom;
|
emu->cpu.fepcFrom = emu->cpu.pcFrom;
|
||||||
|
@ -1573,17 +1573,19 @@ static int cpuException(VB *emu) {
|
||||||
|
|
||||||
/* Exception or interrupt */
|
/* Exception or interrupt */
|
||||||
else {
|
else {
|
||||||
emu->cpu.ecr.eicc = emu->cpu.causeCode;
|
emu->cpu.ecr.eicc = causeCode;
|
||||||
emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW);
|
emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW);
|
||||||
emu->cpu.eipc = emu->cpu.pc;
|
emu->cpu.eipc = emu->cpu.pc;
|
||||||
|
emu->cpu.eipcFrom = emu->cpu.pcFrom;
|
||||||
|
emu->cpu.eipcTo = emu->cpu.pcTo;
|
||||||
emu->cpu.psw.ep = 1;
|
emu->cpu.psw.ep = 1;
|
||||||
emu->cpu.pc = (emu->cpu.causeCode & 0x0040) != 0 ?
|
emu->cpu.pc = (causeCode & 0x0040) != 0 ?
|
||||||
0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | emu->cpu.causeCode);
|
0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | causeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Interrupt */
|
/* Interrupt */
|
||||||
if (emu->cpu.causeCode < 0xFF00)
|
if (causeCode < 0xFF00)
|
||||||
emu->cpu.psw.i = emu->cpu.psw.i == 15 ? 15 : emu->cpu.psw.i + 1;
|
emu->cpu.psw.i += emu->cpu.psw.i == 15 ? 0 : 1;
|
||||||
|
|
||||||
/* Update state */
|
/* Update state */
|
||||||
emu->cpu.causeCode = 0;
|
emu->cpu.causeCode = 0;
|
||||||
|
@ -1593,7 +1595,9 @@ static int cpuException(VB *emu) {
|
||||||
emu->cpu.pcFrom = emu->cpu.pc;
|
emu->cpu.pcFrom = emu->cpu.pc;
|
||||||
emu->cpu.pcTo = emu->cpu.pc;
|
emu->cpu.pcTo = emu->cpu.pc;
|
||||||
/* emu->cpu.clocks = ? */
|
/* emu->cpu.clocks = ? */
|
||||||
return 0;
|
|
||||||
|
/* Call the breakpoint handler if available */
|
||||||
|
return emu->onException != NULL && emu->onException(emu, causeCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform instruction fetch operations */
|
/* Perform instruction fetch operations */
|
||||||
|
|
|
@ -16,7 +16,9 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
/********************************** Macros ***********************************/
|
/********************************** Macros ***********************************/
|
||||||
|
|
||||||
/* Sign-extend a value of some number of bits to 32 bits */
|
/* Sign-extend a value of some number of bits to 32 bits */
|
||||||
#define SignExtend(v,b) ((v) | (((v) & ((1<<(b)) - 1)) ? ~(int32_t)0<<(b) : 0))
|
#define SignExtend(v,b) \
|
||||||
|
((v) | (((v) & (1 << ((b) - 1))) ? (uint32_t) 0xFFFFFFFF << (b) : 0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -220,7 +222,9 @@ void vbReset(VB *emu) {
|
||||||
emu->cpu.pcTo = 0xFFFFFFF0;
|
emu->cpu.pcTo = 0xFFFFFFF0;
|
||||||
|
|
||||||
/* Other CPU state */
|
/* Other CPU state */
|
||||||
|
emu->cpu.busWait = 0;
|
||||||
emu->cpu.state = CPU_FETCH;
|
emu->cpu.state = CPU_FETCH;
|
||||||
|
emu->cpu.substring = 0;
|
||||||
for (x = 0; x < 5; x++)
|
for (x = 0; x < 5; x++)
|
||||||
emu->cpu.irq[x] = 0;
|
emu->cpu.irq[x] = 0;
|
||||||
|
|
||||||
|
@ -277,6 +281,6 @@ uint32_t vbSetSystemRegister(VB *emu, int id, uint32_t value) {
|
||||||
|
|
||||||
/* Write a data unit to the bus */
|
/* Write a data unit to the bus */
|
||||||
void vbWrite(VB *emu, uint32_t address, int type, int32_t value, int debug) {
|
void vbWrite(VB *emu, uint32_t address, int type, int32_t value, int debug) {
|
||||||
if (type < 0 || type >= (int32_t) sizeof TYPE_SIZES)
|
if (type >= 0 && type < (int32_t) sizeof TYPE_SIZES)
|
||||||
busWrite(emu, address, type, value, debug);
|
busWrite(emu, address, type, value, debug);
|
||||||
}
|
}
|
||||||
|
|
2
makefile
2
makefile
|
@ -1,7 +1,7 @@
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@echo
|
@echo
|
||||||
@echo "Virtual Boy Emulator - September 18, 2021"
|
@echo "Virtual Boy Emulator - September 19, 2021"
|
||||||
@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"
|
||||||
|
|
39
wasm/wasm.c
39
wasm/wasm.c
|
@ -31,6 +31,45 @@ EMSCRIPTEN_KEEPALIVE void ReadBuffer(
|
||||||
*dest = vbRead(&sims[sim], address, VB_U8, debug);
|
*dest = vbRead(&sims[sim], address, VB_U8, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute one instruction
|
||||||
|
static uint32_t SingleStepPC;
|
||||||
|
static int SingleStepProc(VB *emu, int fetch, VB_ACCESS *acc) {
|
||||||
|
if (fetch == 0 && vbGetProgramCounter(emu, VB_PC) != SingleStepPC)
|
||||||
|
return 1;
|
||||||
|
acc->value = vbRead(emu, acc->address, acc->type, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EMSCRIPTEN_KEEPALIVE void SingleStep(int sim) {
|
||||||
|
uint32_t clocks = 400000; // 1/50s
|
||||||
|
VB *emu = &sims[sim];
|
||||||
|
SingleStepPC = vbGetProgramCounter(emu, VB_PC);
|
||||||
|
emu->onFetch = &SingleStepProc;
|
||||||
|
vbEmulate(emu, NULL, &clocks);
|
||||||
|
emu->onFetch = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to execute until the following instruction
|
||||||
|
static uint32_t RunNextPC;
|
||||||
|
static int RunNextProcB(VB *emu, int fetch, VB_ACCESS *acc) {
|
||||||
|
if (fetch == 0 && vbGetProgramCounter(emu, VB_PC) == RunNextPC)
|
||||||
|
return 1;
|
||||||
|
acc->value = vbRead(emu, acc->address, acc->type, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int RunNextProcA(VB *emu, VB_INSTRUCTION *inst) {
|
||||||
|
RunNextPC = vbGetProgramCounter(emu, VB_PC) + inst->size;
|
||||||
|
emu->onExecute = NULL;
|
||||||
|
emu->onFetch = &RunNextProcB;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EMSCRIPTEN_KEEPALIVE void RunNext(int sim) {
|
||||||
|
uint32_t clocks = 400000; // 1/50s
|
||||||
|
VB *emu = &sims[sim];
|
||||||
|
emu->onExecute = &RunNextProcA;
|
||||||
|
vbEmulate(emu, NULL, &clocks);
|
||||||
|
emu->onFetch = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////// Core Commands ////////////////////////////////
|
//////////////////////////////// Core Commands ////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue