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
25#if defined(TIC_BUILD_WITH_JS)
26
27#include "tools.h"
28
29#include <ctype.h>
30
31#include "duktape.h"
32
33static const char TicCore[] = "_TIC80";
34
35static void closeJavascript(tic_mem* tic)
36{
37 tic_core* core = (tic_core*)tic;
38
39 if(core->currentVM)
40 {
41 duk_destroy_heap(core->currentVM);
42 core->currentVM = NULL;
43 }
44}
45
46static tic_core* getDukCore(duk_context* duk)
47{
48 duk_push_global_stash(duk);
49 duk_get_prop_string(duk, -1, TicCore);
50 tic_core* core = duk_to_pointer(duk, -1);
51 duk_pop_2(duk);
52
53 return core;
54}
55
56static duk_ret_t duk_print(duk_context* duk)
57{
58 tic_mem* tic = (tic_mem*)getDukCore(duk);
59
60 const char* text = duk_to_string(duk, 0);
61 s32 x = duk_opt_int(duk, 1, 0);
62 s32 y = duk_opt_int(duk, 2, 0);
63 s32 color = duk_opt_int(duk, 3, TIC_DEFAULT_COLOR);
64 bool fixed = duk_opt_boolean(duk, 4, false);
65 s32 scale = duk_opt_int(duk, 5, 1);
66 bool alt = duk_opt_boolean(duk, 6, false);
67
68 s32 size = tic_api_print(tic, text ? text : "nil", x, y, color, fixed, scale, alt);
69
70 duk_push_uint(duk, size);
71
72 return 1;
73}
74
75static duk_ret_t duk_cls(duk_context* duk)
76{
77 tic_mem* tic = (tic_mem*)getDukCore(duk);
78
79 tic_api_cls(tic, duk_opt_int(duk, 0, 0));
80
81 return 0;
82}
83
84static duk_ret_t duk_pix(duk_context* duk)
85{
86 s32 x = duk_to_int(duk, 0);
87 s32 y = duk_to_int(duk, 1);
88
89 tic_mem* tic = (tic_mem*)getDukCore(duk);
90
91 if(duk_is_null_or_undefined(duk, 2))
92 {
93 duk_push_uint(duk, tic_api_pix(tic, x, y, 0, true));
94 return 1;
95 }
96 else
97 {
98 s32 color = duk_to_int(duk, 2);
99 tic_api_pix(tic, x, y, color, false);
100 }
101
102 return 0;
103}
104
105static duk_ret_t duk_line(duk_context* duk)
106{
107 float x0 = duk_to_number(duk, 0);
108 float y0 = duk_to_number(duk, 1);
109 float x1 = duk_to_number(duk, 2);
110 float y1 = duk_to_number(duk, 3);
111 s32 color = duk_to_int(duk, 4);
112
113 tic_mem* tic = (tic_mem*)getDukCore(duk);
114
115 tic_api_line(tic, x0, y0, x1, y1, color);
116
117 return 0;
118}
119
120static duk_ret_t duk_rect(duk_context* duk)
121{
122 s32 x = duk_to_int(duk, 0);
123 s32 y = duk_to_int(duk, 1);
124 s32 w = duk_to_int(duk, 2);
125 s32 h = duk_to_int(duk, 3);
126 s32 color = duk_to_int(duk, 4);
127
128 tic_mem* tic = (tic_mem*)getDukCore(duk);
129 tic_api_rect(tic, x, y, w, h, color);
130
131 return 0;
132}
133
134static duk_ret_t duk_rectb(duk_context* duk)
135{
136 s32 x = duk_to_int(duk, 0);
137 s32 y = duk_to_int(duk, 1);
138 s32 w = duk_to_int(duk, 2);
139 s32 h = duk_to_int(duk, 3);
140 s32 color = duk_to_int(duk, 4);
141
142 tic_mem* tic = (tic_mem*)getDukCore(duk);
143 tic_api_rectb(tic, x, y, w, h, color);
144
145 return 0;
146}
147
148static duk_ret_t duk_spr(duk_context* duk)
149{
150 static u8 colors[TIC_PALETTE_SIZE];
151 s32 count = 0;
152
153 s32 index = duk_opt_int(duk, 0, 0);
154 s32 x = duk_opt_int(duk, 1, 0);
155 s32 y = duk_opt_int(duk, 2, 0);
156
157 {
158 if(!duk_is_null_or_undefined(duk, 3))
159 {
160 if(duk_is_array(duk, 3))
161 {
162 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
163 {
164 duk_get_prop_index(duk, 3, i);
165 if(duk_is_null_or_undefined(duk, -1))
166 {
167 duk_pop(duk);
168 break;
169 }
170 else
171 {
172 colors[i] = duk_to_int(duk, -1);
173 count++;
174 duk_pop(duk);
175 }
176 }
177 }
178 else
179 {
180 colors[0] = duk_to_int(duk, 3);
181 count = 1;
182 }
183 }
184 }
185
186 s32 scale = duk_opt_int(duk, 4, 1);
187 tic_flip flip = duk_is_boolean(duk, 5)
188 ? duk_to_boolean(duk, 5) ? tic_horz_flip : tic_no_flip
189 : duk_opt_int(duk, 5, tic_no_flip);
190 tic_rotate rotate = duk_opt_int(duk, 6, tic_no_rotate);
191 s32 w = duk_opt_int(duk, 7, 1);
192 s32 h = duk_opt_int(duk, 8, 1);
193
194 tic_mem* tic = (tic_mem*)getDukCore(duk);
195 tic_api_spr(tic, index, x, y, w, h, colors, count, scale, flip, rotate);
196
197 return 0;
198}
199
200static duk_ret_t duk_btn(duk_context* duk)
201{
202 tic_core* core = getDukCore(duk);
203 tic_mem* tic = (tic_mem*)core;
204
205 if (duk_is_null_or_undefined(duk, 0))
206 {
207 duk_push_uint(duk, tic_api_btn(tic, -1));
208 }
209 else
210 {
211 bool pressed = tic_api_btn(tic, duk_to_int(duk, 0) & 0x1f);
212 duk_push_boolean(duk, pressed);
213 }
214
215 return 1;
216}
217
218static duk_ret_t duk_btnp(duk_context* duk)
219{
220 tic_core* core = getDukCore(duk);
221 tic_mem* tic = (tic_mem*)core;
222
223 if (duk_is_null_or_undefined(duk, 0))
224 {
225 duk_push_uint(duk, tic_api_btnp(tic, -1, -1, -1));
226 }
227 else if(duk_is_null_or_undefined(duk, 1) && duk_is_null_or_undefined(duk, 2))
228 {
229 s32 index = duk_to_int(duk, 0) & 0x1f;
230
231 duk_push_boolean(duk, tic_api_btnp(tic, index, -1, -1));
232 }
233 else
234 {
235 s32 index = duk_to_int(duk, 0) & 0x1f;
236 u32 hold = duk_to_int(duk, 1);
237 u32 period = duk_to_int(duk, 2);
238
239 duk_push_boolean(duk, tic_api_btnp(tic, index, hold, period));
240 }
241
242 return 1;
243}
244
245static s32 duk_key(duk_context* duk)
246{
247 tic_core* core = getDukCore(duk);
248 tic_mem* tic = &core->memory;
249
250 if (duk_is_null_or_undefined(duk, 0))
251 {
252 duk_push_boolean(duk, tic_api_key(tic, tic_key_unknown));
253 }
254 else
255 {
256 tic_key key = duk_to_int(duk, 0);
257
258 if(key < tic_keys_count)
259 duk_push_boolean(duk, tic_api_key(tic, key));
260 else return duk_error(duk, DUK_ERR_ERROR, "unknown keyboard code\n");
261 }
262
263 return 1;
264}
265
266static s32 duk_keyp(duk_context* duk)
267{
268 tic_core* core = getDukCore(duk);
269 tic_mem* tic = &core->memory;
270
271 if (duk_is_null_or_undefined(duk, 0))
272 {
273 duk_push_boolean(duk, tic_api_keyp(tic, tic_key_unknown, -1, -1));
274 }
275 else
276 {
277 tic_key key = duk_to_int(duk, 0);
278
279 if(key >= tic_keys_count)
280 {
281 return duk_error(duk, DUK_ERR_ERROR, "unknown keyboard code\n");
282 }
283 else
284 {
285 if(duk_is_null_or_undefined(duk, 1) && duk_is_null_or_undefined(duk, 2))
286 {
287 duk_push_boolean(duk, tic_api_keyp(tic, key, -1, -1));
288 }
289 else
290 {
291 u32 hold = duk_to_int(duk, 1);
292 u32 period = duk_to_int(duk, 2);
293
294 duk_push_boolean(duk, tic_api_keyp(tic, key, hold, period));
295 }
296 }
297 }
298
299 return 1;
300}
301
302static duk_ret_t duk_sfx(duk_context* duk)
303{
304 tic_mem* tic = (tic_mem*)getDukCore(duk);
305
306 s32 index = duk_opt_int(duk, 0, -1);
307
308 s32 note = -1;
309 s32 octave = -1;
310 s32 speed = SFX_DEF_SPEED;
311
312 if (index < SFX_COUNT)
313 {
314 if(index >= 0)
315 {
316 tic_sample* effect = tic->ram->sfx.samples.data + index;
317
318 note = effect->note;
319 octave = effect->octave;
320 speed = effect->speed;
321
322 if(!duk_is_null_or_undefined(duk, 1))
323 {
324 if(duk_is_string(duk, 1))
325 {
326 const char* noteStr = duk_to_string(duk, 1);
327
328 if(!tic_tool_parse_note(noteStr, &note, &octave))
329 {
330 return duk_error(duk, DUK_ERR_ERROR, "invalid note, should be like C#4\n");
331 }
332 }
333 else
334 {
335 s32 id = duk_to_int(duk, 1);
336 note = id % NOTES;
337 octave = id / NOTES;
338 }
339 }
340 }
341 }
342 else
343 {
344 return duk_error(duk, DUK_ERR_ERROR, "unknown sfx index\n");
345 }
346
347 s32 duration = duk_opt_int(duk, 2, -1);
348 s32 channel = duk_opt_int(duk, 3, 0);
349 s32 volumes[TIC80_SAMPLE_CHANNELS];
350
351 if(duk_is_array(duk, 4))
352 {
353 for(s32 i = 0; i < COUNT_OF(volumes); i++)
354 {
355 duk_get_prop_index(duk, 4, i);
356 if(!duk_is_null_or_undefined(duk, -1))
357 volumes[i] = duk_to_int(duk, -1);
358 duk_pop(duk);
359 }
360 }
361 else volumes[0] = volumes[1] = duk_opt_int(duk, 4, MAX_VOLUME);
362
363 speed = duk_opt_int(duk, 5, speed);
364
365 if (channel >= 0 && channel < TIC_SOUND_CHANNELS)
366 {
367 tic_api_sfx(tic, index, note, octave, duration, channel, volumes[0] & 0xf, volumes[1] & 0xf, speed);
368 }
369 else return duk_error(duk, DUK_ERR_ERROR, "unknown channel\n");
370
371 return 0;
372}
373
374typedef struct
375{
376 duk_context* duk;
377 void* remap;
378} RemapData;
379
380static void remapCallback(void* data, s32 x, s32 y, RemapResult* result)
381{
382
383 RemapData* remap = (RemapData*)data;
384 duk_context* duk = remap->duk;
385
386 duk_push_heapptr(duk, remap->remap);
387 duk_push_int(duk, result->index);
388 duk_push_int(duk, x);
389 duk_push_int(duk, y);
390 duk_pcall(duk, 3);
391
392 if(duk_is_array(duk, -1))
393 {
394 duk_get_prop_index(duk, -1, 0);
395 result->index = duk_to_int(duk, -1);
396 duk_pop(duk);
397
398 duk_get_prop_index(duk, -1, 1);
399 result->flip = duk_to_int(duk, -1);
400 duk_pop(duk);
401
402 duk_get_prop_index(duk, -1, 2);
403 result->rotate = duk_to_int(duk, -1);
404 duk_pop(duk);
405 }
406 else
407 {
408 result->index = duk_to_int(duk, -1);
409 }
410
411 duk_pop(duk);
412}
413
414static duk_ret_t duk_map(duk_context* duk)
415{
416 s32 x = duk_opt_int(duk, 0, 0);
417 s32 y = duk_opt_int(duk, 1, 0);
418 s32 w = duk_opt_int(duk, 2, TIC_MAP_SCREEN_WIDTH);
419 s32 h = duk_opt_int(duk, 3, TIC_MAP_SCREEN_HEIGHT);
420 s32 sx = duk_opt_int(duk, 4, 0);
421 s32 sy = duk_opt_int(duk, 5, 0);
422 s32 scale = duk_opt_int(duk, 7, 1);
423
424 static u8 colors[TIC_PALETTE_SIZE];
425 s32 count = 0;
426
427 {
428 if(!duk_is_null_or_undefined(duk, 6))
429 {
430 if(duk_is_array(duk, 6))
431 {
432 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
433 {
434 duk_get_prop_index(duk, 6, i);
435 if(duk_is_null_or_undefined(duk, -1))
436 {
437 duk_pop(duk);
438 break;
439 }
440 else
441 {
442 colors[i] = duk_to_int(duk, -1);
443 count++;
444 duk_pop(duk);
445 }
446 }
447 }
448 else
449 {
450 colors[0] = duk_to_int(duk, 6);
451 count = 1;
452 }
453 }
454 }
455
456 tic_mem* tic = (tic_mem*)getDukCore(duk);
457
458 if (duk_is_null_or_undefined(duk, 8))
459 tic_api_map(tic, x, y, w, h, sx, sy, colors, count, scale, NULL, NULL);
460 else
461 {
462 void* remap = duk_get_heapptr(duk, 8);
463
464 RemapData data = {duk, remap};
465
466 tic_api_map((tic_mem*)getDukCore(duk), x, y, w, h, sx, sy, colors, count, scale, remapCallback, &data);
467 }
468
469 return 0;
470}
471
472static duk_ret_t duk_mget(duk_context* duk)
473{
474 s32 x = duk_opt_int(duk, 0, 0);
475 s32 y = duk_opt_int(duk, 1, 0);
476
477 tic_mem* tic = (tic_mem*)getDukCore(duk);
478
479 u8 value = tic_api_mget(tic, x, y);
480 duk_push_uint(duk, value);
481 return 1;
482}
483
484static duk_ret_t duk_mset(duk_context* duk)
485{
486 s32 x = duk_opt_int(duk, 0, 0);
487 s32 y = duk_opt_int(duk, 1, 0);
488 u8 value = duk_opt_int(duk, 2, 0);
489
490 tic_mem* tic = (tic_mem*)getDukCore(duk);
491
492 tic_api_mset(tic, x, y, value);
493
494 return 1;
495}
496
497static duk_ret_t duk_peek(duk_context* duk)
498{
499 s32 address = duk_to_int(duk, 0);
500 s32 bits = duk_opt_int(duk, 1, BITS_IN_BYTE);
501
502 tic_mem* tic = (tic_mem*)getDukCore(duk);
503 duk_push_uint(duk, tic_api_peek(tic, address, bits));
504 return 1;
505}
506
507static duk_ret_t duk_poke(duk_context* duk)
508{
509 s32 address = duk_to_int(duk, 0);
510 u8 value = duk_to_int(duk, 1);
511 s32 bits = duk_opt_int(duk, 2, BITS_IN_BYTE);
512
513 tic_mem* tic = (tic_mem*)getDukCore(duk);
514 tic_api_poke(tic, address, value, bits);
515
516 return 0;
517}
518
519static duk_ret_t duk_peek1(duk_context* duk)
520{
521 s32 address = duk_to_int(duk, 0);
522
523 tic_mem* tic = (tic_mem*)getDukCore(duk);
524 duk_push_uint(duk, tic_api_peek1(tic, address));
525 return 1;
526}
527
528static duk_ret_t duk_poke1(duk_context* duk)
529{
530 s32 address = duk_to_int(duk, 0);
531 u8 value = duk_to_int(duk, 1);
532
533 tic_mem* tic = (tic_mem*)getDukCore(duk);
534 tic_api_poke1(tic, address, value);
535
536 return 0;
537}
538
539static duk_ret_t duk_peek2(duk_context* duk)
540{
541 s32 address = duk_to_int(duk, 0);
542
543 tic_mem* tic = (tic_mem*)getDukCore(duk);
544 duk_push_uint(duk, tic_api_peek2(tic, address));
545 return 1;
546}
547
548static duk_ret_t duk_poke2(duk_context* duk)
549{
550 s32 address = duk_to_int(duk, 0);
551 u8 value = duk_to_int(duk, 1);
552
553 tic_mem* tic = (tic_mem*)getDukCore(duk);
554 tic_api_poke2(tic, address, value);
555
556 return 0;
557}
558
559static duk_ret_t duk_peek4(duk_context* duk)
560{
561 s32 address = duk_to_int(duk, 0);
562
563 tic_mem* tic = (tic_mem*)getDukCore(duk);
564 duk_push_uint(duk, tic_api_peek4(tic, address));
565 return 1;
566}
567
568static duk_ret_t duk_poke4(duk_context* duk)
569{
570 s32 address = duk_to_int(duk, 0);
571 u8 value = duk_to_int(duk, 1);
572
573 tic_mem* tic = (tic_mem*)getDukCore(duk);
574 tic_api_poke4(tic, address, value);
575
576 return 0;
577}
578
579static duk_ret_t duk_memcpy(duk_context* duk)
580{
581 s32 dest = duk_to_int(duk, 0);
582 s32 src = duk_to_int(duk, 1);
583 s32 size = duk_to_int(duk, 2);
584
585 tic_mem* tic = (tic_mem*)getDukCore(duk);
586 tic_api_memcpy(tic, dest, src, size);
587
588 return 0;
589}
590
591static duk_ret_t duk_memset(duk_context* duk)
592{
593 s32 dest = duk_to_int(duk, 0);
594 u8 value = duk_to_int(duk, 1);
595 s32 size = duk_to_int(duk, 2);
596
597 tic_mem* tic = (tic_mem*)getDukCore(duk);
598 tic_api_memset(tic, dest, value, size);
599
600 return 0;
601}
602
603static duk_ret_t duk_trace(duk_context* duk)
604{
605 tic_mem* tic = (tic_mem*)getDukCore(duk);
606
607 const char* text = duk_opt_string(duk, 0, "");
608 u8 color = duk_opt_int(duk, 1, TIC_DEFAULT_COLOR);
609
610 tic_api_trace(tic, text, color);
611
612 return 0;
613}
614
615static duk_ret_t duk_pmem(duk_context* duk)
616{
617 tic_mem* tic = (tic_mem*)getDukCore(duk);
618 u32 index = duk_to_int(duk, 0);
619
620 if(index < TIC_PERSISTENT_SIZE)
621 {
622 u32 val = tic_api_pmem(tic, index, 0, false);
623
624 if(!duk_is_null_or_undefined(duk, 1))
625 {
626 tic_api_pmem(tic, index, duk_to_uint(duk, 1), true);
627 }
628
629 duk_push_int(duk, val);
630
631 return 1;
632 }
633 else return duk_error(duk, DUK_ERR_ERROR, "invalid persistent tic index\n");
634
635 return 0;
636}
637
638static duk_ret_t duk_time(duk_context* duk)
639{
640 tic_mem* tic = (tic_mem*)getDukCore(duk);
641
642 duk_push_number(duk, tic_api_time(tic));
643
644 return 1;
645}
646
647static duk_ret_t duk_tstamp(duk_context* duk)
648{
649 tic_mem* tic = (tic_mem*)getDukCore(duk);
650
651 duk_push_number(duk, tic_api_tstamp(tic));
652
653 return 1;
654}
655
656static duk_ret_t duk_exit(duk_context* duk)
657{
658 tic_api_exit((tic_mem*)getDukCore(duk));
659
660 return 0;
661}
662
663static duk_ret_t duk_font(duk_context* duk)
664{
665 tic_mem* tic = (tic_mem*)getDukCore(duk);
666
667 const char* text = duk_to_string(duk, 0);
668 s32 x = duk_to_int(duk, 1);
669 s32 y = duk_to_int(duk, 2);
670 u8 chromakey = duk_to_int(duk, 3);
671 s32 width = duk_opt_int(duk, 4, TIC_SPRITESIZE);
672 s32 height = duk_opt_int(duk, 5, TIC_SPRITESIZE);
673 bool fixed = duk_opt_boolean(duk, 6, false);
674 s32 scale = duk_opt_int(duk, 7, 1);
675 bool alt = duk_opt_boolean(duk, 8, false);
676 if(scale == 0)
677 {
678 duk_push_int(duk, 0);
679 return 1;
680 }
681
682 s32 size = tic_api_font(tic, text, x, y, &chromakey, 1, width, height, fixed, scale, alt);
683
684 duk_push_int(duk, size);
685
686 return 1;
687}
688
689static duk_ret_t duk_mouse(duk_context* duk)
690{
691 tic_core* core = getDukCore(duk);
692
693 const tic80_mouse* mouse = &core->memory.ram->input.mouse;
694
695 duk_idx_t idx = duk_push_array(duk);
696
697 {
698 tic_point pos = tic_api_mouse((tic_mem*)core);
699
700 duk_push_int(duk, pos.x);
701 duk_put_prop_index(duk, idx, 0);
702 duk_push_int(duk, pos.y);
703 duk_put_prop_index(duk, idx, 1);
704 }
705
706 duk_push_boolean(duk, mouse->left);
707 duk_put_prop_index(duk, idx, 2);
708 duk_push_boolean(duk, mouse->middle);
709 duk_put_prop_index(duk, idx, 3);
710 duk_push_boolean(duk, mouse->right);
711 duk_put_prop_index(duk, idx, 4);
712 duk_push_int(duk, mouse->scrollx);
713 duk_put_prop_index(duk, idx, 5);
714 duk_push_int(duk, mouse->scrolly);
715 duk_put_prop_index(duk, idx, 6);
716
717 return 1;
718}
719
720static duk_ret_t duk_circ(duk_context* duk)
721{
722 s32 x = duk_to_int(duk, 0);
723 s32 y = duk_to_int(duk, 1);
724 s32 radius = duk_to_int(duk, 2);
725 s32 color = duk_to_int(duk, 3);
726
727 tic_mem* tic = (tic_mem*)getDukCore(duk);
728
729 tic_api_circ(tic, x, y, radius, color);
730
731 return 0;
732}
733
734static duk_ret_t duk_circb(duk_context* duk)
735{
736 s32 x = duk_to_int(duk, 0);
737 s32 y = duk_to_int(duk, 1);
738 s32 color = duk_to_int(duk, 3);
739 s32 radius = duk_to_int(duk, 2);
740
741 tic_mem* tic = (tic_mem*)getDukCore(duk);
742
743 tic_api_circb(tic, x, y, radius, color);
744
745 return 0;
746}
747
748static duk_ret_t duk_elli(duk_context* duk)
749{
750 s32 x = duk_to_int(duk, 0);
751 s32 y = duk_to_int(duk, 1);
752 s32 a = duk_to_int(duk, 2);
753 s32 b = duk_to_int(duk, 3);
754 s32 color = duk_to_int(duk, 4);
755
756 tic_mem* tic = (tic_mem*)getDukCore(duk);
757
758 tic_api_elli(tic, x, y, a, b, color);
759
760 return 0;
761}
762
763static duk_ret_t duk_ellib(duk_context* duk)
764{
765 s32 x = duk_to_int(duk, 0);
766 s32 y = duk_to_int(duk, 1);
767 s32 a = duk_to_int(duk, 2);
768 s32 b = duk_to_int(duk, 3);
769 s32 color = duk_to_int(duk, 4);
770
771 tic_mem* tic = (tic_mem*)getDukCore(duk);
772
773 tic_api_ellib(tic, x, y, a, b, color);
774
775 return 0;
776}
777
778static duk_ret_t duk_tri(duk_context* duk)
779{
780 float pt[6];
781
782 for(s32 i = 0; i < COUNT_OF(pt); i++)
783 pt[i] = duk_to_number(duk, i);
784
785 s32 color = duk_to_int(duk, 6);
786
787 tic_mem* tic = (tic_mem*)getDukCore(duk);
788
789 tic_api_tri(tic, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color);
790
791 return 0;
792}
793
794static duk_ret_t duk_trib(duk_context* duk)
795{
796 float pt[6];
797
798 for(s32 i = 0; i < COUNT_OF(pt); i++)
799 pt[i] = duk_to_number(duk, i);
800
801 s32 color = duk_to_int(duk, 6);
802
803 tic_mem* tic = (tic_mem*)getDukCore(duk);
804
805 tic_api_trib(tic, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color);
806
807 return 0;
808}
809
810#if defined(BUILD_DEPRECATED)
811
812void drawTexturedTriangleDep(tic_core* core, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8* colors, s32 count);
813
814static duk_ret_t duk_textri(duk_context* duk)
815{
816 float pt[12];
817
818 for (s32 i = 0; i < COUNT_OF(pt); i++)
819 pt[i] = (float)duk_to_number(duk, i);
820 tic_mem* tic = (tic_mem*)getDukCore(duk);
821 bool use_map = duk_to_boolean(duk, 12);
822
823 static u8 colors[TIC_PALETTE_SIZE];
824 s32 count = 0;
825 {
826 if(!duk_is_null_or_undefined(duk, 13))
827 {
828 if(duk_is_array(duk, 13))
829 {
830 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
831 {
832 duk_get_prop_index(duk, 13, i);
833 if(duk_is_null_or_undefined(duk, -1))
834 {
835 duk_pop(duk);
836 break;
837 }
838 else
839 {
840 colors[i] = duk_to_int(duk, -1);
841 count++;
842 duk_pop(duk);
843 }
844 }
845 }
846 else
847 {
848 colors[0] = duk_to_int(duk, 13);
849 count = 1;
850 }
851 }
852 }
853
854 drawTexturedTriangleDep(getDukCore(duk),
855 pt[0], pt[1], // xy 1
856 pt[2], pt[3], // xy 2
857 pt[4], pt[5], // xy 3
858 pt[6], pt[7], // uv 1
859 pt[8], pt[9], // uv 2
860 pt[10], pt[11], // uv 3
861 use_map, // use_map
862 colors, count); // chroma
863
864 return 0;
865}
866
867#endif
868
869static duk_ret_t duk_ttri(duk_context* duk)
870{
871 float pt[12];
872
873 for (s32 i = 0; i < COUNT_OF(pt); i++)
874 pt[i] = (float)duk_to_number(duk, i);
875 tic_mem* tic = (tic_mem*)getDukCore(duk);
876 tic_texture_src src = duk_to_int(duk, 12);
877
878 static u8 colors[TIC_PALETTE_SIZE];
879 s32 count = 0;
880 {
881 if(!duk_is_null_or_undefined(duk, 13))
882 {
883 if(duk_is_array(duk, 13))
884 {
885 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
886 {
887 duk_get_prop_index(duk, 13, i);
888 if(duk_is_null_or_undefined(duk, -1))
889 {
890 duk_pop(duk);
891 break;
892 }
893 else
894 {
895 colors[i] = duk_to_int(duk, -1);
896 count++;
897 duk_pop(duk);
898 }
899 }
900 }
901 else
902 {
903 colors[0] = duk_to_int(duk, 13);
904 count = 1;
905 }
906 }
907 }
908
909 float z[3];
910 bool depth = true;
911
912 for (s32 i = 0, index = 14; i < COUNT_OF(z); i++, index++)
913 {
914 if(duk_is_null_or_undefined(duk, index)) depth = false;
915 else z[i] = (float)duk_to_number(duk, index);
916 }
917
918 tic_api_ttri(tic, pt[0], pt[1], // xy 1
919 pt[2], pt[3], // xy 2
920 pt[4], pt[5], // xy 3
921 pt[6], pt[7], // uv 1
922 pt[8], pt[9], // uv 2
923 pt[10], pt[11], // uv 3
924 src, // texture source
925 colors, count, // chroma
926 z[0], z[1], z[2], depth); // depth
927
928 return 0;
929}
930
931
932static duk_ret_t duk_clip(duk_context* duk)
933{
934 s32 x = duk_to_int(duk, 0);
935 s32 y = duk_to_int(duk, 1);
936 s32 w = duk_opt_int(duk, 2, TIC80_WIDTH);
937 s32 h = duk_opt_int(duk, 3, TIC80_HEIGHT);
938
939 tic_mem* tic = (tic_mem*)getDukCore(duk);
940
941 tic_api_clip(tic, x, y, w, h);
942
943 return 0;
944}
945
946static duk_ret_t duk_music(duk_context* duk)
947{
948 tic_mem* tic = (tic_mem*)getDukCore(duk);
949
950 s32 track = duk_opt_int(duk, 0, -1);
951 tic_api_music(tic, -1, 0, 0, false, false, -1, -1);
952
953 if(track >= 0)
954 {
955 if(track > MUSIC_TRACKS - 1)
956 return duk_error(duk, DUK_ERR_ERROR, "invalid music track index");
957
958 s32 frame = duk_opt_int(duk, 1, -1);
959 s32 row = duk_opt_int(duk, 2, -1);
960 bool loop = duk_opt_boolean(duk, 3, true);
961 bool sustain = duk_opt_boolean(duk, 4, false);
962 s32 tempo = duk_opt_int(duk, 5, -1);
963 s32 speed = duk_opt_int(duk, 6, -1);
964
965 tic_api_music(tic, track, frame, row, loop, sustain, tempo, speed);
966 }
967
968 return 0;
969}
970
971static duk_ret_t duk_vbank(duk_context* duk)
972{
973 tic_core* core = getDukCore(duk);
974 tic_mem* tic = (tic_mem*)core;
975
976 s32 prev = core->state.vbank.id;
977
978 if(!duk_is_null_or_undefined(duk, 0))
979 tic_api_vbank(tic, duk_opt_int(duk, 0, 0));
980
981 duk_push_uint(duk, prev);
982
983 return 1;
984}
985
986static duk_ret_t duk_sync(duk_context* duk)
987{
988 tic_mem* tic = (tic_mem*)getDukCore(duk);
989
990 u32 mask = duk_opt_int(duk, 0, 0);
991 s32 bank = duk_opt_int(duk, 1, 0);
992 bool toCart = duk_opt_boolean(duk, 2, false);
993
994 if(bank >= 0 && bank < TIC_BANKS)
995 tic_api_sync(tic, mask, bank, toCart);
996 else
997 return duk_error(duk, DUK_ERR_ERROR, "sync() error, invalid bank");
998
999 return 0;
1000}
1001
1002static duk_ret_t duk_reset(duk_context* duk)
1003{
1004 tic_core* core = getDukCore(duk);
1005
1006 core->state.initialized = false;
1007
1008 return 0;
1009}
1010
1011static duk_ret_t duk_fget(duk_context* duk)
1012{
1013 tic_mem* tic = (tic_mem*)getDukCore(duk);
1014
1015 u32 index = duk_opt_int(duk, 0, 0);
1016 u32 flag = duk_opt_int(duk, 1, 0);
1017
1018 bool value = tic_api_fget(tic, index, flag);
1019
1020 duk_push_boolean(duk, value);
1021
1022 return 1;
1023}
1024
1025static duk_ret_t duk_fset(duk_context* duk)
1026{
1027 tic_mem* tic = (tic_mem*)getDukCore(duk);
1028
1029 u32 index = duk_opt_int(duk, 0, 0);
1030 u32 flag = duk_opt_int(duk, 1, 0);
1031 bool value = duk_opt_boolean(duk, 2, false);
1032
1033 tic_api_fset(tic, index, flag, value);
1034
1035 return 0;
1036}
1037
1038static void initDuktape(tic_core* core)
1039{
1040 closeJavascript((tic_mem*)core);
1041
1042 duk_context* duk = core->currentVM = duk_create_heap(NULL, NULL, NULL, core, NULL);
1043
1044 {
1045 duk_push_global_stash(duk);
1046 duk_push_pointer(duk, core);
1047 duk_put_prop_string(duk, -2, TicCore);
1048 duk_pop(duk);
1049 }
1050
1051 static const struct{duk_c_function func; s32 params; const char* name;} ApiItems[] =
1052 {
1053#define API_FUNC_DEF(name, _, __, paramsCount, ...) {duk_ ## name, paramsCount, #name},
1054 TIC_API_LIST(API_FUNC_DEF)
1055#undef API_FUNC_DEF
1056
1057#if defined(BUILD_DEPRECATED)
1058 {duk_textri, 14, "textri"},
1059#endif
1060 };
1061
1062 for (s32 i = 0; i < COUNT_OF(ApiItems); i++)
1063 {
1064 duk_push_c_function(core->currentVM, ApiItems[i].func, ApiItems[i].params);
1065 duk_put_global_string(core->currentVM, ApiItems[i].name);
1066 }
1067}
1068
1069static bool initJavascript(tic_mem* tic, const char* code)
1070{
1071 tic_core* core = (tic_core*)tic;
1072
1073 initDuktape(core);
1074 duk_context* duktape = core->currentVM;
1075
1076 if (duk_pcompile_string(duktape, 0, code) != 0 || duk_peval_string(duktape, code) != 0)
1077 {
1078 core->data->error(core->data->data, duk_safe_to_stacktrace(duktape, -1));
1079 duk_pop(duktape);
1080 return false;
1081 }
1082
1083 return true;
1084}
1085
1086static void callJavascriptTick(tic_mem* tic)
1087{
1088 tic_core* core = (tic_core*)tic;
1089
1090 duk_context* duk = core->currentVM;
1091
1092 if(duk)
1093 {
1094 if(duk_get_global_string(duk, TIC_FN))
1095 {
1096 if(duk_pcall(duk, 0) != DUK_EXEC_SUCCESS)
1097 {
1098 core->data->error(core->data->data, duk_safe_to_stacktrace(duk, -1));
1099 return;
1100 }
1101
1102 // call OVR() callback for backward compatibility
1103 {
1104 if(duk_get_global_string(duk, OVR_FN))
1105 {
1106 OVR(core)
1107 {
1108 if(duk_pcall(duk, 0) != 0)
1109 core->data->error(core->data->data, duk_safe_to_stacktrace(duk, -1));
1110 }
1111 }
1112
1113 duk_pop(duk);
1114 }
1115 }
1116 else core->data->error(core->data->data, "'function TIC()...' isn't found :(");
1117
1118 duk_pop(duk);
1119 }
1120}
1121
1122static void callJavascriptIntCallback(tic_mem* tic, s32 value, void* data, const char* name)
1123{
1124 tic_core* core = (tic_core*)tic;
1125 duk_context* duk = core->currentVM;
1126
1127 if(duk_get_global_string(duk, name))
1128 {
1129 duk_push_int(duk, value);
1130
1131 if(duk_pcall(duk, 1) != 0)
1132 core->data->error(core->data->data, duk_safe_to_stacktrace(duk, -1));
1133 }
1134
1135 duk_pop(duk);
1136}
1137
1138static void callJavascriptScanline(tic_mem* tic, s32 row, void* data)
1139{
1140 callJavascriptIntCallback(tic, row, data, SCN_FN);
1141
1142 // try to call old scanline
1143 callJavascriptIntCallback(tic, row, data, "scanline");
1144}
1145
1146static void callJavascriptBorder(tic_mem* tic, s32 row, void* data)
1147{
1148 callJavascriptIntCallback(tic, row, data, BDR_FN);
1149}
1150
1151static void callJavascriptMenu(tic_mem* tic, s32 index, void* data)
1152{
1153 callJavascriptIntCallback(tic, index, data, MENU_FN);
1154}
1155
1156static void callJavascriptBoot(tic_mem* tic)
1157{
1158 tic_core* core = (tic_core*)tic;
1159 duk_context* duk = core->currentVM;
1160
1161 if(duk_get_global_string(duk, BOOT_FN))
1162 {
1163 if(duk_pcall(duk, 0) != 0)
1164 core->data->error(core->data->data, duk_safe_to_stacktrace(duk, -1));
1165 }
1166
1167 duk_pop(duk);
1168}
1169
1170static const char* const JsKeywords [] =
1171{
1172 "break", "do", "instanceof", "typeof", "case", "else", "new",
1173 "var", "catch", "finally", "return", "void", "continue", "for",
1174 "switch", "while", "debugger", "function", "this", "with",
1175 "default", "if", "throw", "delete", "in", "try", "const",
1176 "true", "false"
1177};
1178
1179static inline bool isalnum_(char c) {return isalnum(c) || c == '_';}
1180
1181static const tic_outline_item* getJsOutline(const char* code, s32* size)
1182{
1183 enum{Size = sizeof(tic_outline_item)};
1184
1185 *size = 0;
1186
1187 static tic_outline_item* items = NULL;
1188
1189 if(items)
1190 {
1191 free(items);
1192 items = NULL;
1193 }
1194
1195 const char* ptr = code;
1196
1197 while(true)
1198 {
1199 static const char FuncString[] = "function ";
1200
1201 ptr = strstr(ptr, FuncString);
1202
1203 if(ptr)
1204 {
1205 ptr += sizeof FuncString - 1;
1206
1207 const char* start = ptr;
1208 const char* end = start;
1209
1210 while(*ptr)
1211 {
1212 char c = *ptr;
1213
1214 if(isalnum_(c));
1215 else if(c == '(')
1216 {
1217 end = ptr;
1218 break;
1219 }
1220 else break;
1221
1222 ptr++;
1223 }
1224
1225 if(end > start)
1226 {
1227 items = realloc(items, (*size + 1) * Size);
1228
1229 items[*size].pos = start;
1230 items[*size].size = (s32)(end - start);
1231
1232 (*size)++;
1233 }
1234 }
1235 else break;
1236 }
1237
1238 return items;
1239}
1240
1241void evalJs(tic_mem* tic, const char* code) {
1242 printf("TODO: JS eval not yet implemented\n.");
1243}
1244
1245const tic_script_config JsSyntaxConfig =
1246{
1247 .id = 12,
1248 .name = "js",
1249 .fileExtension = ".js",
1250 .projectComment = "//",
1251 {
1252 .init = initJavascript,
1253 .close = closeJavascript,
1254 .tick = callJavascriptTick,
1255 .boot = callJavascriptBoot,
1256 .callback =
1257 {
1258 .scanline = callJavascriptScanline,
1259 .border = callJavascriptBorder,
1260 .menu = callJavascriptMenu,
1261 },
1262 },
1263
1264 .getOutline = getJsOutline,
1265 .eval = evalJs,
1266
1267 .blockCommentStart = "/*",
1268 .blockCommentEnd = "*/",
1269 .blockCommentStart2 = "<!--",
1270 .blockCommentEnd2 = "-->",
1271 .blockStringStart = NULL,
1272 .blockStringEnd = NULL,
1273 .singleComment = "//",
1274 .blockEnd = "}",
1275
1276 .keywords = JsKeywords,
1277 .keywordsCount = COUNT_OF(JsKeywords),
1278};
1279
1280#endif /* defined(TIC_BUILD_WITH_JS) */
1281