mirror of
https://github.com/remko/waforth
synced 2025-02-05 20:46:22 +01:00
Support writing WebAssembly in Forth using CODE
.
This commit is contained in:
parent
f94935c1aa
commit
e83b8018bd
3 changed files with 535 additions and 407 deletions
66
README.md
66
README.md
|
@ -156,6 +156,72 @@ forth.interpret(`
|
||||||
`);
|
`);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Writing WebAssembly in Forth
|
||||||
|
|
||||||
|
WAForth supports directly writing WebAssembly in Forth using the [`CODE`](https://forth-standard.org/standard/tools/CODE) word.
|
||||||
|
For example, the following snippet defines a raw WebAssembly version of [`DUP`](https://forth-standard.org/standard/core/DUP):
|
||||||
|
|
||||||
|
```forth
|
||||||
|
CODE DUP' ( n -- n n )
|
||||||
|
\ Put pointer to top of Forth stack (local_0) on the
|
||||||
|
\ Wasm operand stack (for use later)
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
|
||||||
|
\ Load the number at the top of the Forth stack
|
||||||
|
\ (local_0 - 4) on the Wasm operand stack
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
[ 4 ] $I32.CONST
|
||||||
|
$I32.SUB
|
||||||
|
$I32.LOAD
|
||||||
|
|
||||||
|
\ Store the number on the Wasm operand stack on
|
||||||
|
\ top of the Forth stack. The first operand (Forth
|
||||||
|
\ stack pointer) was put on the Wasm operand stack
|
||||||
|
\ at the beginning of this snippet
|
||||||
|
$I32.STORE
|
||||||
|
|
||||||
|
\ Increment the Forth top of stack pointer (local_0),
|
||||||
|
\ and leave it on the Wasm operand stack as return value
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
[ 4 ] $I32.CONST
|
||||||
|
$I32.ADD
|
||||||
|
;CODE
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a word with the specified WebAssembly:
|
||||||
|
|
||||||
|
```wasm
|
||||||
|
(func $DUP' (param $tos i32) (result i32)
|
||||||
|
local.get $tos
|
||||||
|
local.get $tos
|
||||||
|
i32.const 4
|
||||||
|
i32.sub
|
||||||
|
i32.load
|
||||||
|
i32.store
|
||||||
|
local.get $tos
|
||||||
|
i32.const 4
|
||||||
|
i32.add
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that support for writing WebAssembly is still experimental.
|
||||||
|
The assembly words used in the above snippet (`$LOCAL.GET`, `$I32.*`, ...) aren't available in the WAForth core,
|
||||||
|
and have to be manually defined using the low-level `$U,` and `$S,` words that append ([LEB128](https://en.wikipedia.org/wiki/LEB128)-encoded) bytes directly to the WebAssembly module. For example, the code above relies on the following assembly word definitions:
|
||||||
|
|
||||||
|
```forth
|
||||||
|
: $LOCAL.GET ( u -- ) 32 $U, $U, ; IMMEDIATE
|
||||||
|
: $I32.ADD ( -- ) 106 $U, ; IMMEDIATE
|
||||||
|
: $I32.SUB ( -- ) 107 $U, ; IMMEDIATE
|
||||||
|
: $I32.CONST ( n -- ) 65 $U, $S, ; IMMEDIATE
|
||||||
|
: $I32.LOAD ( -- ) 40 $U, 2 $U, 0 $U, ; IMMEDIATE
|
||||||
|
: $I32.STORE ( -- ) 54 $U, 2 $U, 0 $U, ; IMMEDIATE
|
||||||
|
```
|
||||||
|
|
||||||
|
The exact opcodes and format of instructions can be found in
|
||||||
|
[the WebAssembly spec](https://webassembly.github.io/spec/core/binary/instructions.html). In the future, I'll probably make all WebAssembly assembly instructions available somewhere. Using WebAssembly locals also currently isn't possible, although this should be easy to add later.
|
||||||
|
|
||||||
|
|
||||||
## Notebooks
|
## Notebooks
|
||||||
|
|
||||||
The [WAForth Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=remko.waforth-vscode-extension) adds support
|
The [WAForth Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=remko.waforth-vscode-extension) adds support
|
||||||
|
|
845
src/waforth.wat
845
src/waforth.wat
File diff suppressed because it is too large
Load diff
|
@ -1634,6 +1634,37 @@ function loadTests() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("CODE / ;CODE / $U, / $S,", () => {
|
||||||
|
it("should work", () => {
|
||||||
|
run(`
|
||||||
|
: $LOCAL.GET ( n -- ) 32 $U, $U, ; IMMEDIATE
|
||||||
|
: $I32.ADD ( -- ) 106 $U, ; IMMEDIATE
|
||||||
|
: $I32.SUB ( -- ) 107 $U, ; IMMEDIATE
|
||||||
|
: $I32.CONST ( n -- ) 65 $U, $S, ; IMMEDIATE
|
||||||
|
: $I32.LOAD ( -- ) 40 $U, 2 $U, 0 $U, ; IMMEDIATE
|
||||||
|
: $I32.STORE ( -- ) 54 $U, 2 $U, 0 $U, ; IMMEDIATE
|
||||||
|
|
||||||
|
CODE DUP' ( n -- n n )
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
[ 4 ] $I32.CONST
|
||||||
|
$I32.SUB
|
||||||
|
$I32.LOAD
|
||||||
|
|
||||||
|
$I32.STORE
|
||||||
|
|
||||||
|
[ 0 ] $LOCAL.GET
|
||||||
|
[ 4 ] $I32.CONST
|
||||||
|
$I32.ADD
|
||||||
|
;CODE
|
||||||
|
|
||||||
|
42 DUP'
|
||||||
|
`);
|
||||||
|
expect(stackValues()).to.eql([42, 42]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("DEFER", () => {
|
describe("DEFER", () => {
|
||||||
it("should work", () => {
|
it("should work", () => {
|
||||||
run("DEFER DEFER1");
|
run("DEFER DEFER1");
|
||||||
|
|
Loading…
Add table
Reference in a new issue