add assembly view to web
This commit is contained in:
parent
dbc2a4d902
commit
21b3c21170
8
Makefile
8
Makefile
@ -7,13 +7,15 @@ cli: src/cli.c
|
|||||||
gcc -o build/cli.exe src/cli.c $(CFLAGS)
|
gcc -o build/cli.exe src/cli.c $(CFLAGS)
|
||||||
|
|
||||||
web: src/web.c
|
web: src/web.c
|
||||||
rm -rf build/web
|
|
||||||
mkdir -p build/web
|
mkdir -p build/web
|
||||||
emcc -o build/web/sim8086.js src/web.c --no-entry -sEXPORTED_RUNTIME_METHODS=cwrap,AsciiToString -sEXPORTED_FUNCTIONS=_free,_malloc $(CFLAGS)
|
emcc -o build/web/sim8086.js src/web.c --no-entry -sEXPORTED_RUNTIME_METHODS=cwrap,AsciiToString -sEXPORTED_FUNCTIONS=_free,_malloc $(CFLAGS)
|
||||||
cp -r src/web/* build/web
|
cp -r src/web/* build/web
|
||||||
|
|
||||||
serve-web:
|
serve-web: web
|
||||||
cd build/web && python -m http.server
|
cd build/web && python -m http.server
|
||||||
|
|
||||||
|
watch-web:
|
||||||
|
live-server build/web --wait=250
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r build
|
rm -r build
|
||||||
|
38
src/web.c
38
src/web.c
@ -1,4 +1,5 @@
|
|||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
#include <emscripten/emscripten.h>
|
||||||
|
|
||||||
#define SIM8086_EMCC
|
#define SIM8086_EMCC
|
||||||
#include "sim8086/prelude.h"
|
#include "sim8086/prelude.h"
|
||||||
@ -8,29 +9,44 @@
|
|||||||
|
|
||||||
EXPORT struct memory memory_state;
|
EXPORT struct memory memory_state;
|
||||||
EXPORT struct cpu_state cpu_state;
|
EXPORT struct cpu_state cpu_state;
|
||||||
EXPORT struct instruction current_instruction;
|
|
||||||
|
EXPORT void step() {
|
||||||
|
struct instruction inst;
|
||||||
|
enum decode_error err = decode_instruction(&memory_state, &cpu_state.ip, &inst);
|
||||||
|
if (err == DECODE_OK) {
|
||||||
|
execute_instruction(&memory_state, &cpu_state, &inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void reset_cpu() {
|
||||||
|
memset(&cpu_state, 0, sizeof(cpu_state));
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------- Decoder ----------------------- */
|
/* -------------------- Decoder ----------------------- */
|
||||||
|
|
||||||
EXPORT int decode_inst_at_ip() {
|
EXPORT u16 decode_inst_at(u16 addr, char *buff, size_t buff_size) {
|
||||||
return decode_instruction(&memory_state, &cpu_state.ip, ¤t_instruction);
|
struct instruction inst;
|
||||||
}
|
u16 after_addr = addr;
|
||||||
|
enum decode_error err = decode_instruction(&memory_state, &after_addr, &inst);
|
||||||
EXPORT void get_inst_str(char *buff, size_t buff_size) {
|
if (err == DECODE_OK) {
|
||||||
instruction_to_str(buff, buff_size, ¤t_instruction);
|
instruction_to_str(buff, buff_size, &inst);
|
||||||
|
return after_addr - addr;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- Memory ----------------------- */
|
/* -------------------- Memory ----------------------- */
|
||||||
|
|
||||||
EXPORT int load_to_memory_state(u8 *assembly, u16 assembly_size, u16 start) {
|
EXPORT int set_memory_state(u8 *buffer, u16 buffer_size, u16 start) {
|
||||||
return load_mem_from_buff(&memory_state, assembly, assembly_size, start);
|
return load_mem_from_buff(&memory_state, buffer, buffer_size, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT u8 *get_memory_state_base() {
|
EXPORT u8 *get_memory_state_base() {
|
||||||
return memory_state.mem;
|
return memory_state.mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT size_t get_memory_state_size(u8 *assembly, u16 assembly_size, u16 start) {
|
EXPORT size_t get_memory_state_size() {
|
||||||
return MEMORY_SIZE;
|
return MEMORY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,4 +79,4 @@ EXPORT bool cpu_get_zero_flag()
|
|||||||
EXPORT bool cpu_get_sign_flag()
|
EXPORT bool cpu_get_sign_flag()
|
||||||
{
|
{
|
||||||
return cpu_state.flags.sign;
|
return cpu_state.flags.sign;
|
||||||
}
|
}
|
||||||
|
38
src/web/api.js
Normal file
38
src/web/api.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
const registers = {}
|
||||||
|
for (const reg of ["ax", "bx", "cx", "dx", "sp", "bp", "si", "di", "ip"]) {
|
||||||
|
registers[reg] = {
|
||||||
|
set: Module.cwrap(`cpu_set_${reg}`, null, ["number"]),
|
||||||
|
get: Module.cwrap(`cpu_get_${reg}`, "number", [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodeInstAt = Module.cwrap("decode_inst_at", null, ["number", "number", "number"])
|
||||||
|
function getCurrentInstructionAt(address) {
|
||||||
|
let text = undefined
|
||||||
|
const inst = Module._malloc(64)
|
||||||
|
const size = decodeInstAt(address, inst, 64)
|
||||||
|
if (size > 0) {
|
||||||
|
text = Module.AsciiToString(inst)
|
||||||
|
}
|
||||||
|
Module._free(inst)
|
||||||
|
return [text, size]
|
||||||
|
}
|
||||||
|
|
||||||
|
const setMemoryState = Module.cwrap("set_memory_state", null, ["array", "number", "number"])
|
||||||
|
function setMemoryAt(address, value) {
|
||||||
|
setMemoryBufferAt(address, [value])
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMemoryBufferAt(address, buffer) {
|
||||||
|
setMemoryState(buffer, buffer.length, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMemoryBaseAddress = Module.cwrap("get_memory_state_base", null, [])
|
||||||
|
const getMemorySize = Module.cwrap("get_memory_state_size", null, [])
|
||||||
|
function getMemory() {
|
||||||
|
return new Uint8Array(wasmMemory.buffer, getMemoryBaseAddress(), getMemorySize())
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetCPU = Module.cwrap("reset_cpu", null, [])
|
||||||
|
const stepCPU = Module.cwrap("step", null, [])
|
38
src/web/assembly-view.js
Normal file
38
src/web/assembly-view.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
class AssemblyViewElement extends HTMLElement {
|
||||||
|
assemblySize = 0
|
||||||
|
startAddress = 0
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.classList.add("flex")
|
||||||
|
this.addressColumn = document.createElement("div")
|
||||||
|
this.addressColumn.classList.add("line_numbers")
|
||||||
|
this.assemblyColumn = document.createElement("div")
|
||||||
|
this.assemblyColumn.classList.add("assembly_code")
|
||||||
|
this.appendChild(this.addressColumn)
|
||||||
|
this.appendChild(this.assemblyColumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const lineNumbers = []
|
||||||
|
const assemblyLines = []
|
||||||
|
|
||||||
|
let currentAddress = this.startAddress
|
||||||
|
for (var i = 0; i < 1024; i++) {
|
||||||
|
const assemblyDiv = document.createElement("div")
|
||||||
|
let [assemblyStr, assemblySize] = getCurrentInstructionAt(currentAddress)
|
||||||
|
assemblyDiv.textContent = assemblyStr
|
||||||
|
assemblyLines.push(assemblyDiv)
|
||||||
|
|
||||||
|
const addressDiv = document.createElement("div")
|
||||||
|
addressDiv.textContent = "0x" + currentAddress.toString(16).padStart(4, "0")
|
||||||
|
lineNumbers.push(addressDiv)
|
||||||
|
|
||||||
|
currentAddress += assemblySize
|
||||||
|
if (currentAddress >= this.assemblySize) break;
|
||||||
|
}
|
||||||
|
this.addressColumn.replaceChildren(...lineNumbers)
|
||||||
|
this.assemblyColumn.replaceChildren(...assemblyLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("assembly-view", AssemblyViewElement)
|
@ -17,209 +17,179 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--srcery-black: #1C1B19;
|
||||||
|
--srcery-red: #EF2F27;
|
||||||
|
--srcery-green: #519F50;
|
||||||
|
--srcery-yellow: #FBB829;
|
||||||
|
--srcery-blue: #2C78BF;
|
||||||
|
--srcery-magenta: #E02C6D;
|
||||||
|
--srcery-cyan: #0AAEB3;
|
||||||
|
--srcery-white: #BAA67F;
|
||||||
|
--srcery-bright-black: #918175;
|
||||||
|
--srcery-bright-red: #F75341;
|
||||||
|
--srcery-bright-green: #98BC37;
|
||||||
|
--srcery-bright-yellow: #FED06E;
|
||||||
|
--srcery-bright-blue: #68A8E4;
|
||||||
|
--srcery-bright-magenta: #FF5C8F;
|
||||||
|
--srcery-bright-cyan: #2BE4D0;
|
||||||
|
--srcery-bright-white: #FCE8C3;
|
||||||
|
--srcery-orange: #FF5F00;
|
||||||
|
--srcery-bright-orange: #FF8700;
|
||||||
|
--srcery-teal: #008080;
|
||||||
|
--srcery-hard-black: #121212;
|
||||||
|
--srcery-xgray1: #262626;
|
||||||
|
--srcery-xgray2: #303030;
|
||||||
|
--srcery-xgray3: #3A3A3A;
|
||||||
|
--srcery-xgray4: #444444;
|
||||||
|
--srcery-xgray5: #4E4E4E;
|
||||||
|
--srcery-xgray6: #585858;
|
||||||
|
--srcery-xgray7: #626262;
|
||||||
|
--srcery-xgray8: #6C6C6C;
|
||||||
|
--srcery-xgray9: #767676;
|
||||||
|
--srcery-xgray10: #808080;
|
||||||
|
--srcery-xgray11: #8A8A8A;
|
||||||
|
--srcery-xgray12: #949494;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.register {
|
register-field {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
background: #AAA;
|
||||||
background: #AAA;
|
display: flex;
|
||||||
display: flex;
|
gap: 0.25rem;
|
||||||
gap: 0.25rem;
|
}
|
||||||
}
|
register-field .name,
|
||||||
.register__name, .register__value {
|
register-field .value {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: #BBB;
|
background: #BBB;
|
||||||
}
|
}
|
||||||
.register__value {
|
register-field .value {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0.25ch 1ch;
|
padding: 0.25ch 1ch;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
.register__value--byte {
|
register-field .value--byte {
|
||||||
width: 8ch;
|
width: 8ch;
|
||||||
}
|
}
|
||||||
.register__name {
|
register-field .name {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
min-width: 4ch
|
min-width: 4ch
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style>
|
||||||
|
assembly-view {
|
||||||
|
}
|
||||||
|
assembly-view .line_numbers * {
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
assembly-view .assembly_code {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
assembly-view .assembly_code * {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
assembly-view .line_numbers :nth-child(even) {
|
||||||
|
background: red
|
||||||
|
}
|
||||||
|
assembly-view .assembly_code :nth-child(even) {
|
||||||
|
background: red
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="h-screen font-serif">
|
<body class="h-screen font-serif">
|
||||||
<main class="bg-red-500 max-w-screen-md m-auto h-full p-5 gap-3 flex flex-col">
|
<main class="bg-red-500 max-w-screen-md m-auto h-full p-5 gap-3 flex flex-col">
|
||||||
<h1 class="font-bold text-4xl m-auto text-center grow-0">sim8086.wasm</h1>
|
<h1 class="font-bold text-4xl m-auto text-center grow-0">sim8086.wasm</h1>
|
||||||
<div class="flex flex-col min-h-max grow gap-4">
|
<div class="flex flex-col h-full gap-4">
|
||||||
<div class="bg-green-500">a</div>
|
<div class="bg-green-500">
|
||||||
<div class="bg-blue-500 grow flex flex-row gap-4">
|
<button onclick="sim8086_load()">load</button>
|
||||||
<div class="bg-green-500 grow">
|
<button onclick="sim8086_step()">step</button>
|
||||||
|
<button onclick="sim8086_run()">run</button>
|
||||||
</div>
|
<button onclick="sim8086_reset()">reset</button>
|
||||||
<div class="bg-green-500 grow flex flex-row basis-1/4">
|
<button onclick="sim8086_cycle_reg()">display (decimal)</button>
|
||||||
<div class="bg-cyan-500 flex flex-col gap-1">
|
</div>
|
||||||
<register-field reg="ax" has-low-high></register-field>
|
<div class="bg-blue-500 flex gap-4">
|
||||||
<register-field reg="bx" has-low-high></register-field>
|
<assembly-view class="bg-green-500 grow"></assembly-view>
|
||||||
<register-field reg="cx" has-low-high></register-field>
|
<div class="bg-cyan-500 flex flex-col gap-1 basis-1/4">
|
||||||
<register-field reg="dx" has-low-high></register-field>
|
<register-field reg="ax" has-low-high></register-field>
|
||||||
<register-field reg="sp"></register-field>
|
<register-field reg="bx" has-low-high></register-field>
|
||||||
<register-field reg="bp"></register-field>
|
<register-field reg="cx" has-low-high></register-field>
|
||||||
<register-field reg="si"></register-field>
|
<register-field reg="dx" has-low-high></register-field>
|
||||||
<register-field reg="di"></register-field>
|
<register-field reg="sp"></register-field>
|
||||||
<register-field reg="ip" readonly></register-field>
|
<register-field reg="bp"></register-field>
|
||||||
<register-input reg="ax" position="low"> </register-input>
|
<register-field reg="si"></register-field>
|
||||||
</div>
|
<register-field reg="di"></register-field>
|
||||||
<div class="bg-cyan-500">
|
<register-field reg="ip" readonly></register-field>
|
||||||
</div>
|
<register-input reg="ax" position="low"> </register-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-green-500 grow basis-1/4"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="sim8086.js"></script>
|
<script src="sim8086.js"></script>
|
||||||
|
<script src="api.js"></script>
|
||||||
|
<script src="register-field.js"></script>
|
||||||
|
<script src="assembly-view.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const registers = {}
|
let assembly = [
|
||||||
for (const reg of ["ax", "bx", "cx", "dx", "sp", "bp", "si", "di", "ip"]) {
|
184, 1, 0, 187, 2, 0, 185, 3, 0, 186, 4, 0, 137, 196, 137, 221, 137, 206, 137, 215, 137, 226, 137, 233, 137, 243, 137, 248
|
||||||
registers[reg] = {
|
]
|
||||||
set: Module.cwrap(`cpu_set_${reg}`, null, ["number"]),
|
const assemblyView = document.getElementsByTagName("assembly-view")[0]
|
||||||
get: Module.cwrap(`cpu_get_${reg}`, "number", [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RegisterInputElement extends HTMLInputElement {
|
|
||||||
/** @type {"low"|"high"|"full"} */
|
|
||||||
position = "full"
|
|
||||||
reg = "<unknown>"
|
|
||||||
/** @type {"hex"|"decimal"|"binary"} */
|
|
||||||
format = "decimal"
|
|
||||||
validFormatSymbols = {
|
|
||||||
hex: "0123456789abcdef",
|
|
||||||
decimal: "0123456789",
|
|
||||||
binary: "01"
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange(e) {
|
|
||||||
let parsed = parseInt(this.value, this.validFormatSymbols[this.format].length)
|
|
||||||
if (parsed == NaN) {
|
|
||||||
registers[this.reg].set(0)
|
|
||||||
} else {
|
|
||||||
registers[this.reg].set(parsed)
|
|
||||||
}
|
|
||||||
this.render()
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyPress(e) {
|
|
||||||
if (!this.validFormatSymbols[this.format].includes(e.key.toLocaleLowerCase())) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.reg = this.getAttribute("reg").toLocaleLowerCase()
|
|
||||||
this.position = this.getAttribute("position")
|
|
||||||
this.classList.add("register__value")
|
|
||||||
if (this.position !== "full") {
|
|
||||||
this.classList.add("register__value--byte")
|
|
||||||
}
|
|
||||||
this.addEventListener("change", this.onChange)
|
|
||||||
this.addEventListener("keypress", this.onKeyPress)
|
|
||||||
}
|
|
||||||
getRegValue() {
|
|
||||||
const value = registers[this.reg].get()
|
|
||||||
switch (this.position) {
|
|
||||||
case "full": return value & 0xFFFF
|
|
||||||
case "low": return value & 0xFF
|
|
||||||
case "high": return (value >> 8) & 0xFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const value = this.getRegValue()
|
|
||||||
if (this.format == "hex") {
|
|
||||||
this.value = value.toString(16).padStart(this.position == "full" ? 4 : 2, "0")
|
|
||||||
} else if (this.format == "decimal") {
|
|
||||||
this.value = value
|
|
||||||
} else if (this.format == "binary") {
|
|
||||||
this.value = value.toString(2).padStart(this.position == "full" ? 16 : 8, "0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("register-input", RegisterInputElement, { extends: "input" })
|
|
||||||
|
|
||||||
class RegisterElement extends HTMLElement {
|
|
||||||
reg = "<unknown>"
|
|
||||||
readonly = false
|
|
||||||
hasHighLow = false
|
|
||||||
|
|
||||||
/** @type {RegisterInputElement} */
|
|
||||||
valueElement = undefined
|
|
||||||
/** @type {RegisterInputElement} */
|
|
||||||
highByteElement = undefined
|
|
||||||
/** @type {RegisterInputElement} */
|
|
||||||
lowByteElement = undefined
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.readonly = this.getAttribute("readonly") !== null
|
|
||||||
this.reg = this.getAttribute("reg").toLocaleLowerCase()
|
|
||||||
this.hasHighLow = this.getAttribute("has-low-high") !== null
|
|
||||||
this.classList.add("register")
|
|
||||||
|
|
||||||
const nameElement = this.appendChild(document.createElement("span"))
|
|
||||||
nameElement.classList.add("register__name")
|
|
||||||
nameElement.innerText = this.reg.toUpperCase()
|
|
||||||
|
|
||||||
if (this.hasHighLow) {
|
|
||||||
const highByteElem = document.createElement("input", { is: "register-input" })
|
|
||||||
highByteElem.setAttribute("reg", this.reg)
|
|
||||||
highByteElem.setAttribute("position", "high")
|
|
||||||
if (this.readonly) highByteElem.setAttribute("readonly", "")
|
|
||||||
this.highByteElement = this.appendChild(highByteElem)
|
|
||||||
|
|
||||||
const lowByteElem = document.createElement("input", { is: "register-input" })
|
|
||||||
lowByteElem.setAttribute("reg", this.reg)
|
|
||||||
lowByteElem.setAttribute("position", "low")
|
|
||||||
if (this.readonly) lowByteElem.setAttribute("readonly", "")
|
|
||||||
this.lowByteElement = this.appendChild(lowByteElem)
|
|
||||||
} else {
|
|
||||||
const elem = document.createElement("input", { is: "register-input" })
|
|
||||||
elem.setAttribute("reg", this.reg)
|
|
||||||
elem.setAttribute("position", "full")
|
|
||||||
if (this.readonly) elem.setAttribute("readonly", "")
|
|
||||||
this.valueElement = this.appendChild(elem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.hasHighLow) {
|
|
||||||
this.lowByteElement.render()
|
|
||||||
this.highByteElement.render()
|
|
||||||
} else {
|
|
||||||
this.valueElement.render()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("register-field", RegisterElement)
|
|
||||||
|
|
||||||
const assembly = [137, 217]
|
|
||||||
const decodeInstructionAtIP = Module.cwrap("decode_inst_at_ip", "number", [])
|
|
||||||
const loadAsmToMemory = Module.cwrap("load_to_memory_state", null, ["array", "number", "number"])
|
|
||||||
const getInstructionString = Module.cwrap("get_inst_str", null, ["number", "number"])
|
|
||||||
|
|
||||||
Module.onRuntimeInitialized = () => {
|
|
||||||
loadAsmToMemory(assembly, assembly.length, 0)
|
|
||||||
console.log(decodeInstructionAtIP())
|
|
||||||
console.log(getCurrentInstruction())
|
|
||||||
|
|
||||||
|
function renderAllRegisters() {
|
||||||
for (const elem of document.getElementsByTagName("register-field")) {
|
for (const elem of document.getElementsByTagName("register-field")) {
|
||||||
elem.render()
|
elem.render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentInstruction() {
|
function sim8086_cycle_reg() {
|
||||||
const inst = Module._malloc(64)
|
}
|
||||||
getInstructionString(inst, 64)
|
function sim8086_step() {
|
||||||
const text = Module.AsciiToString(inst)
|
if (registers.ip.get() < assembly.length) {
|
||||||
Module._free(inst)
|
stepCPU()
|
||||||
return text
|
renderAllRegisters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sim8086_reset() {
|
||||||
|
resetCPU()
|
||||||
|
renderAllRegisters()
|
||||||
|
}
|
||||||
|
function sim8086_run() {
|
||||||
|
while (registers.ip.get() < assembly.length) {
|
||||||
|
stepCPU()
|
||||||
|
}
|
||||||
|
renderAllRegisters()
|
||||||
|
}
|
||||||
|
async function sim8086_load() {
|
||||||
|
var input = document.createElement('input')
|
||||||
|
input.type = 'file'
|
||||||
|
input.onchange = e => {
|
||||||
|
const file = e.target.files[0]
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
reader.onload = readerEvent => {
|
||||||
|
var content = readerEvent.target.result;
|
||||||
|
const view = new Uint8Array(content);
|
||||||
|
updateAssembly(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssembly(newAssembly) {
|
||||||
|
assembly = newAssembly
|
||||||
|
setMemoryBufferAt(0x0000, newAssembly)
|
||||||
|
assemblyView.assemblySize = newAssembly.length
|
||||||
|
assemblyView.startAddress = 0
|
||||||
|
assemblyView.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.onRuntimeInitialized = () => {
|
||||||
|
updateAssembly(assembly)
|
||||||
|
|
||||||
|
renderAllRegisters()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
132
src/web/register-field.js
Normal file
132
src/web/register-field.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
class RegisterInputElement extends HTMLInputElement {
|
||||||
|
/** @type {"low"|"high"|"full"} */
|
||||||
|
position = "full"
|
||||||
|
reg = "<unknown>"
|
||||||
|
/** @type {"hex"|"decimal"|"binary"} */
|
||||||
|
format = "decimal"
|
||||||
|
validFormatSymbols = {
|
||||||
|
hex: "0123456789abcdef",
|
||||||
|
decimal: "0123456789",
|
||||||
|
binary: "01"
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(e) {
|
||||||
|
let parsed = parseInt(this.value, this.validFormatSymbols[this.format].length)
|
||||||
|
if (parsed === NaN) {
|
||||||
|
parsed = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setInputValue(parsed)
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress(e) {
|
||||||
|
if (!this.validFormatSymbols[this.format].includes(e.key.toLocaleLowerCase())) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.reg = this.getAttribute("reg").toLocaleLowerCase()
|
||||||
|
this.position = this.getAttribute("position")
|
||||||
|
this.classList.add("value")
|
||||||
|
if (this.position !== "full") {
|
||||||
|
this.classList.add("value--byte")
|
||||||
|
}
|
||||||
|
this.addEventListener("change", this.onChange)
|
||||||
|
this.addEventListener("keypress", this.onKeyPress)
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputValue() {
|
||||||
|
const value = registers[this.reg].get()
|
||||||
|
switch (this.position) {
|
||||||
|
case "full": return value & 0xFFFF
|
||||||
|
case "low": return value & 0xFF
|
||||||
|
case "high": return (value >> 8) & 0xFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInputValue(value) {
|
||||||
|
let current = registers[this.reg].get()
|
||||||
|
let newValue
|
||||||
|
switch (this.position) {
|
||||||
|
case "full":
|
||||||
|
newValue = value
|
||||||
|
break;
|
||||||
|
case "low":
|
||||||
|
newValue = (current & 0xFF00) | (value & 0xFF)
|
||||||
|
break;
|
||||||
|
case "high":
|
||||||
|
newValue = (current & 0x00FF) | ((value & 0xFF) << 8)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
registers[this.reg].set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const value = this.getInputValue()
|
||||||
|
if (this.format == "hex") {
|
||||||
|
this.value = value.toString(16).padStart(this.position == "full" ? 4 : 2, "0")
|
||||||
|
} else if (this.format == "decimal") {
|
||||||
|
this.value = value
|
||||||
|
} else if (this.format == "binary") {
|
||||||
|
this.value = value.toString(2).padStart(this.position == "full" ? 16 : 8, "0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegisterElement extends HTMLElement {
|
||||||
|
reg = "<unknown>"
|
||||||
|
readonly = false
|
||||||
|
hasHighLow = false
|
||||||
|
|
||||||
|
/** @type {RegisterInputElement} */
|
||||||
|
valueElement = undefined
|
||||||
|
/** @type {RegisterInputElement} */
|
||||||
|
highByteElement = undefined
|
||||||
|
/** @type {RegisterInputElement} */
|
||||||
|
lowByteElement = undefined
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.readonly = this.getAttribute("readonly") !== null
|
||||||
|
this.reg = this.getAttribute("reg").toLocaleLowerCase()
|
||||||
|
this.hasHighLow = this.getAttribute("has-low-high") !== null
|
||||||
|
|
||||||
|
const nameElement = this.appendChild(document.createElement("span"))
|
||||||
|
nameElement.classList.add("name")
|
||||||
|
nameElement.innerText = this.reg.toUpperCase()
|
||||||
|
|
||||||
|
if (this.hasHighLow) {
|
||||||
|
const highByteElem = document.createElement("input", { is: "register-input" })
|
||||||
|
highByteElem.setAttribute("reg", this.reg)
|
||||||
|
highByteElem.setAttribute("position", "high")
|
||||||
|
if (this.readonly) highByteElem.setAttribute("readonly", "")
|
||||||
|
this.highByteElement = this.appendChild(highByteElem)
|
||||||
|
|
||||||
|
const lowByteElem = document.createElement("input", { is: "register-input" })
|
||||||
|
lowByteElem.setAttribute("reg", this.reg)
|
||||||
|
lowByteElem.setAttribute("position", "low")
|
||||||
|
if (this.readonly) lowByteElem.setAttribute("readonly", "")
|
||||||
|
this.lowByteElement = this.appendChild(lowByteElem)
|
||||||
|
} else {
|
||||||
|
const elem = document.createElement("input", { is: "register-input" })
|
||||||
|
elem.setAttribute("reg", this.reg)
|
||||||
|
elem.setAttribute("position", "full")
|
||||||
|
if (this.readonly) elem.setAttribute("readonly", "")
|
||||||
|
this.valueElement = this.appendChild(elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.hasHighLow) {
|
||||||
|
this.lowByteElement.render()
|
||||||
|
this.highByteElement.render()
|
||||||
|
} else {
|
||||||
|
this.valueElement.render()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("register-input", RegisterInputElement, { extends: "input" })
|
||||||
|
customElements.define("register-field", RegisterElement)
|
Loading…
Reference in New Issue
Block a user