Full front-end rewrite
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
import { Sim } from /**/"./Sim.js";
|
||||
|
||||
let url = u=>u.startsWith("data:")?u:new URL(u,import.meta.url).toString();
|
||||
|
||||
let RESTRICT = {};
|
||||
let WASM_URL = url(/**/"./core.wasm" );
|
||||
let WORKER_URL = url(/**/"./CoreWorker.js");
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Core //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Environment manager for simulated Virtual Boys
|
||||
class Core {
|
||||
|
||||
//////////////////////////////// Constants ////////////////////////////////
|
||||
|
||||
// States
|
||||
static IDLE = 0;
|
||||
static RUNNING = 1;
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Static Methods //////////////////////////////
|
||||
|
||||
// Create a new instance of Core
|
||||
static create(options) {
|
||||
return new Core(RESTRICT).init(options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
// Stub constructor
|
||||
constructor(restrict) {
|
||||
if (restrict != RESTRICT) {
|
||||
throw "Cannot instantiate Core directly. " +
|
||||
"Use Core.create() instead.";
|
||||
}
|
||||
}
|
||||
|
||||
// Substitute constructor
|
||||
async init(options = {}) {
|
||||
|
||||
// Configure instance fields
|
||||
this.length = 0;
|
||||
this.onsubscriptions = null;
|
||||
this.resolutions = [];
|
||||
this.state = Core.IDLE;
|
||||
this.worker = new Worker(WORKER_URL);
|
||||
this.worker.onmessage = e=>this.onMessage(e.data);
|
||||
|
||||
// Issue a create command
|
||||
if ("sims" in options)
|
||||
await this.create(options.sims, WASM_URL);
|
||||
|
||||
// Only initialize the WebAssembly module
|
||||
else this.send("init", false, { wasm: WASM_URL });
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Worker message received
|
||||
onMessage(data) {
|
||||
|
||||
// Process a promised response
|
||||
if ("response" in data)
|
||||
this.resolutions.shift()(data.response);
|
||||
|
||||
// Process subscriptions
|
||||
if (this.onsubscriptions && data.subscriptions)
|
||||
this.onsubscriptions(data.subscriptions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Associate two simulations as peers, or remove an association
|
||||
connect(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "connect",
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Create and initialize new simulations
|
||||
async create(sims, wasm) {
|
||||
let numSims = sims===undefined ? 1 : Math.max(0, parseInt(sims) || 0);
|
||||
|
||||
// Execute the command in the core thread
|
||||
let response = await this.send({
|
||||
command: "create",
|
||||
sims : numSims,
|
||||
wasm : wasm
|
||||
});
|
||||
|
||||
// Process the core thread's response
|
||||
let ret = [];
|
||||
for (let x = 0; x < numSims; x++, this.length++)
|
||||
ret.push(this[this.length] =
|
||||
new Sim(this, response[x], this.length));
|
||||
return sims === undefined ? ret[0] : ret;
|
||||
}
|
||||
|
||||
// Delete a simulation
|
||||
destroy(sim, options = {}) {
|
||||
|
||||
// Configure simulation
|
||||
sim = this[sim] || sim;
|
||||
if (sim.core != this)
|
||||
return;
|
||||
let ptr = sim.destroy();
|
||||
|
||||
// State management
|
||||
for (let x = sim.index + 1; x < this.length; x++)
|
||||
(this[x - 1] = this[x]).index--;
|
||||
delete this[--this.length];
|
||||
|
||||
// Execute the command on the core thread
|
||||
return this.send({
|
||||
command: "destroy",
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sim : ptr
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to run until the next instruction
|
||||
runNext(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "runNext",
|
||||
refresh: !!options.refresh,
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
singleStep(a, b, options = {}) {
|
||||
return this.send({
|
||||
command: "singleStep",
|
||||
refresh: !!options.refresh,
|
||||
respond: !("respond" in options) || !!options.respond,
|
||||
sims : [ a, b ]
|
||||
});
|
||||
}
|
||||
|
||||
// Unsubscribe from frame data
|
||||
unsubscribe(key, sim = 0) {
|
||||
this.send({
|
||||
command: "unsubscribe",
|
||||
key : key,
|
||||
respond: false,
|
||||
sim : sim
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Send a message to the Worker
|
||||
send(data = {}, transfers = []) {
|
||||
|
||||
// Create the message object
|
||||
Object.assign(data, {
|
||||
respond: !("respond" in data) || !!data.respond,
|
||||
run : !("run" in data) || !!data.run
|
||||
});
|
||||
|
||||
// Do not wait on a response
|
||||
if (!data.respond)
|
||||
this.worker.postMessage(data, transfers);
|
||||
|
||||
// Wait for the response to come back
|
||||
else return new Promise((resolve, reject)=>{
|
||||
this.resolutions.push(response=>resolve(response));
|
||||
this.worker.postMessage(data, transfers);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export { Core };
|
||||
@@ -0,0 +1,378 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
|
||||
// Un-sign a 32-bit integer
|
||||
// Emscripten is sign-extending uint32_t and Firefox can't import in Workers
|
||||
let u32 = (()=>{
|
||||
let U32 = new Uint32Array(1);
|
||||
return x=>{ U32[0] = x; return U32[0]; };
|
||||
})();
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CoreWorker //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Thread manager for Core commands
|
||||
new class CoreWorker {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
// Stub constructor
|
||||
constructor() {
|
||||
onmessage = async e=>{
|
||||
await this.init(e.data.wasm);
|
||||
onmessage = e=>this.onCommand(e.data, false);
|
||||
onmessage(e);
|
||||
};
|
||||
}
|
||||
|
||||
// Substitute constructor
|
||||
async init(wasm) {
|
||||
|
||||
// Load the WebAssembly module
|
||||
let imports = {
|
||||
env: { emscripten_notify_memory_growth: ()=>this.onMemory() }
|
||||
};
|
||||
this.wasm = await (typeof wasm == "string" ?
|
||||
WebAssembly.instantiateStreaming(fetch(wasm), imports) :
|
||||
WebAssembly.instantiate ( wasm , imports)
|
||||
);
|
||||
|
||||
// Configure instance fields
|
||||
this.api = this.wasm.instance.exports;
|
||||
this.frameData = null;
|
||||
this.isRunning = false;
|
||||
this.memory = this.api.memory.buffer;
|
||||
this.ptrSize = this.api.PointerSize();
|
||||
this.ptrType = this.ptrSize == 4 ? Uint32Array : Uint64Array;
|
||||
this.subscriptions = {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Event Handlers //////////////////////////////
|
||||
|
||||
// Message from audio thread
|
||||
onAudio(frames) {
|
||||
}
|
||||
|
||||
// Message from main thread
|
||||
onCommand(data) {
|
||||
|
||||
// Subscribe to the command
|
||||
if (data.subscribe) {
|
||||
let sub = data.sim || 0;
|
||||
sub = this.subscriptions[sub] || (this.subscriptions[sub] = {});
|
||||
sub = sub[data.subscribe] = {};
|
||||
Object.assign(sub, data);
|
||||
delete sub.promised;
|
||||
delete sub.run;
|
||||
delete sub.subscribe;
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
if (data.run)
|
||||
this[data.command](data);
|
||||
|
||||
// Process all subscriptions to refresh any debugging interfaces
|
||||
if (data.refresh)
|
||||
this.doSubscriptions(data.sim ? [ data.sim ] : data.sims);
|
||||
|
||||
// Reply to the main thread
|
||||
if (data.respond) {
|
||||
postMessage({
|
||||
response: data.response
|
||||
}, data.transfers);
|
||||
}
|
||||
}
|
||||
|
||||
// Memory growth
|
||||
onMemory() {
|
||||
this.memory = this.api.memory.buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////// Commands /////////////////////////////////
|
||||
|
||||
// Associate two simulations as peers, or remove an association
|
||||
connect(data) {
|
||||
this.api.vbConnect(data.sims[0], data.sims[1]);
|
||||
}
|
||||
|
||||
// Allocate and initialize a new simulation
|
||||
create(data) {
|
||||
let ptr = this.api.Create(data.sims);
|
||||
data.response = new this.ptrType(this.memory, ptr, data.sims).slice();
|
||||
data.transfers = [ data.response.buffer ];
|
||||
this.api.Free(ptr);
|
||||
}
|
||||
|
||||
// Delete a simulation
|
||||
destroy(data) {
|
||||
this.api.Destroy(data.sim);
|
||||
}
|
||||
|
||||
// Locate instructions for disassembly
|
||||
disassemble(data) {
|
||||
let decode; // Address of next row
|
||||
let index; // Index in list of next row
|
||||
let rows = new Array(data.rows);
|
||||
let pc = u32(this.api.vbGetProgramCounter(data.sim));
|
||||
let row; // Located output row
|
||||
|
||||
// The target address is before or on the first row of output
|
||||
if (data.row <= 0) {
|
||||
decode = u32(data.target - 4 * Math.max(0, data.row + 10));
|
||||
|
||||
// Locate the target row
|
||||
for (;;) {
|
||||
row = this.dasmRow(data.sim, decode, pc);
|
||||
if (u32(data.target - decode) < row.size)
|
||||
break;
|
||||
decode = u32(decode + row.size);
|
||||
}
|
||||
|
||||
// Locate the first row of output
|
||||
for (index = data.row; index < 0; index++) {
|
||||
decode = u32(decode + row.size);
|
||||
row = this.dasmRow(data.sim, decode, pc);
|
||||
}
|
||||
|
||||
// Prepare to process remaining rows
|
||||
decode = u32(decode + row.size);
|
||||
rows[0] = row;
|
||||
index = 1;
|
||||
}
|
||||
|
||||
// The target address is after the first row of output
|
||||
else {
|
||||
let circle = new Array(data.row + 1);
|
||||
let count = Math.min(data.row + 1, data.rows);
|
||||
let src = 0;
|
||||
decode = u32(data.target - 4 * (data.row + 10));
|
||||
|
||||
// Locate the target row
|
||||
for (;;) {
|
||||
row = circle[src] = this.dasmRow(data.sim, decode, pc);
|
||||
decode = u32(decode + row.size);
|
||||
if (u32(data.target - row.address) < row.size)
|
||||
break;
|
||||
src = (src + 1) % circle.length;
|
||||
}
|
||||
|
||||
// Copy entries from the circular buffer to the output list
|
||||
for (index = 0; index < count; index++) {
|
||||
src = (src + 1) % circle.length;
|
||||
rows[index] = circle[src];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Locate any remaining rows
|
||||
for (; index < data.rows; index++) {
|
||||
let row = rows[index] = this.dasmRow(data.sim, decode, pc);
|
||||
decode = u32(decode + row.size);
|
||||
}
|
||||
|
||||
// Respond to main thread
|
||||
data.response = {
|
||||
pc : pc,
|
||||
rows : rows,
|
||||
scroll: data.scroll
|
||||
};
|
||||
}
|
||||
|
||||
// Retrieve all CPU program registers
|
||||
getProgramRegisters(data) {
|
||||
let ret = data.response = new Uint32Array(32);
|
||||
for (let x = 0; x < 32; x++)
|
||||
ret[x] = this.api.vbGetProgramRegister(data.sim, x);
|
||||
data.transfers = [ ret.buffer ];
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(data) {
|
||||
data.response = u32(this.api.vbGetSystemRegister(data.sim, data.id));
|
||||
}
|
||||
|
||||
// Retrieve all CPU system registers (including PC)
|
||||
getSystemRegisters(data) {
|
||||
data.response = {
|
||||
adtre: u32(this.api.vbGetSystemRegister(data.sim, 25)),
|
||||
chcw : u32(this.api.vbGetSystemRegister(data.sim, 24)),
|
||||
ecr : u32(this.api.vbGetSystemRegister(data.sim, 4)),
|
||||
eipc : u32(this.api.vbGetSystemRegister(data.sim, 0)),
|
||||
eipsw: u32(this.api.vbGetSystemRegister(data.sim, 1)),
|
||||
fepc : u32(this.api.vbGetSystemRegister(data.sim, 2)),
|
||||
fepsw: u32(this.api.vbGetSystemRegister(data.sim, 3)),
|
||||
pc : u32(this.api.vbGetProgramCounter(data.sim )),
|
||||
pir : u32(this.api.vbGetSystemRegister(data.sim, 6)),
|
||||
psw : u32(this.api.vbGetSystemRegister(data.sim, 5)),
|
||||
tkcw : u32(this.api.vbGetSystemRegister(data.sim, 7)),
|
||||
[29] : u32(this.api.vbGetSystemRegister(data.sim, 29)),
|
||||
[30] : u32(this.api.vbGetSystemRegister(data.sim, 30)),
|
||||
[31] : u32(this.api.vbGetSystemRegister(data.sim, 31))
|
||||
};
|
||||
}
|
||||
|
||||
// Read bytes from the simulation
|
||||
read(data) {
|
||||
let ptr = this.api.Malloc(data.length);
|
||||
this.api.ReadBuffer(data.sim, ptr, data.address, data.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.length).slice();
|
||||
this.api.Free(ptr);
|
||||
data.response = {
|
||||
address: data.address,
|
||||
bytes : buffer
|
||||
};
|
||||
data.transfers = [ buffer.buffer ];
|
||||
}
|
||||
|
||||
// Attempt to execute until the following instruction
|
||||
runNext(data) {
|
||||
this.api.RunNext(data.sims[0], data.sims[1]);
|
||||
let pc = [ u32(this.api.vbGetProgramCounter(data.sims[0])) ];
|
||||
if (data.sims[1])
|
||||
pc.push(u32(this.api.vbGetProgramCounter(data.sims[1])));
|
||||
data.response = {
|
||||
pc: pc
|
||||
}
|
||||
}
|
||||
|
||||
// Specify a new value for PC
|
||||
setProgramCounter(data) {
|
||||
data.response =
|
||||
u32(this.api.vbSetProgramCounter(data.sim, data.value));
|
||||
}
|
||||
|
||||
// Specify a new value for a program register
|
||||
setProgramRegister(data) {
|
||||
data.response = this.api.vbSetProgramRegister
|
||||
(data.sim, data.id, data.value);
|
||||
}
|
||||
|
||||
// Specify a ROM buffer
|
||||
setROM(data) {
|
||||
let ptr = this.api.Malloc(data.rom.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.rom.length);
|
||||
for (let x = 0; x < data.rom.length; x++)
|
||||
buffer[x] = data.rom[x];
|
||||
data.response = !!this.api.SetROM(data.sim, ptr, data.rom.length);
|
||||
}
|
||||
|
||||
// Specify a new value for a system register
|
||||
setSystemRegister(data) {
|
||||
data.response = u32(this.api.vbSetSystemRegister
|
||||
(data.sim, data.id, data.value));
|
||||
}
|
||||
|
||||
// Execute one instruction
|
||||
singleStep(data) {
|
||||
this.api.SingleStep(data.sims[0], data.sims[1]);
|
||||
let pc = [ u32(this.api.vbGetProgramCounter(data.sims[0])) ];
|
||||
if (data.sims[1])
|
||||
pc.push(u32(this.api.vbGetProgramCounter(data.sims[1])));
|
||||
data.response = {
|
||||
pc: pc
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe from frame data
|
||||
unsubscribe(data) {
|
||||
let sim = data.sim || 0;
|
||||
if (sim in this.subscriptions) {
|
||||
let subs = this.subscriptions[sim];
|
||||
delete subs[data.key];
|
||||
if (Object.keys(subs).length == 0)
|
||||
delete this.subscriptions[sim];
|
||||
}
|
||||
}
|
||||
|
||||
// Write bytes to the simulation
|
||||
write(data) {
|
||||
let ptr = this.api.Malloc(data.bytes.length);
|
||||
let buffer = new Uint8Array(this.memory, ptr, data.bytes.length);
|
||||
for (let x = 0; x < data.bytes.length; x++)
|
||||
buffer[x] = data.bytes[x];
|
||||
this.api.WriteBuffer(data.sim, ptr, data.address, data.bytes.length);
|
||||
this.api.Free(ptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Private Methods /////////////////////////////
|
||||
|
||||
// Retrieve basic information for a row of disassembly
|
||||
dasmRow(sim, address, pc) {
|
||||
let bits = this.api.vbRead(sim, address, 3 /* VB_U16 */);
|
||||
let opcode = bits >> 10 & 63;
|
||||
let size = (
|
||||
opcode < 0b101000 || // Formats I through III
|
||||
opcode == 0b110010 || // Illegal
|
||||
opcode == 0b110110 // Illegal
|
||||
) ? 2 : 4;
|
||||
|
||||
// Establish row information
|
||||
let row = {
|
||||
address: address,
|
||||
bytes : [ bits & 0xFF, bits >> 8 ],
|
||||
size : u32(address + 2) == pc ? 2 : size
|
||||
//size : Math.min(u32(pc - address), size)
|
||||
};
|
||||
|
||||
// Read additional bytes
|
||||
if (size == 4) {
|
||||
bits = this.api.vbRead(sim, address + 2, 3 /* VB_U16 */);
|
||||
row.bytes.push(bits & 0xFF, bits >> 8);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
// Process subscriptions and send a message to the main thread
|
||||
doSubscriptions(sims) {
|
||||
let message = { subscriptions: {} };
|
||||
let transfers = [];
|
||||
|
||||
// Process all simulations
|
||||
for (let sim of sims) {
|
||||
|
||||
// There are no subscriptions for this sim
|
||||
if (!(sim in this.subscriptions))
|
||||
continue;
|
||||
|
||||
// Working variables
|
||||
let subs = message.subscriptions[sim] = {};
|
||||
|
||||
// Process all subscriptions
|
||||
for (let sub of Object.entries(this.subscriptions[sim])) {
|
||||
|
||||
// Run the command
|
||||
this[sub[1].command](sub[1]);
|
||||
|
||||
// Add the response to the message
|
||||
if (sub[1].response) {
|
||||
subs[sub[0]] = sub[1].response;
|
||||
delete sub[1].response;
|
||||
}
|
||||
|
||||
// Add the transferable objects to the message
|
||||
if (sub[1].transfers) {
|
||||
transfers.push(... sub[1].transfers);
|
||||
delete sub[1].transfers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Send the message to the main thread
|
||||
if (Object.keys(message).length != 0)
|
||||
postMessage(message, transfers);
|
||||
}
|
||||
|
||||
}();
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sim //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// One simulated Virtual Boy
|
||||
class Sim {
|
||||
|
||||
///////////////////////// Initialization Methods //////////////////////////
|
||||
|
||||
constructor(core, sim, index) {
|
||||
this.core = core;
|
||||
this.index = index;
|
||||
this.sim = sim;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Public Methods //////////////////////////////
|
||||
|
||||
// Locate CPU instructions
|
||||
disassemble(target, row, rows, scroll, options = {}) {
|
||||
return this.core.send({
|
||||
command : "disassemble",
|
||||
row : row,
|
||||
rows : rows,
|
||||
scroll : scroll,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe,
|
||||
target : target
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all CPU program registers
|
||||
getProgramRegisters(options = {}) {
|
||||
return this.core.send({
|
||||
command : "getProgramRegisters",
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve the value of a system register
|
||||
getSystemRegister(id, options = {}) {
|
||||
return this.core.send({
|
||||
command : "getSystemRegister",
|
||||
id : id,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all CPU system registers (including PC)
|
||||
getSystemRegisters(options = {}) {
|
||||
return this.core.send({
|
||||
command : "getSystemRegisters",
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Read multiple bytes from the bus
|
||||
read(address, length, options = {}) {
|
||||
return this.core.send({
|
||||
address : address,
|
||||
command : "read",
|
||||
debug : !("debug" in options) || !!options.debug,
|
||||
length : length,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
});
|
||||
}
|
||||
|
||||
// Specify a new value for PC
|
||||
setProgramCounter(value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setProgramCounter",
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Specify a new value for a program register
|
||||
setProgramRegister(id, value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setProgramRegister",
|
||||
id : id,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Specify the current ROM buffer
|
||||
setROM(rom, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setROM",
|
||||
rom : rom,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim
|
||||
}, [ rom.buffer ]);
|
||||
}
|
||||
|
||||
// Specify a new value for a system register
|
||||
setSystemRegister(id, value, options = {}) {
|
||||
return this.core.send({
|
||||
command: "setSystemRegister",
|
||||
id : id,
|
||||
refresh: !!options.refresh,
|
||||
sim : this.sim,
|
||||
value : value
|
||||
});
|
||||
}
|
||||
|
||||
// Ubsubscribe from frame data
|
||||
unsubscribe(key) {
|
||||
return this.core.unsubscribe(key, this.sim);
|
||||
}
|
||||
|
||||
// Write multiple bytes to the bus
|
||||
write(address, bytes, options = {}) {
|
||||
return this.core.send({
|
||||
address : address,
|
||||
command : "write",
|
||||
bytes : bytes,
|
||||
debug : !("debug" in options) || !!options.debug,
|
||||
refresh : !!options.refresh,
|
||||
sim : this.sim,
|
||||
subscribe: options.subscribe
|
||||
},
|
||||
bytes instanceof ArrayBuffer ? [ bytes ] :
|
||||
bytes.buffer instanceof ArrayBuffer ? [ bytes.buffer ] :
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////// Package Methods /////////////////////////////
|
||||
|
||||
// The simulation has been destroyed
|
||||
destroy() {
|
||||
let sim = this.sim;
|
||||
this.core = null;
|
||||
this.sim = 0;
|
||||
return sim;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Sim };
|
||||
Reference in New Issue
Block a user