Create initial emulator and hello world demo
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.wasm
|
||||
108
LICENSE.md
Normal file
108
LICENSE.md
Normal 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
49
README.md
Normal 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
6
boot.js
Normal 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
69
emu.js
Normal 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
12
index.html
Normal 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
16
server.py
Normal 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
17
styles.css
Normal 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
22
wipforth.wat
Normal 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))))
|
||||
Reference in New Issue
Block a user