Make JavaScript core work like the API

This commit is contained in:
Guy Perfect 2023-03-12 10:59:37 -05:00
parent 1cc268e5d0
commit 386f4a904f
5 changed files with 127 additions and 135 deletions

View File

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

View File

@ -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({

View File

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

View File

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

View File

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