setup compiling and running wasm
This commit is contained in:
parent
371df307f5
commit
dbc2a4d902
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
main.exe
|
build
|
||||||
test-dump-asm.o
|
test-dump-asm.o
|
||||||
test-dump.asm
|
test-dump.asm
|
||||||
test-input.o
|
test-input.o
|
3
.vscode/c_cpp_properties.json
vendored
3
.vscode/c_cpp_properties.json
vendored
@ -5,7 +5,8 @@
|
|||||||
"includePath": [
|
"includePath": [
|
||||||
"${workspaceFolder}/**",
|
"${workspaceFolder}/**",
|
||||||
"C:/MinGW/include/**",
|
"C:/MinGW/include/**",
|
||||||
"src/**"
|
"src/**",
|
||||||
|
"${workspaceFolder}/emsdk/upstream/lib/clang/17/include/**"
|
||||||
],
|
],
|
||||||
"defines": [
|
"defines": [
|
||||||
"_DEBUG",
|
"_DEBUG",
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -8,5 +8,6 @@
|
|||||||
"errno.h": "c",
|
"errno.h": "c",
|
||||||
"stdbool.h": "c",
|
"stdbool.h": "c",
|
||||||
"sim8086.h": "c"
|
"sim8086.h": "c"
|
||||||
}
|
},
|
||||||
|
"liveServer.settings.root": "./build/web"
|
||||||
}
|
}
|
19
Makefile
19
Makefile
@ -1,10 +1,19 @@
|
|||||||
CC=gcc
|
|
||||||
CFLAGS=-g -Wall
|
CFLAGS=-g -Wall
|
||||||
|
|
||||||
.DEFAULT_GOAL := main
|
.PHONY := cli web clean serve-web
|
||||||
|
|
||||||
%: src/%.c
|
cli: src/cli.c
|
||||||
$(CC) -o $@ $< $(CFLAGS)
|
mkdir -p build
|
||||||
|
gcc -o build/cli.exe src/cli.c $(CFLAGS)
|
||||||
|
|
||||||
|
web: src/web.c
|
||||||
|
rm -rf 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)
|
||||||
|
cp -r src/web/* build/web
|
||||||
|
|
||||||
|
serve-web:
|
||||||
|
cd build/web && python -m http.server
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm main.exe
|
rm -r build
|
17
README.md
17
README.md
@ -4,6 +4,23 @@ For [Computer, Enhance!](https://www.computerenhance.com/)
|
|||||||
|
|
||||||
Examples gotten from: https://github.com/cmuratori/computer_enhance/tree/main/perfaware/part1
|
Examples gotten from: https://github.com/cmuratori/computer_enhance/tree/main/perfaware/part1
|
||||||
|
|
||||||
|
## Building & Running
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
```shell
|
||||||
|
make cli
|
||||||
|
./build/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web
|
||||||
|
```shell
|
||||||
|
# This assumes that you already have `emcc` in your path somewhere
|
||||||
|
make web
|
||||||
|
make serve-web
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual
|
||||||
|
|
||||||
8086 reference manual: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
|
8086 reference manual: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
|
||||||
Important pages in manual:
|
Important pages in manual:
|
||||||
* Registers - 24
|
* Registers - 24
|
||||||
|
@ -5,12 +5,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "sim8086.h"
|
#include "sim8086/prelude.h"
|
||||||
|
|
||||||
#include "sim8086.c"
|
|
||||||
#include "sim8086_memory.c"
|
|
||||||
#include "sim8086_decoder.c"
|
|
||||||
#include "sim8086_simulator.c"
|
|
||||||
|
|
||||||
// TODO: refactor cli commands, there is a lot of repeating code for reading assemblies and compiling them.
|
// TODO: refactor cli commands, there is a lot of repeating code for reading assemblies and compiling them.
|
||||||
|
|
||||||
@ -108,8 +103,7 @@ int dissassemble(FILE *src, FILE *dst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
instruction_to_str(buff, sizeof(buff), &inst);
|
instruction_to_str(buff, sizeof(buff), &inst);
|
||||||
fprintf(dst, buff);
|
fprintf(dst, "%s\b", buff);
|
||||||
fprintf(dst, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
@ -1,8 +1,13 @@
|
|||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// TODO: add error codes
|
// TODO: add error codes
|
||||||
|
|
||||||
int load_mem_from_stream(struct memory *mem, FILE *stream, u32 start) {
|
int load_mem_from_buff(struct memory *mem, u8 *buff, u16 buff_size, u16 start)
|
||||||
|
{
|
||||||
|
if (start + buff_size > MEMORY_SIZE) return -1;
|
||||||
|
memcpy(mem->mem + start, buff, buff_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_mem_from_stream(struct memory *mem, FILE *stream, u16 start) {
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
u8 byte = fgetc(stream);
|
u8 byte = fgetc(stream);
|
||||||
@ -14,7 +19,7 @@ int load_mem_from_stream(struct memory *mem, FILE *stream, u32 start) {
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int load_mem_from_file(struct memory *mem, const char *filename, u32 start) {
|
int load_mem_from_file(struct memory *mem, const char *filename, u16 start) {
|
||||||
FILE *stream = fopen(filename, "rb");
|
FILE *stream = fopen(filename, "rb");
|
||||||
if (stream == NULL) {
|
if (stream == NULL) {
|
||||||
return -1;
|
return -1;
|
6
src/sim8086/prelude.h
Normal file
6
src/sim8086/prelude.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "sim8086.h"
|
||||||
|
|
||||||
|
#include "utils.c"
|
||||||
|
#include "memory.c"
|
||||||
|
#include "decoder.c"
|
||||||
|
#include "simulator.c"
|
@ -1,6 +1,8 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define u32 uint32_t
|
#define u32 uint32_t
|
||||||
#define i32 int32_t
|
#define i32 int32_t
|
||||||
@ -9,8 +11,20 @@
|
|||||||
#define u8 uint8_t
|
#define u8 uint8_t
|
||||||
#define i8 int8_t
|
#define i8 int8_t
|
||||||
|
|
||||||
|
#ifdef SIM8086_EMCC
|
||||||
|
#define panic(...) \
|
||||||
|
emscripten_log(EM_LOG_ERROR, "PANIC(%s:%d): ", __FILE__, __LINE__); \
|
||||||
|
emscripten_log(EM_LOG_ERROR, __VA_ARGS__); \
|
||||||
|
abort()
|
||||||
|
#define todo(...) \
|
||||||
|
emscripten_log(EM_LOG_WARN, "TODO(%s:%d): ", __FILE__, __LINE__); \
|
||||||
|
emscripten_log(EM_LOG_WARN, __VA_ARGS__); \
|
||||||
|
abort()
|
||||||
|
#else
|
||||||
#define panic(...) fprintf(stderr, "PANIC(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
#define panic(...) fprintf(stderr, "PANIC(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||||
#define todo(...) fprintf(stderr, "TODO(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
#define todo(...) fprintf(stderr, "TODO(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
#define MEMORY_SIZE 65536 // 2^16
|
#define MEMORY_SIZE 65536 // 2^16
|
||||||
|
|
66
src/web.c
Normal file
66
src/web.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <emscripten.h>
|
||||||
|
|
||||||
|
#define SIM8086_EMCC
|
||||||
|
#include "sim8086/prelude.h"
|
||||||
|
|
||||||
|
#define EXPORT EMSCRIPTEN_KEEPALIVE
|
||||||
|
#define dbg(...) emscripten_log(EM_LOG_CONSOLE, __VA_ARGS__)
|
||||||
|
|
||||||
|
EXPORT struct memory memory_state;
|
||||||
|
EXPORT struct cpu_state cpu_state;
|
||||||
|
EXPORT struct instruction current_instruction;
|
||||||
|
|
||||||
|
/* -------------------- Decoder ----------------------- */
|
||||||
|
|
||||||
|
EXPORT int decode_inst_at_ip() {
|
||||||
|
return decode_instruction(&memory_state, &cpu_state.ip, ¤t_instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void get_inst_str(char *buff, size_t buff_size) {
|
||||||
|
instruction_to_str(buff, buff_size, ¤t_instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- Memory ----------------------- */
|
||||||
|
|
||||||
|
EXPORT int load_to_memory_state(u8 *assembly, u16 assembly_size, u16 start) {
|
||||||
|
return load_mem_from_buff(&memory_state, assembly, assembly_size, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT u8 *get_memory_state_base() {
|
||||||
|
return memory_state.mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT size_t get_memory_state_size(u8 *assembly, u16 assembly_size, u16 start) {
|
||||||
|
return MEMORY_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- CPU ----------------------- */
|
||||||
|
|
||||||
|
EXPORT void cpu_reset()
|
||||||
|
{
|
||||||
|
memset(&cpu_state, 0, sizeof(cpu_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CPU_STATE_GETTER(field) EXPORT u16 cpu_get_##field() { return cpu_state.field; }
|
||||||
|
#define CPU_STATE_SETTER(field) EXPORT void cpu_set_##field(u16 value) { cpu_state.field = value; }
|
||||||
|
#define CPU_STATE_ACCESOR(field) CPU_STATE_SETTER(field) CPU_STATE_GETTER(field)
|
||||||
|
|
||||||
|
CPU_STATE_ACCESOR(ip)
|
||||||
|
CPU_STATE_ACCESOR(ax)
|
||||||
|
CPU_STATE_ACCESOR(bx)
|
||||||
|
CPU_STATE_ACCESOR(cx)
|
||||||
|
CPU_STATE_ACCESOR(dx)
|
||||||
|
CPU_STATE_ACCESOR(sp)
|
||||||
|
CPU_STATE_ACCESOR(bp)
|
||||||
|
CPU_STATE_ACCESOR(si)
|
||||||
|
CPU_STATE_ACCESOR(di)
|
||||||
|
|
||||||
|
EXPORT bool cpu_get_zero_flag()
|
||||||
|
{
|
||||||
|
return cpu_state.flags.zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT bool cpu_get_sign_flag()
|
||||||
|
{
|
||||||
|
return cpu_state.flags.sign;
|
||||||
|
}
|
225
src/web/index.html
Normal file
225
src/web/index.html
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>sim8086.wasm</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@600&display=swap" rel="stylesheet">
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
fontFamily: {
|
||||||
|
serif: ["Source Sans Pro", "sans-serif"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.register {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
background: #AAA;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
.register__name, .register__value {
|
||||||
|
display: inline-block;
|
||||||
|
background: #BBB;
|
||||||
|
}
|
||||||
|
.register__value {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.25ch 1ch;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.register__value--byte {
|
||||||
|
width: 8ch;
|
||||||
|
}
|
||||||
|
.register__name {
|
||||||
|
text-align: right;
|
||||||
|
min-width: 4ch
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<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">
|
||||||
|
<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="bg-green-500">a</div>
|
||||||
|
<div class="bg-blue-500 grow flex flex-row gap-4">
|
||||||
|
<div class="bg-green-500 grow">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="bg-green-500 grow flex flex-row basis-1/4">
|
||||||
|
<div class="bg-cyan-500 flex flex-col gap-1">
|
||||||
|
<register-field reg="ax" has-low-high></register-field>
|
||||||
|
<register-field reg="bx" has-low-high></register-field>
|
||||||
|
<register-field reg="cx" has-low-high></register-field>
|
||||||
|
<register-field reg="dx" has-low-high></register-field>
|
||||||
|
<register-field reg="sp"></register-field>
|
||||||
|
<register-field reg="bp"></register-field>
|
||||||
|
<register-field reg="si"></register-field>
|
||||||
|
<register-field reg="di"></register-field>
|
||||||
|
<register-field reg="ip" readonly></register-field>
|
||||||
|
<register-input reg="ax" position="low"> </register-input>
|
||||||
|
</div>
|
||||||
|
<div class="bg-cyan-500">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-green-500 grow basis-1/4"></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="sim8086.js"></script>
|
||||||
|
<script>
|
||||||
|
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", [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
||||||
|
for (const elem of document.getElementsByTagName("register-field")) {
|
||||||
|
elem.render()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentInstruction() {
|
||||||
|
const inst = Module._malloc(64)
|
||||||
|
getInstructionString(inst, 64)
|
||||||
|
const text = Module.AsciiToString(inst)
|
||||||
|
Module._free(inst)
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user