1// MIT License
2
3// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
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.
22
23#include "core/core.h"
24#if defined(TIC_BUILD_WITH_WASM)
25
26#define dbg(...) printf(__VA_ARGS__)
27//define dbg(...)
28
29
30
31#include "tools.h"
32
33#include <ctype.h>
34
35// Avoid redefining u* ans s*
36#define d_m3ShortTypesDefined 1
37typedef int8_t i8;
38typedef int16_t i16;
39typedef int32_t i32;
40typedef int64_t i64;
41typedef double f64;
42typedef float f32;
43
44#include "wasm3.h"
45#include "m3_exec_defs.h"
46#include "m3_exception.h"
47#include "m3_env.h"
48
49static const char TicCore[] = "_TIC80";
50
51IM3Function BDR_function;
52IM3Function SCN_function;
53IM3Function TIC_function;
54IM3Function BOOT_function;
55IM3Function MENU_function;
56
57#define FATAL(msg, ...) { printf("Error: [Fatal] " msg "\n", ##__VA_ARGS__); goto _onfatal; }
58#define WASM_STACK_SIZE 64*1024
59
60static
61M3Result SuppressLookupFailure (M3Result i_result)
62{
63 if (i_result == m3Err_functionLookupFailed)
64 return m3Err_none;
65 else
66 return i_result;
67}
68
69M3Result wasm_load (IM3Runtime runtime, const char* fn)
70{
71 M3Result result = m3Err_none;
72
73 u8* wasm = NULL;
74 u32 fsize = 0;
75
76 FILE* f = fopen (fn, "rb");
77 if (!f) {
78 return "cannot open file";
79 }
80 fseek (f, 0, SEEK_END);
81 fsize = ftell(f);
82 fseek (f, 0, SEEK_SET);
83
84 if (fsize < 8) {
85 return "file is too small";
86 } else if (fsize > 10*1024*1024) {
87 return "file too big";
88 }
89
90 wasm = (u8*) malloc(fsize);
91 if (!wasm) {
92 return "cannot allocate memory for wasm binary";
93 }
94
95 if (fread (wasm, 1, fsize, f) != fsize) {
96 return "cannot read file";
97 }
98 fclose (f);
99
100 IM3Module module;
101 result = m3_ParseModule (runtime->environment, &module, wasm, fsize);
102 if (result) return result;
103
104 result = m3_LoadModule (runtime, module);
105 if (result) return result;
106
107 return result;
108}
109
110/*
111M3Result wasm_call (IM3Runtime runtime, const char* name, int argc, void* argv)
112{
113 M3Result result = m3Err_none;
114
115 IM3Function func;
116 result = m3_FindFunction (&func, runtime, name);
117 if (result) return result;
118
119 result = m3_CallWithArgs (func, argc, argv);
120 if (result) return result;
121
122 return result;
123}
124*/
125M3Result wasm_dump(IM3Runtime runtime)
126{
127 uint32_t len;
128 uint8_t* mem = m3_GetMemory(runtime, &len, 0);
129 if (mem) {
130 FILE* f = fopen ("wasm3_dump.bin", "wb");
131 if (!f) {
132 return "cannot open file";
133 }
134 if (fwrite (mem, 1, len, f) != len) {
135 return "cannot write file";
136 }
137 fclose (f);
138 }
139 return m3Err_none;
140}
141
142static tic_core* getWasmCore(IM3Runtime ctx)
143{
144 return (tic_core*)ctx->userdata;
145}
146
147m3ApiRawFunction(wasmtic_line)
148{
149 m3ApiGetArg (float, x0)
150 m3ApiGetArg (float, y0)
151 m3ApiGetArg (float, x1)
152 m3ApiGetArg (float, y1)
153 m3ApiGetArg (int8_t, color)
154
155 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
156
157 tic_api_line(tic, x0, y0, x1, y1, color);
158
159 m3ApiSuccess();
160}
161
162m3ApiRawFunction(wasmtic_circ)
163{
164 m3ApiGetArg (int32_t, x)
165 m3ApiGetArg (int32_t, y)
166 m3ApiGetArg (int32_t, radius)
167 m3ApiGetArg (int8_t, color)
168
169 if (radius >= 0)
170 {
171 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
172 tic_api_circ(tic, x, y, radius, color);
173 }
174
175 m3ApiSuccess();
176}
177
178m3ApiRawFunction(wasmtic_circb)
179{
180 m3ApiGetArg (int32_t, x)
181 m3ApiGetArg (int32_t, y)
182 m3ApiGetArg (int32_t, radius)
183 m3ApiGetArg (int8_t, color)
184
185 if (radius >= 0)
186 {
187 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
188 tic_api_circb(tic, x, y, radius, color);
189 }
190
191 m3ApiSuccess();
192}
193
194m3ApiRawFunction(wasmtic_elli)
195{
196 m3ApiGetArg (int32_t, x)
197 m3ApiGetArg (int32_t, y)
198 m3ApiGetArg (int32_t, a)
199 m3ApiGetArg (int32_t, b)
200 m3ApiGetArg (int8_t, color)
201
202 if (a >= 0 && b >= 0)
203 {
204 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
205 tic_api_elli(tic, x, y, a, b, color);
206 }
207
208 m3ApiSuccess();
209}
210
211m3ApiRawFunction(wasmtic_ellib)
212{
213 m3ApiGetArg (int32_t, x)
214 m3ApiGetArg (int32_t, y)
215 m3ApiGetArg (int32_t, a)
216 m3ApiGetArg (int32_t, b)
217 m3ApiGetArg (int8_t, color)
218
219 if (a >= 0 && b >= 0)
220 {
221 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
222 tic_api_ellib(tic, x, y, a, b, color);
223 }
224
225 m3ApiSuccess();
226}
227
228m3ApiRawFunction(wasmtic_rect)
229{
230 m3ApiGetArg (int32_t, x)
231 m3ApiGetArg (int32_t, y)
232 m3ApiGetArg (int32_t, w)
233 m3ApiGetArg (int32_t, h)
234 m3ApiGetArg (int8_t, color)
235
236 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
237
238 tic_api_rect(tic, x, y, w, h, color);
239
240 m3ApiSuccess();
241}
242
243m3ApiRawFunction(wasmtic_rectb)
244{
245 m3ApiGetArg (int32_t, x)
246 m3ApiGetArg (int32_t, y)
247 m3ApiGetArg (int32_t, w)
248 m3ApiGetArg (int32_t, h)
249 m3ApiGetArg (int8_t, color)
250
251 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
252
253 tic_api_rectb(tic, x, y, w, h, color);
254
255 m3ApiSuccess();
256}
257
258m3ApiRawFunction(wasmtic_tri)
259{
260 m3ApiGetArg (float, x1)
261 m3ApiGetArg (float, y1)
262 m3ApiGetArg (float, x2)
263 m3ApiGetArg (float, y2)
264 m3ApiGetArg (float, x3)
265 m3ApiGetArg (float, y3)
266 m3ApiGetArg (int8_t, color)
267
268 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
269
270 tic_api_tri(tic, x1, y1, x2, y2, x3, y3, color);
271
272 m3ApiSuccess();
273}
274
275// ttri x1 y1 x2 y2 x3 y3 u1 v1 u2 v2 u3 v3 [texsrc=0] [trans=-1] [z1 z2 z3 depth]
276m3ApiRawFunction(wasmtic_ttri)
277{
278 m3ApiGetArg (float, x1)
279 m3ApiGetArg (float, y1)
280 m3ApiGetArg (float, x2)
281 m3ApiGetArg (float, y2)
282 m3ApiGetArg (float, x3)
283 m3ApiGetArg (float, y3)
284 m3ApiGetArg (float, u1)
285 m3ApiGetArg (float, v1)
286 m3ApiGetArg (float, u2)
287 m3ApiGetArg (float, v2)
288 m3ApiGetArg (float, u3)
289 m3ApiGetArg (float, v3)
290 m3ApiGetArg (int32_t, texsrc)
291 m3ApiGetArgMem (u8*, trans_colors)
292 m3ApiGetArg (int8_t, colorCount)
293 m3ApiGetArg (float, z1)
294 m3ApiGetArg (float, z2)
295 m3ApiGetArg (float, z3)
296 m3ApiGetArg (bool, depth)
297 if (trans_colors == NULL) {
298 colorCount = 0;
299 }
300
301 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
302
303 tic_api_ttri(tic, x1, y1, x2, y2, x3, y3,
304 u1, v1, u2, v2, u3, v3, texsrc, trans_colors, colorCount, z1, z2, z3, depth);
305
306 m3ApiSuccess();
307}
308
309
310m3ApiRawFunction(wasmtic_trib)
311{
312 m3ApiGetArg (float, x1)
313 m3ApiGetArg (float, y1)
314 m3ApiGetArg (float, x2)
315 m3ApiGetArg (float, y2)
316 m3ApiGetArg (float, x3)
317 m3ApiGetArg (float, y3)
318 m3ApiGetArg (int8_t, color)
319
320 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
321
322 tic_api_trib(tic, x1, y1, x2, y2, x3, y3, color);
323
324 m3ApiSuccess();
325}
326
327m3ApiRawFunction(wasmtic_cls)
328{
329 m3ApiGetArg (int8_t, color)
330
331 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
332
333 color = (color == -1) ? 0 : color;
334
335 tic_api_cls(tic, color);
336
337 m3ApiSuccess();
338}
339
340m3ApiRawFunction(wasmtic_btn)
341{
342 // TODO: should this be boolean, how to deal with
343 // this being multi-typed
344 m3ApiReturnType (int32_t)
345
346 m3ApiGetArg (int32_t, index)
347
348 tic_core* core = getWasmCore(runtime);
349
350 // -1 is a default placeholder here for `id`, but one that `tic_api_btn` already understands, so
351 // it just gets passed straight thru
352
353 m3ApiReturn(tic_api_btn((tic_mem *)core, index));
354
355 m3ApiSuccess();
356}
357
358m3ApiRawFunction(wasmtic_btnp)
359{
360 m3ApiReturnType (bool)
361
362 m3ApiGetArg (int32_t, index)
363 m3ApiGetArg (int32_t, hold)
364 m3ApiGetArg (int32_t, period)
365
366 tic_core* core = getWasmCore(runtime);
367
368 // -1 is the "default" placeholder for index, hold, and period but the TIC side API
369 // knows this so we don't need to do any transation, we can just pass the -1 values
370 // straight thru
371
372 m3ApiReturn(tic_api_btnp((tic_mem *)core, index, hold, period));
373
374 m3ApiSuccess();
375}
376
377m3ApiRawFunction(wasmtic_key)
378{
379 m3ApiReturnType (int32_t)
380
381 m3ApiGetArg (int32_t, index)
382
383 if (index == -1) {
384 index = tic_key_unknown;
385 }
386
387 tic_core* core = getWasmCore(runtime);
388
389 m3ApiReturn(tic_api_key((tic_mem *)core, index));
390
391 m3ApiSuccess();
392}
393
394m3ApiRawFunction(wasmtic_keyp)
395{
396 m3ApiReturnType (int32_t)
397
398 m3ApiGetArg (int8_t, index)
399 m3ApiGetArg (int32_t, hold)
400 m3ApiGetArg (int32_t, period)
401
402 if (index == -1) {
403 index = tic_key_unknown;
404 }
405
406 tic_core* core = getWasmCore(runtime);
407
408 m3ApiReturn(tic_api_keyp((tic_mem *)core, index, hold, period));
409
410 m3ApiSuccess();
411}
412
413m3ApiRawFunction(wasmtic_fget)
414{
415 m3ApiReturnType (bool)
416
417 m3ApiGetArg (int32_t, sprite_index);
418 m3ApiGetArg (int8_t, flag);
419
420 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
421
422 m3ApiReturn(tic_api_fget(tic, sprite_index, flag));
423
424 m3ApiSuccess();
425}
426
427m3ApiRawFunction(wasmtic_fset)
428{
429 m3ApiGetArg (int32_t, sprite_index);
430 m3ApiGetArg (int8_t, flag);
431 m3ApiGetArg (bool, value);
432
433 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
434
435 tic_api_fset(tic, sprite_index, flag, value);
436
437 m3ApiSuccess();
438}
439
440m3ApiRawFunction(wasmtic_mget)
441{
442 m3ApiReturnType (int32_t)
443
444 m3ApiGetArg (int32_t, x);
445 m3ApiGetArg (int32_t, y);
446
447 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
448
449 m3ApiReturn(tic_api_mget(tic, x, y));
450
451 m3ApiSuccess();
452}
453
454m3ApiRawFunction(wasmtic_mset)
455{
456 m3ApiGetArg (int32_t, x);
457 m3ApiGetArg (int32_t, y);
458 m3ApiGetArg (int32_t, value);
459
460 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
461
462 tic_api_mset(tic, x, y, value);
463
464 m3ApiSuccess();
465}
466
467
468m3ApiRawFunction(wasmtic_peek)
469{
470 m3ApiReturnType (int8_t)
471
472 m3ApiGetArg (int32_t, address)
473 m3ApiGetArg (int8_t, bits)
474
475 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
476
477 m3ApiReturn(tic_api_peek(tic, address, bits));
478
479 m3ApiSuccess();
480}
481
482m3ApiRawFunction(wasmtic_peek4)
483{
484 m3ApiReturnType (int8_t)
485
486 m3ApiGetArg (int32_t, address)
487
488 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
489
490 m3ApiReturn(tic_api_peek4(tic, address));
491
492 m3ApiSuccess();
493}
494
495m3ApiRawFunction(wasmtic_peek2)
496{
497 m3ApiReturnType (int8_t)
498
499 m3ApiGetArg (int32_t, address)
500
501 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
502
503 m3ApiReturn(tic_api_peek2(tic, address));
504
505 m3ApiSuccess();
506}
507
508m3ApiRawFunction(wasmtic_peek1)
509{
510 m3ApiReturnType (int8_t)
511
512 m3ApiGetArg (int32_t, address)
513
514 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
515
516 m3ApiReturn(tic_api_peek1(tic, address));
517
518 m3ApiSuccess();
519}
520
521m3ApiRawFunction(wasmtic_poke)
522{
523 m3ApiGetArg (int32_t, address)
524 m3ApiGetArg (int8_t, value)
525 m3ApiGetArg (int8_t, bits)
526
527 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
528
529 tic_api_poke(tic, address, value, bits);
530
531 m3ApiSuccess();
532}
533
534m3ApiRawFunction(wasmtic_poke4)
535{
536
537 m3ApiGetArg (int32_t, address)
538 m3ApiGetArg (int8_t, value)
539
540 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
541
542 tic_api_poke4(tic, address, value);
543
544 m3ApiSuccess();
545}
546
547m3ApiRawFunction(wasmtic_poke2)
548{
549
550 m3ApiGetArg (int32_t, address)
551 m3ApiGetArg (int8_t, value)
552
553 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
554
555 tic_api_poke2(tic, address, value);
556
557 m3ApiSuccess();
558}
559
560m3ApiRawFunction(wasmtic_poke1)
561{
562
563 m3ApiGetArg (int32_t, address)
564 m3ApiGetArg (int8_t, value)
565
566 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
567
568 tic_api_poke1(tic, address, value);
569
570 m3ApiSuccess();
571}
572
573m3ApiRawFunction(wasmtic_pmem)
574{
575 m3ApiReturnType (uint32_t)
576
577 m3ApiGetArg (int32_t, address)
578 m3ApiGetArg (int64_t, value)
579 bool writeToStorage;
580
581 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
582
583 // read value
584 if (value == -1) {
585 writeToStorage = false;
586 };
587
588 // TODO: this should move into tic_api_pmem, should it not?
589 if (address >= TIC_PERSISTENT_SIZE) {
590 m3ApiReturn(0);
591 } else {
592 u32 val = tic_api_pmem(tic, address, value, writeToStorage);
593 m3ApiReturn(val);
594 }
595
596 m3ApiSuccess();
597}
598
599m3ApiRawFunction(wasmtic_pix)
600{
601 m3ApiReturnType (u8)
602
603 m3ApiGetArg (int32_t, x)
604 m3ApiGetArg (int32_t, y)
605 m3ApiGetArg (int8_t, color)
606
607 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
608
609 bool getPixel = color < 0;
610 u8 pix = tic_api_pix(tic, x, y, color, getPixel);
611 m3ApiReturn(pix);
612
613 m3ApiSuccess();
614}
615
616m3ApiRawFunction(wasmtic_spr)
617{
618 m3ApiGetArg (int32_t, index)
619 m3ApiGetArg (int32_t, x)
620 m3ApiGetArg (int32_t, y)
621 m3ApiGetArgMem (u8*, trans_colors)
622 m3ApiGetArg (int8_t, colorCount)
623 if (trans_colors == NULL) {
624 colorCount = 0;
625 }
626 m3ApiGetArg (int32_t, scale)
627 m3ApiGetArg (int32_t, flip)
628 m3ApiGetArg (int32_t, rotate)
629 m3ApiGetArg (int32_t, w)
630 m3ApiGetArg (int32_t, h)
631
632 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
633
634
635 // defaults
636 if (scale == -1) { scale = 1; }
637 if (flip == -1) { flip = 0; }
638 if (rotate == -1) { rotate = 0; }
639 if (w == -1) { w = 1; }
640 if (h == -1) { h = 1; }
641
642 tic_api_spr(tic, index, x, y, w, h, trans_colors, colorCount, scale, flip, rotate) ;
643
644 m3ApiSuccess();
645}
646
647m3ApiRawFunction(wasmtic_clip)
648{
649 m3ApiGetArg (int32_t, x)
650 m3ApiGetArg (int32_t, y)
651 m3ApiGetArg (int32_t, w)
652 m3ApiGetArg (int32_t, h)
653
654 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
655
656 // defaults
657 if (x == -1) { x = 0; }
658 if (y == -1) { y = 0; }
659 if (w == -1) { w = TIC80_WIDTH; }
660 if (h == -1) { h = TIC80_HEIGHT; }
661
662 tic_api_clip(tic, x, y, w, h);
663
664 m3ApiSuccess();
665}
666
667m3ApiRawFunction(wasmtic_map)
668{
669 m3ApiGetArg (int32_t, x)
670 m3ApiGetArg (int32_t, y)
671 m3ApiGetArg (int32_t, w)
672 m3ApiGetArg (int32_t, h)
673 m3ApiGetArg (int32_t, sx)
674 m3ApiGetArg (int32_t, sy)
675 m3ApiGetArgMem (u8*, trans_colors)
676 m3ApiGetArg (int8_t, colorCount)
677 if (trans_colors == NULL) {
678 colorCount = 0;
679 } m3ApiGetArg (int8_t, scale)
680 // TODO: actually test that this works
681 m3ApiGetArg (int32_t, remap)
682
683 // defaults
684 if (x == -1) { x = 0; }
685 if (y == -1) { y = 0; }
686 if (w == -1) { w = 30; }
687 if (h == -1) { h = 17; }
688 if (sx == -1) { sx = 0; }
689 if (sy == -1) { sy = 0; }
690 if (scale == -1) { scale = 1; }
691
692 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
693
694 tic_api_map(tic, x, y, w, h, sx, sy, trans_colors, colorCount, scale, NULL, NULL);
695
696 m3ApiSuccess();
697}
698
699m3ApiRawFunction(wasmtic_print)
700{
701 m3ApiReturnType (int32_t)
702
703 m3ApiGetArgMem (const char *, text)
704 m3ApiGetArg (int32_t, x)
705 m3ApiGetArg (int32_t, y)
706 m3ApiGetArg (int8_t, color)
707 // optional arguments, but we'll force people to pass them all
708 // let the pre-language APIs deal with simplifying
709 m3ApiGetArg (int8_t, fixed)
710 m3ApiGetArg (int8_t, scale)
711 m3ApiGetArg (int8_t, alt)
712
713 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
714
715 // TODO: how to deal with defaults when we can't pass -1 as a bool?
716
717 int32_t text_width;
718 if (scale==0) {
719 text_width = 0;
720 } else {
721 text_width = tic_api_print(tic, text, x, y, color, fixed, scale, alt);
722 }
723 m3ApiReturn(text_width);
724
725 m3ApiSuccess();
726}
727
728m3ApiRawFunction(wasmtic_font)
729{
730 m3ApiReturnType (int32_t)
731
732 m3ApiGetArgMem (const char *, text)
733 m3ApiGetArg (int32_t, x)
734 m3ApiGetArg (int32_t, y)
735 m3ApiGetArgMem (u8*, trans_colors)
736 m3ApiGetArg (int8_t, trans_count)
737 if (trans_colors == NULL) {
738 trans_count = 0;
739 }
740 m3ApiGetArg (int8_t, char_width)
741 m3ApiGetArg (int8_t, char_height)
742 m3ApiGetArg (bool, fixed)
743 m3ApiGetArg (int8_t, scale)
744 m3ApiGetArg (bool, alt)
745
746 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
747
748 if (scale == -1) { scale = 1; }
749 if (char_width == -1 ) { char_width = TIC_SPRITESIZE; }
750 if (char_height == -1 ) { char_height = TIC_SPRITESIZE; }
751
752 int32_t text_width;
753 if (scale==0) {
754 text_width = 0;
755 } else {
756 text_width = tic_api_font(tic, text, x, y, trans_colors, trans_count, char_width, char_height, fixed, scale, alt);
757 }
758 m3ApiReturn(text_width);
759
760 m3ApiSuccess();
761}
762
763// audio
764
765// id [note] [duration=-1] [channel=0] [volume=15] [speed=0]
766m3ApiRawFunction(wasmtic_sfx)
767{
768 m3ApiGetArg (int32_t, sfx_id);
769 m3ApiGetArg (int32_t, note);
770 m3ApiGetArg (int32_t, octave);
771 m3ApiGetArg (int32_t, duration);
772 m3ApiGetArg (int32_t, channel);
773 m3ApiGetArg (int32_t, volumeLeft);
774 m3ApiGetArg (int32_t, volumeRight);
775 m3ApiGetArg (int32_t, speed);
776
777 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
778
779 if (channel >= 0 && channel < TIC_SOUND_CHANNELS)
780 {
781 tic_api_sfx(tic, sfx_id, note, octave, duration, channel, volumeLeft & 0xf, volumeRight & 0xf, speed);
782 }
783
784 m3ApiSuccess();
785}
786
787
788m3ApiRawFunction(wasmtic_music)
789{
790 m3ApiGetArg (int32_t, track);
791 m3ApiGetArg (int32_t, frame);
792 m3ApiGetArg (int32_t, row);
793 m3ApiGetArg (bool, loop);
794 m3ApiGetArg (bool, sustain);
795 m3ApiGetArg (int32_t, tempo);
796 m3ApiGetArg (int32_t, speed);
797
798 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
799
800 // TODO: defaults... how with bool vs int
801
802 if(track > MUSIC_TRACKS - 1)
803 m3ApiTrap("invalid music track index");
804
805 tic_api_music(tic, track, frame, row, loop, sustain, tempo, speed);
806
807 m3ApiSuccess();
808}
809
810// memory
811
812m3ApiRawFunction(wasmtic_memset)
813{
814 m3ApiGetArg (int32_t, address);
815 m3ApiGetArg (int32_t, value);
816 m3ApiGetArg (int32_t, length);
817
818 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
819
820 tic_api_memset(tic, address, value, length);
821
822 m3ApiSuccess();
823}
824
825m3ApiRawFunction(wasmtic_memcpy)
826{
827 m3ApiGetArg (int32_t, dest);
828 m3ApiGetArg (int32_t, src);
829 m3ApiGetArg (int32_t, length);
830
831 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
832
833 tic_api_memcpy(tic, dest, src, length);
834
835 m3ApiSuccess();
836}
837
838
839m3ApiRawFunction(wasmtic_exit)
840{
841 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
842
843 tic_api_exit(tic);
844
845 m3ApiSuccess();
846}
847
848m3ApiRawFunction(wasmtic_sync)
849{
850 m3ApiGetArg (int32_t, mask);
851 m3ApiGetArg (int8_t, bank);
852 m3ApiGetArg (int8_t, tocart);
853
854 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
855
856 bool toCart;
857
858 if (mask == -1) {
859 mask = 0;
860 }
861 if (bank == -1) {
862 bank = 0;
863 }
864 toCart = (tocart == -1 || tocart == 0) ? false : true;
865
866 // TODO: how to throw error if bank out of bounds?
867 if (bank >=0 && bank < TIC_BANKS)
868 tic_api_sync(tic, mask, bank, toCart);
869
870 m3ApiSuccess();
871}
872
873m3ApiRawFunction(wasmtic_time)
874{
875 m3ApiReturnType (float) // 32 bit float
876
877 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
878
879 m3ApiReturn(tic_api_time(tic));
880
881 m3ApiSuccess();
882}
883
884m3ApiRawFunction(wasmtic_tstamp)
885{
886 m3ApiReturnType (uint32_t)
887
888 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
889
890 m3ApiReturn(tic_api_tstamp(tic));
891
892 m3ApiSuccess();
893}
894
895m3ApiRawFunction(wasmtic_trace)
896{
897 m3ApiGetArgMem(const char*, text);
898 m3ApiGetArg(int8_t, color);
899
900 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
901
902 if (color == -1) {
903 color = 15;
904 }
905
906 tic_api_trace(tic, text, color);
907
908 m3ApiSuccess();
909}
910
911m3ApiRawFunction(wasmtic_vbank)
912{
913 m3ApiReturnType(int8_t)
914 m3ApiGetArg(int8_t, bank);
915
916 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
917
918 uint8_t previous_bank = tic_api_vbank(tic, bank);
919 m3ApiReturn(previous_bank);
920
921 m3ApiSuccess();
922}
923
924
925// input
926
927m3ApiRawFunction(wasmtic_mouse)
928{
929 struct Mouse {
930 int16_t x;
931 int16_t y;
932 int8_t scrollx;
933 int8_t scrolly;
934 bool left;
935 bool middle;
936 bool right;
937 };
938
939 m3ApiGetArgMem(struct Mouse*, mouse_ptr_addy);
940
941 tic_mem* tic = (tic_mem*)getWasmCore(runtime);
942
943 struct Mouse* mouse_data = mouse_ptr_addy;
944 const tic80_mouse* mouse = &tic->ram->input.mouse;
945
946 tic_point pos = tic_api_mouse(tic);
947
948 mouse_data->x = pos.x;
949 mouse_data->y = pos.y;
950 mouse_data->left = mouse->left;
951 mouse_data->middle = mouse->middle;
952 mouse_data->right = mouse->right;
953 mouse_data->scrollx = mouse->scrollx;
954 mouse_data->scrolly = mouse->scrolly;
955
956 m3ApiSuccess();
957}
958
959
960
961M3Result linkTicAPI(IM3Module module)
962{
963 M3Result result = m3Err_none;
964 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "btn", "i(i)", &wasmtic_btn)));
965 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "btnp", "i(iii)", &wasmtic_btnp)));
966 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "clip", "v(iiii)", &wasmtic_clip)));
967 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "cls", "v(i)", &wasmtic_cls)));
968 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "circ", "v(iiii)", &wasmtic_circ)));
969 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "circb", "v(iiii)", &wasmtic_circb)));
970 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "elli", "v(iiiii)", &wasmtic_elli)));
971 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "ellib", "v(iiiii)", &wasmtic_ellib)));
972 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "exit", "v()", &wasmtic_exit)));
973 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "fget", "i(ii)", &wasmtic_fget)));
974 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "fset", "v(iii)", &wasmtic_fset)));
975 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "font", "i(*iiiiiiiii)", &wasmtic_font)));
976 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "key", "i(i)", &wasmtic_key)));
977 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "keyp", "i(iii)", &wasmtic_keyp)));
978 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "line", "v(ffffi)", &wasmtic_line)));
979 // TODO: needs a lot of help for all the optional arguments
980 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "map", "v(iiiiiiiiii)", &wasmtic_map)));
981 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "memcpy", "v(iii)", &wasmtic_memcpy)));
982 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "memset", "v(iii)", &wasmtic_memset)));
983 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "mget", "i(ii)", &wasmtic_mget)));
984 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "mset", "v(iii)", &wasmtic_mset)));
985 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "mouse", "v(*)", &wasmtic_mouse)));
986 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "music", "v(iiiiiii)", &wasmtic_music)));
987 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "pix", "i(iii)", &wasmtic_pix)));
988 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "peek", "i(ii)", &wasmtic_peek)));
989 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "peek4", "i(i)", &wasmtic_peek4)));
990 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "peek2", "i(i)", &wasmtic_peek2)));
991 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "peek1", "i(i)", &wasmtic_peek1)));
992 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "pmem", "i(ii)", &wasmtic_pmem)));
993 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "poke", "v(iii)", &wasmtic_poke)));
994 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "poke4", "v(ii)", &wasmtic_poke4)));
995 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "poke2", "v(ii)", &wasmtic_poke2)));
996 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "poke1", "v(ii)", &wasmtic_poke1)));
997 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "print", "i(*iiiiii)", &wasmtic_print)));
998 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "rect", "v(iiiii)", &wasmtic_rect)));
999 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "rectb", "v(iiiii)", &wasmtic_rectb)));
1000 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "sfx", "v(iiiiiiii)", &wasmtic_sfx)));
1001 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "spr", "v(iiiiiiiiii)", &wasmtic_spr)));
1002 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "sync", "v(iii)", &wasmtic_sync)));
1003 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "time", "f()", &wasmtic_time)));
1004 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "tstamp", "i()", &wasmtic_tstamp)));
1005 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "trace", "v(*i)", &wasmtic_trace)));
1006 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "tri", "v(ffffffi)", &wasmtic_tri)));
1007 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "trib", "v(ffffffi)", &wasmtic_trib)));
1008 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "ttri", "v(ffffffffffffiiifffi)", &wasmtic_ttri)));
1009 _ (SuppressLookupFailure (m3_LinkRawFunction (module, "env", "vbank", "i(i)", &wasmtic_vbank)));
1010
1011_catch:
1012 return result;
1013}
1014
1015void deinitWasmRuntime( IM3Runtime runtime )
1016{
1017 dbg("Denitializing wasm runtime\n");
1018 if (runtime == NULL)
1019 {
1020 printf("WARNING deinitWasm of null");
1021 return;
1022 }
1023 IM3Environment env = runtime -> environment;
1024 printf("deiniting env %d\n", env);
1025
1026 tic_core* tic = getWasmCore(runtime);
1027 m3_FreeRuntime (runtime);
1028 m3_FreeEnvironment (env);
1029}
1030
1031static void closeWasm(tic_mem* tic)
1032{
1033 tic_core* core = (tic_core*)tic;
1034
1035 if(core->currentVM)
1036 {
1037 // this is necessary because when TIC-80 calls tic_reset_api, we
1038 // may not even have a VM. Because the [non WASM] VM isn't
1039 // initialized yet when loading a new cartridge the "init" steps
1040 // are being applied to the WASM RAM, not the base RAM, so as we're
1041 // on our way out we need to copy the our lower 96kb back into base
1042 // RAM to close this loophole
1043
1044 // TODO: This could also be fixed by correcting the sequencing such
1045 // that prior VMs were released before starting the init process
1046 // for an entirely different VM. This sequencing matters a lot
1047 // less if one assumes (like before) that all the VMs share a
1048 // common memory area.
1049 u8* low_ram = (u8*)core->memory.base_ram;
1050 u8* wasm_ram = m3_GetMemory(core->currentVM, NULL, 0);
1051 memcpy(low_ram, wasm_ram, TIC_RAM_SIZE);
1052 deinitWasmRuntime(core->currentVM);
1053 core->currentVM = NULL;
1054 core->memory.ram = NULL;
1055 }
1056}
1057
1058// TODO: restore functionality
1059// static u64 ForceExitCounter = 0;
1060
1061// s32 wasm_timeout_check(void* udata)
1062// {
1063// tic_core* core = (tic_core*)udata;
1064// tic_tick_data* tick = core->data;
1065
1066// return ForceExitCounter++ > 1000 ? tick->forceExit && tick->forceExit(tick->data) : false;
1067// }
1068
1069
1070static bool initWasm(tic_mem* tic, const char* code)
1071{
1072 // closeWasm(tic);
1073 tic_core* core = (tic_core*)tic;
1074 dbg("Initializing WASM3 runtime %d\n", core);
1075
1076 IM3Environment env = m3_NewEnvironment ();
1077 if(!env)
1078 {
1079 core->data->error(core->data->data, "Unable to init WASM env");
1080 return false;
1081 }
1082 IM3Runtime runtime = m3_NewRuntime (env, WASM_STACK_SIZE, core);
1083 if(!runtime)
1084 {
1085 core->data->error(core->data->data, "Unable to init WASM runtime");
1086 return false;
1087 }
1088
1089 runtime->memory.maxPages = TIC_WASM_PAGE_COUNT;
1090 ResizeMemory(runtime, TIC_WASM_PAGE_COUNT);
1091
1092 u8* low_ram = (u8*)core->memory.ram;
1093 u8* wasm_ram = m3_GetMemory(runtime, NULL, 0);
1094 memcpy(wasm_ram, low_ram, TIC_RAM_SIZE);
1095 core->memory.ram = (tic_ram*)wasm_ram;
1096
1097 core->currentVM = runtime;
1098
1099 // TODO: if compiling from WAT is an option where should this
1100 // code go?
1101
1102 // long unsigned int srcSize = strlen(code);
1103 // long unsigned int fsize;
1104 // char* error;
1105 // void* wasmcode = wat2wasm(code ,srcSize, &fsize, &error);
1106 // if (!wasmcode){
1107 // core->data->error(core->data->data, error);
1108 // free(error);
1109 // return false;
1110 // }
1111
1112 void* wasmcode = tic->cart.binary.data;
1113 // TODO: will this blow up or have bad effects if we are zero-padded?
1114 // if so we'll need to find a way to pass in size here
1115 // int fsize = TIC_BINARY_SIZE;
1116 int fsize = tic->cart.binary.size;
1117
1118 IM3Module module;
1119 M3Result result = m3_ParseModule (runtime->environment, &module, wasmcode, fsize);
1120
1121 if (result){
1122 core->data->error(core->data->data, result);
1123 return false;
1124 }
1125
1126 result = m3_LoadModule (runtime, module);
1127 if (result){
1128 core->data->error(core->data->data, result);
1129 return false;
1130 }
1131
1132 result = linkTicAPI(runtime->modules);
1133 if (result)
1134 {
1135 core->data->error(core->data->data, result);
1136 return false;
1137 }
1138
1139 m3_FindFunction (&BDR_function, runtime, BDR_FN);
1140 m3_FindFunction (&SCN_function, runtime, SCN_FN);
1141 m3_FindFunction (&BOOT_function, runtime, BOOT_FN);
1142 m3_FindFunction (&MENU_function, runtime, MENU_FN);
1143 result = m3_FindFunction (&TIC_function, runtime, TIC_FN);
1144
1145 if (result)
1146 {
1147 core->data->error(core->data->data, "Error: WASM must export a TIC function.");
1148 return false;
1149 }
1150
1151 return true;
1152}
1153
1154static void callWasmTick(tic_mem* tic)
1155{
1156 // ForceExitCounter = 0;
1157
1158 tic_core* core = (tic_core*)tic;
1159
1160 IM3Runtime runtime = core->currentVM;
1161
1162 if(!runtime) { return; }
1163
1164 M3Result res = m3_CallV(TIC_function);
1165 if(res)
1166 {
1167 core->data->error(core->data->data, res);
1168 }
1169}
1170
1171static void callWasmBoot(tic_mem* tic)
1172{
1173 tic_core* core = (tic_core*)tic;
1174
1175 IM3Runtime runtime = core->currentVM;
1176
1177 if(!runtime) { return; }
1178 if (BOOT_function == NULL) { return; }
1179
1180 M3Result res = m3_CallV(BOOT_function);
1181 if(res)
1182 {
1183 core->data->error(core->data->data, res);
1184 }
1185}
1186
1187static void callWasmIntFunc(tic_mem* tic, IM3Function func, s32 value, void* data)
1188{
1189 tic_core* core = (tic_core*)tic;
1190
1191 IM3Runtime runtime = core->currentVM;
1192
1193 if(!runtime) { return; }
1194 if (func == NULL) { return; }
1195
1196 M3Result res = m3_CallV(func, value);
1197 if(res)
1198 {
1199 core->data->error(core->data->data, res);
1200 }
1201}
1202
1203static void callWasmScanline(tic_mem* tic, s32 row, void* data)
1204{
1205 callWasmIntFunc(tic, SCN_function, row, data);
1206}
1207
1208static void callWasmBorder(tic_mem* tic, s32 row, void* data)
1209{
1210 callWasmIntFunc(tic, BDR_function, row, data);
1211}
1212
1213static void callWasmMenu(tic_mem* tic, s32 index, void* data)
1214{
1215 callWasmIntFunc(tic, MENU_function, index, data);
1216}
1217
1218static inline bool isalnum_(char c) {return isalnum(c) || c == '_';}
1219
1220static const tic_outline_item* getWasmOutline(const char* code, s32* size)
1221{
1222 enum{Size = sizeof(tic_outline_item)};
1223
1224 *size = 0;
1225
1226 static tic_outline_item* items = NULL;
1227
1228 if(items)
1229 {
1230 free(items);
1231 items = NULL;
1232 }
1233
1234 const char* ptr = code;
1235
1236 while(true)
1237 {
1238 static const char FuncString[] = "function ";
1239
1240 ptr = strstr(ptr, FuncString);
1241
1242 if(ptr)
1243 {
1244 ptr += sizeof FuncString - 1;
1245
1246 const char* start = ptr;
1247 const char* end = start;
1248
1249 while(*ptr)
1250 {
1251 char c = *ptr;
1252
1253 if(isalnum_(c));
1254 else if(c == '(')
1255 {
1256 end = ptr;
1257 break;
1258 }
1259 else break;
1260
1261 ptr++;
1262 }
1263
1264 if(end > start)
1265 {
1266 items = realloc(items, (*size + 1) * Size);
1267
1268 items[*size].pos = start;
1269 items[*size].size = (s32)(end - start);
1270
1271 (*size)++;
1272 }
1273 }
1274 else break;
1275 }
1276
1277 return items;
1278}
1279
1280void evalWasm(tic_mem* tic, const char* code) {
1281 printf("TODO: Wasm eval not yet implemented\n.");
1282}
1283
1284const tic_script_config WasmSyntaxConfig =
1285{
1286 .id = 17,
1287 .name = "wasm",
1288 .fileExtension = ".wasmp",
1289 .projectComment = "--",
1290 {
1291 .init = initWasm,
1292 .close = closeWasm,
1293 .tick = callWasmTick,
1294 .boot = callWasmBoot,
1295
1296 .callback =
1297 {
1298 .scanline = callWasmScanline,
1299 .border = callWasmBorder,
1300 .menu = callWasmMenu,
1301 },
1302 },
1303
1304 .getOutline = getWasmOutline,
1305 .eval = evalWasm,
1306
1307 .blockCommentStart = "(;",
1308 .blockCommentEnd = ";)",
1309 .blockCommentStart2 = NULL,
1310 .blockCommentEnd2 = NULL,
1311 .blockStringStart = "**",
1312 .blockStringEnd = "**",
1313 .singleComment = "--",
1314
1315 .keywords = NULL,
1316 .keywordsCount = 0,
1317};
1318
1319const tic_script_config* get_wasm_script_config()
1320{
1321 return &WasmSyntaxConfig;
1322}
1323
1324#else
1325
1326// ??
1327s32 wasm_timeout_check(void* udata){return 0;}
1328
1329#endif /* defined(TIC_BUILD_WITH_WASM) */
1330