diff --git a/binutils.mjs b/binutils.mjs
index e4dd990..a2a8001 100644
--- a/binutils.mjs
+++ b/binutils.mjs
@@ -56,7 +56,11 @@ function Hexlify(buf) {
* @returns {Uint8Array} Decoded array
*/
function Unhexlify(str) {
- let a = str.match(/[0-9a-fA-F]{2}/g).map(v => parseInt(v, 16))
+ let a = []
+ let match = str.match(/[0-9a-fA-F]{2}/g) || []
+ for (let m of match) {
+ a.push(parseInt(m, 16))
+ }
return new Uint8Array(a)
}
diff --git a/index.html b/index.html
index f96ca3a..6ab62a3 100644
--- a/index.html
+++ b/index.html
@@ -2,11 +2,12 @@
Triscit
+
-
-
+
+
@@ -26,30 +27,26 @@
-
+
-
-
-
-
-
+
+
+
+
+
diff --git a/triscit.mjs b/triscit.mjs
index 58a3e7e..ba62625 100644
--- a/triscit.mjs
+++ b/triscit.mjs
@@ -86,13 +86,14 @@ class CPU {
Step() {
let prog = this.Program.slice(this.PC)
let inst = disassemble(prog)
+ let flags = this.Flags
this.Flags = 0
switch (inst.name) {
case "JUMP":
this.PC = inst.args[0]
return
case "JNEQ":
- if (this.Flags && FLAG_NEGATIVE) {
+ if (flags && FLAG_NEGATIVE) {
this.PC = inst.args[0]
return
}
@@ -119,15 +120,16 @@ class CPU {
let addr = inst.args[0]
let prog = this.Program.slice(addr)
this.Output = Binutils.CString(prog)
- console.log(addr, this.Output)
break
}
case "CMPS": {
- let a = Binutils.CString(this.Program.slice(inst.args[0]))
- let b = Binutils.CString(this.Program.slice(inst.args[1]))
- if (a == b) {
+ let decoder = new TextDecoder()
+ let a = decoder.decode(Binutils.CString(this.Program.slice(inst.args[0])))
+ let b = decoder.decode(Binutils.CString(this.Program.slice(inst.args[1])))
+ if (a != b) {
this.Flags = FLAG_NEGATIVE
}
+ break
}
case "HALT":
// Don't modify PC
@@ -145,149 +147,8 @@ class CPU {
}
}
-class Triscit {
- constructor(program=[]) {
- this.program = program
- this.Init()
- this.Reset()
- }
-
- Init() {
- for (let e of document.querySelectorAll("[data-control]")) {
- switch (e.dataset.control) {
- case "step":
- e.addEventListener("click", () => this.Step())
- break
- case "back":
- e.addEventListener("click", () => this.Unstep())
- break
- case "reset":
- e.addEventListener("click", () => this.Reset())
- break
- case "input":
- e.addEventListener("input", () => this.SetInput())
- break
- case "program":
- e.addEventListener("input", () => this.SetProgram())
- }
- }
-
- let ih = document.querySelector("#instructions-help")
- for (let i = 0; i < Instructions.length; i++) {
- let inst = Instructions[i]
- let doc = document.querySelector("template#instruction-help").content.cloneNode(true)
- let tr = doc.firstElementChild
- tr.querySelector(".num").textContent = i
- tr.querySelector(".name").textContent = inst.Name
- tr.querySelector(".description").textContent = inst.Description
- ih.appendChild(doc)
- }
-
- this.SetInput()
- this.SetProgram()
- }
-
- Reset() {
- this.cpu = new CPU(this.program, this.input)
- this.stack = []
- this.Refresh()
- }
-
- Refresh() {
- let inste = document.querySelector("#instructions")
- while (inste.firstChild) inste.firstChild.remove()
- for (let i of this.cpu.DisassembleProgram()) {
- let doc = document.querySelector("template#instruction").content.cloneNode(true)
- let tr = doc.firstElementChild
- tr.querySelector(".addr").textContent = i.addr
- tr.querySelector(".name").textContent = i.name
- tr.querySelector(".hex").textContent = Binutils.Hexlify(i.buf)
-
- let args = tr.querySelector(".args")
- for (let a of i.args) {
- args.textContent += `${a} `
- }
-
- if (i.addr == this.cpu.PC) {
- tr.classList.add("is-selected")
- }
- inste.appendChild(tr)
- }
-
- for (let e of document.querySelectorAll("[data-flag]")) {
- let mask = 0
- let className = "is-info"
- switch (e.dataset.flag) {
- case "negative":
- mask = FLAG_NEGATIVE
- break
- case "halt":
- mask = FLAG_HALT
- break
- case "fire":
- mask = FLAG_ABLAZE
- className = "is-danger"
- break
- }
- if (this.cpu.Flags & mask) {
- e.classList.add(className)
- } else {
- e.classList.remove(className)
- }
- }
-
- for (let e of document.querySelectorAll("[data-value]")) {
- switch (e.dataset.value) {
- case "pc":
- e.textContent = this.cpu.PC
- break
- case "output":
- console.log(this.cpu.Output)
- e.value = Binutils.Stringify(this.cpu.Output)
- break
- }
- }
- }
-
- Step() {
- if (this.cpu.Flags & FLAG_HALT) {
- return
- }
- this.stack.push(this.cpu)
- this.cpu = this.cpu.Clone()
- this.cpu.Step()
- this.Refresh()
- }
-
- Unstep() {
- if (this.stack.length == 0) {
- return
- }
- this.cpu = this.stack.pop()
- this.Refresh()
- }
-
- SetProgram() {
- let e =document.querySelector('[data-control="program"]')
- this.program = Binutils.Unhexlify(e.value || "")
- this.Reset()
- }
-
- SetInput() {
- let e = document.querySelector('[data-control="input"]')
- let v = e.value || ""
- this.input = Binutils.Unescape(v)
- this.Reset()
- }
-}
-
-function init() {
- let app = new Triscit()
- window.app = app
-}
-
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", init)
-} else {
- init()
-}
+export {
+ FLAG_NEGATIVE, FLAG_HALT, FLAG_ABLAZE,
+ Instructions,
+ CPU,
+}
\ No newline at end of file
diff --git a/triscit.css b/ui.css
similarity index 71%
rename from triscit.css
rename to ui.css
index b3b48c8..6c1e3d7 100644
--- a/triscit.css
+++ b/ui.css
@@ -1,3 +1,8 @@
+.may-be-tall {
+ overflow-y: scroll;
+ max-height: 80vh;
+}
+
#instructions th.addr {
text-align: right;
color: brown;
diff --git a/ui.mjs b/ui.mjs
new file mode 100644
index 0000000..481de35
--- /dev/null
+++ b/ui.mjs
@@ -0,0 +1,176 @@
+import * as Binutils from "./binutils.mjs"
+import * as Triscit from "./triscit.mjs"
+
+const SamplePrograms = [
+ `05 0d
+61 62 63 64 65 66 67 68 69 6a 00
+02 02
+01 02
+06
+`,
+ `05 31
+ 61 62 63 64 65 66 67 68 69 6a 00
+ 70 61 73 73 77 6f 72 64 00
+ 53 75 70 65 72 20 53 65 63 72 65 74 00
+ 53 6f 72 72 79 2c 20 77 72 6f 6e 67 2e 00
+ 02 02
+ 03 02 0d
+ 04 3b
+ 01 16
+ 06
+ 01 23
+ 06
+`
+]
+
+class UI {
+ constructor(program=[]) {
+ this.program = program
+ this.Init()
+ this.Reset()
+ }
+
+ Init() {
+ for (let e of document.querySelectorAll("[data-control]")) {
+ switch (e.dataset.control) {
+ case "step":
+ e.addEventListener("click", () => this.Step())
+ break
+ case "back":
+ e.addEventListener("click", () => this.Unstep())
+ break
+ case "reset":
+ e.addEventListener("click", () => this.Reset())
+ break
+ case "input":
+ e.addEventListener("input", () => this.SetInput())
+ break
+ case "program":
+ e.addEventListener("input", () => this.SetProgram())
+ }
+ }
+
+ let ih = document.querySelector("#instructions-help")
+ for (let i = 0; i < Triscit.Instructions.length; i++) {
+ let inst = Triscit.Instructions[i]
+ let doc = document.querySelector("template#instruction-help").content.cloneNode(true)
+ let tr = doc.firstElementChild
+ tr.querySelector(".num").textContent = i
+ tr.querySelector(".name").textContent = inst.Name
+ tr.querySelector(".description").textContent = inst.Description
+ ih.appendChild(doc)
+ }
+
+ let fields = new URLSearchParams(window.location.search);
+ let progNumber = fields.get("p") || 0
+ let progElement = document.querySelector('[data-control="program"]')
+ progElement.value = SamplePrograms[progNumber]
+
+ this.SetInput()
+ this.SetProgram()
+ }
+
+ Reset() {
+ this.cpu = new Triscit.CPU(this.program, this.input)
+ this.stack = []
+ this.Refresh()
+ }
+
+ Refresh() {
+ let inste = document.querySelector("#instructions")
+ while (inste.firstChild) inste.firstChild.remove()
+ for (let i of this.cpu.DisassembleProgram()) {
+ let doc = document.querySelector("template#instruction").content.cloneNode(true)
+ let tr = doc.firstElementChild
+ tr.querySelector(".addr").textContent = i.addr
+ tr.querySelector(".name").textContent = i.name
+ tr.querySelector(".hex").textContent = Binutils.Hexlify(i.buf)
+
+ let args = tr.querySelector(".args")
+ for (let a of i.args) {
+ args.textContent += `${a} `
+ }
+
+ inste.appendChild(tr)
+ if (i.addr == this.cpu.PC) {
+ tr.classList.add("is-selected")
+ tr.scrollIntoView(false)
+ }
+ }
+
+ for (let e of document.querySelectorAll("[data-flag]")) {
+ let mask = 0
+ let className = "is-info"
+ switch (e.dataset.flag) {
+ case "negative":
+ mask = Triscit.FLAG_NEGATIVE
+ break
+ case "halt":
+ mask = Triscit.FLAG_HALT
+ break
+ case "fire":
+ mask = Triscit.FLAG_ABLAZE
+ className = "is-danger"
+ break
+ }
+ if (this.cpu.Flags & mask) {
+ e.classList.add(className)
+ } else {
+ e.classList.remove(className)
+ }
+ }
+
+ for (let e of document.querySelectorAll("[data-value]")) {
+ switch (e.dataset.value) {
+ case "pc":
+ e.textContent = this.cpu.PC
+ break
+ case "output":
+ e.textContent = Binutils.Stringify(this.cpu.Output)
+ break
+ }
+ }
+ }
+
+ Step() {
+ if (this.cpu.Flags & Triscit.FLAG_HALT) {
+ return
+ }
+ this.stack.push(this.cpu)
+ this.cpu = this.cpu.Clone()
+ this.cpu.Step()
+ this.Refresh()
+ }
+
+ Unstep() {
+ if (this.stack.length == 0) {
+ return
+ }
+ this.cpu = this.stack.pop()
+ this.Refresh()
+ }
+
+ SetProgram() {
+ let e =document.querySelector('[data-control="program"]')
+ this.program = Binutils.Unhexlify(e.value || "")
+ this.Reset()
+ }
+
+ SetInput() {
+ let e = document.querySelector('[data-control="input"]')
+ let v = e.value || ""
+ this.input = Binutils.Unescape(v)
+ this.Reset()
+ }
+}
+
+function init() {
+ let app = new UI()
+ window.app = app
+}
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init)
+} else {
+ init()
+}