Enabling CPU trace and hex edit

This commit is contained in:
Guy Perfect 2021-09-19 19:36:30 +00:00
parent 94486ecf02
commit 573f72e46f
14 changed files with 449 additions and 91 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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
@ -159,32 +175,81 @@ globalThis.MemoryWindow = class MemoryWindow extends Toolkit.Window {
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();
e.stopPropagation();
@ -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());
}
};

View File

@ -96,7 +96,7 @@ static void busWrite(
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
/* Process by address range */
switch (address >> 24 ^ 7) {
switch (address >> 24 & 7) {
case 0 : return; /* VIP */
case 1 : return; /* VSU */
case 2 : return; /* Miscellaneous hardware */

View File

@ -756,15 +756,15 @@ static void cpuAND(VB *emu, VB_INSTRUCTION *inst) {
/* And Bit String Upward */
#define cpuANDBSU(emu, inst) cpuBitString(emu, inst)
/* And Not Bit String Upward */
#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst)
/* And Immediate */
static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) {
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]);
}
/* And Not Bit String Upward */
#define cpuANDNBSU(emu, inst) cpuBitString(emu, inst)
/* Conditional Branch */
static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) {
int32_t disp; /* Target address displacement */
@ -1124,15 +1124,15 @@ static void cpuOR(VB *emu, VB_INSTRUCTION *inst) {
/* Or Bit String Upward */
#define cpuORBSU(emu, inst) cpuBitString(emu, inst)
/* Or Not Bit String Upward */
#define cpuORNBSU(emu, inst) cpuBitString(emu, inst)
/* Or Immediate */
static void cpuORI(VB *emu, VB_INSTRUCTION *inst) {
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]);
}
/* Or Not Bit String Upward */
#define cpuORNBSU(emu, inst) cpuBitString(emu, inst)
/* Output Byte */
#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 */
#define cpuXORBSU(emu, inst) cpuBitString(emu, inst)
/* Exclusive Or Not Bit String Upward */
#define cpuXORNBSU(emu, inst) cpuBitString(emu, inst)
/* Exclusive Or Immediate */
static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) {
emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu,
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 ******************************/
@ -1505,14 +1505,14 @@ static int cpuExecute(VB *emu) {
/* Enter an exception state */
static int cpuException(VB *emu) {
uint16_t causeCode = emu->cpu.causeCode;
/* Fatal exception */
if (emu->cpu.psw.np) {
/* Write the cause code for debugging */
if (emu->cpu.busWait == 0) {
if (cpuWrite(emu, 0x00000000, VB_S32,
0xFFFF0000 | emu->cpu.causeCode))
if (cpuWrite(emu, 0x00000000, VB_S32, 0xFFFF0000 | causeCode))
return 1;
/* Update state */
@ -1562,7 +1562,7 @@ static int cpuException(VB *emu) {
/* Duplexed exception */
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.fepc = emu->cpu.pc;
emu->cpu.fepcFrom = emu->cpu.pcFrom;
@ -1573,17 +1573,19 @@ static int cpuException(VB *emu) {
/* Exception or interrupt */
else {
emu->cpu.ecr.eicc = emu->cpu.causeCode;
emu->cpu.ecr.eicc = causeCode;
emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW);
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.pc = (emu->cpu.causeCode & 0x0040) != 0 ?
0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | emu->cpu.causeCode);
emu->cpu.pc = (causeCode & 0x0040) != 0 ?
0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | causeCode);
}
/* Interrupt */
if (emu->cpu.causeCode < 0xFF00)
emu->cpu.psw.i = emu->cpu.psw.i == 15 ? 15 : emu->cpu.psw.i + 1;
if (causeCode < 0xFF00)
emu->cpu.psw.i += emu->cpu.psw.i == 15 ? 0 : 1;
/* Update state */
emu->cpu.causeCode = 0;
@ -1593,7 +1595,9 @@ static int cpuException(VB *emu) {
emu->cpu.pcFrom = emu->cpu.pc;
emu->cpu.pcTo = emu->cpu.pc;
/* emu->cpu.clocks = ? */
return 0;
/* Call the breakpoint handler if available */
return emu->onException != NULL && emu->onException(emu, causeCode);
}
/* Perform instruction fetch operations */

View File

@ -16,7 +16,9 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/********************************** Macros ***********************************/
/* 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;
/* Other CPU state */
emu->cpu.state = CPU_FETCH;
emu->cpu.busWait = 0;
emu->cpu.state = CPU_FETCH;
emu->cpu.substring = 0;
for (x = 0; x < 5; x++)
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 */
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);
}

View File

@ -1,7 +1,7 @@
.PHONY: help
help:
@echo
@echo "Virtual Boy Emulator - September 18, 2021"
@echo "Virtual Boy Emulator - September 19, 2021"
@echo
@echo "Target build environment is any Debian with the following packages:"
@echo " emscripten"

View File

@ -31,6 +31,45 @@ EMSCRIPTEN_KEEPALIVE void ReadBuffer(
*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 ////////////////////////////////