203 lines
5.5 KiB
JavaScript
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()
|
|
}
|