triscit/ui.mjs

203 lines
5.5 KiB
JavaScript

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
`
]
function fill(element, values) {
for (let e of element.querySelectorAll("[data-fill]")) {
e.textContent = values[e.dataset.fill]
}
}
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
fill(tr, {
num: Binutils.Hexlify([i]),
args: inst.Args.join(" "),
name: inst.Name,
description: 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
fill(tr, {
addr: Binutils.Hexlify([i.addr]),
name: i.name,
hex: Binutils.Hexlify(i.buf),
})
let args = tr.querySelector(".args")
for (let a of i.args) {
if (! isNaN(a)) {
a = Binutils.Hexlify([a])
}
args.textContent += `${a} `
}
inste.appendChild(tr)
if (i.addr == this.cpu.PC) {
tr.classList.add("is-selected")
tr.scrollIntoView(false)
// let top = tr.offsetTop
// let parent = tr.parentElement
// tr.parentElement.scrollTop = top
}
}
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 cpuBox of document.querySelectorAll("#cpu-box")) {
if (this.cpu.Flags & Triscit.FLAG_ABLAZE) {
cpuBox.classList.add("ablaze")
} else {
cpuBox.classList.remove("ablaze")
}
}
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()
}