Make JavaScript core work like the API
This commit is contained in:
parent
1cc268e5d0
commit
386f4a904f
2
makefile
2
makefile
|
@ -1,7 +1,7 @@
|
|||
.PHONY: help
|
||||
help:
|
||||
@echo
|
||||
@echo "Virtual Boy Emulator - March 11, 2023"
|
||||
@echo "Virtual Boy Emulator - March 12, 2023"
|
||||
@echo
|
||||
@echo "Target build environment is any Debian with the following packages:"
|
||||
@echo " emscripten"
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
// Interface between application and WebAssembly worker thread
|
||||
class Core {
|
||||
|
||||
//////////////////////////////// Constants ////////////////////////////////
|
||||
|
||||
/* Register types */
|
||||
static VB_PROGRAM = 0;
|
||||
static VB_SYSTEM = 1;
|
||||
static VB_OTHER = 2;
|
||||
|
||||
/* System registers */
|
||||
static VB_ADTRE = 25;
|
||||
static VB_CHCW = 24;
|
||||
static VB_ECR = 4;
|
||||
static VB_EIPC = 0;
|
||||
static VB_EIPSW = 1;
|
||||
static VB_FEPC = 2;
|
||||
static VB_FEPSW = 3;
|
||||
static VB_PIR = 6;
|
||||
static VB_PSW = 5;
|
||||
static VB_TKCW = 7;
|
||||
|
||||
/* Other registers */
|
||||
static VB_PC = 0;
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor() {
|
||||
|
@ -138,29 +162,13 @@ class Core {
|
|||
}, [], options);
|
||||
}
|
||||
|
||||
// Retrieve the value of PC
|
||||
getProgramCounter(sim, options) {
|
||||
// Retrieve the value of a register
|
||||
getRegister(sim, type, id, options) {
|
||||
return this.message({
|
||||
command: "getProgramCounter",
|
||||
sim : sim.pointer
|
||||
}, [], options);
|
||||
}
|
||||
|
||||
// Retrieve the value of a program register
|
||||
getProgramRegister(sim, id, options) {
|
||||
return this.message({
|
||||
command: "getProgramRegister",
|
||||
command: "getRegister",
|
||||
id : id,
|
||||
sim : sim.pointer
|
||||
}, [], options);
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(sim, id, options) {
|
||||
return this.message({
|
||||
command: "getSystemRegister",
|
||||
id : id,
|
||||
sim : sim.pointer
|
||||
sim : sim.pointer,
|
||||
type : type
|
||||
}, [], options);
|
||||
}
|
||||
|
||||
|
@ -199,21 +207,13 @@ class Core {
|
|||
}, [], options);
|
||||
}
|
||||
|
||||
// Specify a value for the program counter
|
||||
setProgramCounter(sim, value, options) {
|
||||
// Specify a value for a register
|
||||
setRegister(sim, type, id, value, options) {
|
||||
return this.message({
|
||||
command: "setProgramCounter",
|
||||
sim : sim.pointer,
|
||||
value : value
|
||||
}, [], options);
|
||||
}
|
||||
|
||||
// Specify a value for a program register
|
||||
setProgramRegister(sim, id, value, options) {
|
||||
return this.message({
|
||||
command: "setProgramRegister",
|
||||
command: "setRegister",
|
||||
id : id,
|
||||
sim : sim.pointer,
|
||||
type : type,
|
||||
value : value
|
||||
}, [], options);
|
||||
}
|
||||
|
@ -229,16 +229,6 @@ class Core {
|
|||
}, [data.buffer], options);
|
||||
}
|
||||
|
||||
// Specify a value for a system register
|
||||
setSystemRegister(sim, id, value, options) {
|
||||
return this.message({
|
||||
command: "setSystemRegister",
|
||||
id : id,
|
||||
sim : sim.pointer,
|
||||
value : value
|
||||
}, [], options);
|
||||
}
|
||||
|
||||
// Execute the current instruction
|
||||
singleStep(sims, options) {
|
||||
return this.message({
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
"use strict";
|
||||
|
||||
/* Register types */
|
||||
const VB_PROGRAM = 0;
|
||||
const VB_SYSTEM = 1;
|
||||
const VB_OTHER = 2;
|
||||
|
||||
/* System registers */
|
||||
const VB_ADTRE = 25;
|
||||
const VB_CHCW = 24;
|
||||
const VB_ECR = 4;
|
||||
const VB_EIPC = 0;
|
||||
const VB_EIPSW = 1;
|
||||
const VB_FEPC = 2;
|
||||
const VB_FEPSW = 3;
|
||||
const VB_PIR = 6;
|
||||
const VB_PSW = 5;
|
||||
const VB_TKCW = 7;
|
||||
|
||||
/* Other registers */
|
||||
const VB_PC = 0;
|
||||
|
||||
// Dedicated emulation thread
|
||||
class CoreThread {
|
||||
|
||||
|
@ -130,19 +150,12 @@ class CoreThread {
|
|||
};
|
||||
}
|
||||
|
||||
// Retrieve the value of PC
|
||||
getProgramCounter(msg) {
|
||||
return { value: this.vbGetRegister(msg.sim, 2, 0) >>> 0 };
|
||||
}
|
||||
|
||||
// Retrieve the value of a program register
|
||||
getProgramRegister(msg) {
|
||||
return { value: this.vbGetRegister(msg.sim, 0, msg.id) };
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(msg) {
|
||||
return { value: this.vbGetRegister(msg.sim, 1, msg.id) >>> 0 };
|
||||
// Retrieve the value of a register
|
||||
getRegister(msg) {
|
||||
let value = this.vbGetRegister(msg.sim, msg.type, msg.id);
|
||||
if (msg.type != VB_PROGRAM)
|
||||
value >>>= 0;
|
||||
return { value: value };
|
||||
}
|
||||
|
||||
// Read multiple bytes from memory
|
||||
|
@ -193,13 +206,10 @@ class CoreThread {
|
|||
delete rep.transfers;
|
||||
}
|
||||
|
||||
// Do not send a reply
|
||||
if (subscriptions.length == 0 && !msg.reply)
|
||||
return;
|
||||
|
||||
// Send the response to the main thread
|
||||
if (subscriptions.length == 0)
|
||||
return;
|
||||
this.main.postMessage({
|
||||
isReply : !!msg.reply,
|
||||
subscriptions: subscriptions.sort(CoreThread.REFRESH_ORDER)
|
||||
}, transfers);
|
||||
}
|
||||
|
@ -224,14 +234,12 @@ class CoreThread {
|
|||
return { pcs: pcs };
|
||||
}
|
||||
|
||||
// Specify a value for the program counter
|
||||
setProgramCounter(msg) {
|
||||
return { value: this.vbSetRegister(msg.sim, 2, 0, msg.value) >>> 0 };
|
||||
}
|
||||
|
||||
// Specify a value for a program register
|
||||
setProgramRegister(msg) {
|
||||
return { value: this.vbSetRegister(msg.sim, 0, msg.id, msg.value) };
|
||||
// Specify a value for a register
|
||||
setRegister(msg) {
|
||||
let value = this.vbSetRegister(msg.sim, msg.type, msg.id, msg.value);
|
||||
if (msg.type != VB_PROGRAM)
|
||||
value >>>= 0;
|
||||
return { value: value };
|
||||
}
|
||||
|
||||
// Specify a cartridge ROM buffer
|
||||
|
@ -261,11 +269,6 @@ class CoreThread {
|
|||
return { success: success };
|
||||
}
|
||||
|
||||
// Specify a value for a system register
|
||||
setSystemRegister(msg) {
|
||||
return {value:this.vbSetRegister(msg.sim, 1, msg.id, msg.value)>>>0};
|
||||
}
|
||||
|
||||
// Execute the current instruction
|
||||
singleStep(msg) {
|
||||
let sims = this.malloc(msg.sims.length, true);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Core } from /**/"../core/Core.js";
|
||||
import { Disassembler } from /**/"../core/Disassembler.js";
|
||||
import { Toolkit } from /**/"../toolkit/Toolkit.js";
|
||||
let register = Debugger => {
|
||||
|
@ -657,10 +658,12 @@ class Register {
|
|||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(registers, key, type) {
|
||||
constructor(registers, key, type, apiType, apiId) {
|
||||
let app = registers.cpu.debug.app;
|
||||
|
||||
// Configure instance fields
|
||||
this.apiId = apiId;
|
||||
this.apiType = apiType;
|
||||
this.controls = [];
|
||||
this.dasm = registers.cpu.debug.app.dasm;
|
||||
this.debug = registers.cpu.debug;
|
||||
|
@ -1159,17 +1162,11 @@ class Register {
|
|||
async setValue(value) {
|
||||
|
||||
// Update the value in the simulation state
|
||||
let sim = this.debug.sim;
|
||||
let result = await (
|
||||
this.key == "pc" ? this.debug.core
|
||||
.setProgramCounter(sim, value, {
|
||||
refresh: [ this.registers.cpu.disassembler.subscription ]
|
||||
}) :
|
||||
this.type != Register.PROGRAM ? this.debug.core
|
||||
.setSystemRegister(sim, this.key, value) :
|
||||
this.debug.core
|
||||
.setProgramRegister(sim, this.key, value)
|
||||
);
|
||||
let options = {};
|
||||
if (this.key == "pc")
|
||||
options.refresh = [ this.registers.cpu.disassembler.subscription ];
|
||||
let result = await this.debug.core.setRegister(
|
||||
this.debug.sim, this.apiType, this.apiId, value, options);
|
||||
|
||||
// Update the value in the debugger window
|
||||
this.target[this.key] = result.value;
|
||||
|
@ -1194,20 +1191,20 @@ class RegisterPane extends Toolkit.SplitPane {
|
|||
|
||||
// System register templates
|
||||
static SYSTEMS = [
|
||||
[ [ "pc" ], Register.PLAIN ],
|
||||
[ [ "system", 5 ], Register.PSW ],
|
||||
[ [ "system", 25 ], Register.PLAIN ],
|
||||
[ [ "system", 24 ], Register.CHCW ],
|
||||
[ [ "system", 4 ], Register.ECR ],
|
||||
[ [ "system", 0 ], Register.PLAIN ],
|
||||
[ [ "system", 1 ], Register.PSW ],
|
||||
[ [ "system", 2 ], Register.PLAIN ],
|
||||
[ [ "system", 3 ], Register.PSW ],
|
||||
[ [ "system", 6 ], Register.PIR ],
|
||||
[ [ "system", 7 ], Register.TKCW ],
|
||||
[ [ "system", 29 ], Register.PLAIN ],
|
||||
[ [ "system", 30 ], Register.PLAIN ],
|
||||
[ [ "system", 31 ], Register.PLAIN ]
|
||||
[["pc" ],Register.PLAIN,Core.VB_OTHER ,Core.VB_PC ],
|
||||
[["system",Core.VB_PSW ],Register.PSW ,Core.VB_SYSTEM,Core.VB_PSW ],
|
||||
[["system",Core.VB_ADTRE],Register.PLAIN,Core.VB_SYSTEM,Core.VB_ADTRE],
|
||||
[["system",Core.VB_CHCW ],Register.CHCW ,Core.VB_SYSTEM,Core.VB_CHCW ],
|
||||
[["system",Core.VB_ECR ],Register.ECR ,Core.VB_SYSTEM,Core.VB_ECR ],
|
||||
[["system",Core.VB_EIPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_EIPC ],
|
||||
[["system",Core.VB_EIPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_EIPSW],
|
||||
[["system",Core.VB_FEPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_FEPC ],
|
||||
[["system",Core.VB_FEPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_FEPSW],
|
||||
[["system",Core.VB_PIR ],Register.PIR ,Core.VB_SYSTEM,Core.VB_PIR ],
|
||||
[["system",Core.VB_TKCW ],Register.TKCW ,Core.VB_SYSTEM,Core.VB_TKCW ],
|
||||
[["system",29 ],Register.PLAIN,Core.VB_SYSTEM,29 ],
|
||||
[["system",30 ],Register.PLAIN,Core.VB_SYSTEM,30 ],
|
||||
[["system",31 ],Register.PLAIN,Core.VB_SYSTEM,31 ]
|
||||
];
|
||||
|
||||
|
||||
|
@ -1278,10 +1275,10 @@ class RegisterPane extends Toolkit.SplitPane {
|
|||
|
||||
// Configure register lists
|
||||
for (let sys of RegisterPane.SYSTEMS)
|
||||
this.addRegister(new Register(this, sys[0], sys[1]));
|
||||
this.addRegister(new Register(this, ... sys));
|
||||
for (let x = 0; x < 32; x++) {
|
||||
this.addRegister(new Register(this,
|
||||
[ "program", x ], Register.PROGRAM));
|
||||
[ "program", x ], Register.PROGRAM, Core.VB_PROGRAM, x));
|
||||
}
|
||||
|
||||
// Value text box measurer
|
||||
|
|
|
@ -4,13 +4,6 @@ let register = Debugger => Debugger.Memory =
|
|||
// Debugger memory window
|
||||
class Memory extends Toolkit.Window {
|
||||
|
||||
//////////////////////////////// Constants ////////////////////////////////
|
||||
|
||||
// Bus indexes
|
||||
static MEMORY = 0;
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(debug, index) {
|
||||
|
@ -35,12 +28,11 @@ class Memory extends Toolkit.Window {
|
|||
// Available buses
|
||||
this.buses = [
|
||||
{
|
||||
index : Memory.MEMORY,
|
||||
editAddress: 0x05000000,
|
||||
viewAddress: 0x05000000
|
||||
}
|
||||
];
|
||||
this.bus = this.buses[Memory.MEMORY];
|
||||
this.bus = this.buses[0];
|
||||
|
||||
// Window
|
||||
this.setTitle("{debug.memory._}", true);
|
||||
|
@ -60,8 +52,7 @@ class Memory extends Toolkit.Window {
|
|||
this.drpBus = new Toolkit.DropDown(debug.app);
|
||||
this.drpBus.setLabel("{debug.memory.bus}", true);
|
||||
this.drpBus.setTitle("{debug.memory.bus}", true);
|
||||
this.drpBus.add("{debug.memory.busMemory}", true,
|
||||
this.buses[Memory.MEMORY]);
|
||||
this.drpBus.add("{debug.memory.busMemory}", true, this.buses[0]);
|
||||
this.drpBus.addEventListener("input", e=>this.busInput());
|
||||
this.add(this.drpBus);
|
||||
|
||||
|
@ -86,6 +77,7 @@ class Memory extends Toolkit.Window {
|
|||
};
|
||||
this.hexEditor.setLabel("{debug.memory.hexEditor}", true);
|
||||
this.hexEditor.setRoleDescription("{debug.memory.hexEditor}", true);
|
||||
this.hexEditor.addEventListener("focusout", e=>this.commit ( ));
|
||||
this.hexEditor.addEventListener("keydown" , e=>this.hexKeyDown(e));
|
||||
this.hexEditor.addEventListener("resize" , e=>this.hexResize ( ));
|
||||
this.hexEditor.addEventListener("wheel" , e=>this.hexWheel (e));
|
||||
|
@ -187,7 +179,7 @@ class Memory extends Toolkit.Window {
|
|||
// Processing by key, scroll lock on
|
||||
else switch (e.key) {
|
||||
case "ArrowDown":
|
||||
this.bus.viewAddress += 16;
|
||||
this.setViewAddress(this.bus.viewAddress + 16);
|
||||
this.fetch();
|
||||
Toolkit.handle(e);
|
||||
return;
|
||||
|
@ -200,17 +192,17 @@ class Memory extends Toolkit.Window {
|
|||
Toolkit.handle(e);
|
||||
return;
|
||||
case "ArrowUp":
|
||||
this.bus.viewAddress -= 16;
|
||||
this.setViewAddress(this.bus.viewAddress - 16);
|
||||
this.fetch();
|
||||
Toolkit.handle(e);
|
||||
return;
|
||||
case "PageDown":
|
||||
this.bus.viewAddress += this.tall(true) * 16;
|
||||
this.setViewAddress(this.bus.viewAddress + this.tall(true)*16);
|
||||
this.fetch();
|
||||
Toolkit.handle(e);
|
||||
return;
|
||||
case "PageUp":
|
||||
this.bus.viewAddress -= this.tall(true) * 16;
|
||||
this.setViewAddress(this.bus.viewAddress - this.tall(true)*16);
|
||||
this.fetch();
|
||||
Toolkit.handle(e);
|
||||
return;
|
||||
|
@ -280,7 +272,7 @@ class Memory extends Toolkit.Window {
|
|||
}
|
||||
|
||||
// Update the selection address
|
||||
let address = this.bus.viewAddress + y * 16 + x >>> 0;
|
||||
let address = this.toAddress(this.bus.viewAddress + y * 16 + x);
|
||||
if (this.editDigit !== null && address != this.bus.editAddress)
|
||||
this.commit();
|
||||
this.setEditAddress(address);
|
||||
|
@ -356,7 +348,7 @@ class Memory extends Toolkit.Window {
|
|||
return;
|
||||
|
||||
// Scroll the view
|
||||
this.bus.viewAddress = this.bus.viewAddress + scr.lines * 16 >>> 0;
|
||||
this.setViewAddress(this.bus.viewAddress + scr.lines * 16);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
|
@ -407,7 +399,7 @@ class Memory extends Toolkit.Window {
|
|||
|
||||
// The edited value is in the bus's data buffer
|
||||
if (this.data != null) {
|
||||
let offset = this.bus.editAddress - this.dataAddress >>> 0;
|
||||
let offset = this.toAddress(this.bus.editAddress-this.dataAddress);
|
||||
if (offset < this.data.length)
|
||||
this.data[offset] = this.editDigit;
|
||||
}
|
||||
|
@ -425,7 +417,7 @@ class Memory extends Toolkit.Window {
|
|||
|
||||
// Select the parameters for the simulation fetch
|
||||
let params = {
|
||||
address: this.bus.viewAddress - 10 * 16,
|
||||
address: this.toAddress(this.bus.viewAddress - 10 * 16),
|
||||
length : (this.tall(false) + 20) * 16
|
||||
};
|
||||
|
||||
|
@ -478,7 +470,7 @@ class Memory extends Toolkit.Window {
|
|||
|
||||
// Process all lines
|
||||
for (let y = 0; y < this.lines.length; y++) {
|
||||
let address = this.bus.viewAddress + y * 16 >>> 0;
|
||||
let address = this.toAddress(this.bus.viewAddress + y * 16);
|
||||
let line = this.lines[y];
|
||||
|
||||
// Address label
|
||||
|
@ -496,7 +488,7 @@ class Memory extends Toolkit.Window {
|
|||
|
||||
// Bus data exists
|
||||
else if (this.data != null) {
|
||||
let offset = address - this.dataAddress + x >>> 0;
|
||||
let offset = this.toAddress(address-this.dataAddress+x);
|
||||
|
||||
// The byte is contained in the bus data buffer
|
||||
if (offset >= 0 && offset < this.data.length)
|
||||
|
@ -515,18 +507,18 @@ class Memory extends Toolkit.Window {
|
|||
setEditAddress(address, auto = false) {
|
||||
let col = this.lines[0].lblBytes[address&15].getBoundingClientRect();
|
||||
let port = this.scrHex.viewport.element.getBoundingClientRect();
|
||||
let row = (address & ~15) >>> 0;
|
||||
let row = this.toAddress(address & ~15);
|
||||
let scr = this.scrHex.scrollLeft;
|
||||
let tall = this.tall(true, 0);
|
||||
|
||||
// Ensure the data row is fully visible
|
||||
if (row - this.bus.viewAddress >>> 0 >= tall * 16) {
|
||||
if (this.toAddress(row - this.bus.viewAddress) >= tall * 16) {
|
||||
if (!auto) {
|
||||
this.bus.viewAddress =
|
||||
this.bus.viewAddress - row >>> 0 <=
|
||||
row - (this.bus.viewAddress + tall * 16) >>> 0
|
||||
? row : row - (tall - 1) * 16;
|
||||
} else this.bus.viewAddress = row - Math.floor(tall * auto) * 16;
|
||||
this.setViewAddress(
|
||||
this.toAddress(this.bus.viewAddress - row) <=
|
||||
this.toAddress(row - (this.bus.viewAddress + tall * 16))
|
||||
? row : this.toAddress(row - (tall - 1) * 16));
|
||||
} else this.setViewAddress(row - Math.floor(tall * auto) * 16);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
|
@ -542,10 +534,15 @@ class Memory extends Toolkit.Window {
|
|||
;
|
||||
|
||||
// Refresh the display;
|
||||
this.bus.editAddress = address;
|
||||
this.bus.editAddress = this.toAddress(address);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
// Specify the address of the hex editor's view
|
||||
setViewAddress(address) {
|
||||
this.bus.viewAddress = this.toAddress(address);
|
||||
}
|
||||
|
||||
// Measure the number of lines visible in the view
|
||||
tall(fully = null, plus = 1) {
|
||||
return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"](
|
||||
|
@ -554,6 +551,11 @@ class Memory extends Toolkit.Window {
|
|||
)) + plus;
|
||||
}
|
||||
|
||||
// Ensure an address is in the proper range
|
||||
toAddress(address) {
|
||||
return address >>> 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { register };
|
||||
|
|
Loading…
Reference in New Issue