@raskell/wasm-ad 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 raskell.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,123 @@
1
- # Temporary Holding Version
1
+ # WASM-AD for AssemblyScript
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An AssemblyScript SDK for [WASM-AD](https://github.com/raskell-io/wasm-ad) carts: register
4
+ addresses, typed builders for backgrounds (tiled and affine), sprites, affine matrices and
5
+ sound, input, save RAM and `trace`, with no runtime allocation and no mutable globals.
6
+
7
+ ## The starter cart
8
+
9
+ ```sh
10
+ npm install
11
+ npm run build # build/starter.wasm
12
+ node ../../tools/wasmad.mjs check build/starter.wasm
13
+ ```
14
+
15
+ [`examples/starter.ts`](examples/starter.ts) is the same cart as every other SDK's starter: a
16
+ rotating affine checkerboard, a spinning star and a little tune on every sound channel, with
17
+ bit-identical frames and sound. Drop `build/starter.wasm` into the player.
18
+
19
+ ## A new cart
20
+
21
+ ```sh
22
+ npm install @raskell/wasm-ad assemblyscript@0.28
23
+ echo '{ "extends": "./node_modules/@raskell/wasm-ad/cart.asconfig.json" }' > asconfig.json
24
+ ```
25
+
26
+ ```ts
27
+ import { write16, reg, dispcnt, keys, key } from "@raskell/wasm-ad/assembly";
28
+ ```
29
+
30
+ ```sh
31
+ npx asc cart.ts --config asconfig.json -o cart.wasm
32
+ npx wasmad meta cart.wasm title="My Cart" author=you
33
+ npx wasmad check cart.wasm
34
+ ```
35
+
36
+ `cart.asconfig.json` is the configuration below with `abort` pointing into the
37
+ installed package; extend it rather than copying it. `wasmad` is the cart
38
+ checker from `tools/wasmad.mjs`, shipped with this package.
39
+
40
+ `asconfig.json` sets what the spec requires:
41
+
42
+ | Option | Why |
43
+ |---|---|
44
+ | `memoryBase: 163840` | Static data starts at 0x28000, above the hardware region (SPEC.md §4). |
45
+ | `initialMemory: 4` | At least 3 pages; 4 leaves room above the data. |
46
+ | `runtime: "stub"` | No garbage collector; carts do not allocate. |
47
+ | `use: abort=assembly/index/abort` | Traps instead of importing `env.abort`; a cart may only import `env.trace`. |
48
+
49
+ AssemblyScript cannot write custom sections, so `wasmad meta` adds the `wasm-ad` metadata
50
+ (`spec` is filled in). Run `wasmad check` before sharing a cart.
51
+
52
+ ## Rules
53
+
54
+ A cart exports `update()`, and optionally `start()` and `scanline(y: i32)`. Do the setup in
55
+ `start`, not at the top level of a module, which would run before the host clears the hardware.
56
+
57
+ **Keep state in linear memory.** AssemblyScript compiles a module-level `let` or `var` to a
58
+ mutable WebAssembly global, which SPEC.md §12 forbids (hosts snapshot linear memory for save
59
+ states and netplay), and `wasmad check` rejects it. Put the cart's state in an
60
+ `@unmanaged` class placed in a static block of memory:
61
+
62
+ ```ts
63
+ @unmanaged
64
+ class State {
65
+ x: i32;
66
+ y: i32;
67
+ }
68
+ const state = changetype<State>(memory.data(offsetof<State>()));
69
+
70
+ export function start(): void {
71
+ state.x = 112; // memory starts zeroed; set initial values here
72
+ }
73
+ ```
74
+
75
+ `const` values are fine. Avoid `new`, string concatenation, arrays and anything else that
76
+ allocates; use `memory.data` for static buffers and tables, e.g.
77
+ `const NOTES = memory.data<u32>([262, 330, 392])` read with `load<u32>(NOTES + (i << 2))`.
78
+
79
+ ## Sound
80
+
81
+ `sound.enable()` turns sound on with every channel on both sides. A channel plays when its
82
+ start bit is written; the host clears it after the frame (SPEC.md §14):
83
+
84
+ ```ts
85
+ write16(reg.SOUND2CNT_L, sound.DUTY_50 | sound.envelope(12, 2, false));
86
+ write16(reg.SOUND2CNT_H, sound.START | sound.rate(440));
87
+ ```
88
+
89
+ Sample channels play signed 8-bit samples you write to `sound.sampleRam()`:
90
+ `pcm.play(0, offset, length, 16000, 64)`.
91
+
92
+ ## Affine backgrounds
93
+
94
+ In modes 1 and 2, BG2 (and BG3 in mode 2) is drawn through a matrix. Its tiles are 8bpp
95
+ (`bgTile8`) and its map is one byte per tile (`affineMap`). `setBgAffine(2, m, tx, ty, sx, sy)`
96
+ puts texture pixel (tx, ty) at screen pixel (sx, sy) and rotates and scales around it.
97
+
98
+ ## API
99
+
100
+ All values are plain integers; builders take a value and return a new one.
101
+
102
+ | | |
103
+ |---|---|
104
+ | `read16`, `write16`, `read32`, `write32` | Raw register access, e.g. `write16(reg.DISPCNT, dispcnt.MODE0 \| dispcnt.BG0)` |
105
+ | `reg.*`, `reg.BGCNT(i)`, `reg.BGHOFS(i)`, `reg.BGVOFS(i)` | Register addresses by their SPEC.md names |
106
+ | `dispcnt`, `layer`, `blend`, `key` | Bit constants; `blend.cnt(effect, first, second)`, `blend.alpha(eva, evb)` |
107
+ | `bgcnt.priority/charBlock/screenBlock/size/bpp8/mosaic/affineSize/wrap` | `BGxCNT` builders |
108
+ | `rgb(r, g, b)` | 8-bit channels to RGB555 |
109
+ | `setBgColor(i, c)`, `setObjColor(i, c)` | Palette RAM |
110
+ | `bgTile(block, i)`, `objTile(i)`, `fillTile(addr, byte)` | Addresses of 32-byte 4bpp tiles |
111
+ | `screenBlock(b)`, `setScreenEntry(b, i, e)`, `screenEntry.make/hflip/vflip` | Background maps |
112
+ | `bgTile8(block, i)`, `affineMap(b)`, `setBgAffine(bg, m, tx, ty, sx, sy)` | Affine backgrounds (modes 1 and 2) |
113
+ | `sprite.make(x, y, SpriteSize.S16x16, tile)` then `.bank/.priority/.hflip/.vflip/.semiTransparent/.window/.mosaic/.bpp8/.affine` | Sprites, packed in a `u64` |
114
+ | `oam.set(i, s)`, `oam.hide(i)`, `oam.hideAll()`, `oam.setAffine(slot, m)` | OAM |
115
+ | `affine.rotateScaleFixed(angle, sx, sy)`, `affine.IDENTITY`, `affine.make` | Affine matrices; angle in 1/256 turns, scale in 8.8 |
116
+ | `fixed.sin(a)`, `fixed.cos(a)` | Q14 trigonometry, identical in every SDK |
117
+ | `buttons.update()`, `buttons.held(k)`, `buttons.pressed(k)`, `keys()` | Input |
118
+ | `frame()`, `seed()` | Time and the only entropy source |
119
+ | `saveRam()`, `saveRestored()`, `requestSave()` | 32 KB of save RAM at 0x19000 |
120
+ | `sound.enable/disable`, `sound.rate(hz)`, `sound.sweep/envelope/length/noise`, `sound.START/LENGTH/DUTY_*/WAVE_*` | Sound channels 1–4, through `reg.SOUND*` |
121
+ | `sound.waveRam()`, `sound.sampleRam()`, `sound.status()` | Wave RAM, sample RAM, which channels are playing |
122
+ | `pcm.play(ch, start, len, rate, vol)`, `pcm.playLooped`, `pcm.stop`, `pcm.position`, `pcm.playing` | Sample channels A (0) and B (1) |
123
+ | `trace(msg)` | UTF-8 to the host's debug log (at most 1 KB) |
package/asconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "options": {
3
+ "runtime": "stub",
4
+ "memoryBase": 163840,
5
+ "initialMemory": 4,
6
+ "use": ["abort=assembly/index/abort"],
7
+ "optimizeLevel": 3,
8
+ "shrinkLevel": 1,
9
+ "noAssert": true,
10
+ "exportRuntime": false
11
+ }
12
+ }