diff --git a/makefile b/makefile index 9246f3e..2f8c750 100644 --- a/makefile +++ b/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" diff --git a/web/core/Core.js b/web/core/Core.js index fbb313b..98350cd 100644 --- a/web/core/Core.js +++ b/web/core/Core.js @@ -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({ diff --git a/web/core/CoreThread.js b/web/core/CoreThread.js index e90aaad..7b029b1 100644 --- a/web/core/CoreThread.js +++ b/web/core/CoreThread.js @@ -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); diff --git a/web/debugger/CPU.js b/web/debugger/CPU.js index 954132d..a15d9f3 100644 --- a/web/debugger/CPU.js +++ b/web/debugger/CPU.js @@ -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 diff --git a/web/debugger/Memory.js b/web/debugger/Memory.js index d3947e6..36cafc3 100644 --- a/web/debugger/Memory.js +++ b/web/debugger/Memory.js @@ -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,9 +77,10 @@ class Memory extends Toolkit.Window { }; this.hexEditor.setLabel("{debug.memory.hexEditor}", true); this.hexEditor.setRoleDescription("{debug.memory.hexEditor}", true); - this.hexEditor.addEventListener("keydown", e=>this.hexKeyDown(e)); - this.hexEditor.addEventListener("resize" , e=>this.hexResize ( )); - this.hexEditor.addEventListener("wheel" , e=>this.hexWheel (e)); + 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)); this.hexEditor.addEventListener( "pointerdown", e=>this.hexPointerDown(e)); this.lastFocus = this.hexEditor; @@ -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 };