Compare commits
No commits in common. "475c4a90d3c317549c364f4526b2e7e93a291c63" and "b0a3ca6c5bd7a9c8fcdc8f2fa04905aac0a6254a" have entirely different histories.
475c4a90d3
...
b0a3ca6c5b
|
@ -1,92 +0,0 @@
|
||||||
const glyphs = [
|
|
||||||
"\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07",
|
|
||||||
"\\x08", "\\x09", "\\x0A", "\\x0B", "\\x0C", "\\x0D", "\\x0E", "\\x0F",
|
|
||||||
"⏵", "⏴", "↕", "‼", "¶", "§", "‽", "↨", "↑", "↓", "→", "←", "∟", "↔", "⏶", "⏷",
|
|
||||||
" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
|
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
|
|
||||||
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
|
|
||||||
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
|
|
||||||
"`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
|
|
||||||
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "⌂",
|
|
||||||
|
|
||||||
"Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å",
|
|
||||||
"É", "æ", "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£", "¥", "₧", "ƒ",
|
|
||||||
"á", "í", "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "⌐", "¬", "½", "¼", "¡", "«", "»",
|
|
||||||
"░", "▒", "▓", "│", "┤", "╡", "╢", "╖", "╕", "╣", "║", "╗", "╝", "╜", "╛", "┐",
|
|
||||||
"└", "┴", "┬", "├", "─", "┼", "╞", "╟", "╚", "╔", "╩", "╦", "╠", "═", "╬", "╧",
|
|
||||||
"╨", "╤", "╥", "╙", "╘", "╒", "╓", "╫", "╪", "┘", "┌", "█", "▄", "▌", "▐", "▀",
|
|
||||||
"α", "ß", "Γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "∞", "φ", "ε", "∩",
|
|
||||||
"≡", "±", "≥", "≤", "⌠", "⌡", "÷", "≈", "°", "∞", "⊻", "√", "ⁿ", "²", "■", "¤",
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a buffer using fluffy glyphs
|
|
||||||
*
|
|
||||||
* @param {Uint8Array} buf Buffer to encode
|
|
||||||
* @returns {String} Glyph-encoded string
|
|
||||||
*/
|
|
||||||
function Stringify(buf) {
|
|
||||||
let ret = []
|
|
||||||
for (let i of buf) {
|
|
||||||
ret.push(glyphs[i])
|
|
||||||
}
|
|
||||||
return ret.join("")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hex encode a buffer
|
|
||||||
*
|
|
||||||
* @param {Uint8Array} buf Buffer to encode
|
|
||||||
* @returns {String} Hexlified version
|
|
||||||
*/
|
|
||||||
function Hexlify(buf) {
|
|
||||||
let ret = []
|
|
||||||
for (let b of buf) {
|
|
||||||
let hex = "00" + b.toString(16)
|
|
||||||
ret.push(hex.slice(-2))
|
|
||||||
}
|
|
||||||
return ret.join(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hex decode a string
|
|
||||||
*
|
|
||||||
* @param {String} str String to decode
|
|
||||||
* @returns {Uint8Array} Decoded array
|
|
||||||
*/
|
|
||||||
function Unhexlify(str) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a NULL-Terminated string from a buffer.
|
|
||||||
*
|
|
||||||
* @param {Uint8Arrary} buf Buffer to read
|
|
||||||
* @param {Boolean} includeNull True to include the trailing NUL
|
|
||||||
* @returns {Uint8Array} String
|
|
||||||
*/
|
|
||||||
function CString(buf, includeNull=false) {
|
|
||||||
let end = buf.indexOf(0)
|
|
||||||
if (end == -1) {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
return buf.slice(0, end + (includeNull?1:0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unescapes a string with things like "\x02" in it.
|
|
||||||
*
|
|
||||||
* @param {String} str String to unescape
|
|
||||||
* @returns {String} Unescaped string
|
|
||||||
*/
|
|
||||||
function Unescape(str) {
|
|
||||||
return str.replaceAll(/\\x?([0-9]+)/g, (_, p1) => String.fromCharCode(p1))
|
|
||||||
}
|
|
||||||
|
|
||||||
export {Stringify, Hexlify, Unhexlify, CString, Unescape}
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB |
|
@ -1,95 +0,0 @@
|
||||||
---
|
|
||||||
title: Triscit
|
|
||||||
description: A CPU simulator for cybersecurity education
|
|
||||||
type: bare
|
|
||||||
---
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Triscit</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.min.css">
|
|
||||||
|
|
||||||
<script src="ui.mjs" type="module"></script>
|
|
||||||
<link rel="stylesheet" href="ui.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<section class="container">
|
|
||||||
<div class="tile is-ancestor">
|
|
||||||
<div class="tile is-parent">
|
|
||||||
<div class="tile is-child notification" id="cpu-box">
|
|
||||||
<div class="block is-flex is-justify-content-space-between is-align-items-end">
|
|
||||||
<div class="block">
|
|
||||||
<span class="tag is-info">PC=<b data-value="pc"></b></span>
|
|
||||||
<span class="tag" data-flag="negative"><i class="mdi mdi-not-equal-variant"></i></span>
|
|
||||||
<span class="tag" data-flag="halt"><i class="mdi mdi-stop"></i></span>
|
|
||||||
<span class="tag" data-flag="fire"><i class="mdi mdi-fire"></i></span>
|
|
||||||
</div>
|
|
||||||
<div class="controls">
|
|
||||||
<button class="button is-primary" data-control="reset"><i class="mdi mdi-undo"></i></button>
|
|
||||||
<button class="button is-primary" data-control="back"><i class="mdi mdi-skip-previous"></i></button>
|
|
||||||
<button class="button is-primary" data-control="step"><i class="mdi mdi-skip-next"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="table-container">
|
|
||||||
<table class="table is-striped is-hoverable" id="instructions"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tile is-vertical is-parent">
|
|
||||||
<div class="tile is-child notification" id="io-box">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Input</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" data-control="input" placeholder="\x00 style escapes accepted" value="hello">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Output</label>
|
|
||||||
<div class="control">
|
|
||||||
<output class="input" data-value="output"></output>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Program</label>
|
|
||||||
<textarea class="textarea" data-control="program"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tile is-child notification" id="instructions-box">
|
|
||||||
<h2 class="title">Instructions</h2>
|
|
||||||
<div class="table-container">
|
|
||||||
<table class="table is-striped is-hoverable" id="instructions-help"></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<template id="instruction">
|
|
||||||
<tr>
|
|
||||||
<th class="addr has-text-right"><span data-fill="addr"></span></th>
|
|
||||||
<td class="name"><span data-fill="name"></span></td>
|
|
||||||
<td class="args"><span data-fill="args"></span></td>
|
|
||||||
<td class="hex"><span data-fill="hex"></span></td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
<template id="instruction-help">
|
|
||||||
<tr>
|
|
||||||
<td class="name" data-fill="name"></td>
|
|
||||||
<td class="args" data-fill="args"></td>
|
|
||||||
<td class="hex">
|
|
||||||
<span data-fill="num"></span>
|
|
||||||
<span data-fill="args"></span>
|
|
||||||
</td>
|
|
||||||
<td class="description" data-fill="description"></td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,154 +0,0 @@
|
||||||
import * as Binutils from "./binutils.mjs"
|
|
||||||
|
|
||||||
const FLAG_NEGATIVE = 1 << 0
|
|
||||||
const FLAG_HALT = 1 << 1
|
|
||||||
const FLAG_ABLAZE = 1 << 2
|
|
||||||
|
|
||||||
class Instruction {
|
|
||||||
constructor(name, args, description) {
|
|
||||||
this.Name = name
|
|
||||||
this.Args = args
|
|
||||||
this.Description = description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Instructions = [
|
|
||||||
new Instruction("NOOP", [], "Do nothing"),
|
|
||||||
new Instruction("PRNT", ["x"], "Print string at x"),
|
|
||||||
new Instruction("READ", ["x"], "Read input, store in x"),
|
|
||||||
new Instruction("CMPS", ["x", "y"], "Compare string x to string y"),
|
|
||||||
new Instruction("JNEQ", ["x"], "If not equal, set PC to x"),
|
|
||||||
new Instruction("JUMP", ["x"], "Set PC to x"),
|
|
||||||
new Instruction("HALT", [], "Terminate program"),
|
|
||||||
new Instruction("HACF", [], "Halt And Catch Fire (never use this!)"),
|
|
||||||
]
|
|
||||||
|
|
||||||
function disassemble(program) {
|
|
||||||
let b = program[0]
|
|
||||||
let inst = Instructions[b]
|
|
||||||
if (inst) {
|
|
||||||
let nargs = inst.Args.length
|
|
||||||
let args = []
|
|
||||||
for (let i = 0; i < nargs; i++) {
|
|
||||||
args.push(program[i+1])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1+nargs <= program.length) {
|
|
||||||
// Only return an opcode if there was enough data for its arguments as well
|
|
||||||
return {length: 1+nargs, name: inst.Name, args: args}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = Binutils.CString(program, true)
|
|
||||||
let s = Binutils.Stringify(data)
|
|
||||||
return {length: data.length, name: "DATA", args: [s]}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CPU {
|
|
||||||
/**
|
|
||||||
* @param {Uint8Array} program Program to load
|
|
||||||
* @param {string} input input string
|
|
||||||
* @param {Number} pc initial program counter
|
|
||||||
* @param {Number} flags initial flags
|
|
||||||
*/
|
|
||||||
constructor(program, input, output="", pc=0, flags=0) {
|
|
||||||
this.Program = program
|
|
||||||
this.Input = input
|
|
||||||
this.Output = output
|
|
||||||
this.PC = pc
|
|
||||||
this.Flags = flags
|
|
||||||
}
|
|
||||||
|
|
||||||
Clone() {
|
|
||||||
return new CPU(this.Program, this.Input, this.Output, this.PC, this.Flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
DisassembleProgram() {
|
|
||||||
let ret = []
|
|
||||||
let prog1 = this.Program.slice(0, this.PC)
|
|
||||||
let addr = 0
|
|
||||||
for (let prog of [prog1, this.Program]) {
|
|
||||||
while (addr < prog.length) {
|
|
||||||
let subprog = prog.slice(addr)
|
|
||||||
let inst = disassemble(subprog)
|
|
||||||
ret.push({
|
|
||||||
addr,
|
|
||||||
buf: this.Program.slice(addr, addr+inst.length),
|
|
||||||
name: inst.name,
|
|
||||||
args: inst.args,
|
|
||||||
})
|
|
||||||
addr += inst.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (flags && FLAG_NEGATIVE) {
|
|
||||||
this.PC = inst.args[0]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "READ": {
|
|
||||||
let addr = inst.args[0]
|
|
||||||
let b = new TextEncoder().encode(this.Input + "\0")
|
|
||||||
let newproglen = Math.max(this.Program.length, addr+b.length)
|
|
||||||
let newprog = new Uint8Array(newproglen)
|
|
||||||
|
|
||||||
for (let i = 0; i < addr; i++) {
|
|
||||||
newprog[i] = this.Program[i]
|
|
||||||
}
|
|
||||||
for (let i = 0; i < b.length; i++) {
|
|
||||||
newprog[addr+i] = b[i]
|
|
||||||
}
|
|
||||||
for (let i = addr+b.length; i < this.Program.length; i++) {
|
|
||||||
newprog[i] = this.Program[i]
|
|
||||||
}
|
|
||||||
this.Program = newprog
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "PRNT": {
|
|
||||||
let addr = inst.args[0]
|
|
||||||
let prog = this.Program.slice(addr)
|
|
||||||
this.Output = Binutils.CString(prog)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "CMPS": {
|
|
||||||
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
|
|
||||||
this.Flags = FLAG_HALT
|
|
||||||
return
|
|
||||||
case "HACF":
|
|
||||||
this.Flags = FLAG_HALT | FLAG_ABLAZE
|
|
||||||
return
|
|
||||||
case "DATA":
|
|
||||||
// Keep stepping through data one byte at a time trying to execute something
|
|
||||||
inst.length = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
this.PC += inst.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
FLAG_NEGATIVE, FLAG_HALT, FLAG_ABLAZE,
|
|
||||||
Instructions,
|
|
||||||
CPU,
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
|
|
||||||
#instructions th.addr {
|
|
||||||
text-align: right;
|
|
||||||
color: brown;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.name {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.args {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.hex {
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ablaze {
|
|
||||||
background: url(fire.gif) bottom repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
#cpu-box {
|
|
||||||
max-height: 95vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-container {
|
|
||||||
max-height: calc(100% - 4em);
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,206 +0,0 @@
|
||||||
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"]')
|
|
||||||
let prog = SamplePrograms[progNumber]
|
|
||||||
if (prog) {
|
|
||||||
progElement.value = SamplePrograms[progNumber]
|
|
||||||
progElement.parentElement.classList.add("is-hidden")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = Binutils.Hexlify([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()
|
|
||||||
}
|
|
Loading…
Reference in New Issue