Vm working. simple interface built

- still need stack view
- still ned to work with devices
This commit is contained in:
Rachel
2024-03-28 01:31:09 -07:00
parent 3047f774c5
commit baf67b2c6a
25 changed files with 2295 additions and 586 deletions

View File

@@ -36,6 +36,8 @@ ace.config.setModuleLoader('ace/keyboard/sublime', () => import('ace-code/src/ke
ace.config.setModuleLoader('ace/keyboard/vim', () => import('ace-code/src/keyboard/vim.js'));
ace.config.setModuleLoader('ace/keyboard/vscode', () => import('ace-code/src/keyboard/vscode.js'));
ace.config.setModuleLoader('ace/range', () => import('ace-code/src/range'));
console.log("ace module loaders patched");
export { ace };

View File

@@ -3,7 +3,7 @@ import { Mode as IC10Mode } from "./ic10_mode.js";
import * as one_dark from "ace-code/src/theme/one_dark";
import { AceLanguageClient } from "ace-linters/build/ace-language-client";
import { IC10EditorUI } from './ui.js';
import { Range } from 'ace-code/src/range.js';
// to make sure language tools are loaded
import _ace_ext_langue_tools from "ace-code/src/ext/language_tools";
@@ -21,139 +21,173 @@ async function setupLspWorker() {
}
function IC10Editor(session_id) {
this.mode = new IC10Mode()
class IC10Editor {
constructor(session_id) {
window.Editor = this;
this.mode = new IC10Mode();
this.settings = {
keyboard: "ace",
cursor: "ace",
fontSize: 16,
relativeLineNumbers: false,
};
this.settings = {
keyboard: "ace",
cursor: "ace",
fontSize: 16,
relativeLineNumbers: false,
};
this.editor = ace.edit('editor', {
mode: this.mode,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
theme: "ace/theme/one_dark",
fontSize: "16px",
customScrollbar: true,
firstLineNumber: 0,
printMarginColumn: 52,
});
this.aceEditor = ace.edit('editor', {
mode: this.mode,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
theme: "ace/theme/one_dark",
fontSize: "16px",
customScrollbar: false,
firstLineNumber: 0,
printMarginColumn: 52,
});
this.sessions = {};
this.sessions[session_id] = this.editor.getSession();
this.active_session = session_id;
this.bindSession(session_id, this.sessions[session_id]);
this.sessions = {};
this.sessions[session_id] = this.aceEditor.getSession();
this.active_session = session_id;
this.bindSession(session_id, this.sessions[session_id]);
this.active_line_markers = {};
this.active_line_markers[session_id] = null;
this.languageProvider = null;
this.languageProvider = null;
this.ui = new IC10EditorUI(this);
this.ui = new IC10EditorUI(this);
const self = this;
const self = this;
App.session.onLoad((session) => {
const updated_ids = [];
for (const id in session.programs) {
updated_ids.push(id);
self.createOrSetSession(id, session.programs[id]);
App.session.onLoad((session) => {
const updated_ids = [];
for (const id in session.programs) {
updated_ids.push(id);
self.createOrSetSession(id, session.programs[id]);
}
for (const id in self.sessions) {
if (!updated_ids.includes(id)) {
self.destroySession(id);
}
}
});
App.session.loadFromFragment();
App.session.onActiveLine(session => {
for (const id in Object.keys(session.programs)) {
const active_line = session.getActiveLine(id);
if (typeof active_line !== "undefined") {
const marker = self.active_line_markers[id];
if (marker) {
self.sessions[id].removeMarker(marker);
self.active_line_markers[id] = null;
}
self.active_line_markers[id] = self.sessions[id].addMarker(new Range(active_line, 0, active_line, 1), "vm_ic_active_line", "fullLine", true);
if (self.active_session == id) {
// editor.resize(true);
self.aceEditor.scrollToLine(active_line, true, true)
}
}
}
})
}
createOrSetSession(session_id, content) {
if (!this.sessions.hasOwnProperty(session_id)) {
this.newSession(session_id);
}
for (const id in self.sessions) {
if (!updated_ids.includes(id)) {
self.destroySession(id);
this.sessions[session_id].setValue(content);
}
newSession(session_id) {
if (this.sessions.hasOwnProperty(session_id)) {
return false;
}
this.sessions[session_id] = ace.createEditSession("", this.mode);
this.bindSession(session_id, this.sessions[session_id]);
}
setupLsp(lsp_worker) {
const serverData = {
module: () => import("ace-linters/build/language-client"),
modes: "ic10",
type: "webworker",
worker: lsp_worker,
};
// Create a language provider for web worker
this.languageProvider = AceLanguageClient.for(serverData);
this.languageProvider.registerEditor(this.aceEditor);
for (const session_id in this.sessions) {
let options = this.mode.options ?? {};
this.languageProvider.setSessionOptions(this.sessions[session_id], options);
}
}
activateSession(session_id) {
if (!this.sessions.hasOwnProperty(session_id)) {
return false;
}
this.aceEditor.setSession(this.sessions[session_id]);
this.active_session = session_id;
let options = this.mode.options ?? {};
if (this.languageProvider !== null) {
this.languageProvider.setSessionOptions(this.sessions[session_id], options);
}
return true;
}
loadEditorSettings() {
const saved_settings = window.localStorage.getItem("editorSettings");
if (saved_settings !== null && saved_settings.length > 0) {
try {
const saved = JSON.parse(saved_settings);
const temp = Object.assign({}, this.settings, saved);
Object.assign(this.settings, temp);
} catch (e) {
console.log("error loading editor settings", e);
}
}
})
App.session.loadFromFragment();
}
IC10Editor.prototype.createOrSetSession = function(session_id, content) {
if (!this.sessions.hasOwnProperty(session_id)) {
this.newSession(session_id);
}
this.sessions[session_id].setValue(content);
}
IC10Editor.prototype.newSession = function(session_id) {
if (this.sessions.hasOwnProperty(session_id)) {
return false;
}
this.sessions[session_id] = ace.createEditSession("", this.mode);
this.bindSession(session_id, this.sessions[session_id]);
}
IC10Editor.prototype.setupLsp = function(lsp_worker) {
const serverData = {
module: () => import("ace-linters/build/language-client"),
modes: "ic10",
type: "webworker",
worker: lsp_worker,
};
// Create a language provider for web worker
this.languageProvider = AceLanguageClient.for(serverData);
this.languageProvider.registerEditor(this.editor);
for (const session_id in this.sessions) {
let options = this.mode.options ?? {};
this.languageProvider.setSessionOptions(this.sessions[session_id], options);
}
}
IC10Editor.prototype.activateSession = function(session_id) {
if (!this.sessions.hasOwnProperty(session_id)) {
return false;
saveEditorSettings() {
const toSave = JSON.stringify(this.settings);
window.localStorage.setItem("editorSettings", toSave);
}
this.editor.setSession(this.sessions[session_id]);
let options = this.mode.options ?? {};
if (this.languageProvider !== null) {
this.languageProvider.setSessionOptions(this.sessions[session_id], options);
}
return true;
}
IC10Editor.prototype.loadEditorSettings = function() {
const saved_settings = window.localStorage.getItem("editorSettings");
if (saved_settings !== null && saved_settings.length > 0) {
try {
const saved = JSON.parse(saved_settings);
const temp = Object.assign({}, this.settings, saved);
Object.assign(this.settings, temp);
} catch (e) {
console.log("error loading editor settings", e);
destroySession(session_id) {
if (!this.sessions.hasOwnProperty(session_id)) {
return false;
}
if (!(Object.keys(this.sessions).length > 1)) {
return false;
}
const session = this.sessions[session_id];
delete this.sessions[session_id];
if (this.active_session = session_id) {
this.activateSession(Object.keys(this.sessions)[0]);
}
session.destroy();
return true;
}
bindSession(session_id, session) {
session.on('change', () => {
var val = session.getValue();
window.App.session.setProgramCode(session_id, val);
});
}
}
IC10Editor.prototype.saveEditorSettings = function() {
const toSave = JSON.stringify(this.settings);
window.localStorage.setItem("editorSettings", toSave);
}
IC10Editor.prototype.destroySession = function(session_id) {
if (!this.sessions.hasOwnProperty(session_id)) {
return false;
}
if (!(Object.keys(this.sessions).length > 1)) {
return false;
}
const session = this.sessions[session_id];
delete this.sessions[session_id];
if (this.active_session = session_id) {
this.activateSession(Object.keys(this.sessions)[0]);
}
session.destroy();
return true;
}
IC10Editor.prototype.bindSession = function(session_id, session) {
session.on('change', () => {
var val = session.getValue();
window.App.session.setProgramCode(session_id, val);
});
}
export { IC10Editor, setupLspWorker };

View File

@@ -107,9 +107,9 @@ export class AsyncStreamQueue {
async next() {
const done = false;
console.log(`AsyncStream(${this.tag}) waiting for message`)
// console.log(`AsyncStream(${this.tag}) waiting for message`)
const value = await this.dequeue();
console.log(`AsyncStream(${this.tag}) got message`, decoder.decode(value))
// console.log(`AsyncStream(${this.tag}) got message`, decoder.decode(value))
return { done, value };
}

View File

@@ -1,104 +1,111 @@
import { ace } from "./ace";
import { Offcanvas } from 'bootstrap';
function IC10EditorUI(ic10editor) {
class IC10EditorUI {
const self = this;
constructor(ic10editor) {
self.ic10editor = ic10editor;
const self = this;
self.ic10editor.editor.commands.addCommand({
name: "showSettingsMenu",
description: "Show settings menu",
bindKey: { win: "Ctrl-,", mac: "Command-," },
exec: (_editor) => {
const offCanvas = new Offcanvas(document.getElementById("editorSettings"));
offCanvas.toggle();
}
});
self.ic10editor = ic10editor;
ace.config.loadModule("ace/ext/keyboard_menu", function(module) {
console.log("keybinding_menu loaded");
module.init(self.ic10editor.editor);
})
self.ic10editor.aceEditor.commands.addCommand({
name: "showSettingsMenu",
description: "Show settings menu",
bindKey: { win: "Ctrl-,", mac: "Command-," },
exec: (_editor) => {
const offCanvas = new Offcanvas(document.getElementById("editorSettings"));
offCanvas.toggle();
}
});
self.ic10editor.loadEditorSettings();
self.displayEditorSettings();
self.updateEditorSettings();
self.reCalcEditorSize();
window.addEventListener('resize', (e) => { self.reCalcEditorSize() });
ace.config.loadModule("ace/ext/keyboard_menu", function (module) {
console.log("keybinding_menu loaded");
module.init(self.ic10editor.aceEditor);
});
document.getElementsByName("editorKeybindRadio").forEach((el) => {
el.addEventListener('change', (e) => {
self.ic10editor.settings.keyboard = e.target.value;
self.ic10editor.loadEditorSettings();
self.displayEditorSettings();
self.updateEditorSettings();
self.reCalcEditorSize();
window.addEventListener('resize', (e) => { self.reCalcEditorSize(); });
document.getElementsByName("editorKeybindRadio").forEach((el) => {
el.addEventListener('change', (e) => {
self.ic10editor.settings.keyboard = e.target.value;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
});
});
document.getElementsByName("editorCursorRadio").forEach((el) => {
el.addEventListener('change', (e) => {
self.ic10editor.settings.cursor = e.target.value;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
});
});
document.getElementById("editorSettingsFontSize").addEventListener('change', (e) => {
window.App.editorSettings.fontSize = e.target.value;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
})
});
document.getElementsByName("editorCursorRadio").forEach((el) => {
el.addEventListener('change', (e) => {
self.ic10editor.settings.cursor = e.target.value;
});
document.getElementById("editorSettingsRelativeLineNumbers").addEventListener('change', (e) => {
window.App.editorSettings.relativeLineNumbers = e.target.checked;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
})
});
document.getElementById("editorSettingsFontSize").addEventListener('change', (e) => {
window.App.editorSettings.fontSize = e.target.value;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
});
document.getElementById("editorSettingsRelativeLineNumbers").addEventListener('change', (e) => {
window.App.editorSettings.relativeLineNumbers = e.target.checked;
self.ic10editor.saveEditorSettings();
self.updateEditorSettings();
})
});
console.log(self.ic10editor.editor.getOption('keyboardHandler'));
console.log(self.ic10editor.aceEditor.getOption('keyboardHandler'));
self.ic10editor.editor.setTheme("ace/theme/one_dark");
ace.config.loadModule("ace/ext/statusbar", function(module) {
const statusBar = new module.StatusBar(self.ic10editor.editor, document.getElementById("statusBar"));
statusBar.updateStatus(self.ic10editor.editor);
})
self.ic10editor.aceEditor.setTheme("ace/theme/one_dark");
ace.config.loadModule("ace/ext/statusbar", function (module) {
const statusBar = new module.StatusBar(self.ic10editor.aceEditor, document.getElementById("statusBar"));
statusBar.updateStatus(self.ic10editor.aceEditor);
});
self.ic10editor.editor.setAutoScrollEditorIntoView(true);
}
self.ic10editor.aceEditor.setAutoScrollEditorIntoView(true);
IC10EditorUI.prototype.updateEditorSettings = function() {
const settings = this.ic10editor.settings;
const editor = this.ic10editor.editor;
if (settings.keyboard === 'ace') {
editor.setOption('keyboardHandler', null);
} else {
editor.setOption('keyboardHandler', `ace/keyboard/${settings.keyboard}`);
}
editor.setOption('cursorStyle', settings.cursor);
editor.setOption('fontSize', `${settings.fontSize}px`);
editor.setOption('relativeLineNumbers', settings.relativeLineNumbers);
updateEditorSettings() {
const settings = this.ic10editor.settings;
const editor = this.ic10editor.aceEditor;
if (settings.keyboard === 'ace') {
editor.setOption('keyboardHandler', null);
} else {
editor.setOption('keyboardHandler', `ace/keyboard/${settings.keyboard}`);
}
editor.setOption('cursorStyle', settings.cursor);
editor.setOption('fontSize', `${settings.fontSize}px`);
editor.setOption('relativeLineNumbers', settings.relativeLineNumbers);
}
displayEditorSettings() {
const settings = this.ic10editor.settings;
document.getElementsByName("editorKeybindRadio").forEach((el) => {
el.checked = el.value === settings.keyboard;
});
document.getElementsByName("editorCursorRadio").forEach((el) => {
el.checked = el.value === settings.cursor;
});
document.getElementById("editorSettingsFontSize").value = settings.fontSize;
document.getElementById("editorSettingsRelativeLineNumbers").checked = settings.relativeLineNumbers;
}
reCalcEditorSize() {
const editor = this.ic10editor.aceEditor;
const navBar = document.getElementById("navBar");
const statusBarContainer = document.getElementById("statusBarContainer");
const correction = navBar.offsetHeight + statusBarContainer.offsetHeight;
const editorContainer = document.getElementById("editor");
editorContainer.style.height = `calc( 100vh - ${correction}px - 0.5rem)`;
editor.resize(true);
}
}
IC10EditorUI.prototype.displayEditorSettings = function() {
const settings = this.ic10editor.settings;
document.getElementsByName("editorKeybindRadio").forEach((el) => {
el.checked = el.value === settings.keyboard;
});
document.getElementsByName("editorCursorRadio").forEach((el) => {
el.checked = el.value === settings.cursor;
});
document.getElementById("editorSettingsFontSize").value = settings.fontSize;
document.getElementById("editorSettingsRelativeLineNumbers").checked = settings.relativeLineNumbers;
}
IC10EditorUI.prototype.reCalcEditorSize = function() {
const editor = this.ic10editor.editor;
const navBar = document.getElementById("navBar");
const statusBarContainer = document.getElementById("statusBarContainer");
const correction = navBar.offsetHeight + statusBarContainer.offsetHeight;
const editorContainer = document.getElementById("editor");
editorContainer.style.height = `calc( 100vh - ${correction}px - 0.5rem)`;
editor.resize(true);
}
export { IC10EditorUI };

View File

@@ -1,10 +1,10 @@
import { init } from "ic10emu_wasm";
import { IC10Editor, setupLspWorker } from "./editor";
import { Session } from './session';
import { VirtualMachine } from "./virtual_machine";
const App = {
editor: null,
vm: null,
session: new Session()
};
@@ -20,10 +20,10 @@ function docReady(fn) {
}
}
init();
docReady(() => {
App.vm = new VirtualMachine();
App.editor = new IC10Editor();
setupLspWorker().then((worker) => {

View File

@@ -37,7 +37,7 @@ move r2 100000.001
# to get their documentation
# vvvvvvvvvvvvvvv
move r0 HASH("AccessCardBlack")
beqz r1 test
beqzal r1 test
# -2045627372 is the crc32 hash of a SolarPanel,
# hover it to see the documentation!
@@ -45,7 +45,7 @@ beqz r1 test
move r1 -2045627372
jal test
move r1 $FF
beqz 0 test
beqzal 0 test
move r1 %1000
yield
j main
@@ -61,6 +61,9 @@ class Session {
this._programs = {};
this._save_timeout = 0;
this._onLoadCallbacks = [];
this._activeSession = 0;
this._activeLines = {};
this._onActiveLineCallbacks = [];
this.loadFromFragment();
const self = this;
@@ -77,6 +80,23 @@ class Session {
Object.assign(this._programs, programs);
}
get activeSession() {
return this._activeSession;
}
getActiveLine(id) {
return this._activeLines[id];
}
setActiveLine(id, line) {
this._activeLines[id] = line;
this._fireOnActiveLine();
}
set activeLine(line) {
this._activeLine = line;
}
setProgramCode(id, code) {
this._programs[id] = code;
this.save();
@@ -93,10 +113,25 @@ class Session {
}
}
onActiveLine(callback) {
this._onActiveLineCallbacks.push(callback);
}
_fireOnActiveLine() {
for (const i in this._onActiveLineCallbacks) {
const callback = this._onActiveLineCallbacks[i];
callback(this);
}
}
save() {
if (this._save_timeout) clearTimeout(this._save_timeout);
this._save_timeout = setTimeout(() => {
this.saveToFragment();
if (window.App.vm) {
window.App.vm.updateCode();
}
}, 1000);
}

View File

@@ -0,0 +1,201 @@
import { init } from "ic10emu_wasm";
// import { Card } from 'bootstrap';
class VirtualMachine {
constructor() {
const vm = init();
window.VM = this;
this.ic10vm = vm;
this.ui = new VirtualMachineUI(this);
this.ics = {}
const ics = this.ic10vm.ics;
for (const id of Object.keys(ics)) {
this.ics[id] = this.ic10vm.getDevice(parseInt(id));
}
this.updateCode()
}
updateCode() {
const progs = window.App.session.programs;
for (const id of Object.keys(progs)) {
const ic = this.ics[id];
const prog = progs[id];
if (ic && prog) {
console.time(`CompileProgram_${id}`);
this.ics[id].setCode(progs[id]);
console.timeEnd(`CompileProgram_${id}`);
}
}
this.update();
}
step() {
const ic = this.ics[window.App.session.activeSession];
if (ic) {
try {
ic.step();
} catch (e) {
console.log(e);
}
this.update();
}
}
run() {
const ic = this.ics[window.App.session.activeSession];
if (ic) {
try {
ic.run(false);
} catch (e) {
console.log(e);
}
this.update();
}
}
reset() {
const ic = this.ics[window.App.session.activeSession];
if (ic) {
ic.reset();
this.update();
}
}
update() {
const ic = this.ics[window.App.session.activeSession];
window.App.session.setActiveLine(window.App.session.activeSession, ic.ip);
this.ui.update(ic);
}
}
class VirtualMachineUI {
constructor(vm) {
this.vm = vm
this.state = new VMStateUI();
this.registers = new VMRegistersUI();
this.buildStackDisplay();
const self = this;
document.getElementById("vmControlRun").addEventListener('click', (_event) => {
self.vm.run();
}, { capture: true });
document.getElementById("vmControlStep").addEventListener('click', (_event) => {
self.vm.step();
}, { capture: true });
document.getElementById("vmControlReset").addEventListener('click', (_event) => {
self.vm.reset();
}, { capture: true });
}
update(ic) {
this.state.update(ic);
this.registers.update(ic);
}
buildStackDisplay() {
}
}
class VMStateUI {
constructor() {
const stateDom = document.getElementById("vmActiveICState");
this.tbl = document.createElement("table");
this.tbl.classList.add("table");
this.ipRow = this.tbl.insertRow();
this.counterRow = this.tbl.insertRow()
this.stateRow = this.tbl.insertRow()
const ipTh = document.createElement("th");
ipTh.appendChild(document.createTextNode("Instruction Pointer"));
this.ipRow.appendChild(ipTh);
this.instructionPointer = this.ipRow.insertCell();
const conuterTh = document.createElement("th");
conuterTh.appendChild(document.createTextNode("Last Run Operations"));
this.counterRow.appendChild(conuterTh);
this.instructionCounter = this.counterRow.insertCell();
const stateTh = document.createElement("th");
stateTh.appendChild(document.createTextNode("Last State"));
this.stateRow.appendChild(stateTh);
this.lastState = this.stateRow.insertCell();
stateDom.appendChild(this.tbl);
}
update(ic) {
if (ic) {
this.instructionPointer.innerText = ic.ip.toString();
this.instructionCounter.innerText = ic.instructionCount.toString();
this.lastState.innerText = ic.state.toString();
}
}
}
class VMRegistersUI {
constructor() {
const regDom = document.getElementById("vmActiveRegisters");
this.tbl = document.createElement("div");
this.tbl.classList.add("d-flex", "flex-wrap", "justify-content-start", "align-items-start", "align-self-center");
this.regCels = [];
for (var i = 0; i < 18; i++) {
const container = document.createElement("div");
container.classList.add("vm_reg_cel");
const cell = document.createElement("div");
cell.classList.add("input-group", "input-group-sm")
// cell.style.width = "30%";
const nameLabel = document.createElement("span");
nameLabel.innerText = `r${i}`;
nameLabel.classList.add("input-group-text")
cell.appendChild(nameLabel);
const input = document.createElement("input");
input.type = "text"
input.value = 0;
// input.size = 3;
// input.style.width = 40;
cell.appendChild(input);
const aliasesLabel = document.createElement("span");
aliasesLabel.classList.add("input-group-text")
aliasesLabel.innerText = "\xa0";
cell.appendChild(aliasesLabel);
if (i == 16 ) {
aliasesLabel.innerText = "sp";
} else if (i == 17) {
aliasesLabel.innerText = "ra";
}
this.regCels.push({
cell,
nameLabel,
aliasesLabel,
input,
});
container.appendChild(cell);
this.tbl.appendChild(container);
}
regDom.appendChild(this.tbl);
}
update(ic) {
if (ic) {
const registers = ic.registers;
if (registers) {
console.log(registers)
for (var i = 0; i < registers.length; i++) {
this.regCels[i].input.value = registers[i];
}
}
}
}
}
export { VirtualMachine }