Create initial emulator and hello world demo

This commit is contained in:
2026-02-23 17:37:30 +00:00
commit d3e2407f3b
9 changed files with 300 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.wasm

108
LICENSE.md Normal file
View File

@@ -0,0 +1,108 @@
# Komorebi License
Version 2.0.0
## Acceptance
In order to get any license under these terms, you must agree
to them as both strict obligations and conditions to all
your licenses.
## Copyright License
The licensor grants you a copyright license for the software
to do everything you might do with the software that would
otherwise infringe the licensor's copyright in it for any
permitted purpose. However, you may only distribute the source
code of the software according to the [Distribution License](
#distribution-license), you may only make changes according
to the [Changes License](#changes-license), and you may not
otherwise distribute the software or new works based on the
software.
## Distribution License
The licensor grants you an additional copyright license to
distribute copies of the source code of the software. Your
license to distribute covers distributing the source code of
the software with changes permitted by the [Changes License](
#changes-license).
## Changes License
The licensor grants you an additional copyright license to
make changes for any permitted purpose.
## Patent License
The licensor grants you a patent license for the software that
covers patent claims the licensor can license, or becomes able
to license, that you would infringe by using the software.
## Personal Uses
Personal use for research, experiment, and testing for
the benefit of public knowledge, personal study, private
entertainment, hobby projects, amateur pursuits, or religious
observance, without any anticipated commercial application,
is use for a permitted purpose.
## Fair Use
You may have "fair use" rights for the software under the
law. These terms do not limit them.
## No Other Rights
These terms do not allow you to sublicense or transfer any of
your licenses to anyone else, or prevent the licensor from
granting licenses to anyone else. These terms do not imply
any other licenses.
## Patent Defense
If you make any written claim that the software infringes or
contributes to infringement of any patent, your patent license
for the software granted under these terms ends immediately. If
your company makes such a claim, your patent license ends
immediately for work on behalf of your company.
## Violations
The first time you are notified in writing that you have
violated any of these terms, or done anything with the software
not covered by your licenses, your licenses can nonetheless
continue if you come into full compliance with these terms,
and take practical steps to correct past violations, within
32 days of receiving notice. Otherwise, all your licenses
end immediately.
## No Liability
***As far as the law allows, the software comes as is, without
any warranty or condition, and the licensor will not be liable
to you for any damages arising out of these terms or the use
or nature of the software, under any kind of legal claim.***
## Definitions
The **licensor** is the individual or entity offering these
terms, and the **software** is the software the licensor makes
available under these terms.
**You** refers to the individual or entity agreeing to these
terms.
**Your company** is any legal entity, sole proprietorship,
or other kind of organization that you work for, plus all
organizations that have control over, are under the control of,
or are under common control with that organization. **Control**
means ownership of substantially all the assets of an entity,
or the power to direct its management and policies by vote,
contract, or otherwise. Control can be direct or indirect.
**Your licenses** are all the licenses granted to you for the
software under these terms.
**Use** means anything you do with the software requiring one
of your licenses.

49
README.md Normal file
View File

@@ -0,0 +1,49 @@
# Wipforth
## Building and Running
To run, first compile the WebAssembly module:
```
wat2wasm --enable-threads wipforth.wat
```
Then run the server:
```
python3 server.py
```
You should then be able to open <http://localhost:8080> in a browser
and use the system from there.
**NOTE**: The server is just a very simple instantiation of Python's
built-in `http.server` that sets the cross-origin headers required for
`SharedMemoryBuffer` use. You could use any HTTP server that sets
these headers.
## Peripherals
# Serial
| Name | Offset | Size / B | Access |
|--------|--------|----------|--------------|
| TXBUF | 0 | 32 | write |
| RXBUF | 32 | 32 | read |
| TXDATA | 64 | 1 | atomic write |
| TXHEAD | 65 | 1 | atomic read |
| TXTAIL | 66 | 1 | atomic write |
| RXDATA | 67 | 1 | atomic read |
| RXHEAD | 68 | 1 | atomic write |
| RXTAIL | 69 | 1 | atomic read |
For both sending (`TX`) and receiving (`RX`), there are four
registers: `xBUF`, `xDATA`, `xHEAD` and `xTAIL`:
- `xBUF` registers are 32-byte FIFO ring buffers used for data
- The `xDATA` registers indicate whether data is available (0 for
data, FFh for no data)
- The `xHEAD` and `xTAIL` registers specify the start and end of
data in the FIFO, `xHEAD` being the offset of the first byte of
data, and `xTAIL` being the offset of the first byte after the
data.

6
boot.js Normal file
View File

@@ -0,0 +1,6 @@
self.onmessage = async (e) => {
const exports = { emu: { mem: e.data } };
const mod = await WebAssembly.instantiateStreaming(
fetch('wipforth.wasm'), exports)
mod.instance.exports.reset();
};

69
emu.js Normal file
View File

@@ -0,0 +1,69 @@
const TXBUF = 0;
const RXBUF = 32;
const TXDATA = 64;
const TXHEAD = 65;
const TXTAIL = 66;
const RXDATA = 67;
const RXHEAD = 68;
const RXTAIL = 69;
const TXBUF_SIZE = 32;
const RXBUF_SIZE = 32;
const PERIPHS_SIZE = 70;
const POLL_INTERVAL_MS = 20;
class Emulator {
constructor() {
this.mem = new WebAssembly.Memory({
initial: 1,
maximum: 1,
shared: true
});
this.mem_u8 = new Uint8Array(this.mem.buffer);
for (let i = 0; i < PERIPHS_SIZE; ++i)
this.mem_u8[i] = 0;
this.decoder = new TextDecoder('utf-8');
this.output = document.getElementById('output');
this.input = document.getElementById('input');
this.timer = setInterval(() => this.poll(), POLL_INTERVAL_MS);
this.worker = new Worker('boot.js');
this.worker.postMessage(this.mem);
}
poll() {
const txdata = Atomics.load(this.mem_u8, TXDATA);
if (txdata !== 0)
this.handle_txdata();
}
handle_txdata() {
const head = Atomics.load(this.mem_u8, TXHEAD);
const tail = Atomics.load(this.mem_u8, TXTAIL);
const data = [];
let i = head;
do {
data.push(this.mem_u8[TXBUF + i]);
i = (i + 1) % TXBUF_SIZE;
} while (i !== tail);
Atomics.store(this.mem_u8, TXHEAD, tail);
// More data could have been added -- only clear TXDATA if
// tail is unchanged.
if (tail === Atomics.load(this.mem_u8, TXTAIL))
Atomics.store(this.mem_u8, TXDATA, 0);
const str = this.decoder.decode(new Uint8Array(data));
this.output.innerText += str;
}
}
window.addEventListener('DOMContentLoaded', () => {
window.emu = new Emulator();
});

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Wipforth</title>
<link rel="stylesheet" type="text/css" href="styles.css"/>
</head>
<body>
<script type="text/javascript" src="emu.js"></script>
<div id="output"></div>
<input id="input" type="text" autofocus></input>
</body>
</html>

16
server.py Normal file
View File

@@ -0,0 +1,16 @@
from http.server import HTTPServer, SimpleHTTPRequestHandler
import sys
class CORSRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header(
'Cross-Origin-Opener-Policy', 'same-origin')
self.send_header(
'Cross-Origin-Embedder-Policy', 'require-corp')
self.send_header(
'Cache-Control', 'no-store, no-cache, must-revalidate')
super().end_headers()
if __name__ == '__main__':
server = HTTPServer(('localhost', 8080), CORSRequestHandler)
server.serve_forever()

17
styles.css Normal file
View File

@@ -0,0 +1,17 @@
body {
line-height: 1.4em;
font-family: 'Courier 10 Pitch', monospace;
font-size: 12pt;
background-color: black;
color: white;
}
#output {
margin: 1.4em auto;
width: 80em;
height: 48em;
}
#input {
display: none;
}

22
wipforth.wat Normal file
View File

@@ -0,0 +1,22 @@
(module
(import "emu" "mem" (memory 1 1 shared))
(func (export "reset")
;; Write message into TXBUF
(i32.store8 (i32.const 0x0) (i32.const 0x48)) ;; H
(i32.store8 (i32.const 0x1) (i32.const 0x65)) ;; e
(i32.store8 (i32.const 0x2) (i32.const 0x6c)) ;; l
(i32.store8 (i32.const 0x3) (i32.const 0x6c)) ;; l
(i32.store8 (i32.const 0x4) (i32.const 0x6f)) ;; o
(i32.store8 (i32.const 0x5) (i32.const 0x2c)) ;; ,
(i32.store8 (i32.const 0x6) (i32.const 0x20)) ;; <space>
(i32.store8 (i32.const 0x7) (i32.const 0x77)) ;; w
(i32.store8 (i32.const 0x8) (i32.const 0x6f)) ;; o
(i32.store8 (i32.const 0x9) (i32.const 0x72)) ;; r
(i32.store8 (i32.const 0xa) (i32.const 0x6c)) ;; l
(i32.store8 (i32.const 0xb) (i32.const 0x64)) ;; d
(i32.store8 (i32.const 0xc) (i32.const 0x21)) ;; !
;; Update TXTAIL and set TXDATA
(i32.atomic.store8 (i32.const 0x42) (i32.const 0xd))
(i32.atomic.store8 (i32.const 0x40) (i32.const 0xff))))