@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 +21 -0
- package/README.md +122 -2
- package/asconfig.json +12 -0
- package/assembly/index.ts +769 -0
- package/bin/wasmad.mjs +461 -0
- package/cart.asconfig.json +12 -0
- package/package.json +41 -4
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
// AssemblyScript SDK for WASM-AD carts.
|
|
2
|
+
//
|
|
3
|
+
// A cart exports `update` (and optionally `start` and `scanline`) and drives
|
|
4
|
+
// the console by writing its memory-mapped hardware, which this module wraps
|
|
5
|
+
// in typed helpers. See SPEC.md for the hardware.
|
|
6
|
+
//
|
|
7
|
+
// AssemblyScript keeps module-level `let`/`var` in WebAssembly globals, but a
|
|
8
|
+
// cart's state must live in linear memory (SPEC.md §12). So this SDK has no
|
|
9
|
+
// mutable globals, its values are plain integers (no allocation), and carts
|
|
10
|
+
// keep their own state in a `@unmanaged` class placed with `memory.data`:
|
|
11
|
+
//
|
|
12
|
+
// @unmanaged class State { x: i32; y: i32 }
|
|
13
|
+
// const state = changetype<State>(memory.data(offsetof<State>()));
|
|
14
|
+
|
|
15
|
+
// ---- Memory map (SPEC.md §4) ------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export const SCREEN_W: i32 = 240;
|
|
18
|
+
export const SCREEN_H: i32 = 160;
|
|
19
|
+
|
|
20
|
+
export const IO: usize = 0x00400;
|
|
21
|
+
export const PAL_BG: usize = 0x00800;
|
|
22
|
+
export const PAL_OBJ: usize = 0x00a00;
|
|
23
|
+
export const OAM: usize = 0x00c00;
|
|
24
|
+
export const VRAM: usize = 0x01000;
|
|
25
|
+
export const VRAM_SIZE: usize = 0x18000;
|
|
26
|
+
/** Sprite tile 0. */
|
|
27
|
+
export const OBJ_VRAM: usize = VRAM + 0x10000;
|
|
28
|
+
export const SAVE: usize = 0x19000;
|
|
29
|
+
export const SAVE_SIZE: usize = 0x8000;
|
|
30
|
+
/** Sample RAM: signed 8-bit samples for the sample channels (SPEC.md §14.6). */
|
|
31
|
+
export const SAMPLE_RAM: usize = 0x21000;
|
|
32
|
+
export const SAMPLE_RAM_SIZE: usize = 0x7000;
|
|
33
|
+
/** Wave RAM: two banks of 16 bytes (SPEC.md §14.5). */
|
|
34
|
+
export const WAVE_RAM: usize = 0x490;
|
|
35
|
+
/** First address the cart may use for its own data, stack and heap. */
|
|
36
|
+
export const CART: usize = 0x28000;
|
|
37
|
+
/** OAM attribute 0 that hides a sprite. */
|
|
38
|
+
export const OBJ_HIDDEN: u16 = 0x0200;
|
|
39
|
+
export const SPEC_VERSION: u16 = 0x0002;
|
|
40
|
+
|
|
41
|
+
// ---- Registers (SPEC.md §5, §6, §14) ----------------------------------------
|
|
42
|
+
|
|
43
|
+
/** Every register address, by its SPEC.md name. Use with `read16`/`write16`. */
|
|
44
|
+
export namespace reg {
|
|
45
|
+
export const DISPCNT: usize = 0x400;
|
|
46
|
+
export const VCOUNT: usize = 0x406;
|
|
47
|
+
export const WIN0H: usize = 0x440;
|
|
48
|
+
export const WIN1H: usize = 0x442;
|
|
49
|
+
export const WIN0V: usize = 0x444;
|
|
50
|
+
export const WIN1V: usize = 0x446;
|
|
51
|
+
export const WININ: usize = 0x448;
|
|
52
|
+
export const WINOUT: usize = 0x44a;
|
|
53
|
+
export const MOSAIC: usize = 0x44c;
|
|
54
|
+
export const BLDCNT: usize = 0x450;
|
|
55
|
+
export const BLDALPHA: usize = 0x452;
|
|
56
|
+
export const BLDY: usize = 0x454;
|
|
57
|
+
export const KEYS: usize = 0x700;
|
|
58
|
+
export const FRAME: usize = 0x704; // u32
|
|
59
|
+
export const SEED: usize = 0x708; // u32
|
|
60
|
+
export const SAVECNT: usize = 0x70c;
|
|
61
|
+
export const SAVESTAT: usize = 0x70e;
|
|
62
|
+
export const SPEC: usize = 0x710;
|
|
63
|
+
|
|
64
|
+
export const BG2PA: usize = 0x420;
|
|
65
|
+
export const BG2PB: usize = 0x422;
|
|
66
|
+
export const BG2PC: usize = 0x424;
|
|
67
|
+
export const BG2PD: usize = 0x426;
|
|
68
|
+
export const BG2X: usize = 0x428; // u32
|
|
69
|
+
export const BG2Y: usize = 0x42c; // u32
|
|
70
|
+
export const BG3PA: usize = 0x430;
|
|
71
|
+
export const BG3PB: usize = 0x432;
|
|
72
|
+
export const BG3PC: usize = 0x434;
|
|
73
|
+
export const BG3PD: usize = 0x436;
|
|
74
|
+
export const BG3X: usize = 0x438; // u32
|
|
75
|
+
export const BG3Y: usize = 0x43c; // u32
|
|
76
|
+
|
|
77
|
+
export const SOUND1CNT_L: usize = 0x460;
|
|
78
|
+
export const SOUND1CNT_H: usize = 0x462;
|
|
79
|
+
export const SOUND1CNT_X: usize = 0x464;
|
|
80
|
+
export const SOUND2CNT_L: usize = 0x468;
|
|
81
|
+
export const SOUND2CNT_H: usize = 0x46c;
|
|
82
|
+
export const SOUND3CNT_L: usize = 0x470;
|
|
83
|
+
export const SOUND3CNT_H: usize = 0x472;
|
|
84
|
+
export const SOUND3CNT_X: usize = 0x474;
|
|
85
|
+
export const SOUND4CNT_L: usize = 0x478;
|
|
86
|
+
export const SOUND4CNT_H: usize = 0x47c;
|
|
87
|
+
export const SOUNDCNT_L: usize = 0x480;
|
|
88
|
+
export const SOUNDCNT_H: usize = 0x482;
|
|
89
|
+
export const SOUNDCNT_X: usize = 0x484;
|
|
90
|
+
|
|
91
|
+
export const PCMA_SAD: usize = 0x4b0;
|
|
92
|
+
export const PCMA_LEN: usize = 0x4b2;
|
|
93
|
+
export const PCMA_LOOP: usize = 0x4b4;
|
|
94
|
+
export const PCMA_RATE: usize = 0x4b6;
|
|
95
|
+
export const PCMA_CNT: usize = 0x4b8;
|
|
96
|
+
export const PCMA_POS: usize = 0x4ba;
|
|
97
|
+
export const PCMB_SAD: usize = 0x4c0;
|
|
98
|
+
export const PCMB_LEN: usize = 0x4c2;
|
|
99
|
+
export const PCMB_LOOP: usize = 0x4c4;
|
|
100
|
+
export const PCMB_RATE: usize = 0x4c6;
|
|
101
|
+
export const PCMB_CNT: usize = 0x4c8;
|
|
102
|
+
export const PCMB_POS: usize = 0x4ca;
|
|
103
|
+
|
|
104
|
+
/** `BGxCNT` for background 0–3. */
|
|
105
|
+
// @ts-ignore: decorator
|
|
106
|
+
@inline export function BGCNT(bg: i32): usize {
|
|
107
|
+
return 0x408 + (<usize>bg << 1);
|
|
108
|
+
}
|
|
109
|
+
/** `BGxHOFS` for background 0–3. */
|
|
110
|
+
// @ts-ignore: decorator
|
|
111
|
+
@inline export function BGHOFS(bg: i32): usize {
|
|
112
|
+
return 0x410 + (<usize>bg << 2);
|
|
113
|
+
}
|
|
114
|
+
/** `BGxVOFS` for background 0–3. */
|
|
115
|
+
// @ts-ignore: decorator
|
|
116
|
+
@inline export function BGVOFS(bg: i32): usize {
|
|
117
|
+
return 0x412 + (<usize>bg << 2);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// @ts-ignore: decorator
|
|
122
|
+
@inline export function read16(addr: usize): u16 {
|
|
123
|
+
return load<u16>(addr);
|
|
124
|
+
}
|
|
125
|
+
// @ts-ignore: decorator
|
|
126
|
+
@inline export function write16(addr: usize, v: u16): void {
|
|
127
|
+
store<u16>(addr, v);
|
|
128
|
+
}
|
|
129
|
+
// @ts-ignore: decorator
|
|
130
|
+
@inline export function read32(addr: usize): u32 {
|
|
131
|
+
return load<u32>(addr);
|
|
132
|
+
}
|
|
133
|
+
// @ts-ignore: decorator
|
|
134
|
+
@inline export function write32(addr: usize, v: u32): void {
|
|
135
|
+
store<u32>(addr, v);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** `DISPCNT` bits (SPEC.md §6.1). */
|
|
139
|
+
export namespace dispcnt {
|
|
140
|
+
export const MODE0: u16 = 0;
|
|
141
|
+
/** BG0 and BG1 tiled, BG2 affine. */
|
|
142
|
+
export const MODE1: u16 = 1;
|
|
143
|
+
/** BG2 and BG3 affine. */
|
|
144
|
+
export const MODE2: u16 = 2;
|
|
145
|
+
export const MODE3: u16 = 3;
|
|
146
|
+
export const MODE4: u16 = 4;
|
|
147
|
+
export const MODE5: u16 = 5;
|
|
148
|
+
export const PAGE1: u16 = 1 << 4;
|
|
149
|
+
export const OBJ_1D: u16 = 1 << 6;
|
|
150
|
+
export const FORCED_BLANK: u16 = 1 << 7;
|
|
151
|
+
export const BG0: u16 = 1 << 8;
|
|
152
|
+
export const BG1: u16 = 1 << 9;
|
|
153
|
+
export const BG2: u16 = 1 << 10;
|
|
154
|
+
export const BG3: u16 = 1 << 11;
|
|
155
|
+
export const OBJ: u16 = 1 << 12;
|
|
156
|
+
export const WIN0: u16 = 1 << 13;
|
|
157
|
+
export const WIN1: u16 = 1 << 14;
|
|
158
|
+
export const OBJ_WIN: u16 = 1 << 15;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Layer bits for `BLDCNT` targets and window masks. */
|
|
162
|
+
export namespace layer {
|
|
163
|
+
export const BG0: u16 = 1 << 0;
|
|
164
|
+
export const BG1: u16 = 1 << 1;
|
|
165
|
+
export const BG2: u16 = 1 << 2;
|
|
166
|
+
export const BG3: u16 = 1 << 3;
|
|
167
|
+
export const OBJ: u16 = 1 << 4;
|
|
168
|
+
export const BACKDROP: u16 = 1 << 5;
|
|
169
|
+
/** In window masks: color effects enabled. */
|
|
170
|
+
export const EFFECTS: u16 = 1 << 5;
|
|
171
|
+
export const ALL: u16 = 0x3f;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** `BLDCNT` effects (SPEC.md §6.5). */
|
|
175
|
+
export namespace blend {
|
|
176
|
+
export const NONE: u16 = 0;
|
|
177
|
+
export const ALPHA: u16 = 1 << 6;
|
|
178
|
+
export const BRIGHTEN: u16 = 2 << 6;
|
|
179
|
+
export const DARKEN: u16 = 3 << 6;
|
|
180
|
+
|
|
181
|
+
/** `BLDCNT` value: `effect` on `first` targets, blending onto `second`. */
|
|
182
|
+
// @ts-ignore: decorator
|
|
183
|
+
@inline export function cnt(effect: u16, first: u16, second: u16): u16 {
|
|
184
|
+
return effect | first | (second << 8);
|
|
185
|
+
}
|
|
186
|
+
/** `BLDALPHA` value; coefficients 0–16. */
|
|
187
|
+
// @ts-ignore: decorator
|
|
188
|
+
@inline export function alpha(eva: u16, evb: u16): u16 {
|
|
189
|
+
return eva | (evb << 8);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** `KEYS` bits (SPEC.md §5). */
|
|
194
|
+
export namespace key {
|
|
195
|
+
export const A: u16 = 1 << 0;
|
|
196
|
+
export const B: u16 = 1 << 1;
|
|
197
|
+
export const SELECT: u16 = 1 << 2;
|
|
198
|
+
export const START: u16 = 1 << 3;
|
|
199
|
+
export const RIGHT: u16 = 1 << 4;
|
|
200
|
+
export const LEFT: u16 = 1 << 5;
|
|
201
|
+
export const UP: u16 = 1 << 6;
|
|
202
|
+
export const DOWN: u16 = 1 << 7;
|
|
203
|
+
export const R: u16 = 1 << 8;
|
|
204
|
+
export const L: u16 = 1 << 9;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---- Backgrounds (SPEC.md §6.2, §8) -----------------------------------------
|
|
208
|
+
|
|
209
|
+
/** Background map sizes, in tiles. */
|
|
210
|
+
export enum MapSize {
|
|
211
|
+
S32x32 = 0,
|
|
212
|
+
S64x32 = 1,
|
|
213
|
+
S32x64 = 2,
|
|
214
|
+
S64x64 = 3,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Affine background map sizes, in tiles (SPEC.md §8.4). */
|
|
218
|
+
export enum AffineSize {
|
|
219
|
+
S16x16 = 0,
|
|
220
|
+
S32x32 = 1,
|
|
221
|
+
S64x64 = 2,
|
|
222
|
+
S128x128 = 3,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Builders for a `BGxCNT` value (SPEC.md §6.2), e.g. `bgcnt.priority(bgcnt.screenBlock(0, 31), 1)`. */
|
|
226
|
+
export namespace bgcnt {
|
|
227
|
+
// @ts-ignore: decorator
|
|
228
|
+
@inline export function priority(c: u16, p: u16): u16 {
|
|
229
|
+
return (c & ~3) | (p & 3);
|
|
230
|
+
}
|
|
231
|
+
// @ts-ignore: decorator
|
|
232
|
+
@inline export function charBlock(c: u16, b: u16): u16 {
|
|
233
|
+
return (c & ~0xc) | ((b & 3) << 2);
|
|
234
|
+
}
|
|
235
|
+
// @ts-ignore: decorator
|
|
236
|
+
@inline export function screenBlock(c: u16, b: u16): u16 {
|
|
237
|
+
return (c & ~0x1f00) | ((b & 31) << 8);
|
|
238
|
+
}
|
|
239
|
+
// @ts-ignore: decorator
|
|
240
|
+
@inline export function size(c: u16, s: MapSize): u16 {
|
|
241
|
+
return (c & 0x3fff) | (<u16>s << 14);
|
|
242
|
+
}
|
|
243
|
+
// @ts-ignore: decorator
|
|
244
|
+
@inline export function bpp8(c: u16): u16 {
|
|
245
|
+
return c | 0x80;
|
|
246
|
+
}
|
|
247
|
+
// @ts-ignore: decorator
|
|
248
|
+
@inline export function mosaic(c: u16): u16 {
|
|
249
|
+
return c | 0x40;
|
|
250
|
+
}
|
|
251
|
+
/** Map size of an affine background. */
|
|
252
|
+
// @ts-ignore: decorator
|
|
253
|
+
@inline export function affineSize(c: u16, s: AffineSize): u16 {
|
|
254
|
+
return (c & 0x3fff) | (<u16>s << 14);
|
|
255
|
+
}
|
|
256
|
+
/** An affine background repeats instead of being transparent outside its map. */
|
|
257
|
+
// @ts-ignore: decorator
|
|
258
|
+
@inline export function wrap(c: u16): u16 {
|
|
259
|
+
return c | 0x2000;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Packs 8-bit channels into RGB555. */
|
|
264
|
+
// @ts-ignore: decorator
|
|
265
|
+
@inline export function rgb(r: i32, g: i32, b: i32): u16 {
|
|
266
|
+
return <u16>(((r & 255) >> 3) | (((g & 255) >> 3) << 5) | (((b & 255) >> 3) << 10));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Sets background color `i` (0–255; 16 banks of 16 for 4bpp graphics). */
|
|
270
|
+
// @ts-ignore: decorator
|
|
271
|
+
@inline export function setBgColor(i: i32, c: u16): void {
|
|
272
|
+
store<u16>(PAL_BG + (<usize>i << 1), c);
|
|
273
|
+
}
|
|
274
|
+
/** Sets sprite color `i` (0–255). */
|
|
275
|
+
// @ts-ignore: decorator
|
|
276
|
+
@inline export function setObjColor(i: i32, c: u16): void {
|
|
277
|
+
store<u16>(PAL_OBJ + (<usize>i << 1), c);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Address of 4bpp background tile `index` counted from character block `block` (0–3). Tiles are 32 bytes. */
|
|
281
|
+
// @ts-ignore: decorator
|
|
282
|
+
@inline export function bgTile(block: i32, index: i32): usize {
|
|
283
|
+
return VRAM + <usize>block * 0x4000 + (<usize>index << 5);
|
|
284
|
+
}
|
|
285
|
+
/** Address of 8bpp background tile `index` counted from character block `block` (0–3). Tiles are 64 bytes;
|
|
286
|
+
* affine backgrounds always use these. */
|
|
287
|
+
// @ts-ignore: decorator
|
|
288
|
+
@inline export function bgTile8(block: i32, index: i32): usize {
|
|
289
|
+
return VRAM + <usize>block * 0x4000 + (<usize>index << 6);
|
|
290
|
+
}
|
|
291
|
+
/** Address of an affine background's map at screen block `block` (0–31): one tile index per byte,
|
|
292
|
+
* row-major (SPEC.md §8.4). */
|
|
293
|
+
// @ts-ignore: decorator
|
|
294
|
+
@inline export function affineMap(block: i32): usize {
|
|
295
|
+
return VRAM + <usize>block * 0x800;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** Address of 4bpp sprite tile `index` (0–1023). */
|
|
299
|
+
// @ts-ignore: decorator
|
|
300
|
+
@inline export function objTile(index: i32): usize {
|
|
301
|
+
return OBJ_VRAM + (<usize>index << 5);
|
|
302
|
+
}
|
|
303
|
+
/** Fills a 32-byte 4bpp tile with one byte value, e.g. 0x11 for solid color 1. */
|
|
304
|
+
// @ts-ignore: decorator
|
|
305
|
+
@inline export function fillTile(tile: usize, byte: u8): void {
|
|
306
|
+
memory.fill(tile, byte, 32);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Address of screen block `block` (0–31): a 32×32 map of u16 entries, row-major. */
|
|
310
|
+
// @ts-ignore: decorator
|
|
311
|
+
@inline export function screenBlock(block: i32): usize {
|
|
312
|
+
return VRAM + <usize>block * 0x800;
|
|
313
|
+
}
|
|
314
|
+
/** Sets entry `i` (0–1023) of screen block `block`. */
|
|
315
|
+
// @ts-ignore: decorator
|
|
316
|
+
@inline export function setScreenEntry(block: i32, i: i32, e: u16): void {
|
|
317
|
+
store<u16>(screenBlock(block) + (<usize>i << 1), e);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Background map entries (SPEC.md §8.2). */
|
|
321
|
+
export namespace screenEntry {
|
|
322
|
+
// @ts-ignore: decorator
|
|
323
|
+
@inline export function make(tile: u16, bank: u16): u16 {
|
|
324
|
+
return (tile & 0x3ff) | ((bank & 0xf) << 12);
|
|
325
|
+
}
|
|
326
|
+
// @ts-ignore: decorator
|
|
327
|
+
@inline export function hflip(e: u16): u16 {
|
|
328
|
+
return e | (1 << 10);
|
|
329
|
+
}
|
|
330
|
+
// @ts-ignore: decorator
|
|
331
|
+
@inline export function vflip(e: u16): u16 {
|
|
332
|
+
return e | (1 << 11);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ---- Sprites (SPEC.md §9) ------------------------------------------------------
|
|
337
|
+
|
|
338
|
+
/** Sprite sizes as shape × 4 + size (SPEC.md §9.2). */
|
|
339
|
+
export enum SpriteSize {
|
|
340
|
+
S8x8 = 0,
|
|
341
|
+
S16x16 = 1,
|
|
342
|
+
S32x32 = 2,
|
|
343
|
+
S64x64 = 3,
|
|
344
|
+
W16x8 = 4,
|
|
345
|
+
W32x8 = 5,
|
|
346
|
+
W32x16 = 6,
|
|
347
|
+
W64x32 = 7,
|
|
348
|
+
T8x16 = 8,
|
|
349
|
+
T8x32 = 9,
|
|
350
|
+
T16x32 = 10,
|
|
351
|
+
T32x64 = 11,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** Width in pixels of a sprite size. */
|
|
355
|
+
export function spriteWidth(s: SpriteSize): i32 {
|
|
356
|
+
const shape = <i32>s >> 2, size = <i32>s & 3;
|
|
357
|
+
if (shape == 0) return 8 << size;
|
|
358
|
+
if (shape == 1) return size == 0 ? 16 : size == 3 ? 64 : 32;
|
|
359
|
+
return size == 3 ? 32 : size == 2 ? 16 : 8;
|
|
360
|
+
}
|
|
361
|
+
/** Height in pixels of a sprite size. */
|
|
362
|
+
export function spriteHeight(s: SpriteSize): i32 {
|
|
363
|
+
const shape = <i32>s >> 2, size = <i32>s & 3;
|
|
364
|
+
if (shape == 0) return 8 << size;
|
|
365
|
+
if (shape == 1) return size == 3 ? 32 : size == 2 ? 16 : 8;
|
|
366
|
+
return size == 0 ? 16 : size == 3 ? 64 : 32;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** One OAM entry's three attributes, packed as attr0 | attr1 << 16 | attr2 << 32. */
|
|
370
|
+
export type Sprite = u64;
|
|
371
|
+
|
|
372
|
+
/** Builders for sprites (SPEC.md §9.1), e.g. `sprite.affine(sprite.make(x, y, SpriteSize.S16x16, 0), 0, true)`. */
|
|
373
|
+
export namespace sprite {
|
|
374
|
+
export const HIDDEN: Sprite = <u64>OBJ_HIDDEN;
|
|
375
|
+
|
|
376
|
+
// @ts-ignore: decorator
|
|
377
|
+
@inline export function attr0(s: Sprite): u16 {
|
|
378
|
+
return <u16>s;
|
|
379
|
+
}
|
|
380
|
+
// @ts-ignore: decorator
|
|
381
|
+
@inline export function attr1(s: Sprite): u16 {
|
|
382
|
+
return <u16>(s >> 16);
|
|
383
|
+
}
|
|
384
|
+
// @ts-ignore: decorator
|
|
385
|
+
@inline export function attr2(s: Sprite): u16 {
|
|
386
|
+
return <u16>(s >> 32);
|
|
387
|
+
}
|
|
388
|
+
// @ts-ignore: decorator
|
|
389
|
+
@inline export function pack(a0: u16, a1: u16, a2: u16): Sprite {
|
|
390
|
+
return <u64>a0 | (<u64>a1 << 16) | (<u64>a2 << 32);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** A sprite with its top-left at screen (x, y), using `tile` and bank 0. */
|
|
394
|
+
export function make(x: i32, y: i32, size: SpriteSize, tile: u16): Sprite {
|
|
395
|
+
const shape = <u16>(<i32>size >> 2), sz = <u16>(<i32>size & 3);
|
|
396
|
+
return pack(<u16>(y & 0xff) | (shape << 14), <u16>(x & 0x1ff) | (sz << 14), tile & 0x3ff);
|
|
397
|
+
}
|
|
398
|
+
export function bank(s: Sprite, b: u16): Sprite {
|
|
399
|
+
return pack(attr0(s), attr1(s), (attr2(s) & 0x0fff) | ((b & 0xf) << 12));
|
|
400
|
+
}
|
|
401
|
+
export function priority(s: Sprite, p: u16): Sprite {
|
|
402
|
+
return pack(attr0(s), attr1(s), (attr2(s) & ~0x0c00) | ((p & 3) << 10));
|
|
403
|
+
}
|
|
404
|
+
export function hflip(s: Sprite, on: bool): Sprite {
|
|
405
|
+
return pack(attr0(s), on ? attr1(s) | 0x1000 : attr1(s) & ~0x1000, attr2(s));
|
|
406
|
+
}
|
|
407
|
+
export function vflip(s: Sprite, on: bool): Sprite {
|
|
408
|
+
return pack(attr0(s), on ? attr1(s) | 0x2000 : attr1(s) & ~0x2000, attr2(s));
|
|
409
|
+
}
|
|
410
|
+
export function semiTransparent(s: Sprite): Sprite {
|
|
411
|
+
return pack((attr0(s) & ~0x0c00) | 0x0400, attr1(s), attr2(s));
|
|
412
|
+
}
|
|
413
|
+
export function window(s: Sprite): Sprite {
|
|
414
|
+
return pack((attr0(s) & ~0x0c00) | 0x0800, attr1(s), attr2(s));
|
|
415
|
+
}
|
|
416
|
+
export function mosaic(s: Sprite): Sprite {
|
|
417
|
+
return pack(attr0(s) | 0x1000, attr1(s), attr2(s));
|
|
418
|
+
}
|
|
419
|
+
export function bpp8(s: Sprite): Sprite {
|
|
420
|
+
return pack(attr0(s) | 0x2000, attr1(s), attr2(s));
|
|
421
|
+
}
|
|
422
|
+
/** Uses affine matrix `slot` (0–31). With `doubleSize` the bounding box
|
|
423
|
+
* doubles so rotated or enlarged pixels are not clipped. */
|
|
424
|
+
export function affine(s: Sprite, slot: u16, doubleSize: bool): Sprite {
|
|
425
|
+
const a0 = attr0(s) | 0x0100 | (doubleSize ? 0x0200 : 0);
|
|
426
|
+
const a1 = (attr1(s) & ~0x3e00) | ((slot & 31) << 9);
|
|
427
|
+
return pack(<u16>a0, a1, attr2(s));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** An affine matrix in 8.8 fixed point, mapping screen to texture space,
|
|
432
|
+
* packed as pa | pb << 16 | pc << 32 | pd << 48. */
|
|
433
|
+
export type AffineMatrix = u64;
|
|
434
|
+
|
|
435
|
+
export namespace affine {
|
|
436
|
+
export const IDENTITY: AffineMatrix = 256 | (<u64>256 << 48);
|
|
437
|
+
|
|
438
|
+
// @ts-ignore: decorator
|
|
439
|
+
@inline export function make(pa: i16, pb: i16, pc: i16, pd: i16): AffineMatrix {
|
|
440
|
+
return <u64><u16>pa | (<u64><u16>pb << 16) | (<u64><u16>pc << 32) | (<u64><u16>pd << 48);
|
|
441
|
+
}
|
|
442
|
+
// @ts-ignore: decorator
|
|
443
|
+
@inline export function pa(m: AffineMatrix): i16 {
|
|
444
|
+
return <i16>m;
|
|
445
|
+
}
|
|
446
|
+
// @ts-ignore: decorator
|
|
447
|
+
@inline export function pb(m: AffineMatrix): i16 {
|
|
448
|
+
return <i16>(m >> 16);
|
|
449
|
+
}
|
|
450
|
+
// @ts-ignore: decorator
|
|
451
|
+
@inline export function pc(m: AffineMatrix): i16 {
|
|
452
|
+
return <i16>(m >> 32);
|
|
453
|
+
}
|
|
454
|
+
// @ts-ignore: decorator
|
|
455
|
+
@inline export function pd(m: AffineMatrix): i16 {
|
|
456
|
+
return <i16>(m >> 48);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function clamp16(v: i32): i16 {
|
|
460
|
+
return <i16>(v < -32768 ? -32768 : v > 32767 ? 32767 : v);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Rotates counter-clockwise by `angle` 1/256 turns and scales by (sx, sy)
|
|
464
|
+
* in 8.8 fixed point (256 = 1×, nonzero). Integer math, identical in
|
|
465
|
+
* every WASM-AD SDK. */
|
|
466
|
+
export function rotateScaleFixed(angle: i32, sx: i32, sy: i32): AffineMatrix {
|
|
467
|
+
const s = fixed.sin(angle), c = fixed.cos(angle);
|
|
468
|
+
return make(clamp16((c * 4) / sx), clamp16((-s * 4) / sx), clamp16((s * 4) / sy), clamp16((c * 4) / sy));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** OAM: 128 sprites, plus affine matrices stored in the gaps. */
|
|
473
|
+
export namespace oam {
|
|
474
|
+
export function set(i: i32, s: Sprite): void {
|
|
475
|
+
const at = OAM + (<usize>i << 3);
|
|
476
|
+
store<u16>(at, sprite.attr0(s));
|
|
477
|
+
store<u16>(at + 2, sprite.attr1(s));
|
|
478
|
+
store<u16>(at + 4, sprite.attr2(s));
|
|
479
|
+
}
|
|
480
|
+
export function hide(i: i32): void {
|
|
481
|
+
set(i, sprite.HIDDEN);
|
|
482
|
+
}
|
|
483
|
+
export function hideAll(): void {
|
|
484
|
+
for (let i = 0; i < 128; i++) hide(i);
|
|
485
|
+
}
|
|
486
|
+
export function setAffine(slot: i32, m: AffineMatrix): void {
|
|
487
|
+
const at = OAM + <usize>slot * 32 + 6;
|
|
488
|
+
store<u16>(at, <u16>affine.pa(m));
|
|
489
|
+
store<u16>(at + 8, <u16>affine.pb(m));
|
|
490
|
+
store<u16>(at + 16, <u16>affine.pc(m));
|
|
491
|
+
store<u16>(at + 24, <u16>affine.pd(m));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Sets affine background `bg` (2 or 3) to matrix `m`, placed so that texture
|
|
496
|
+
* pixel (texX, texY) appears at screen pixel (scrX, scrY). Rotation and
|
|
497
|
+
* scaling then happen around that point. */
|
|
498
|
+
export function setBgAffine(bg: i32, m: AffineMatrix, texX: i32, texY: i32, scrX: i32, scrY: i32): void {
|
|
499
|
+
const at = reg.BG2PA + <usize>(bg - 2) * 0x10;
|
|
500
|
+
const pa = <i32>affine.pa(m), pb = <i32>affine.pb(m), pc = <i32>affine.pc(m), pd = <i32>affine.pd(m);
|
|
501
|
+
store<u16>(at, <u16>pa);
|
|
502
|
+
store<u16>(at + 2, <u16>pb);
|
|
503
|
+
store<u16>(at + 4, <u16>pc);
|
|
504
|
+
store<u16>(at + 6, <u16>pd);
|
|
505
|
+
store<u32>(at + 8, <u32>((texX << 8) - pa * scrX - pb * scrY));
|
|
506
|
+
store<u32>(at + 12, <u32>((texY << 8) - pc * scrX - pd * scrY));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// ---- Sound (SPEC.md §14) -------------------------------------------------------
|
|
510
|
+
|
|
511
|
+
/** Sound: four GBA-style channels written through `reg.SOUND*`, with
|
|
512
|
+
* builders for their fields, and setup:
|
|
513
|
+
*
|
|
514
|
+
* sound.enable();
|
|
515
|
+
* write16(reg.SOUND2CNT_L, sound.DUTY_50 | sound.envelope(12, 2, false));
|
|
516
|
+
* write16(reg.SOUND2CNT_H, sound.START | sound.rate(440));
|
|
517
|
+
*/
|
|
518
|
+
export namespace sound {
|
|
519
|
+
/** Starts (or restarts) a channel; the host clears it. */
|
|
520
|
+
export const START: u16 = 1 << 15;
|
|
521
|
+
/** Stops the channel when its length runs out. */
|
|
522
|
+
export const LENGTH: u16 = 1 << 14;
|
|
523
|
+
|
|
524
|
+
export const DUTY_12: u16 = 0 << 6;
|
|
525
|
+
export const DUTY_25: u16 = 1 << 6;
|
|
526
|
+
export const DUTY_50: u16 = 2 << 6;
|
|
527
|
+
export const DUTY_75: u16 = 3 << 6;
|
|
528
|
+
|
|
529
|
+
/** `SOUND3CNT_L`: the wave channel is on. */
|
|
530
|
+
export const WAVE_ON: u16 = 1 << 7;
|
|
531
|
+
/** `SOUND3CNT_L`: play bank 1 (or start with it, with `WAVE_64`). */
|
|
532
|
+
export const WAVE_BANK1: u16 = 1 << 6;
|
|
533
|
+
/** `SOUND3CNT_L`: play both banks as one 64-sample wave. */
|
|
534
|
+
export const WAVE_64: u16 = 1 << 5;
|
|
535
|
+
/** `SOUND3CNT_H` volumes. */
|
|
536
|
+
export const WAVE_100: u16 = 1 << 13;
|
|
537
|
+
export const WAVE_50: u16 = 2 << 13;
|
|
538
|
+
export const WAVE_25: u16 = 3 << 13;
|
|
539
|
+
export const WAVE_75: u16 = 1 << 15;
|
|
540
|
+
|
|
541
|
+
/** Turns sound on with every channel on both sides at full volume. */
|
|
542
|
+
export function enable(): void {
|
|
543
|
+
store<u16>(reg.SOUNDCNT_X, 0x80);
|
|
544
|
+
store<u16>(reg.SOUNDCNT_L, 0xff77);
|
|
545
|
+
store<u16>(reg.SOUNDCNT_H, 0x330e);
|
|
546
|
+
}
|
|
547
|
+
/** Turns sound off; every channel stops. */
|
|
548
|
+
export function disable(): void {
|
|
549
|
+
store<u16>(reg.SOUNDCNT_X, 0);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/** The rate field for a square channel playing `hz` (64 Hz and up); the
|
|
553
|
+
* wave channel plays an octave lower at the same rate. */
|
|
554
|
+
// @ts-ignore: decorator
|
|
555
|
+
@inline export function rate(hz: u32): u16 {
|
|
556
|
+
return hz < 64 ? 0 : <u16>(2048 - 131072 / hz);
|
|
557
|
+
}
|
|
558
|
+
/** `SOUND1CNT_L`: every `time` 128ths of a second (1–7, 0 = off), the
|
|
559
|
+
* rate changes by rate >> `shift` (1–7), down if `decrease`. */
|
|
560
|
+
// @ts-ignore: decorator
|
|
561
|
+
@inline export function sweep(time: u16, shift: u16, decrease: bool): u16 {
|
|
562
|
+
return ((time & 7) << 4) | (shift & 7) | (decrease ? 8 : 0);
|
|
563
|
+
}
|
|
564
|
+
/** The envelope fields: start at `volume` (0–15), stepping by one every
|
|
565
|
+
* `step` 64ths of a second (0 = constant), up if `increase`. */
|
|
566
|
+
// @ts-ignore: decorator
|
|
567
|
+
@inline export function envelope(volume: u16, step: u16, increase: bool): u16 {
|
|
568
|
+
return ((volume & 15) << 12) | ((step & 7) << 8) | (increase ? 1 << 11 : 0);
|
|
569
|
+
}
|
|
570
|
+
/** The length field of channels 1, 2 and 4 for `n` 256ths of a second
|
|
571
|
+
* (1–64); it only counts with `LENGTH` set. Channel 3 takes 256 − n. */
|
|
572
|
+
// @ts-ignore: decorator
|
|
573
|
+
@inline export function length(n: u16): u16 {
|
|
574
|
+
return (64 - n) & 63;
|
|
575
|
+
}
|
|
576
|
+
/** `SOUND4CNT_H`: noise from a divider (0–7) and shift (0–13), with the
|
|
577
|
+
* 7-bit register if `short` (more tonal). */
|
|
578
|
+
// @ts-ignore: decorator
|
|
579
|
+
@inline export function noise(divider: u16, shift: u16, short: bool): u16 {
|
|
580
|
+
return (divider & 7) | ((shift & 15) << 4) | (short ? 8 : 0);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Address of wave RAM: two banks of 16 bytes, each 32 4-bit samples, high nibble first. */
|
|
584
|
+
// @ts-ignore: decorator
|
|
585
|
+
@inline export function waveRam(): usize {
|
|
586
|
+
return WAVE_RAM;
|
|
587
|
+
}
|
|
588
|
+
/** Address of the 28 KB of sample RAM (signed 8-bit) that the sample channels play from. */
|
|
589
|
+
// @ts-ignore: decorator
|
|
590
|
+
@inline export function sampleRam(): usize {
|
|
591
|
+
return SAMPLE_RAM;
|
|
592
|
+
}
|
|
593
|
+
/** Which channels are playing: bits 0–3 channels 1–4, bits 4 and 5 sample
|
|
594
|
+
* channels A and B, as of the end of the last frame. */
|
|
595
|
+
// @ts-ignore: decorator
|
|
596
|
+
@inline export function status(): u16 {
|
|
597
|
+
return load<u16>(reg.SOUNDCNT_X) & 0x3f;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/** The two sample channels, A (0) and B (1), playing signed 8-bit samples
|
|
602
|
+
* from sample RAM (SPEC.md §14.6). */
|
|
603
|
+
export namespace pcm {
|
|
604
|
+
// @ts-ignore: decorator
|
|
605
|
+
@inline function base(ch: i32): usize {
|
|
606
|
+
return ch == 0 ? reg.PCMA_SAD : reg.PCMB_SAD;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/** Plays `len` samples from sample RAM offset `start` at `rate` Hz and `volume` 0–64, once. */
|
|
610
|
+
export function play(ch: i32, start: u16, len: u16, rate: u16, volume: u16): void {
|
|
611
|
+
const b = base(ch);
|
|
612
|
+
store<u16>(b, start);
|
|
613
|
+
store<u16>(b + 2, len);
|
|
614
|
+
store<u16>(b + 6, rate);
|
|
615
|
+
store<u16>(b + 8, sound.START | (volume < 64 ? volume : 64));
|
|
616
|
+
}
|
|
617
|
+
/** Like `play`, then repeats from sample `loopAt` until stopped. */
|
|
618
|
+
export function playLooped(ch: i32, start: u16, len: u16, loopAt: u16, rate: u16, volume: u16): void {
|
|
619
|
+
store<u16>(base(ch) + 4, loopAt);
|
|
620
|
+
play(ch, start, len, rate, volume);
|
|
621
|
+
store<u16>(base(ch) + 8, load<u16>(base(ch) + 8) | (1 << 13));
|
|
622
|
+
}
|
|
623
|
+
export function stop(ch: i32): void {
|
|
624
|
+
store<u16>(base(ch) + 8, 1 << 14);
|
|
625
|
+
}
|
|
626
|
+
/** The playback position, in samples from the start, as of the end of the last frame. */
|
|
627
|
+
export function position(ch: i32): u16 {
|
|
628
|
+
return load<u16>(base(ch) + 10);
|
|
629
|
+
}
|
|
630
|
+
export function playing(ch: i32): bool {
|
|
631
|
+
return (sound.status() & (1 << (4 + ch))) != 0;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ---- Fixed-point trigonometry --------------------------------------------------
|
|
636
|
+
|
|
637
|
+
/** Fixed-point trigonometry shared by all WASM-AD SDKs, so carts in any
|
|
638
|
+
* language compute bit-identical results (SPEC.md §12). */
|
|
639
|
+
export namespace fixed {
|
|
640
|
+
/** sin over a quarter turn in 1/256-turn steps, scaled by 16384 (Q14). */
|
|
641
|
+
export const SIN_Q14: usize = memory.data<i32>([
|
|
642
|
+
0, 402, 804, 1205, 1606, 2006, 2404, 2801, 3196, 3590, 3981, 4370, 4756, 5139, 5520, 5897, 6270, 6639, 7005,
|
|
643
|
+
7366, 7723, 8076, 8423, 8765, 9102, 9434, 9760, 10080, 10394, 10702, 11003, 11297, 11585, 11866, 12140, 12406,
|
|
644
|
+
12665, 12916, 13160, 13395, 13623, 13842, 14053, 14256, 14449, 14635, 14811, 14978, 15137, 15286, 15426, 15557,
|
|
645
|
+
15679, 15791, 15893, 15986, 16069, 16143, 16207, 16261, 16305, 16340, 16364, 16379, 16384,
|
|
646
|
+
]);
|
|
647
|
+
|
|
648
|
+
// @ts-ignore: decorator
|
|
649
|
+
@inline function q(i: i32): i32 {
|
|
650
|
+
return load<i32>(SIN_Q14 + (<usize>i << 2));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/** sin of `angle` 1/256 turns (only the low 8 bits count), in Q14. */
|
|
654
|
+
export function sin(angle: i32): i32 {
|
|
655
|
+
const a = angle & 255;
|
|
656
|
+
if (a < 64) return q(a);
|
|
657
|
+
if (a < 128) return q(128 - a);
|
|
658
|
+
if (a < 192) return -q(a - 128);
|
|
659
|
+
return -q(256 - a);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** cos of `angle` 1/256 turns, in Q14. */
|
|
663
|
+
export function cos(angle: i32): i32 {
|
|
664
|
+
return sin(angle + 64);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ---- Input, time, save RAM -------------------------------------------------------
|
|
669
|
+
|
|
670
|
+
/** Buttons held this frame. */
|
|
671
|
+
// @ts-ignore: decorator
|
|
672
|
+
@inline export function keys(): u16 {
|
|
673
|
+
return load<u16>(reg.KEYS);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** Edge detection over `KEYS`: call `buttons.update()` at the start of every
|
|
677
|
+
* frame. Its two words of state live in linear memory. */
|
|
678
|
+
export namespace buttons {
|
|
679
|
+
const STATE: usize = memory.data(4); // held: u16, prev: u16
|
|
680
|
+
|
|
681
|
+
export function update(): void {
|
|
682
|
+
store<u16>(STATE + 2, load<u16>(STATE));
|
|
683
|
+
store<u16>(STATE, keys());
|
|
684
|
+
}
|
|
685
|
+
export function held(k: u16): bool {
|
|
686
|
+
return (load<u16>(STATE) & k) != 0;
|
|
687
|
+
}
|
|
688
|
+
/** True only on the frame the button went down. */
|
|
689
|
+
export function pressed(k: u16): bool {
|
|
690
|
+
return (load<u16>(STATE) & ~load<u16>(STATE + 2) & k) != 0;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/** Frames since boot. */
|
|
695
|
+
// @ts-ignore: decorator
|
|
696
|
+
@inline export function frame(): u32 {
|
|
697
|
+
return load<u32>(reg.FRAME);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** The boot-time random seed, the cart's only entropy (SPEC.md §12). */
|
|
701
|
+
// @ts-ignore: decorator
|
|
702
|
+
@inline export function seed(): u32 {
|
|
703
|
+
return load<u32>(reg.SEED);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/** Address of the 32 KB battery-backed save RAM. */
|
|
707
|
+
// @ts-ignore: decorator
|
|
708
|
+
@inline export function saveRam(): usize {
|
|
709
|
+
return SAVE;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/** Whether the host restored save RAM from storage at boot. */
|
|
713
|
+
// @ts-ignore: decorator
|
|
714
|
+
@inline export function saveRestored(): bool {
|
|
715
|
+
return (load<u16>(reg.SAVESTAT) & 1) != 0;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** Asks the host to persist save RAM after this frame. */
|
|
719
|
+
// @ts-ignore: decorator
|
|
720
|
+
@inline export function requestSave(): void {
|
|
721
|
+
store<u16>(reg.SAVECNT, load<u16>(reg.SAVECNT) | 1);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// ---- Debug log -------------------------------------------------------------------
|
|
725
|
+
|
|
726
|
+
// @ts-ignore: decorator
|
|
727
|
+
@external("env", "trace")
|
|
728
|
+
declare function hostTrace(ptr: usize, len: i32): void;
|
|
729
|
+
|
|
730
|
+
/** Writes a message to the host's debug log, as UTF-8 (at most 1 KB). */
|
|
731
|
+
export function trace(msg: string): void {
|
|
732
|
+
const buf = memory.data(1024);
|
|
733
|
+
let n: usize = 0;
|
|
734
|
+
const len = msg.length;
|
|
735
|
+
for (let i = 0; i < len; i++) {
|
|
736
|
+
let c = <u32>msg.charCodeAt(i);
|
|
737
|
+
if (c >= 0xd800 && c < 0xdc00 && i + 1 < len) {
|
|
738
|
+
const lo = <u32>msg.charCodeAt(i + 1);
|
|
739
|
+
if (lo >= 0xdc00 && lo < 0xe000) {
|
|
740
|
+
c = 0x10000 + ((c - 0xd800) << 10) + (lo - 0xdc00);
|
|
741
|
+
i++;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const need: usize = c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
|
|
745
|
+
if (n + need > 1024) break;
|
|
746
|
+
if (need == 1) store<u8>(buf + n, c);
|
|
747
|
+
else if (need == 2) {
|
|
748
|
+
store<u8>(buf + n, 0xc0 | (c >> 6));
|
|
749
|
+
store<u8>(buf + n + 1, 0x80 | (c & 63));
|
|
750
|
+
} else if (need == 3) {
|
|
751
|
+
store<u8>(buf + n, 0xe0 | (c >> 12));
|
|
752
|
+
store<u8>(buf + n + 1, 0x80 | ((c >> 6) & 63));
|
|
753
|
+
store<u8>(buf + n + 2, 0x80 | (c & 63));
|
|
754
|
+
} else {
|
|
755
|
+
store<u8>(buf + n, 0xf0 | (c >> 18));
|
|
756
|
+
store<u8>(buf + n + 1, 0x80 | ((c >> 12) & 63));
|
|
757
|
+
store<u8>(buf + n + 2, 0x80 | ((c >> 6) & 63));
|
|
758
|
+
store<u8>(buf + n + 3, 0x80 | (c & 63));
|
|
759
|
+
}
|
|
760
|
+
n += need;
|
|
761
|
+
}
|
|
762
|
+
hostTrace(buf, <i32>n);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/** Replaces AssemblyScript's `env.abort` import (`--use abort=...` in asconfig.json):
|
|
766
|
+
* a failed assertion or bounds check traps. */
|
|
767
|
+
export function abort(message: string | null, file: string | null, line: u32, column: u32): void {
|
|
768
|
+
unreachable();
|
|
769
|
+
}
|