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 .PHONY: help
help: help:
@echo @echo
@echo "Virtual Boy Emulator - March 11, 2023" @echo "Virtual Boy Emulator - March 12, 2023"
@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"

View File

@ -1,6 +1,30 @@
// Interface between application and WebAssembly worker thread // Interface between application and WebAssembly worker thread
class Core { 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 ////////////////////////// ///////////////////////// Initialization Methods //////////////////////////
constructor() { constructor() {
@ -138,29 +162,13 @@ class Core {
}, [], options); }, [], options);
} }
// Retrieve the value of PC // Retrieve the value of a register
getProgramCounter(sim, options) { getRegister(sim, type, id, options) {
return this.message({ return this.message({
command: "getProgramCounter", command: "getRegister",
sim : sim.pointer
}, [], options);
}
// Retrieve the value of a program register
getProgramRegister(sim, id, options) {
return this.message({
command: "getProgramRegister",
id : id, id : id,
sim : sim.pointer sim : sim.pointer,
}, [], options); type : type
}
// Retrieve the value of a system register
getSystemRegister(sim, id, options) {
return this.message({
command: "getSystemRegister",
id : id,
sim : sim.pointer
}, [], options); }, [], options);
} }
@ -199,21 +207,13 @@ class Core {
}, [], options); }, [], options);
} }
// Specify a value for the program counter // Specify a value for a register
setProgramCounter(sim, value, options) { setRegister(sim, type, id, value, options) {
return this.message({ return this.message({
command: "setProgramCounter", command: "setRegister",
sim : sim.pointer,
value : value
}, [], options);
}
// Specify a value for a program register
setProgramRegister(sim, id, value, options) {
return this.message({
command: "setProgramRegister",
id : id, id : id,
sim : sim.pointer, sim : sim.pointer,
type : type,
value : value value : value
}, [], options); }, [], options);
} }
@ -229,16 +229,6 @@ class Core {
}, [data.buffer], options); }, [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 // Execute the current instruction
singleStep(sims, options) { singleStep(sims, options) {
return this.message({ return this.message({

View File

@ -1,5 +1,25 @@
"use strict"; "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 // Dedicated emulation thread
class CoreThread { class CoreThread {
@ -130,19 +150,12 @@ class CoreThread {
}; };
} }
// Retrieve the value of PC // Retrieve the value of a register
getProgramCounter(msg) { getRegister(msg) {
return { value: this.vbGetRegister(msg.sim, 2, 0) >>> 0 }; let value = this.vbGetRegister(msg.sim, msg.type, msg.id);
} if (msg.type != VB_PROGRAM)
value >>>= 0;
// Retrieve the value of a program register return { value: value };
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 };
} }
// Read multiple bytes from memory // Read multiple bytes from memory
@ -193,13 +206,10 @@ class CoreThread {
delete rep.transfers; delete rep.transfers;
} }
// Do not send a reply
if (subscriptions.length == 0 && !msg.reply)
return;
// Send the response to the main thread // Send the response to the main thread
if (subscriptions.length == 0)
return;
this.main.postMessage({ this.main.postMessage({
isReply : !!msg.reply,
subscriptions: subscriptions.sort(CoreThread.REFRESH_ORDER) subscriptions: subscriptions.sort(CoreThread.REFRESH_ORDER)
}, transfers); }, transfers);
} }
@ -224,14 +234,12 @@ class CoreThread {
return { pcs: pcs }; return { pcs: pcs };
} }
// Specify a value for the program counter // Specify a value for a register
setProgramCounter(msg) { setRegister(msg) {
return { value: this.vbSetRegister(msg.sim, 2, 0, msg.value) >>> 0 }; let value = this.vbSetRegister(msg.sim, msg.type, msg.id, msg.value);
} if (msg.type != VB_PROGRAM)
value >>>= 0;
// Specify a value for a program register return { value: value };
setProgramRegister(msg) {
return { value: this.vbSetRegister(msg.sim, 0, msg.id, msg.value) };
} }
// Specify a cartridge ROM buffer // Specify a cartridge ROM buffer
@ -261,11 +269,6 @@ class CoreThread {
return { success: success }; 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 // Execute the current instruction
singleStep(msg) { singleStep(msg) {
let sims = this.malloc(msg.sims.length, true); 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 { Disassembler } from /**/"../core/Disassembler.js";
import { Toolkit } from /**/"../toolkit/Toolkit.js"; import { Toolkit } from /**/"../toolkit/Toolkit.js";
let register = Debugger => { let register = Debugger => {
@ -657,10 +658,12 @@ class Register {
///////////////////////// Initialization Methods ////////////////////////// ///////////////////////// Initialization Methods //////////////////////////
constructor(registers, key, type) { constructor(registers, key, type, apiType, apiId) {
let app = registers.cpu.debug.app; let app = registers.cpu.debug.app;
// Configure instance fields // Configure instance fields
this.apiId = apiId;
this.apiType = apiType;
this.controls = []; this.controls = [];
this.dasm = registers.cpu.debug.app.dasm; this.dasm = registers.cpu.debug.app.dasm;
this.debug = registers.cpu.debug; this.debug = registers.cpu.debug;
@ -1159,17 +1162,11 @@ class Register {
async setValue(value) { async setValue(value) {
// Update the value in the simulation state // Update the value in the simulation state
let sim = this.debug.sim; let options = {};
let result = await ( if (this.key == "pc")
this.key == "pc" ? this.debug.core options.refresh = [ this.registers.cpu.disassembler.subscription ];
.setProgramCounter(sim, value, { let result = await this.debug.core.setRegister(
refresh: [ this.registers.cpu.disassembler.subscription ] this.debug.sim, this.apiType, this.apiId, value, options);
}) :
this.type != Register.PROGRAM ? this.debug.core
.setSystemRegister(sim, this.key, value) :
this.debug.core
.setProgramRegister(sim, this.key, value)
);
// Update the value in the debugger window // Update the value in the debugger window
this.target[this.key] = result.value; this.target[this.key] = result.value;
@ -1194,20 +1191,20 @@ class RegisterPane extends Toolkit.SplitPane {
// System register templates // System register templates
static SYSTEMS = [ static SYSTEMS = [
[ [ "pc" ], Register.PLAIN ], [["pc" ],Register.PLAIN,Core.VB_OTHER ,Core.VB_PC ],
[ [ "system", 5 ], Register.PSW ], [["system",Core.VB_PSW ],Register.PSW ,Core.VB_SYSTEM,Core.VB_PSW ],
[ [ "system", 25 ], Register.PLAIN ], [["system",Core.VB_ADTRE],Register.PLAIN,Core.VB_SYSTEM,Core.VB_ADTRE],
[ [ "system", 24 ], Register.CHCW ], [["system",Core.VB_CHCW ],Register.CHCW ,Core.VB_SYSTEM,Core.VB_CHCW ],
[ [ "system", 4 ], Register.ECR ], [["system",Core.VB_ECR ],Register.ECR ,Core.VB_SYSTEM,Core.VB_ECR ],
[ [ "system", 0 ], Register.PLAIN ], [["system",Core.VB_EIPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_EIPC ],
[ [ "system", 1 ], Register.PSW ], [["system",Core.VB_EIPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_EIPSW],
[ [ "system", 2 ], Register.PLAIN ], [["system",Core.VB_FEPC ],Register.PLAIN,Core.VB_SYSTEM,Core.VB_FEPC ],
[ [ "system", 3 ], Register.PSW ], [["system",Core.VB_FEPSW],Register.PSW ,Core.VB_SYSTEM,Core.VB_FEPSW],
[ [ "system", 6 ], Register.PIR ], [["system",Core.VB_PIR ],Register.PIR ,Core.VB_SYSTEM,Core.VB_PIR ],
[ [ "system", 7 ], Register.TKCW ], [["system",Core.VB_TKCW ],Register.TKCW ,Core.VB_SYSTEM,Core.VB_TKCW ],
[ [ "system", 29 ], Register.PLAIN ], [["system",29 ],Register.PLAIN,Core.VB_SYSTEM,29 ],
[ [ "system", 30 ], Register.PLAIN ], [["system",30 ],Register.PLAIN,Core.VB_SYSTEM,30 ],
[ [ "system", 31 ], Register.PLAIN ] [["system",31 ],Register.PLAIN,Core.VB_SYSTEM,31 ]
]; ];
@ -1278,10 +1275,10 @@ class RegisterPane extends Toolkit.SplitPane {
// Configure register lists // Configure register lists
for (let sys of RegisterPane.SYSTEMS) 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++) { for (let x = 0; x < 32; x++) {
this.addRegister(new Register(this, this.addRegister(new Register(this,
[ "program", x ], Register.PROGRAM)); [ "program", x ], Register.PROGRAM, Core.VB_PROGRAM, x));
} }
// Value text box measurer // Value text box measurer

View File

@ -4,13 +4,6 @@ let register = Debugger => Debugger.Memory =
// Debugger memory window // Debugger memory window
class Memory extends Toolkit.Window { class Memory extends Toolkit.Window {
//////////////////////////////// Constants ////////////////////////////////
// Bus indexes
static MEMORY = 0;
///////////////////////// Initialization Methods ////////////////////////// ///////////////////////// Initialization Methods //////////////////////////
constructor(debug, index) { constructor(debug, index) {
@ -35,12 +28,11 @@ class Memory extends Toolkit.Window {
// Available buses // Available buses
this.buses = [ this.buses = [
{ {
index : Memory.MEMORY,
editAddress: 0x05000000, editAddress: 0x05000000,
viewAddress: 0x05000000 viewAddress: 0x05000000
} }
]; ];
this.bus = this.buses[Memory.MEMORY]; this.bus = this.buses[0];
// Window // Window
this.setTitle("{debug.memory._}", true); this.setTitle("{debug.memory._}", true);
@ -60,8 +52,7 @@ class Memory extends Toolkit.Window {
this.drpBus = new Toolkit.DropDown(debug.app); this.drpBus = new Toolkit.DropDown(debug.app);
this.drpBus.setLabel("{debug.memory.bus}", true); this.drpBus.setLabel("{debug.memory.bus}", true);
this.drpBus.setTitle("{debug.memory.bus}", true); this.drpBus.setTitle("{debug.memory.bus}", true);
this.drpBus.add("{debug.memory.busMemory}", true, this.drpBus.add("{debug.memory.busMemory}", true, this.buses[0]);
this.buses[Memory.MEMORY]);
this.drpBus.addEventListener("input", e=>this.busInput()); this.drpBus.addEventListener("input", e=>this.busInput());
this.add(this.drpBus); this.add(this.drpBus);
@ -86,6 +77,7 @@ class Memory extends Toolkit.Window {
}; };
this.hexEditor.setLabel("{debug.memory.hexEditor}", true); this.hexEditor.setLabel("{debug.memory.hexEditor}", true);
this.hexEditor.setRoleDescription("{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("keydown" , e=>this.hexKeyDown(e));
this.hexEditor.addEventListener("resize" , e=>this.hexResize ( )); this.hexEditor.addEventListener("resize" , e=>this.hexResize ( ));
this.hexEditor.addEventListener("wheel" , e=>this.hexWheel (e)); this.hexEditor.addEventListener("wheel" , e=>this.hexWheel (e));
@ -187,7 +179,7 @@ class Memory extends Toolkit.Window {
// Processing by key, scroll lock on // Processing by key, scroll lock on
else switch (e.key) { else switch (e.key) {
case "ArrowDown": case "ArrowDown":
this.bus.viewAddress += 16; this.setViewAddress(this.bus.viewAddress + 16);
this.fetch(); this.fetch();
Toolkit.handle(e); Toolkit.handle(e);
return; return;
@ -200,17 +192,17 @@ class Memory extends Toolkit.Window {
Toolkit.handle(e); Toolkit.handle(e);
return; return;
case "ArrowUp": case "ArrowUp":
this.bus.viewAddress -= 16; this.setViewAddress(this.bus.viewAddress - 16);
this.fetch(); this.fetch();
Toolkit.handle(e); Toolkit.handle(e);
return; return;
case "PageDown": case "PageDown":
this.bus.viewAddress += this.tall(true) * 16; this.setViewAddress(this.bus.viewAddress + this.tall(true)*16);
this.fetch(); this.fetch();
Toolkit.handle(e); Toolkit.handle(e);
return; return;
case "PageUp": case "PageUp":
this.bus.viewAddress -= this.tall(true) * 16; this.setViewAddress(this.bus.viewAddress - this.tall(true)*16);
this.fetch(); this.fetch();
Toolkit.handle(e); Toolkit.handle(e);
return; return;
@ -280,7 +272,7 @@ class Memory extends Toolkit.Window {
} }
// Update the selection address // 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) if (this.editDigit !== null && address != this.bus.editAddress)
this.commit(); this.commit();
this.setEditAddress(address); this.setEditAddress(address);
@ -356,7 +348,7 @@ class Memory extends Toolkit.Window {
return; return;
// Scroll the view // Scroll the view
this.bus.viewAddress = this.bus.viewAddress + scr.lines * 16 >>> 0; this.setViewAddress(this.bus.viewAddress + scr.lines * 16);
this.fetch(); this.fetch();
} }
@ -407,7 +399,7 @@ class Memory extends Toolkit.Window {
// The edited value is in the bus's data buffer // The edited value is in the bus's data buffer
if (this.data != null) { 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) if (offset < this.data.length)
this.data[offset] = this.editDigit; this.data[offset] = this.editDigit;
} }
@ -425,7 +417,7 @@ class Memory extends Toolkit.Window {
// Select the parameters for the simulation fetch // Select the parameters for the simulation fetch
let params = { let params = {
address: this.bus.viewAddress - 10 * 16, address: this.toAddress(this.bus.viewAddress - 10 * 16),
length : (this.tall(false) + 20) * 16 length : (this.tall(false) + 20) * 16
}; };
@ -478,7 +470,7 @@ class Memory extends Toolkit.Window {
// Process all lines // Process all lines
for (let y = 0; y < this.lines.length; y++) { 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]; let line = this.lines[y];
// Address label // Address label
@ -496,7 +488,7 @@ class Memory extends Toolkit.Window {
// Bus data exists // Bus data exists
else if (this.data != null) { 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 // The byte is contained in the bus data buffer
if (offset >= 0 && offset < this.data.length) if (offset >= 0 && offset < this.data.length)
@ -515,18 +507,18 @@ class Memory extends Toolkit.Window {
setEditAddress(address, auto = false) { setEditAddress(address, auto = false) {
let col = this.lines[0].lblBytes[address&15].getBoundingClientRect(); let col = this.lines[0].lblBytes[address&15].getBoundingClientRect();
let port = this.scrHex.viewport.element.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 scr = this.scrHex.scrollLeft;
let tall = this.tall(true, 0); let tall = this.tall(true, 0);
// Ensure the data row is fully visible // 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) { if (!auto) {
this.bus.viewAddress = this.setViewAddress(
this.bus.viewAddress - row >>> 0 <= this.toAddress(this.bus.viewAddress - row) <=
row - (this.bus.viewAddress + tall * 16) >>> 0 this.toAddress(row - (this.bus.viewAddress + tall * 16))
? row : row - (tall - 1) * 16; ? row : this.toAddress(row - (tall - 1) * 16));
} else this.bus.viewAddress = row - Math.floor(tall * auto) * 16; } else this.setViewAddress(row - Math.floor(tall * auto) * 16);
this.fetch(); this.fetch();
} }
@ -542,10 +534,15 @@ class Memory extends Toolkit.Window {
; ;
// Refresh the display; // Refresh the display;
this.bus.editAddress = address; this.bus.editAddress = this.toAddress(address);
this.refresh(); 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 // Measure the number of lines visible in the view
tall(fully = null, plus = 1) { tall(fully = null, plus = 1) {
return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"]( return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"](
@ -554,6 +551,11 @@ class Memory extends Toolkit.Window {
)) + plus; )) + plus;
} }
// Ensure an address is in the proper range
toAddress(address) {
return address >>> 0;
}
} }
export { register }; export { register };