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_SQUIRREL)
26
27//#define USE_FOREIGN_POINTER
28
29#include <stdlib.h>
30#include <stdio.h>
31#include <string.h>
32#include <squirrel.h>
33#include <sqstdmath.h>
34#include <sqstdstring.h>
35#include <sqstdblob.h>
36#include <ctype.h>
37
38static const char TicCore[] = "_TIC80";
39
40static float getSquirrelFloat(HSQUIRRELVM vm, s32 index)
41{
42 SQFloat f = 0.0;
43 sq_getfloat(vm, index, &f);
44 return f;
45}
46
47// !TODO: get rid of this wrap
48static s32 getSquirrelNumber(HSQUIRRELVM vm, s32 index)
49{
50 SQInteger i;
51 if (SQ_SUCCEEDED(sq_getinteger(vm, index, &i)))
52 return (s32)i;
53
54 return (s32)getSquirrelFloat(vm, index);
55}
56
57static void registerSquirrelFunction(tic_core* core, SQFUNCTION func, const char *name)
58{
59 sq_pushroottable(core->currentVM);
60 sq_pushstring(core->currentVM, name, -1);
61 sq_newclosure(core->currentVM, func, 0);
62 sq_newslot(core->currentVM, -3, SQTrue);
63 sq_poptop(core->currentVM); // remove root table.
64}
65
66static tic_core* getSquirrelCore(HSQUIRRELVM vm)
67{
68#if USE_FOREIGN_POINTER
69 return (tic_core*)sq_getforeignpointer(vm);
70#else
71 sq_pushregistrytable(vm);
72 sq_pushstring(vm, TicCore, -1);
73 if (SQ_FAILED(sq_get(vm, -2)))
74 {
75 fprintf(stderr, "FATAL ERROR: TicCore not found!\n");
76 abort();
77 }
78 SQUserPointer ptr;
79 if (SQ_FAILED(sq_getuserpointer(vm, -1, &ptr)))
80 {
81 fprintf(stderr, "FATAL ERROR: Cannot get user pointer for TicCore!\n");
82 abort();
83 }
84 tic_core* core = (tic_core*)ptr;
85 sq_pop(vm, 2); // user pointer and registry table.
86 return core;
87#endif
88}
89
90void squirrel_compilerError(HSQUIRRELVM vm, const SQChar* desc, const SQChar* source,
91 SQInteger line, SQInteger column)
92{
93 tic_core* core = getSquirrelCore(vm);
94 char buffer[1024];
95 snprintf(buffer, 1023, "%.40s line %.6d column %.6d: %s\n", source, (int)line, (int)column, desc);
96
97 if (core->data)
98 core->data->error(core->data->data, buffer);
99}
100
101static SQInteger squirrel_errorHandler(HSQUIRRELVM vm)
102{
103 tic_core* core = getSquirrelCore(vm);
104
105 SQStackInfos si;
106 SQInteger level = 0;
107 while (SQ_SUCCEEDED(sq_stackinfos(vm, level, &si)))
108 {
109 char buffer[100];
110 snprintf(buffer, 99, "%.40s %.40s %.6d\n", si.funcname, si.source, (int)si.line);
111
112 if (core->data)
113 core->data->error(core->data->data, buffer);
114 ++level;
115 }
116
117 return 0;
118}
119
120
121static SQInteger squirrel_peek(HSQUIRRELVM vm)
122{
123 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
124 SQInteger top = sq_gettop(vm);
125
126 // check number of args
127 if (top < 2)
128 return sq_throwerror(vm, "invalid parameters, peek(address)");
129
130 s32 address = getSquirrelNumber(vm, 2);
131 s32 bits = BITS_IN_BYTE;
132
133 if(top == 3)
134 bits = getSquirrelNumber(vm, 3);
135
136 sq_pushinteger(vm, tic_api_peek(tic, address, bits));
137 return 1;
138}
139
140static SQInteger squirrel_poke(HSQUIRRELVM vm)
141{
142 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
143 SQInteger top = sq_gettop(vm);
144
145 if (top < 3)
146 return sq_throwerror( vm, "invalid parameters, poke(address,value)" );
147
148 s32 address = getSquirrelNumber(vm, 2);
149 u8 value = getSquirrelNumber(vm, 3);
150 s32 bits = BITS_IN_BYTE;
151
152 if(top == 4)
153 bits = getSquirrelNumber(vm, 4);
154
155 tic_api_poke(tic, address, value, bits);
156
157 return 0;
158}
159
160static SQInteger squirrel_peek1(HSQUIRRELVM vm)
161{
162 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
163
164 // check number of args
165 if (sq_gettop(vm) != 2)
166 return sq_throwerror(vm, "invalid parameters, peek4(address)");
167 s32 address = getSquirrelNumber(vm, 2);
168
169 sq_pushinteger(vm, tic_api_peek1(tic, address));
170 return 1;
171}
172
173static SQInteger squirrel_poke1(HSQUIRRELVM vm)
174{
175 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
176
177 if (sq_gettop(vm) != 3)
178 return sq_throwerror( vm, "invalid parameters, poke4(address,value)" );
179
180 s32 address = getSquirrelNumber(vm, 2);
181 u8 value = getSquirrelNumber(vm, 3);
182
183 tic_api_poke1(tic, address, value);
184
185 return 0;
186}
187
188static SQInteger squirrel_peek2(HSQUIRRELVM vm)
189{
190 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
191
192 // check number of args
193 if (sq_gettop(vm) != 2)
194 return sq_throwerror(vm, "invalid parameters, peek2(address)");
195 s32 address = getSquirrelNumber(vm, 2);
196
197 sq_pushinteger(vm, tic_api_peek2(tic, address));
198 return 1;
199}
200
201static SQInteger squirrel_poke2(HSQUIRRELVM vm)
202{
203 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
204
205 if (sq_gettop(vm) != 3)
206 return sq_throwerror( vm, "invalid parameters, poke2(address,value)" );
207
208 s32 address = getSquirrelNumber(vm, 2);
209 u8 value = getSquirrelNumber(vm, 3);
210
211 tic_api_poke2(tic, address, value);
212
213 return 0;
214}
215
216static SQInteger squirrel_peek4(HSQUIRRELVM vm)
217{
218 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
219
220 // check number of args
221 if (sq_gettop(vm) != 2)
222 return sq_throwerror(vm, "invalid parameters, peek4(address)");
223 s32 address = getSquirrelNumber(vm, 2);
224
225 sq_pushinteger(vm, tic_api_peek4(tic, address));
226 return 1;
227}
228
229static SQInteger squirrel_poke4(HSQUIRRELVM vm)
230{
231 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
232
233 if (sq_gettop(vm) != 3)
234 return sq_throwerror( vm, "invalid parameters, poke4(address,value)" );
235
236 s32 address = getSquirrelNumber(vm, 2);
237 u8 value = getSquirrelNumber(vm, 3);
238
239 tic_api_poke4(tic, address, value);
240
241 return 0;
242}
243
244static SQInteger squirrel_cls(HSQUIRRELVM vm)
245{
246 SQInteger top = sq_gettop(vm);
247
248 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
249
250 tic_api_cls(tic, top == 2 ? getSquirrelNumber(vm, 2) : 0);
251
252 return 0;
253}
254
255static SQInteger squirrel_pix(HSQUIRRELVM vm)
256{
257 SQInteger top = sq_gettop(vm);
258
259 if(top >= 3)
260 {
261 s32 x = getSquirrelNumber(vm, 2);
262 s32 y = getSquirrelNumber(vm, 3);
263
264 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
265
266 if(top >= 4)
267 {
268 s32 color = getSquirrelNumber(vm, 4);
269 tic_api_pix(tic, x, y, color, false);
270 }
271 else
272 {
273 sq_pushinteger(vm, tic_api_pix(tic, x, y, 0, true));
274 return 1;
275 }
276
277 }
278 else return sq_throwerror(vm, "invalid parameters, pix(x y [color])\n");
279
280 return 0;
281}
282
283static SQInteger squirrel_line(HSQUIRRELVM vm)
284{
285 SQInteger top = sq_gettop(vm);
286
287 if(top == 6)
288 {
289 float x0 = getSquirrelFloat(vm, 2);
290 float y0 = getSquirrelFloat(vm, 3);
291 float x1 = getSquirrelFloat(vm, 4);
292 float y1 = getSquirrelFloat(vm, 5);
293 s32 color = getSquirrelNumber(vm, 6);
294
295 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
296
297 tic_api_line(tic, x0, y0, x1, y1, color);
298 }
299 else return sq_throwerror(vm, "invalid parameters, line(x0,y0,x1,y1,color)\n");
300
301 return 0;
302}
303
304static SQInteger squirrel_rect(HSQUIRRELVM vm)
305{
306 SQInteger top = sq_gettop(vm);
307
308 if(top == 6)
309 {
310 s32 x = getSquirrelNumber(vm, 2);
311 s32 y = getSquirrelNumber(vm, 3);
312 s32 w = getSquirrelNumber(vm, 4);
313 s32 h = getSquirrelNumber(vm, 5);
314 s32 color = getSquirrelNumber(vm, 6);
315
316 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
317
318 tic_api_rect(tic, x, y, w, h, color);
319 }
320 else return sq_throwerror(vm, "invalid parameters, rect(x,y,w,h,color)\n");
321
322 return 0;
323}
324
325static SQInteger squirrel_rectb(HSQUIRRELVM vm)
326{
327 SQInteger top = sq_gettop(vm);
328
329 if(top == 6)
330 {
331 s32 x = getSquirrelNumber(vm, 2);
332 s32 y = getSquirrelNumber(vm, 3);
333 s32 w = getSquirrelNumber(vm, 4);
334 s32 h = getSquirrelNumber(vm, 5);
335 s32 color = getSquirrelNumber(vm, 6);
336
337 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
338
339 tic_api_rectb(tic, x, y, w, h, color);
340 }
341 else return sq_throwerror(vm, "invalid parameters, rectb(x,y,w,h,color)\n");
342
343 return 0;
344}
345
346static SQInteger squirrel_circ(HSQUIRRELVM vm)
347{
348 SQInteger top = sq_gettop(vm);
349
350 if(top == 5)
351 {
352 s32 x = getSquirrelNumber(vm, 2);
353 s32 y = getSquirrelNumber(vm, 3);
354 s32 radius = getSquirrelNumber(vm, 4);
355 s32 color = getSquirrelNumber(vm, 5);
356
357 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
358
359 tic_api_circ(tic, x, y, radius, color);
360 }
361 else return sq_throwerror(vm, "invalid parameters, circ(x,y,radius,color)\n");
362
363 return 0;
364}
365
366static SQInteger squirrel_circb(HSQUIRRELVM vm)
367{
368 SQInteger top = sq_gettop(vm);
369
370 if(top == 5)
371 {
372 s32 x = getSquirrelNumber(vm, 2);
373 s32 y = getSquirrelNumber(vm, 3);
374 s32 radius = getSquirrelNumber(vm, 4);
375 s32 color = getSquirrelNumber(vm, 5);
376
377 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
378
379 tic_api_circb(tic, x, y, radius, color);
380 }
381 else return sq_throwerror(vm, "invalid parameters, circb(x,y,radius,color)\n");
382
383 return 0;
384}
385
386static SQInteger squirrel_elli(HSQUIRRELVM vm)
387{
388 SQInteger top = sq_gettop(vm);
389
390 if(top == 6)
391 {
392 s32 x = getSquirrelNumber(vm, 2);
393 s32 y = getSquirrelNumber(vm, 3);
394 s32 a = getSquirrelNumber(vm, 4);
395 s32 b = getSquirrelNumber(vm, 5);
396 s32 color = getSquirrelNumber(vm, 6);
397
398 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
399
400 tic_api_elli(tic, x, y, a, b, color);
401 }
402 else return sq_throwerror(vm, "invalid parameters, elli(x,y,a,b,color)\n");
403
404 return 0;
405}
406
407static SQInteger squirrel_ellib(HSQUIRRELVM vm)
408{
409 SQInteger top = sq_gettop(vm);
410
411 if(top == 6)
412 {
413 s32 x = getSquirrelNumber(vm, 2);
414 s32 y = getSquirrelNumber(vm, 3);
415 s32 a = getSquirrelNumber(vm, 4);
416 s32 b = getSquirrelNumber(vm, 5);
417 s32 color = getSquirrelNumber(vm, 6);
418
419 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
420
421 tic_api_ellib(tic, x, y, a, b, color);
422 }
423 else return sq_throwerror(vm, "invalid parameters, ellib(x,y,a,b,color)\n");
424
425 return 0;
426}
427
428static SQInteger squirrel_tri(HSQUIRRELVM vm)
429{
430 SQInteger top = sq_gettop(vm);
431
432 if(top == 8)
433 {
434 float pt[6];
435
436 for(s32 i = 0; i < COUNT_OF(pt); i++)
437 pt[i] = getSquirrelFloat(vm, i + 2);
438
439 s32 color = getSquirrelNumber(vm, 8);
440
441 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
442
443 tic_api_tri(tic, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color);
444 }
445 else return sq_throwerror(vm, "invalid parameters, tri(x1,y1,x2,y2,x3,y3,color)\n");
446
447 return 0;
448}
449
450static SQInteger squirrel_trib(HSQUIRRELVM vm)
451{
452 SQInteger top = sq_gettop(vm);
453
454 if(top == 8)
455 {
456 float pt[6];
457
458 for(s32 i = 0; i < COUNT_OF(pt); i++)
459 pt[i] = getSquirrelFloat(vm, i + 2);
460
461 s32 color = getSquirrelNumber(vm, 8);
462
463 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
464
465 tic_api_trib(tic, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color);
466 }
467 else return sq_throwerror(vm, "invalid parameters, trib(x1,y1,x2,y2,x3,y3,color)\n");
468
469 return 0;
470}
471
472static SQInteger squirrel_ttri(HSQUIRRELVM vm)
473{
474 SQInteger top = sq_gettop(vm);
475
476 if (top >= 13)
477 {
478 float pt[12];
479
480 for (s32 i = 0; i < COUNT_OF(pt); i++)
481 pt[i] = getSquirrelFloat(vm, i + 2);
482
483 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
484 static u8 colors[TIC_PALETTE_SIZE];
485 s32 count = 0;
486 tic_texture_src src = tic_tiles_texture;
487
488 // check for texture source
489 if (top >= 14)
490 {
491 src = getSquirrelNumber(vm, 14);
492 }
493 // check for chroma
494 if(OT_ARRAY == sq_gettype(vm, 15))
495 {
496 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
497 {
498 sq_pushinteger(vm, (SQInteger)i);
499 sq_rawget(vm, 15);
500 if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER))
501 {
502 colors[i-1] = getSquirrelNumber(vm, -1);
503 count++;
504 sq_poptop(vm);
505 }
506 else
507 {
508 sq_poptop(vm);
509 break;
510 }
511 }
512 }
513 else
514 {
515 colors[0] = getSquirrelNumber(vm, 15);
516 count = 1;
517 }
518
519 float z[3];
520 bool depth = false;
521
522 if (top == 18)
523 {
524 for (s32 i = 0; i < COUNT_OF(pt); i++)
525 pt[i] = getSquirrelFloat(vm, i + 16);
526
527 depth = true;
528 }
529
530 tic_api_ttri(tic, pt[0], pt[1], // xy 1
531 pt[2], pt[3], // xy 2
532 pt[4], pt[5], // xy 3
533 pt[6], pt[7], // uv 1
534 pt[8], pt[9], // uv 2
535 pt[10], pt[11], // uv 3
536 src, // texture source
537 colors, count, // chroma
538 z[0], z[1], z[2], depth); // depth
539 }
540 else return sq_throwerror(vm, "invalid parameters, ttri(x1,y1,x2,y2,x3,y3,u1,v1,u2,v2,u3,v3,[texsrc=0],[chroma=off],[z1=0],[z2=0],[z3=0])\n");
541 return 0;
542}
543
544
545static SQInteger squirrel_clip(HSQUIRRELVM vm)
546{
547 SQInteger top = sq_gettop(vm);
548
549 if(top == 1)
550 {
551 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
552
553 tic_api_clip(tic, 0, 0, TIC80_WIDTH, TIC80_HEIGHT);
554 }
555 else if(top == 5)
556 {
557 s32 x = getSquirrelNumber(vm, 2);
558 s32 y = getSquirrelNumber(vm, 3);
559 s32 w = getSquirrelNumber(vm, 4);
560 s32 h = getSquirrelNumber(vm, 5);
561
562 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
563
564 tic_api_clip((tic_mem*)getSquirrelCore(vm), x, y, w, h);
565 }
566 else return sq_throwerror(vm, "invalid parameters, use clip(x,y,w,h) or clip()\n");
567
568 return 0;
569}
570
571static SQInteger squirrel_btnp(HSQUIRRELVM vm)
572{
573 tic_core* core = getSquirrelCore(vm);
574 tic_mem* tic = (tic_mem*)core;
575
576 SQInteger top = sq_gettop(vm);
577
578 if (top == 1)
579 {
580 sq_pushinteger(vm, tic_api_btnp(tic, -1, -1, -1));
581 }
582 else if(top == 2)
583 {
584 s32 index = getSquirrelNumber(vm, 2) & 0x1f;
585
586 sq_pushbool(vm, (tic_api_btnp(tic, index, -1, -1) ? SQTrue : SQFalse));
587 }
588 else if (top == 4)
589 {
590 s32 index = getSquirrelNumber(vm, 2) & 0x1f;
591 u32 hold = getSquirrelNumber(vm, 3);
592 u32 period = getSquirrelNumber(vm, 4);
593
594 sq_pushbool(vm, (tic_api_btnp(tic, index, hold, period) ? SQTrue : SQFalse));
595 }
596 else
597 {
598 return sq_throwerror(vm, "invalid params, btnp [ id [ hold period ] ]\n");
599 }
600
601 return 1;
602}
603
604static SQInteger squirrel_btn(HSQUIRRELVM vm)
605{
606 tic_core* core = getSquirrelCore(vm);
607 tic_mem* tic = (tic_mem*)core;
608
609 SQInteger top = sq_gettop(vm);
610
611 if (top == 1)
612 {
613 sq_pushinteger(vm, tic_api_btn(tic, -1));
614 }
615 else if (top == 2)
616 {
617 bool pressed = tic_api_btn(tic, getSquirrelNumber(vm, 2) & 0x1f);
618 sq_pushbool(vm, pressed ? SQTrue : SQFalse);
619 }
620 else
621 {
622 return sq_throwerror(vm, "invalid params, btn [ id ]\n");
623 }
624
625 return 1;
626}
627
628static SQInteger squirrel_spr(HSQUIRRELVM vm)
629{
630 SQInteger top = sq_gettop(vm);
631
632 s32 index = 0;
633 s32 x = 0;
634 s32 y = 0;
635 s32 w = 1;
636 s32 h = 1;
637 s32 scale = 1;
638 tic_flip flip = tic_no_flip;
639 tic_rotate rotate = tic_no_rotate;
640 static u8 colors[TIC_PALETTE_SIZE];
641 s32 count = 0;
642
643 if(top >= 2)
644 {
645 index = getSquirrelNumber(vm, 2);
646
647 if(top >= 4)
648 {
649 x = getSquirrelNumber(vm, 3);
650 y = getSquirrelNumber(vm, 4);
651
652 if(top >= 5)
653 {
654 if(OT_ARRAY == sq_gettype(vm, 5))
655 {
656 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
657 {
658 sq_pushinteger(vm, (SQInteger)i);
659 sq_rawget(vm, 5);
660 if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER))
661 {
662 colors[i-1] = getSquirrelNumber(vm, -1);
663 count++;
664 sq_poptop(vm);
665 }
666 else
667 {
668 sq_poptop(vm);
669 break;
670 }
671 }
672 }
673 else
674 {
675 colors[0] = getSquirrelNumber(vm, 5);
676 count = 1;
677 }
678
679 if(top >= 6)
680 {
681 scale = getSquirrelNumber(vm, 6);
682
683 if(top >= 7)
684 {
685 flip = getSquirrelNumber(vm, 7);
686
687 if(top >= 8)
688 {
689 rotate = getSquirrelNumber(vm, 8);
690
691 if(top >= 10)
692 {
693 w = getSquirrelNumber(vm, 9);
694 h = getSquirrelNumber(vm, 10);
695 }
696 }
697 }
698 }
699 }
700 }
701 }
702
703 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
704
705 tic_api_spr(tic, index, x, y, w, h, colors, count, scale, flip, rotate);
706
707 return 0;
708}
709
710static SQInteger squirrel_mget(HSQUIRRELVM vm)
711{
712 SQInteger top = sq_gettop(vm);
713
714 if(top == 3)
715 {
716 s32 x = getSquirrelNumber(vm, 2);
717 s32 y = getSquirrelNumber(vm, 3);
718
719 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
720
721 u8 value = tic_api_mget(tic, x, y);
722 sq_pushinteger(vm, value);
723 return 1;
724 }
725 else return sq_throwerror(vm, "invalid params, mget(x,y)\n");
726
727 return 0;
728}
729
730static SQInteger squirrel_mset(HSQUIRRELVM vm)
731{
732 SQInteger top = sq_gettop(vm);
733
734 if(top == 4)
735 {
736 s32 x = getSquirrelNumber(vm, 2);
737 s32 y = getSquirrelNumber(vm, 3);
738 u8 val = getSquirrelNumber(vm, 4);
739
740 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
741
742 tic_api_mset(tic, x, y, val);
743 }
744 else return sq_throwerror(vm, "invalid params, mget(x,y)\n");
745
746 return 0;
747}
748
749typedef struct
750{
751 HSQUIRRELVM vm;
752 HSQOBJECT reg;
753} RemapData;
754
755static void remapCallback(void* data, s32 x, s32 y, RemapResult* result)
756{
757 RemapData* remap = (RemapData*)data;
758 HSQUIRRELVM vm = remap->vm;
759
760 SQInteger top = sq_gettop(vm);
761
762 sq_pushobject(vm, remap->reg);
763 sq_pushroottable(vm);
764 sq_pushinteger(vm, result->index);
765 sq_pushinteger(vm, x);
766 sq_pushinteger(vm, y);
767 //lua_pcall(lua, 3, 3, 0);
768
769 if (SQ_SUCCEEDED(sq_call(vm, 4, SQTrue, SQTrue)))
770 {
771 sq_pushinteger(vm, 0);
772 if (SQ_SUCCEEDED(sq_get(vm, -2)))
773 {
774 result->index = getSquirrelNumber(vm, -1);
775 sq_poptop(vm);
776 sq_pushinteger(vm, 1);
777 if (SQ_SUCCEEDED(sq_get(vm, -2)))
778 {
779 result->flip = getSquirrelNumber(vm, -1);
780 sq_poptop(vm);
781 sq_pushinteger(vm, 2);
782 if (SQ_SUCCEEDED(sq_get(vm, -2)))
783 {
784 result->rotate = getSquirrelNumber(vm, -1);
785 sq_poptop(vm);
786 }
787 }
788 }
789 }
790
791 sq_settop(vm, top);
792}
793
794static SQInteger squirrel_map(HSQUIRRELVM vm)
795{
796 s32 x = 0;
797 s32 y = 0;
798 s32 w = TIC_MAP_SCREEN_WIDTH;
799 s32 h = TIC_MAP_SCREEN_HEIGHT;
800 s32 sx = 0;
801 s32 sy = 0;
802 s32 scale = 1;
803 static u8 colors[TIC_PALETTE_SIZE];
804 s32 count = 0;
805
806 SQInteger top = sq_gettop(vm);
807
808 if(top >= 3)
809 {
810 x = getSquirrelNumber(vm, 2);
811 y = getSquirrelNumber(vm, 3);
812
813 if(top >= 5)
814 {
815 w = getSquirrelNumber(vm, 4);
816 h = getSquirrelNumber(vm, 5);
817
818 if(top >= 7)
819 {
820 sx = getSquirrelNumber(vm, 6);
821 sy = getSquirrelNumber(vm, 7);
822
823 if(top >= 8)
824 {
825 if(OT_ARRAY == sq_gettype(vm, 8))
826 {
827 for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
828 {
829 sq_pushinteger(vm, (SQInteger)i);
830 sq_rawget(vm, 8);
831 if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER))
832 {
833 colors[i-1] = getSquirrelNumber(vm, -1);
834 count++;
835 sq_poptop(vm);
836 }
837 else
838 {
839 sq_poptop(vm);
840 break;
841 }
842 }
843 }
844 else
845 {
846 colors[0] = getSquirrelNumber(vm, 8);
847 count = 1;
848 }
849
850 if(top >= 9)
851 {
852 scale = getSquirrelNumber(vm, 9);
853
854 if(top >= 10)
855 {
856 SQObjectType type = sq_gettype(vm, 10);
857 if (type & (OT_CLOSURE|OT_NATIVECLOSURE|OT_INSTANCE))
858 {
859 RemapData data = {vm};
860 sq_resetobject(&data.reg);
861 sq_getstackobj(vm, 10, &data.reg);
862 sq_addref(vm, &data.reg);
863
864 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
865
866 tic_api_map(tic, x, y, w, h, sx, sy, colors, count, scale, remapCallback, &data);
867
868 //luaL_unref(lua, LUA_REGISTRYINDEX, data.reg);
869 sq_release(vm, &data.reg);
870
871 return 0;
872 }
873 }
874 }
875 }
876 }
877 }
878 }
879
880 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
881
882 tic_api_map((tic_mem*)getSquirrelCore(vm), x, y, w, h, sx, sy, colors, count, scale, NULL, NULL);
883
884 return 0;
885}
886
887static SQInteger squirrel_music(HSQUIRRELVM vm)
888{
889 SQInteger top = sq_gettop(vm);
890 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
891
892 if(top == 1) tic_api_music(tic, -1, 0, 0, false, false, -1, -1);
893 else if(top >= 2)
894 {
895 tic_api_music(tic, -1, 0, 0, false, false, -1, -1);
896
897 s32 track = getSquirrelNumber(vm, 2);
898
899 if(track > MUSIC_TRACKS - 1)
900 return sq_throwerror(vm, "invalid music track index\n");
901
902 s32 frame = -1;
903 s32 row = -1;
904 bool loop = true;
905 bool sustain = false;
906 s32 tempo = -1;
907 s32 speed = -1;
908
909 if(top >= 3)
910 {
911 frame = getSquirrelNumber(vm, 3);
912
913 if(top >= 4)
914 {
915 row = getSquirrelNumber(vm, 4);
916
917 if(top >= 5)
918 {
919 SQBool b = SQFalse;
920 sq_getbool(vm, 5, &b);
921 loop = (b != SQFalse);
922 if(top >= 6)
923 {
924 SQBool b = SQFalse;
925 sq_getbool(vm, 6, &b);
926 sustain = (b != SQFalse);
927
928 if (top >= 7)
929 {
930 tempo = getSquirrelNumber(vm, 7);
931
932 if (top >= 8)
933 {
934 speed = getSquirrelNumber(vm, 8);
935 }
936 }
937 }
938 }
939 }
940 }
941
942 tic_api_music(tic, track, frame, row, loop, sustain, tempo, speed);
943 }
944 else return sq_throwerror(vm, "invalid params, use music(track)\n");
945
946 return 0;
947}
948
949static SQInteger squirrel_sfx(HSQUIRRELVM vm)
950{
951 SQInteger top = sq_gettop(vm);
952
953 if(top >= 2)
954 {
955 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
956
957 s32 note = -1;
958 s32 octave = -1;
959 s32 duration = -1;
960 s32 channel = 0;
961 s32 volumes[TIC80_SAMPLE_CHANNELS] = {MAX_VOLUME, MAX_VOLUME};
962 s32 speed = SFX_DEF_SPEED;
963
964 s32 index = getSquirrelNumber(vm, 2);
965
966 if(index < SFX_COUNT)
967 {
968 if (index >= 0)
969 {
970 tic_sample* effect = tic->ram->sfx.samples.data + index;
971
972 note = effect->note;
973 octave = effect->octave;
974 speed = effect->speed;
975 }
976
977 if(top >= 3)
978 {
979 if(sq_gettype(vm, 3) & (OT_INTEGER|OT_FLOAT))
980 {
981 s32 id = getSquirrelNumber(vm, 3);
982 note = id % NOTES;
983 octave = id / NOTES;
984 }
985 else if(sq_gettype(vm, 3) == OT_STRING)
986 {
987 const SQChar* str;
988 sq_getstring(vm, 3, &str);
989 const char* noteStr = (const char*)str;
990
991 if(!tic_tool_parse_note(noteStr, &note, &octave))
992 {
993 return sq_throwerror(vm, "invalid note, should be like C#4\n");
994 }
995 }
996
997 if(top >= 4)
998 {
999 duration = getSquirrelNumber(vm, 4);
1000
1001 if(top >= 5)
1002 {
1003 channel = getSquirrelNumber(vm, 5);
1004
1005 if(top >= 6)
1006 {
1007 if(OT_ARRAY == sq_gettype(vm, 6))
1008 {
1009 for(s32 i = 0; i < COUNT_OF(volumes); i++)
1010 {
1011 sq_pushinteger(vm, (SQInteger)i);
1012 sq_rawget(vm, 6);
1013 if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER))
1014 volumes[i] = getSquirrelNumber(vm, -1);
1015 sq_poptop(vm);
1016 }
1017 }
1018 else volumes[0] = volumes[1] = getSquirrelNumber(vm, 6);
1019
1020 if(top >= 7)
1021 {
1022 speed = getSquirrelNumber(vm, 7);
1023 }
1024 }
1025 }
1026 }
1027 }
1028
1029 if (channel >= 0 && channel < TIC_SOUND_CHANNELS)
1030 {
1031 tic_api_sfx(tic, index, note, octave, duration, channel, volumes[0] & 0xf, volumes[1] & 0xf, speed);
1032 }
1033 else return sq_throwerror(vm, "unknown channel\n");
1034 }
1035 else return sq_throwerror(vm, "unknown sfx index\n");
1036 }
1037 else return sq_throwerror(vm, "invalid sfx params\n");
1038
1039 return 0;
1040}
1041
1042static SQInteger squirrel_vbank(HSQUIRRELVM vm)
1043{
1044 tic_core* core = getSquirrelCore(vm);
1045 tic_mem* tic = (tic_mem*)core;
1046
1047 s32 prev = core->state.vbank.id;
1048
1049 if(sq_gettop(vm) == 2)
1050 tic_api_vbank(tic, getSquirrelNumber(vm, 2));
1051
1052 sq_pushinteger(vm, prev);
1053 return 1;
1054}
1055
1056static SQInteger squirrel_sync(HSQUIRRELVM vm)
1057{
1058 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1059
1060 bool toCart = false;
1061 u32 mask = 0;
1062 s32 bank = 0;
1063
1064 if(sq_gettop(vm) >= 2)
1065 {
1066 mask = getSquirrelNumber(vm, 2);
1067
1068 if(sq_gettop(vm) >= 3)
1069 {
1070 bank = getSquirrelNumber(vm, 3);
1071
1072 if(sq_gettop(vm) >= 4)
1073 {
1074 SQBool b = SQFalse;
1075 sq_getbool(vm, 4, &b);
1076 toCart = (b != SQFalse);
1077 }
1078 }
1079 }
1080
1081 if(bank >= 0 && bank < TIC_BANKS)
1082 tic_api_sync(tic, mask, bank, toCart);
1083 else
1084 return sq_throwerror(vm, "sync() error, invalid bank");
1085
1086 return 0;
1087}
1088
1089static SQInteger squirrel_reset(HSQUIRRELVM vm)
1090{
1091 tic_core* core = getSquirrelCore(vm);
1092
1093 core->state.initialized = false;
1094
1095 return 0;
1096}
1097
1098static SQInteger squirrel_key(HSQUIRRELVM vm)
1099{
1100 tic_core* core = getSquirrelCore(vm);
1101 tic_mem* tic = &core->memory;
1102
1103 SQInteger top = sq_gettop(vm);
1104
1105 if (top == 1)
1106 {
1107 sq_pushbool(vm, tic_api_key(tic, tic_key_unknown) ? SQTrue : SQFalse);
1108 }
1109 else if (top == 2)
1110 {
1111 tic_key key = getSquirrelNumber(vm, 2);
1112
1113 if(key < tic_keys_count)
1114 sq_pushbool(vm, tic_api_key(tic, key) ? SQTrue : SQFalse);
1115 else
1116 {
1117 return sq_throwerror(vm, "unknown keyboard code\n");
1118 }
1119 }
1120 else
1121 {
1122 return sq_throwerror(vm, "invalid params, key [code]\n");
1123 }
1124
1125 return 1;
1126}
1127
1128static SQInteger squirrel_keyp(HSQUIRRELVM vm)
1129{
1130 tic_core* core = getSquirrelCore(vm);
1131 tic_mem* tic = &core->memory;
1132
1133 SQInteger top = sq_gettop(vm);
1134
1135 if (top == 1)
1136 {
1137 sq_pushbool(vm, tic_api_keyp(tic, tic_key_unknown, -1, -1) ? SQTrue : SQFalse);
1138 }
1139 else
1140 {
1141 tic_key key = getSquirrelNumber(vm, 2);
1142
1143 if(key >= tic_keys_count)
1144 {
1145 return sq_throwerror(vm, "unknown keyboard code\n");
1146 }
1147 else
1148 {
1149 if(top == 2)
1150 {
1151 sq_pushbool(vm, tic_api_keyp(tic, key, -1, -1) ? SQTrue : SQFalse);
1152 }
1153 else if(top == 4)
1154 {
1155 u32 hold = getSquirrelNumber(vm, 3);
1156 u32 period = getSquirrelNumber(vm, 4);
1157
1158 sq_pushbool(vm, tic_api_keyp(tic, key, hold, period) ? SQTrue : SQFalse);
1159 }
1160 else
1161 {
1162 return sq_throwerror(vm, "invalid params, keyp [ code [ hold period ] ]\n");
1163 }
1164 }
1165 }
1166
1167 return 1;
1168}
1169
1170static SQInteger squirrel_memcpy(HSQUIRRELVM vm)
1171{
1172 SQInteger top = sq_gettop(vm);
1173
1174 if(top == 4)
1175 {
1176 s32 dest = getSquirrelNumber(vm, 2);
1177 s32 src = getSquirrelNumber(vm, 3);
1178 s32 size = getSquirrelNumber(vm, 4);
1179
1180 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1181 tic_api_memcpy(tic, dest, src, size);
1182 return 0;
1183 }
1184
1185 return sq_throwerror(vm, "invalid params, memcpy(dest,src,size)\n");
1186}
1187
1188static SQInteger squirrel_memset(HSQUIRRELVM vm)
1189{
1190 SQInteger top = sq_gettop(vm);
1191
1192 if(top == 4)
1193 {
1194 s32 dest = getSquirrelNumber(vm, 2);
1195 u8 value = getSquirrelNumber(vm, 3);
1196 s32 size = getSquirrelNumber(vm, 4);
1197
1198 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1199 tic_api_memset(tic, dest, value, size);
1200 return 0;
1201 }
1202
1203 return sq_throwerror(vm, "invalid params, memset(dest,val,size)\n");
1204}
1205
1206// NB we leave the string on the stack so that the char* pointer remains valid.
1207static const char* printString(HSQUIRRELVM vm, s32 index)
1208{
1209 const SQChar* text = "";
1210 if (SQ_SUCCEEDED(sq_tostring(vm, index)))
1211 {
1212 sq_getstring(vm, -1, &text);
1213 }
1214
1215 return (const char*)text;
1216}
1217
1218static SQInteger squirrel_font(HSQUIRRELVM vm)
1219{
1220 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1221 SQInteger top = sq_gettop(vm);
1222
1223 if(top >= 2)
1224 {
1225 const char* text = printString(vm, 2);
1226 s32 x = 0;
1227 s32 y = 0;
1228 s32 width = TIC_SPRITESIZE;
1229 s32 height = TIC_SPRITESIZE;
1230 u8 chromakey = 0;
1231 bool fixed = false;
1232 bool alt = false;
1233 s32 scale = 1;
1234
1235 if(top >= 4)
1236 {
1237 x = getSquirrelNumber(vm, 3);
1238 y = getSquirrelNumber(vm, 4);
1239
1240 if(top >= 5)
1241 {
1242 chromakey = getSquirrelNumber(vm, 5);
1243
1244 if(top >= 7)
1245 {
1246 width = getSquirrelNumber(vm, 6);
1247 height = getSquirrelNumber(vm, 7);
1248
1249 if(top >= 8)
1250 {
1251 SQBool b = SQFalse;
1252 sq_getbool(vm, 8, &b);
1253 fixed = (b != SQFalse);
1254
1255 if(top >= 9)
1256 {
1257 scale = getSquirrelNumber(vm, 9);
1258
1259 if (top >= 10)
1260 {
1261 SQBool b = SQFalse;
1262 sq_getbool(vm, 10, &b);
1263 alt = (b != SQFalse);
1264 }
1265
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 if(scale == 0)
1273 {
1274 sq_pushinteger(vm, 0);
1275 return 1;
1276 }
1277
1278 s32 size = tic_api_font(tic, text, x, y, &chromakey, 1, width, height, fixed, scale, alt);
1279
1280 sq_pushinteger(vm, size);
1281 return 1;
1282 }
1283
1284 return 0;
1285}
1286
1287static SQInteger squirrel_print(HSQUIRRELVM vm)
1288{
1289 SQInteger top = sq_gettop(vm);
1290
1291 if(top >= 2)
1292 {
1293 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1294
1295 s32 x = 0;
1296 s32 y = 0;
1297 s32 color = TIC_DEFAULT_COLOR;
1298 bool fixed = false;
1299 bool alt = false;
1300 s32 scale = 1;
1301
1302 const char* text = printString(vm, 2);
1303
1304 if(top >= 4)
1305 {
1306 x = getSquirrelNumber(vm, 3);
1307 y = getSquirrelNumber(vm, 4);
1308
1309 if(top >= 5)
1310 {
1311 color = getSquirrelNumber(vm, 5) % TIC_PALETTE_SIZE;
1312
1313 if(top >= 6)
1314 {
1315 SQBool b = SQFalse;
1316 sq_getbool(vm, 6, &b);
1317 fixed = (b != SQFalse);
1318
1319 if(top >= 7)
1320 {
1321 scale = getSquirrelNumber(vm, 7);
1322
1323 if (top >= 8)
1324 {
1325 SQBool b = SQFalse;
1326 sq_getbool(vm, 8, &b);
1327 alt = (b != SQFalse);
1328 }
1329 }
1330 }
1331 }
1332 }
1333
1334 if(scale == 0)
1335 {
1336 sq_pushinteger(vm, 0);
1337 return 1;
1338 }
1339
1340 s32 size = tic_api_print(tic, text ? text : "nil", x, y, color, fixed, scale, alt);
1341
1342 sq_pushinteger(vm, size);
1343
1344 return 1;
1345 }
1346
1347 return 0;
1348}
1349
1350static SQInteger squirrel_trace(HSQUIRRELVM vm)
1351{
1352 SQInteger top = sq_gettop(vm);
1353 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1354
1355 if(top >= 2)
1356 {
1357 const char* text = printString(vm, 2);
1358 u8 color = TIC_DEFAULT_COLOR;
1359
1360 if(top >= 3)
1361 {
1362 color = getSquirrelNumber(vm, 3);
1363 }
1364
1365 tic_api_trace(tic, text, color);
1366 }
1367
1368 return 0;
1369}
1370
1371static SQInteger squirrel_pmem(HSQUIRRELVM vm)
1372{
1373 SQInteger top = sq_gettop(vm);
1374 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1375
1376 if(top >= 2)
1377 {
1378 u32 index = getSquirrelNumber(vm, 2);
1379
1380 if(index < TIC_PERSISTENT_SIZE)
1381 {
1382 u32 val = tic_api_pmem(tic, index, 0, false);
1383
1384 if(top >= 3)
1385 {
1386 SQInteger i = 0;
1387 sq_getinteger(vm, 3, &i);
1388 tic_api_pmem(tic, index, (u32)i, true);
1389 }
1390
1391 sq_pushinteger(vm, val);
1392
1393 return 1;
1394 }
1395 return sq_throwerror(vm, "invalid persistent tic index\n");
1396 }
1397 else return sq_throwerror(vm, "invalid params, pmem(index [val]) -> val\n");
1398
1399 return 0;
1400}
1401
1402static SQInteger squirrel_time(HSQUIRRELVM vm)
1403{
1404 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1405
1406 sq_pushfloat(vm, (SQFloat)(tic_api_time(tic)));
1407
1408 return 1;
1409}
1410
1411static SQInteger squirrel_tstamp(HSQUIRRELVM vm)
1412{
1413 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1414
1415 sq_pushinteger(vm, tic_api_tstamp(tic));
1416
1417 return 1;
1418}
1419
1420static SQInteger squirrel_exit(HSQUIRRELVM vm)
1421{
1422 tic_api_exit((tic_mem*)getSquirrelCore(vm));
1423
1424 return 0;
1425}
1426
1427static SQInteger squirrel_mouse(HSQUIRRELVM vm)
1428{
1429 tic_core* core = getSquirrelCore(vm);
1430
1431 const tic80_mouse* mouse = &core->memory.ram->input.mouse;
1432
1433 sq_newarray(vm, 0);
1434
1435 {
1436 tic_point pos = tic_api_mouse((tic_mem*)core);
1437
1438 sq_pushinteger(vm, pos.x);
1439 sq_arrayappend(vm, -2);
1440 sq_pushinteger(vm, pos.y);
1441 sq_arrayappend(vm, -2);
1442 }
1443
1444 sq_pushbool(vm, mouse->left ? SQTrue : SQFalse);
1445 sq_arrayappend(vm, -2);
1446 sq_pushbool(vm, mouse->middle ? SQTrue : SQFalse);
1447 sq_arrayappend(vm, -2);
1448 sq_pushbool(vm, mouse->right ? SQTrue : SQFalse);
1449 sq_arrayappend(vm, -2);
1450 sq_pushinteger(vm, mouse->scrollx);
1451 sq_arrayappend(vm, -2);
1452 sq_pushinteger(vm, mouse->scrolly);
1453 sq_arrayappend(vm, -2);
1454
1455 return 1;
1456}
1457
1458static SQInteger squirrel_fget(HSQUIRRELVM vm)
1459{
1460 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1461
1462 SQInteger top = sq_gettop(vm);
1463
1464 if(top >= 2)
1465 {
1466 u32 index = getSquirrelNumber(vm, 2);
1467
1468 if(top >= 3)
1469 {
1470 u32 flag = getSquirrelNumber(vm, 3);
1471 sq_pushbool(vm, tic_api_fget(tic, index, flag));
1472 return 1;
1473 }
1474 }
1475
1476 sq_throwerror(vm, "invalid params, fget(index, flag) -> val\n");
1477
1478 return 0;
1479}
1480
1481static SQInteger squirrel_fset(HSQUIRRELVM vm)
1482{
1483 tic_mem* tic = (tic_mem*)getSquirrelCore(vm);
1484
1485 SQInteger top = sq_gettop(vm);
1486
1487 if(top >= 2)
1488 {
1489 u32 index = getSquirrelNumber(vm, 2);
1490
1491 if(top >= 3)
1492 {
1493 u32 flag = getSquirrelNumber(vm, 3);
1494
1495 if(top >= 4)
1496 {
1497 SQBool value = SQFalse;
1498 sq_getbool(vm, 4, &value);
1499
1500 tic_api_fset(tic, index, flag, value);
1501 return 0;
1502 }
1503 }
1504 }
1505
1506 sq_throwerror(vm, "invalid params, fset(index, flag, value)\n");
1507
1508 return 0;
1509}
1510
1511static SQInteger squirrel_dofile(HSQUIRRELVM vm)
1512{
1513 return sq_throwerror(vm, "unknown method: \"dofile\"\n");
1514}
1515
1516static SQInteger squirrel_loadfile(HSQUIRRELVM vm)
1517{
1518 return sq_throwerror(vm, "unknown method: \"loadfile\"\n");
1519}
1520
1521static void squirrel_open_builtins(HSQUIRRELVM vm)
1522{
1523 sq_pushroottable(vm);
1524 sqstd_register_mathlib(vm);
1525 sqstd_register_stringlib(vm);
1526 sqstd_register_bloblib(vm);
1527 sq_poptop(vm);
1528}
1529
1530static void initAPI(tic_core* core)
1531{
1532 HSQUIRRELVM vm = core->currentVM;
1533
1534 sq_setcompilererrorhandler(vm, squirrel_compilerError);
1535
1536 sq_pushregistrytable(vm);
1537 sq_pushstring(vm, TicCore, -1);
1538 sq_pushuserpointer(core->currentVM, core);
1539 sq_newslot(vm, -3, SQTrue);
1540 sq_poptop(vm);
1541
1542#if USE_FOREIGN_POINTER
1543 sq_setforeignptr(vm, core);
1544#endif
1545
1546#define API_FUNC_DEF(name, ...) {squirrel_ ## name, #name},
1547 static const struct{SQFUNCTION func; const char* name;} ApiItems[] = {TIC_API_LIST(API_FUNC_DEF)};
1548#undef API_FUNC_DEF
1549
1550 for (s32 i = 0; i < COUNT_OF(ApiItems); i++)
1551 registerSquirrelFunction(core, ApiItems[i].func, ApiItems[i].name);
1552
1553 registerSquirrelFunction(core, squirrel_dofile, "dofile");
1554 registerSquirrelFunction(core, squirrel_loadfile, "loadfile");
1555
1556 sq_enabledebuginfo(vm, SQTrue);
1557
1558}
1559
1560static void closeSquirrel(tic_mem* tic)
1561{
1562 tic_core* core = (tic_core*)tic;
1563
1564 if(core->currentVM)
1565 {
1566 sq_close(core->currentVM);
1567 core->currentVM = NULL;
1568 }
1569}
1570
1571static bool initSquirrel(tic_mem* tic, const char* code)
1572{
1573 tic_core* core = (tic_core*)tic;
1574
1575 closeSquirrel(tic);
1576
1577 HSQUIRRELVM vm = core->currentVM = sq_open(100);
1578 squirrel_open_builtins(vm);
1579
1580 sq_newclosure(vm, squirrel_errorHandler, 0);
1581 sq_seterrorhandler(vm);
1582
1583 initAPI(core);
1584
1585 {
1586 HSQUIRRELVM vm = core->currentVM;
1587
1588 sq_settop(vm, 0);
1589
1590 if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQTrue))) ||
1591 (sq_pushroottable(vm), false) ||
1592 (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))))
1593 {
1594 sq_getlasterror(vm);
1595 sq_tostring(vm, -1);
1596 const SQChar* errorString = "unknown error";
1597 sq_getstring(vm, -1, &errorString);
1598
1599 if (core->data)
1600 core->data->error(core->data->data, errorString);
1601
1602 sq_pop(vm, 2); // error and error string
1603
1604 return false;
1605 }
1606 }
1607
1608 return true;
1609}
1610
1611static void errorReport(tic_mem* tic)
1612{
1613 tic_core* core = (tic_core*)tic;
1614
1615 HSQUIRRELVM vm = core->currentVM;
1616
1617 sq_getlasterror(vm);
1618 sq_tostring(vm, -1);
1619 const SQChar* errorString = "unknown error";
1620 sq_getstring(vm, -1, &errorString);
1621
1622 if (core->data)
1623 core->data->error(core->data->data, errorString);
1624 sq_pop(vm, 3); // remove string, error and root table.
1625}
1626
1627static void callSquirrelTick(tic_mem* tic)
1628{
1629 tic_core* core = (tic_core*)tic;
1630
1631 HSQUIRRELVM vm = core->currentVM;
1632
1633 if(vm)
1634 {
1635 sq_pushroottable(vm);
1636 sq_pushstring(vm, TIC_FN, -1);
1637
1638 if (SQ_SUCCEEDED(sq_get(vm, -2)))
1639 {
1640 sq_pushroottable(vm);
1641 if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
1642 {
1643 errorReport(tic);
1644 return;
1645 }
1646
1647 // call OVR() callback for backward compatibility
1648 {
1649 sq_pushroottable(vm);
1650 sq_pushstring(vm, OVR_FN, -1);
1651
1652 if(SQ_SUCCEEDED(sq_get(vm, -2)))
1653 {
1654 OVR(core)
1655 {
1656 sq_pushroottable(vm);
1657
1658 if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
1659 {
1660 errorReport(tic);
1661 }
1662 }
1663 }
1664 else sq_poptop(vm);
1665 }
1666 }
1667 else
1668 {
1669 sq_pop(vm, 1);
1670 if (core->data)
1671 core->data->error(core->data->data, "'function TIC()...' isn't found :(");
1672 }
1673 }
1674}
1675
1676static void callSquirrelBoot(tic_mem* tic)
1677{
1678 tic_core* core = (tic_core*)tic;
1679
1680 HSQUIRRELVM vm = core->currentVM;
1681
1682 if(vm)
1683 {
1684 sq_pushroottable(vm);
1685 sq_pushstring(vm, BOOT_FN, -1);
1686
1687 if (SQ_SUCCEEDED(sq_get(vm, -2)))
1688 {
1689 sq_pushroottable(vm);
1690 if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
1691 {
1692 errorReport(tic);
1693 return;
1694 }
1695 }
1696 }
1697}
1698
1699static void callSquirrelIntCallback(tic_mem* tic, s32 value, void* data, const char* name)
1700{
1701 tic_core* core = (tic_core*)tic;
1702 HSQUIRRELVM vm = core->currentVM;
1703
1704 if (vm)
1705 {
1706 sq_pushroottable(vm);
1707 sq_pushstring(vm, name, -1);
1708 if (SQ_SUCCEEDED(sq_get(vm, -2)))
1709 {
1710 sq_pushroottable(vm);
1711 sq_pushinteger(vm, value);
1712
1713 if(SQ_FAILED(sq_call(vm, 2, SQFalse, SQTrue)))
1714 {
1715 sq_getlasterror(vm);
1716 sq_tostring(vm, -1);
1717
1718 const SQChar* errorString = "unknown error";
1719 sq_getstring(vm, -1, &errorString);
1720 if (core->data)
1721 core->data->error(core->data->data, errorString);
1722 sq_pop(vm, 3); // error string, error and root table
1723 }
1724 }
1725 else sq_poptop(vm);
1726 }
1727}
1728
1729static void callSquirrelScanline(tic_mem* tic, s32 row, void* data)
1730{
1731 callSquirrelIntCallback(tic, row, data, SCN_FN);
1732
1733 // try to call old scanline
1734 callSquirrelIntCallback(tic, row, data, "scanline");
1735}
1736
1737static void callSquirrelBorder(tic_mem* tic, s32 row, void* data)
1738{
1739 callSquirrelIntCallback(tic, row, data, BDR_FN);
1740}
1741
1742static void callSquirrelMenu(tic_mem* tic, s32 index, void* data)
1743{
1744 callSquirrelIntCallback(tic, index, data, MENU_FN);
1745}
1746
1747static const char* const SquirrelKeywords [] =
1748{
1749 "base", "break", "case", "catch", "class", "clone",
1750 "continue", "const", "default", "delete", "else", "enum",
1751 "extends", "for", "foreach", "function", "if", "in",
1752 "local", "null", "resume", "return", "switch", "this",
1753 "throw", "try", "typeof", "while", "yield", "constructor",
1754 "instanceof", "true", "false", "static", "__LINE__", "__FILE__"
1755};
1756
1757static inline bool isalnum_(char c) {return isalnum(c) || c == '_';}
1758
1759static const tic_outline_item* getSquirrelOutline(const char* code, s32* size)
1760{
1761 enum{Size = sizeof(tic_outline_item)};
1762
1763 *size = 0;
1764
1765 static tic_outline_item* items = NULL;
1766
1767 if(items)
1768 {
1769 free(items);
1770 items = NULL;
1771 }
1772
1773 const char* ptr = code;
1774
1775 while(true)
1776 {
1777 static const char FuncString[] = "function ";
1778
1779 ptr = strstr(ptr, FuncString);
1780
1781 if(ptr)
1782 {
1783 ptr += sizeof FuncString - 1;
1784
1785 const char* start = ptr;
1786 const char* end = start;
1787
1788 while(*ptr)
1789 {
1790 char c = *ptr;
1791
1792 if(isalnum_(c) || c == ':');
1793 else if(c == '(')
1794 {
1795 end = ptr;
1796 break;
1797 }
1798 else break;
1799
1800 ptr++;
1801 }
1802
1803 if(end > start)
1804 {
1805 items = realloc(items, (*size + 1) * Size);
1806
1807 items[*size].pos = start;
1808 items[*size].size = (s32)(end - start);
1809
1810 (*size)++;
1811 }
1812 }
1813 else break;
1814 }
1815
1816 return items;
1817}
1818
1819void evalSquirrel(tic_mem* tic, const char* code) {
1820 tic_core* core = (tic_core*)tic;
1821 HSQUIRRELVM vm = core->currentVM;
1822
1823 // make sure that the Squirrel interpreter is initialized.
1824 if (vm == NULL)
1825 {
1826 if (!initSquirrel(tic, ""))
1827 return;
1828 vm = core->currentVM;
1829 }
1830
1831 sq_settop(vm, 0);
1832
1833 if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQTrue))) ||
1834 (sq_pushroottable(vm), false) ||
1835 (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))))
1836 {
1837 sq_getlasterror(vm);
1838 sq_tostring(vm, -1);
1839 const SQChar* errorString = "unknown error";
1840 sq_getstring(vm, -1, &errorString);
1841 if (core->data)
1842 core->data->error(core->data->data, errorString);
1843 }
1844
1845 sq_settop(vm, 0);
1846}
1847
1848tic_script_config SquirrelSyntaxConfig =
1849{
1850 .id = 15,
1851 .name = "squirrel",
1852 .fileExtension = ".nut",
1853 .projectComment = "//",
1854 {
1855 .init = initSquirrel,
1856 .close = closeSquirrel,
1857 .tick = callSquirrelTick,
1858 .boot = callSquirrelBoot,
1859
1860 .callback =
1861 {
1862 .scanline = callSquirrelScanline,
1863 .border = callSquirrelBorder,
1864 .menu = callSquirrelMenu,
1865 },
1866 },
1867
1868 .getOutline = getSquirrelOutline,
1869 .eval = evalSquirrel,
1870
1871 .blockCommentStart = "/*",
1872 .blockCommentEnd = "*/",
1873 .blockCommentStart2 = NULL,
1874 .blockCommentEnd2 = NULL,
1875 .singleComment = "//",
1876 .blockStringStart = "@\"",
1877 .blockStringEnd = "\"",
1878 .blockEnd = "}",
1879
1880 .keywords = SquirrelKeywords,
1881 .keywordsCount = COUNT_OF(SquirrelKeywords),
1882};
1883
1884#endif /* defined(TIC_BUILD_WITH_SQUIRREL) */
1885