this repo has no description
at main 1320 lines 36 kB view raw
1// MIT License 2 3// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com 4// Copyright (c) 2020-2021 Julia Nelz <julia ~at~ nelz.pl> 5 6// Permission is hereby granted, free of charge, to any person obtaining a copy 7// of this software and associated documentation files (the "Software"), to deal 8// in the Software without restriction, including without limitation the rights 9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10// copies of the Software, and to permit persons to whom the Software is 11// furnished to do so, subject to the following conditions: 12 13// The above copyright notice and this permission notice shall be included in all 14// copies or substantial portions of the Software. 15 16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22// SOFTWARE. 23 24#include "core/core.h" 25 26#include <stdlib.h> 27#include <string.h> 28#include <ctype.h> 29 30#include <mruby.h> 31#include <mruby/compile.h> 32#include <mruby/error.h> 33#include <mruby/throw.h> 34#include <mruby/array.h> 35#include <mruby/hash.h> 36#include <mruby/variable.h> 37#include <mruby/value.h> 38#include <mruby/string.h> 39 40extern bool parse_note(const char* noteStr, s32* note, s32* octave); 41 42typedef struct { 43 struct mrb_state* mrb; 44 struct mrbc_context* mrb_cxt; 45} mrbVm; 46 47static tic_core* CurrentMachine = NULL; 48static inline tic_core* getMRubyMachine(mrb_state* mrb) 49{ 50 return CurrentMachine; 51} 52 53static mrb_value mrb_peek(mrb_state* mrb, mrb_value self) 54{ 55 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 56 57 mrb_int address; 58 mrb_int bits = BITS_IN_BYTE; 59 mrb_get_args(mrb, "i|i", &address, &bits); 60 61 return mrb_fixnum_value(core->api.peek(tic, address, bits)); 62} 63 64static mrb_value mrb_poke(mrb_state* mrb, mrb_value self) 65{ 66 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 67 68 69 mrb_int address, value; 70 mrb_int bits = BITS_IN_BYTE; 71 mrb_get_args(mrb, "ii|i", &address, &value, &bits); 72 73 core->api.poke(tic, address, value, bits); 74 75 return mrb_nil_value(); 76} 77 78static mrb_value mrb_peek1(mrb_state* mrb, mrb_value self) 79{ 80 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 81 82 83 mrb_int address; 84 mrb_get_args(mrb, "i", &address); 85 86 return mrb_fixnum_value(core->api.peek1(tic, address)); 87} 88 89static mrb_value mrb_poke1(mrb_state* mrb, mrb_value self) 90{ 91 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 92 93 94 mrb_int address, value; 95 mrb_get_args(mrb, "ii", &address, &value); 96 97 core->api.poke1(tic, address, value); 98 99 return mrb_nil_value(); 100} 101 102static mrb_value mrb_peek2(mrb_state* mrb, mrb_value self) 103{ 104 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 105 106 107 mrb_int address; 108 mrb_get_args(mrb, "i", &address); 109 110 return mrb_fixnum_value(core->api.peek2(tic, address)); 111} 112 113static mrb_value mrb_poke2(mrb_state* mrb, mrb_value self) 114{ 115 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 116 117 118 mrb_int address, value; 119 mrb_get_args(mrb, "ii", &address, &value); 120 121 core->api.poke2(tic, address, value); 122 123 return mrb_nil_value(); 124} 125 126static mrb_value mrb_peek4(mrb_state* mrb, mrb_value self) 127{ 128 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 129 130 131 mrb_int address; 132 mrb_get_args(mrb, "i", &address); 133 134 return mrb_fixnum_value(core->api.peek4(tic, address)); 135} 136 137static mrb_value mrb_poke4(mrb_state* mrb, mrb_value self) 138{ 139 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 140 141 142 mrb_int address, value; 143 mrb_get_args(mrb, "ii", &address, &value); 144 145 core->api.poke4(tic, address, value); 146 147 return mrb_nil_value(); 148} 149 150static mrb_value mrb_cls(mrb_state* mrb, mrb_value self) 151{ 152 mrb_int color = 0; 153 mrb_get_args(mrb, "|i", &color); 154 155 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 156 157 core->api.cls(tic, color); 158 159 return mrb_nil_value(); 160} 161 162static mrb_value mrb_pix(mrb_state* mrb, mrb_value self) 163{ 164 mrb_int x, y, color; 165 mrb_int argc = mrb_get_args(mrb, "ii|i", &x, &y, &color); 166 167 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 168 169 if(argc == 3) 170 { 171 core->api.pix(tic, x, y, color, false); 172 return mrb_nil_value(); 173 } 174 else 175 { 176 return mrb_fixnum_value(core->api.pix(tic, x, y, 0, true)); 177 } 178} 179 180static mrb_value mrb_line(mrb_state* mrb, mrb_value self) 181{ 182 mrb_float x0, y0, x1, y1; 183 mrb_int color; 184 mrb_get_args(mrb, "ffffi", &x0, &y0, &x1, &y1, &color); 185 186 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 187 188 core->api.line(tic, x0, y0, x1, y1, color); 189 190 return mrb_nil_value(); 191} 192 193static mrb_value mrb_rect(mrb_state* mrb, mrb_value self) 194{ 195 mrb_int x, y, w, h, color; 196 mrb_get_args(mrb, "iiiii", &x, &y, &w, &h, &color); 197 198 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 199 200 core->api.rect(tic, x, y, w, h, color); 201 202 return mrb_nil_value(); 203} 204 205static mrb_value mrb_rectb(mrb_state* mrb, mrb_value self) 206{ 207 mrb_int x, y, w, h, color; 208 mrb_get_args(mrb, "iiiii", &x, &y, &w, &h, &color); 209 210 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 211 212 core->api.rectb(tic, x, y, w, h, color); 213 214 return mrb_nil_value(); 215} 216 217static mrb_value mrb_circ(mrb_state* mrb, mrb_value self) 218{ 219 mrb_int x, y, radius, color; 220 mrb_get_args(mrb, "iiii", &x, &y, &radius, &color); 221 222 if(radius >= 0) { 223 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 224 225 core->api.circ(tic, x, y, radius, color); 226 } else { 227 mrb_raise(mrb, E_ARGUMENT_ERROR, "radius must be greater than or equal 0"); 228 } 229 230 return mrb_nil_value(); 231} 232 233static mrb_value mrb_circb(mrb_state* mrb, mrb_value self) 234{ 235 mrb_int x, y, radius, color; 236 mrb_get_args(mrb, "iiii", &x, &y, &radius, &color); 237 238 if(radius >= 0) { 239 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 240 241 core->api.circb(tic, x, y, radius, color); 242 } else { 243 mrb_raise(mrb, E_ARGUMENT_ERROR, "radius must be greater than or equal 0"); 244 } 245 246 return mrb_nil_value(); 247} 248 249static mrb_value mrb_elli(mrb_state* mrb, mrb_value self) 250{ 251 mrb_int x, y, a, b, color; 252 mrb_get_args(mrb, "iiiii", &x, &y, &a, &b, &color); 253 254 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 255 256 core->api.elli(tic, x, y, a, b, color); 257 258 return mrb_nil_value(); 259} 260 261static mrb_value mrb_ellib(mrb_state* mrb, mrb_value self) 262{ 263 mrb_int x, y, a, b, color; 264 mrb_get_args(mrb, "iiiii", &x, &y, &a, &b, &color); 265 266 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 267 268 core->api.ellib(tic, x, y, a, b, color); 269 270 return mrb_nil_value(); 271} 272 273static mrb_value mrb_paint(mrb_state* mrb, mrb_value self) 274{ 275 mrb_int x, y, color; 276 mrb_int bordercolor = -1; 277 mrb_get_args(mrb, "iii|i", &x, &y, &color, &bordercolor); 278 279 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 280 281 core->api.paint(tic, x, y, color, bordercolor); 282 283 return mrb_nil_value(); 284} 285 286static mrb_value mrb_tri(mrb_state* mrb, mrb_value self) 287{ 288 mrb_float x1, y1, x2, y2, x3, y3; 289 mrb_int color; 290 mrb_get_args(mrb, "ffffffi", &x1, &y1, &x2, &y2, &x3, &y3, &color); 291 292 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 293 294 core->api.tri(tic, x1, y1, x2, y2, x3, y3, color); 295 296 return mrb_nil_value(); 297} 298 299static mrb_value mrb_trib(mrb_state* mrb, mrb_value self) 300{ 301 mrb_float x1, y1, x2, y2, x3, y3; 302 mrb_int color; 303 mrb_get_args(mrb, "ffffffi", &x1, &y1, &x2, &y2, &x3, &y3, &color); 304 305 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 306 307 core->api.trib(tic, x1, y1, x2, y2, x3, y3, color); 308 309 return mrb_nil_value(); 310} 311 312static mrb_value mrb_ttri(mrb_state* mrb, mrb_value self) 313{ 314 mrb_value chroma = mrb_fixnum_value(0xff); 315 mrb_int src = tic_tiles_texture; 316 317 mrb_float x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, z1, z2, z3; 318 mrb_int argc = mrb_get_args(mrb, "ffffffffffff|iofff", 319 &x1, &y1, &x2, &y2, &x3, &y3, 320 &u1, &v1, &u2, &v2, &u3, &v3, 321 &src, &chroma, &z1, &z2, &z3); 322 323 mrb_int count; 324 u8 *chromas; 325 if (mrb_array_p(chroma)) 326 { 327 count = ARY_LEN(RARRAY(chroma)); 328 chromas = malloc(count * sizeof(u8)); 329 330 for (mrb_int i = 0; i < count; ++i) 331 { 332 chromas[i] = mrb_integer(mrb_ary_entry(chroma, i)); 333 } 334 } 335 else 336 { 337 count = 1; 338 chromas = malloc(sizeof(u8)); 339 chromas[0] = mrb_integer(chroma); 340 } 341 342 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 343 344 core->api.ttri(tic, 345 x1, y1, x2, y2, x3, y3, 346 u1, v1, u2, v2, u3, v3, 347 src, chromas, count, z1, z2, z3, argc == 17); 348 349 free(chromas); 350 351 return mrb_nil_value(); 352} 353 354 355static mrb_value mrb_clip(mrb_state* mrb, mrb_value self) 356{ 357 mrb_int x, y, w, h; 358 mrb_int argc = mrb_get_args(mrb, "|iiii", &x, &y, &w, &h); 359 360 if(argc == 0) 361 { 362 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 363 364 core->api.clip(tic, 0, 0, TIC80_WIDTH, TIC80_HEIGHT); 365 } 366 else if(argc == 4) 367 { 368 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 369 370 core->api.clip((tic_mem*)getMRubyMachine(mrb), x, y, w, h); 371 } 372 else mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid parameters, use clip(x,y,w,h) or clip()"); 373 374 return mrb_nil_value(); 375} 376 377static mrb_value mrb_btnp(mrb_state* mrb, mrb_value self) 378{ 379 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 380 381 382 mrb_int index, hold, period; 383 mrb_int argc = mrb_get_args(mrb, "|iii", &index, &hold, &period); 384 385 if (argc == 0) 386 { 387 return mrb_fixnum_value(core->api.btnp(tic, -1, -1, -1)); 388 } 389 else if(argc == 1) 390 { 391 return mrb_bool_value(core->api.btnp(tic, index & 0x1f, -1, -1) != 0); 392 } 393 else if (argc == 3) 394 { 395 return mrb_bool_value(core->api.btnp(tic, index & 0x1f, hold, period) != 0); 396 } 397 else 398 { 399 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, btnp [ id [ hold period ] ]"); 400 return mrb_nil_value(); 401 } 402} 403 404static mrb_value mrb_btn(mrb_state* mrb, mrb_value self) 405{ 406 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 407 408 409 mrb_int index, hold, period; 410 mrb_int argc = mrb_get_args(mrb, "|i", &index, &hold, &period); 411 412 if (argc == 0) 413 { 414 return mrb_bool_value(core->api.btn(tic, -1) != 0); 415 } 416 else if (argc == 1) 417 { 418 return mrb_bool_value(core->api.btn(tic, index & 0x1f) != 0); 419 } 420 else 421 { 422 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, btn [ id ]\n"); 423 return mrb_nil_value(); 424 } 425} 426 427static mrb_value mrb_spr(mrb_state* mrb, mrb_value self) 428{ 429 mrb_int index, x, y; 430 mrb_int w = 1, h = 1, scale = 1; 431 mrb_int flip = tic_no_flip, rotate = tic_no_rotate; 432 mrb_value colors_obj; 433 static u8 colors[TIC_PALETTE_SIZE]; 434 mrb_int count = 0; 435 436 mrb_int argc = mrb_get_args(mrb, "iii|oiiiii", &index, &x, &y, &colors_obj, &scale, &flip, &rotate, &w, &h); 437 438 if(mrb_array_p(colors_obj)) 439 { 440 for(; count < TIC_PALETTE_SIZE && count < ARY_LEN(RARRAY(colors_obj)); count++) // HACK 441 colors[count] = (u8) mrb_int(mrb, mrb_ary_entry(colors_obj, count)); 442 count++; 443 } 444 else if(mrb_fixnum_p(colors_obj)) 445 { 446 colors[0] = mrb_int(mrb, colors_obj); 447 count = 1; 448 } 449 else 450 { 451 mrb_raise(mrb, E_ARGUMENT_ERROR, "color must be either an array or a palette index"); 452 return mrb_nil_value(); 453 } 454 455 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 456 457 core->api.spr(tic, index, x, y, w, h, colors, count, scale, flip, rotate); 458 459 return mrb_nil_value(); 460} 461 462static mrb_value mrb_mget(mrb_state* mrb, mrb_value self) 463{ 464 mrb_int x, y; 465 mrb_get_args(mrb, "ii", &x, &y); 466 467 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 468 469 u8 value = core->api.mget(tic, x, y); 470 return mrb_fixnum_value(value); 471} 472 473static mrb_value mrb_mset(mrb_state* mrb, mrb_value self) 474{ 475 mrb_int x, y, val; 476 mrb_get_args(mrb, "iii", &x, &y, &val); 477 478 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 479 480 core->api.mset(tic, x, y, val); 481 482 return mrb_nil_value(); 483} 484 485static mrb_value mrb_tstamp(mrb_state* mrb, mrb_value self) 486{ 487 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 488 489 return mrb_fixnum_value(core->api.tstamp(tic)); 490} 491 492static mrb_value mrb_vbank(mrb_state* mrb, mrb_value self) 493{ 494 tic_core *core = getMRubyMachine(mrb); 495 tic_mem *tic = (tic_mem*)core; 496 497 s32 prev = core->state.vbank.id; 498 499 mrb_int vbank; 500 mrb_int argc = mrb_get_args(mrb, "|i", &vbank); 501 502 if (argc >= 1) 503 { 504 core->api.vbank(tic, vbank); 505 } 506 507 return mrb_fixnum_value(prev); 508} 509 510static mrb_value mrb_fget(mrb_state* mrb, mrb_value self) 511{ 512 mrb_int index, flag; 513 mrb_get_args(mrb, "ii", &index, &flag); 514 515 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 516 517 return mrb_bool_value(core->api.fget(tic, index, flag)); 518} 519 520static mrb_value mrb_fset(mrb_state* mrb, mrb_value self) 521{ 522 mrb_int index, flag; 523 mrb_bool value; 524 mrb_get_args(mrb, "iib", &index, &flag, &value); 525 526 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 527 528 core->api.fset(tic, index, flag, value); 529 530 return mrb_nil_value(); 531} 532 533static mrb_value mrb_fft(mrb_state* mrb, mrb_value self) 534{ 535 mrb_int start_freq, end_freq = -1; 536 mrb_int argc = mrb_get_args(mrb, "i|i", &start_freq, &end_freq); 537 538 tic_core* core = getMRubyMachine(mrb); 539 tic_mem* tic = (tic_mem*)core; 540 541 if (argc == 0) 542 { 543 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, fft [ start_freq end_freq ]\n"); 544 return mrb_nil_value(); 545 } 546 else 547 { 548 return mrb_float_value(mrb, core->api.fft(tic, start_freq, end_freq)); 549 } 550} 551 552static mrb_value mrb_ffts(mrb_state* mrb, mrb_value self) 553{ 554 mrb_int start_freq, end_freq = -1; 555 mrb_int argc = mrb_get_args(mrb, "i|i", &start_freq, &end_freq); 556 557 tic_core* core = getMRubyMachine(mrb); 558 tic_mem* tic = (tic_mem*)core; 559 560 if (argc == 0) 561 { 562 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, ffts [ start_freq end_freq ]\n"); 563 return mrb_nil_value(); 564 } 565 else 566 { 567 return mrb_float_value(mrb, core->api.ffts(tic, start_freq, end_freq)); 568 } 569} 570 571typedef struct 572{ 573 mrb_state* mrb; 574 mrb_value block; 575} RemapData; 576 577static void remapCallback(void* data, s32 x, s32 y, RemapResult* result) 578{ 579 RemapData* remap = (RemapData*)data; 580 mrb_state* mrb = remap->mrb; 581 582 mrb_value vals[] = { 583 mrb_fixnum_value(result->index), 584 mrb_fixnum_value(x), 585 mrb_fixnum_value(y) 586 }; 587 mrb_value out = mrb_yield_argv(mrb, remap->block, 3, vals); 588 589 if (mrb_array_p(out)) 590 { 591 mrb_int len = ARY_LEN(RARRAY(out)); 592 if (len > 3) len = 3; 593 switch (len) 594 { 595 case 3: 596 result->rotate = mrb_int(mrb, mrb_ary_entry(out, 2)); 597 case 2: 598 result->flip = mrb_int(mrb, mrb_ary_entry(out, 1)); 599 case 1: 600 result->index = mrb_int(mrb, mrb_ary_entry(out, 0)); 601 } 602 } 603 else 604 { 605 result->index = mrb_int(mrb, out); 606 } 607} 608 609static mrb_value mrb_map(mrb_state* mrb, mrb_value self) 610{ 611 mrb_int x = 0; 612 mrb_int y = 0; 613 mrb_int w = TIC_MAP_SCREEN_WIDTH; 614 mrb_int h = TIC_MAP_SCREEN_HEIGHT; 615 mrb_int sx = 0; 616 mrb_int sy = 0; 617 mrb_value chroma; 618 mrb_int scale = 1; 619 mrb_value block; 620 621 mrb_int argc = mrb_get_args(mrb, "|iiiiiioi&", &x, &y, &w, &h, &sx, &sy, &chroma, &scale, &block); 622 623 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 624 625 mrb_int count; 626 u8 *chromas; 627 628 if (mrb_array_p(chroma)) 629 { 630 count = ARY_LEN(RARRAY(chroma)); 631 chromas = malloc(count * sizeof(u8)); 632 633 for (mrb_int i = 0; i < count; ++i) 634 { 635 chromas[i] = mrb_integer(mrb_ary_entry(chroma, i)); 636 } 637 } 638 else 639 { 640 count = 1; 641 chromas = malloc(sizeof(u8)); 642 chromas[0] = mrb_integer(chroma); 643 } 644 645 if (mrb_proc_p(block)) 646 { 647 RemapData data = { mrb, block }; 648 core->api.map(tic, x, y, w, h, sx, sy, chromas, count, scale, remapCallback, &data); 649 } 650 else 651 { 652 core->api.map(tic, x, y, w, h, sx, sy, chromas, count, scale, NULL, NULL); 653 } 654 655 free(chromas); 656 657 return mrb_nil_value(); 658} 659 660static mrb_value mrb_music(mrb_state* mrb, mrb_value self) 661{ 662 mrb_int track = 0; 663 mrb_int frame = -1; 664 mrb_int row = -1; 665 bool loop = true; 666 bool sustain = false; 667 mrb_int tempo = -1; 668 mrb_int speed = -1; 669 670 mrb_int argc = mrb_get_args(mrb, "|iiibbii", &track, &frame, &row, &loop, &sustain, &tempo, &speed); 671 672 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 673 674 core->api.music(tic, -1, 0, 0, false, false, -1, -1); 675 676 if(track >= 0) 677 { 678 if(track > MUSIC_TRACKS - 1) 679 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid music track index"); 680 else 681 core->api.music(tic, track, frame, row, loop, sustain, tempo, speed); 682 } 683 684 return mrb_nil_value(); 685} 686 687static mrb_value mrb_sfx(mrb_state* mrb, mrb_value self) 688{ 689 mrb_int index; 690 691 mrb_value note_obj; 692 s32 note = -1; 693 mrb_int octave = -1; 694 mrb_int duration = -1; 695 mrb_int channel = 0; 696 mrb_value volume = mrb_int_value(mrb, MAX_VOLUME); 697 mrb_int volumes[TIC80_SAMPLE_CHANNELS]; 698 mrb_int speed = SFX_DEF_SPEED; 699 700 mrb_int argc = mrb_get_args(mrb, "i|oiio!i", &index, &note_obj, &duration, &channel, &volume, &speed); 701 702 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 703 704 if (mrb_array_p(volume)) 705 { 706 for (mrb_int i = 0; i < TIC80_SAMPLE_CHANNELS; ++i) 707 { 708 volumes[i] = mrb_integer(mrb_ary_entry(volume, i)); 709 } 710 } 711 else if (mrb_fixnum_p(volume)) 712 { 713 for (size_t ch = 0; ch < TIC80_SAMPLE_CHANNELS; ++ch) 714 { 715 volumes[ch] = mrb_integer(volume); 716 } 717 } 718 else 719 { 720 mrb_raise(mrb, E_ARGUMENT_ERROR, "volume must be an integer or a array of integers per channel"); 721 return mrb_nil_value(); 722 } 723 724 if(index < SFX_COUNT) 725 { 726 if (index >= 0) 727 { 728 tic_sample* effect = tic->ram->sfx.samples.data + index; 729 730 note = effect->note; 731 octave = effect->octave; 732 speed = effect->speed; 733 } 734 735 if (argc >= 2) 736 { 737 if (mrb_fixnum_p(note_obj)) 738 { 739 mrb_int id = mrb_integer(note_obj); 740 note = id % NOTES; 741 octave = id / NOTES; 742 } 743 else if (mrb_string_p(note_obj)) 744 { 745 const char* noteStr = mrb_str_to_cstr(mrb, mrb_funcall(mrb, note_obj, "to_s", 0)); 746 747 s32 octave32; 748 if (!parse_note(noteStr, &note, &octave32)) 749 { 750 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid note, should be like C#4"); 751 return mrb_nil_value(); 752 } 753 octave = octave32; 754 } 755 else 756 { 757 mrb_raise(mrb, E_ARGUMENT_ERROR, "note must be either an integer number or a string like \"C#4\""); 758 return mrb_nil_value(); 759 } 760 } 761 762 if (channel >= 0 && channel < TIC_SOUND_CHANNELS) 763 { 764 core->api.sfx(tic, index, note, octave, duration, channel, volumes[0] & 0xf, volumes[1] & 0xf, speed); 765 } 766 else mrb_raise(mrb, E_ARGUMENT_ERROR, "unknown channel"); 767 } 768 else mrb_raise(mrb, E_ARGUMENT_ERROR, "unknown sfx index"); 769 770 return mrb_nil_value(); 771} 772 773static mrb_value mrb_sync(mrb_state* mrb, mrb_value self) 774{ 775 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 776 777 mrb_int mask = 0; 778 mrb_int bank = 0; 779 mrb_bool toCart = false; 780 781 mrb_int argc = mrb_get_args(mrb, "|iib", &mask, &bank, &toCart); 782 783 if(bank >= 0 && bank < TIC_BANKS) 784 core->api.sync(tic, mask, bank, toCart); 785 else 786 mrb_raise(mrb, E_ARGUMENT_ERROR, "sync() error, invalid bank"); 787 788 return mrb_nil_value(); 789} 790 791static mrb_value mrb_reset(mrb_state* mrb, mrb_value self) 792{ 793 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 794 795 core->state.initialized = false; 796 797 return mrb_nil_value(); 798} 799 800static mrb_value mrb_key(mrb_state* mrb, mrb_value self) 801{ 802 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 803 804 mrb_int key; 805 mrb_int argc = mrb_get_args(mrb, "|i", &key); 806 807 if (argc == 0) 808 return mrb_bool_value(core->api.key(tic, tic_key_unknown)); 809 else 810 { 811 if(key < tic_key_escape) 812 return mrb_bool_value(core->api.key(tic, key)); 813 else 814 { 815 mrb_raise(mrb, E_ARGUMENT_ERROR, "unknown keyboard code"); 816 return mrb_nil_value(); 817 } 818 } 819} 820 821static mrb_value mrb_keyp(mrb_state* mrb, mrb_value self) 822{ 823 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 824 825 826 mrb_int key, hold, period; 827 mrb_int argc = mrb_get_args(mrb, "|iii", &key, &hold, &period); 828 829 if (argc == 0) 830 { 831 return mrb_fixnum_value(core->api.keyp(tic, -1, -1, -1)); 832 } 833 else if (key >= tic_key_escape) 834 { 835 mrb_raise(mrb, E_ARGUMENT_ERROR, "unknown keyboard code"); 836 } 837 else if(argc == 1) 838 { 839 return mrb_bool_value(core->api.keyp(tic, key, -1, -1)); 840 } 841 else if (argc == 3) 842 { 843 return mrb_bool_value(core->api.keyp(tic, key, hold, period)); 844 } 845 else 846 { 847 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid params, btnp [ id [ hold period ] ]"); 848 return mrb_nil_value(); 849 } 850} 851 852static mrb_value mrb_memcpy(mrb_state* mrb, mrb_value self) 853{ 854 mrb_int dest, src, size; 855 mrb_int argc = mrb_get_args(mrb, "iii", &dest, &src, &size); 856 857 s32 bound = sizeof(tic_ram) - size; 858 859 if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && src >= 0 && dest <= bound && src <= bound) 860 { 861 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 862 core->api.memcpy(tic, dest, src, size); 863 } 864 else 865 { 866 mrb_raise(mrb, E_ARGUMENT_ERROR, "tic address not in range!"); 867 } 868 869 return mrb_nil_value(); 870} 871 872static mrb_value mrb_memset(mrb_state* mrb, mrb_value self) 873{ 874 mrb_int dest, value, size; 875 mrb_int argc = mrb_get_args(mrb, "iii", &dest, &value, &size); 876 877 s32 bound = sizeof(tic_ram) - size; 878 879 if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && dest <= bound) 880 { 881 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 882 883 core->api.memset(tic, dest, value, size); 884 } 885 else 886 { 887 mrb_raise(mrb, E_ARGUMENT_ERROR, "tic address not in range!"); 888 } 889 890 return mrb_nil_value(); 891} 892 893static char* mrb_value_to_cstr(mrb_state* mrb, mrb_value value) 894{ 895 mrb_value str = mrb_funcall(mrb, value, "to_s", 0); 896 return mrb_str_to_cstr(mrb, str); 897} 898 899static mrb_value mrb_font(mrb_state* mrb, mrb_value self) 900{ 901 mrb_value text_obj; 902 mrb_int x = 0; 903 mrb_int y = 0; 904 mrb_int width = TIC_SPRITESIZE; 905 mrb_int height = TIC_SPRITESIZE; 906 mrb_int chromakey = 0; 907 mrb_bool fixed = false; 908 mrb_int scale = 1; 909 mrb_bool alt = false; 910 mrb_get_args(mrb, "S|iiiiibib", 911 &text_obj, &x, &y, &chromakey, 912 &width, &height, &fixed, &scale, &alt); 913 914 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 915 916 if(scale == 0) 917 return mrb_fixnum_value(0); 918 919 const char* text = mrb_value_to_cstr(mrb, text_obj); 920 921 s32 size = core->api.font(tic, text, x, y, (u8*)&chromakey, 1, width, height, fixed, scale, alt); 922 return mrb_fixnum_value(size); 923} 924 925static mrb_value mrb_print(mrb_state* mrb, mrb_value self) 926{ 927 mrb_value text_obj; 928 mrb_int x = 0; 929 mrb_int y = 0; 930 mrb_int color = TIC_PALETTE_SIZE-1; 931 mrb_bool fixed = false; 932 mrb_int scale = 1; 933 mrb_bool alt = false; 934 mrb_get_args(mrb, "S|iiibib", 935 &text_obj, &x, &y, &color, 936 &fixed, &scale, &alt); 937 938 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 939 940 if(scale == 0) 941 return mrb_fixnum_value(0); 942 943 const char* text = mrb_str_to_cstr(mrb, text_obj); 944 945 s32 size = core->api.print(tic, text, x, y, color, fixed, scale, alt); 946 return mrb_fixnum_value(size); 947} 948 949static mrb_value mrb_trace(mrb_state *mrb, mrb_value self) 950{ 951 mrb_value text_obj; 952 mrb_int color = TIC_DEFAULT_COLOR; 953 mrb_get_args(mrb, "o|i", &text_obj, &color); 954 955 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 956 957 const char* text = mrb_value_to_cstr(mrb, text_obj); 958 core->data->trace(core->data->data, text, color); 959 960 return mrb_nil_value(); 961} 962 963static mrb_value mrb_pmem(mrb_state *mrb, mrb_value self) 964{ 965 mrb_int index, value; 966 mrb_int argc = mrb_get_args(mrb, "i|i", &index, &value); 967 968 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 969 970 if(index < TIC_PERSISTENT_SIZE) 971 { 972 u32 val = core->api.pmem((tic_mem*)core, index, 0, false); 973 974 if(argc == 2) 975 { 976 u32 val = core->api.pmem((tic_mem*)core, index, value, true); 977 } 978 979 return mrb_fixnum_value(val); 980 } 981 else 982 { 983 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid persistent tic index"); 984 985 return mrb_nil_value(); 986 } 987} 988 989static mrb_value mrb_time(mrb_state *mrb, mrb_value self) 990{ 991 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 992 return mrb_float_value(mrb, core->api.time(tic)); 993} 994 995static mrb_value mrb_exit(mrb_state *mrb, mrb_value self) 996{ 997 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 998 core->data->exit(core->data->data); 999 1000 return mrb_nil_value(); 1001} 1002 1003static mrb_value mrb_mouse(mrb_state *mrb, mrb_value self) 1004{ 1005 mrb_value sym_x = mrb_symbol_value(mrb_intern_cstr(mrb, "x")); 1006 mrb_value sym_y = mrb_symbol_value(mrb_intern_cstr(mrb, "y")); 1007 mrb_value sym_left = mrb_symbol_value(mrb_intern_cstr(mrb, "left")); 1008 mrb_value sym_middle = mrb_symbol_value(mrb_intern_cstr(mrb, "middle")); 1009 mrb_value sym_right = mrb_symbol_value(mrb_intern_cstr(mrb, "right")); 1010 mrb_value sym_scrollx = mrb_symbol_value(mrb_intern_cstr(mrb, "scrollx")); 1011 mrb_value sym_scrolly = mrb_symbol_value(mrb_intern_cstr(mrb, "scrolly")); 1012 1013 tic_core* core = getMRubyMachine(mrb); tic_mem* tic = (tic_mem*)core; 1014 1015 tic_point pos = core->api.mouse(tic); 1016 const tic80_mouse* mouse = &tic->ram->input.mouse; 1017 1018 mrb_value hash = mrb_hash_new(mrb); 1019 1020 mrb_hash_set(mrb, hash, sym_x, mrb_fixnum_value(pos.x)); 1021 mrb_hash_set(mrb, hash, sym_y, mrb_fixnum_value(pos.y)); 1022 mrb_hash_set(mrb, hash, sym_left, mrb_bool_value(mouse->left)); 1023 mrb_hash_set(mrb, hash, sym_middle, mrb_bool_value(mouse->middle)); 1024 mrb_hash_set(mrb, hash, sym_right, mrb_bool_value(mouse->right)); 1025 mrb_hash_set(mrb, hash, sym_scrollx, mrb_fixnum_value(mouse->scrollx)); 1026 mrb_hash_set(mrb, hash, sym_scrolly, mrb_fixnum_value(mouse->scrolly)); 1027 1028 return hash; 1029} 1030 1031static void closeMRuby(tic_mem* tic) 1032{ 1033 tic_core* core = (tic_core*)tic; 1034 1035 if(core->currentVM) 1036 { 1037 mrbVm *currentVM = (mrbVm*)core->currentVM; 1038 mrbc_context_free(currentVM->mrb, currentVM->mrb_cxt); 1039 mrb_close(currentVM->mrb); 1040 currentVM->mrb_cxt = NULL; 1041 currentVM->mrb = NULL; 1042 1043 free(currentVM); 1044 CurrentMachine = core->currentVM = NULL; 1045 } 1046} 1047 1048static mrb_bool catcherr(tic_core* core) 1049{ 1050 mrb_state* mrb = ((mrbVm*)core->currentVM)->mrb; 1051 if (mrb->exc) 1052 { 1053 mrb_value ex = mrb_obj_value(mrb->exc); 1054 mrb_value bt = mrb_exc_backtrace(mrb, ex); 1055 if (!mrb_array_p(bt)) 1056 bt = mrb_get_backtrace(mrb); 1057 mrb_value insp = mrb_inspect(mrb, ex); 1058 mrb_ary_unshift(mrb, bt, insp); 1059 mrb_value msg = mrb_ary_join(mrb, bt, mrb_str_new_cstr(mrb, "\n")); 1060 char* cmsg = mrb_str_to_cstr(mrb, msg); 1061 core->data->error(core->data->data, cmsg); 1062 mrb->exc = NULL; 1063 1064 return false; 1065 } 1066 1067 return true; 1068} 1069 1070static bool initMRuby(tic_mem* tic, const char* code) 1071{ 1072 tic_core* core = (tic_core*)tic; 1073 1074 closeMRuby(tic); 1075 1076 CurrentMachine = core; 1077 1078 core->currentVM = malloc(sizeof(mrbVm)); 1079 mrbVm *currentVM = (mrbVm*)core->currentVM; 1080 1081 mrb_state* mrb = currentVM->mrb = mrb_open(); 1082 mrbc_context* mrb_cxt = currentVM->mrb_cxt = mrbc_context_new(mrb); 1083 mrb_cxt->capture_errors = 1; 1084 mrbc_filename(mrb, mrb_cxt, "user code"); 1085 1086#define API_FUNC_DEF(name, _, __, nparam, nrequired, callback, ...) {mrb_ ## name, nrequired, (nparam - nrequired), callback, #name}, 1087 static const struct{mrb_func_t func; s32 nrequired; s32 noptional; bool block; const char* name;} ApiItems[] = {TIC_API_LIST(API_FUNC_DEF)}; 1088#undef API_FUNC_DEF 1089 1090 for (s32 i = 0; i < COUNT_OF(ApiItems); ++i) 1091 { 1092 mrb_aspec args = MRB_ARGS_NONE(); 1093 if (ApiItems[i].nrequired > 0) 1094 args |= MRB_ARGS_REQ(ApiItems[i].nrequired); 1095 if (ApiItems[i].noptional > 0) 1096 args |= MRB_ARGS_OPT(ApiItems[i].noptional); 1097 if (ApiItems[i].block) 1098 args |= MRB_ARGS_BLOCK(); 1099 1100 mrb_define_method(mrb, mrb->kernel_module, ApiItems[i].name, ApiItems[i].func, args); 1101 } 1102 1103 mrb_load_string_cxt(mrb, code, mrb_cxt); 1104 return catcherr(core); 1105} 1106 1107static void evalMRuby(tic_mem* tic, const char* code) { 1108 tic_core* core = (tic_core*)tic; 1109 1110 mrbVm* vm=(mrbVm*)core->currentVM; 1111 if(!vm || !vm->mrb) 1112 return; 1113 1114 mrb_state* mrb = vm->mrb; 1115 1116 if (((mrbVm*)core->currentVM)->mrb_cxt) 1117 { 1118 mrbc_context_free(mrb, ((mrbVm*)core->currentVM)->mrb_cxt); 1119 } 1120 1121 mrbc_context* mrb_cxt = ((mrbVm*)core->currentVM)->mrb_cxt = mrbc_context_new(mrb); 1122 mrbc_filename(mrb, mrb_cxt, "eval"); 1123 mrb_load_string_cxt(mrb, code, mrb_cxt); 1124 catcherr(core); 1125} 1126 1127static void callMRubyTick(tic_mem* tic) 1128{ 1129 tic_core* core = (tic_core*)tic; 1130 const char* TicFunc = TIC_FN; 1131 1132 mrb_state* mrb = ((mrbVm*)core->currentVM)->mrb; 1133 1134 if(mrb) 1135 { 1136 if (mrb_respond_to(mrb, mrb_top_self(mrb), mrb_intern_cstr(mrb, TicFunc))) 1137 { 1138 mrb_funcall(mrb, mrb_top_self(mrb), TicFunc, 0); 1139 catcherr(core); 1140 } 1141 else 1142 { 1143 core->data->error(core->data->data, "'def TIC...' isn't found :("); 1144 } 1145 } 1146} 1147 1148static void callMRubyBoot(tic_mem* tic) 1149{ 1150 tic_core* core = (tic_core*)tic; 1151 const char* BootFunc = BOOT_FN; 1152 1153 mrb_state* mrb = ((mrbVm*)core->currentVM)->mrb; 1154 1155 if(mrb) 1156 { 1157 if (mrb_respond_to(mrb, mrb_top_self(mrb), mrb_intern_cstr(mrb, BootFunc))) 1158 { 1159 mrb_funcall(mrb, mrb_top_self(mrb), BootFunc, 0); 1160 catcherr(core); 1161 } 1162 } 1163} 1164 1165static void callMRubyIntCallback(tic_mem* tic, s32 value, void* data, const char* name) 1166{ 1167 tic_core* core = (tic_core*)tic; 1168 mrb_state* mrb = ((mrbVm*)core->currentVM)->mrb; 1169 1170 if (mrb && mrb_respond_to(mrb, mrb_top_self(mrb), mrb_intern_cstr(mrb, name))) 1171 { 1172 mrb_funcall(mrb, mrb_top_self(mrb), name, 1, mrb_fixnum_value(value)); 1173 catcherr(core); 1174 } 1175} 1176 1177static void callMRubyScanline(tic_mem* tic, s32 row, void* data) 1178{ 1179 callMRubyIntCallback(tic, row, data, SCN_FN); 1180 1181 callMRubyIntCallback(tic, row, data, "scanline"); 1182} 1183 1184static void callMRubyBorder(tic_mem* tic, s32 row, void* data) 1185{ 1186 callMRubyIntCallback(tic, row, data, BDR_FN); 1187} 1188 1189static void callMRubyMenu(tic_mem* tic, s32 index, void* data) 1190{ 1191 callMRubyIntCallback(tic, index, data, MENU_FN); 1192} 1193 1194/** 1195 * External Resources 1196 * ================== 1197 * 1198 * [Outdated official documentation regarding syntax](https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#resword) 1199 * [Ruby for dummies reserved word listing](https://www.dummies.com/programming/ruby/rubys-reserved-words/) 1200 */ 1201static const char* const MRubyKeywords [] = 1202{ 1203 "BEGIN", "class", "ensure", "nil", "self", "when", 1204 "END", "def", "false", "not", "super", "while", 1205 "alias", "defined", "for", "or", "then", "yield", 1206 "and", "do", "if", "redo", "true", 1207 "begin", "else", "in", "rescue", "undef", 1208 "break", "elsif", "module", "retry", "unless", 1209 "case", "end", "next", "return", "until", 1210 "__FILE__", "__LINE__" 1211}; 1212 1213static inline bool isalnum_(char c) {return isalnum(c) || c == '_' || c == '?' || c == '=' || c == '!';} 1214 1215static const tic_outline_item* getMRubyOutline(const char* code, s32* size) 1216{ 1217 enum{Size = sizeof(tic_outline_item)}; 1218 1219 *size = 0; 1220 1221 static tic_outline_item* items = NULL; 1222 1223 if(items) 1224 { 1225 free(items); 1226 items = NULL; 1227 } 1228 1229 const char* ptr = code; 1230 1231 while(true) 1232 { 1233 static const char FuncString[] = "def "; 1234 1235 ptr = strstr(ptr, FuncString); 1236 1237 if(ptr) 1238 { 1239 ptr += sizeof FuncString - 1; 1240 1241 const char* start = ptr; 1242 const char* end = start; 1243 1244 while(*ptr) 1245 { 1246 char c = *ptr; 1247 1248 if(isalnum_(c)); 1249 else if(c == '(' || c == ' ' || c == '\n') 1250 { 1251 end = ptr; 1252 break; 1253 } 1254 else break; 1255 1256 ptr++; 1257 } 1258 1259 if(end > start) 1260 { 1261 items = realloc(items, (*size + 1) * Size); 1262 1263 items[*size].pos = start; 1264 items[*size].size = (s32)(end - start); 1265 1266 (*size)++; 1267 } 1268 } 1269 else break; 1270 } 1271 1272 return items; 1273} 1274 1275static const u8 DemoRom[] = 1276{ 1277 #include "../build/assets/rubydemo.tic.dat" 1278}; 1279 1280static const u8 MarkRom[] = 1281{ 1282 #include "../build/assets/rubymark.tic.dat" 1283}; 1284 1285TIC_EXPORT const tic_script EXPORT_SCRIPT(Ruby) = 1286{ 1287 .id = 11, 1288 .name = "ruby", 1289 .fileExtension = ".rb", 1290 .projectComment = "#", 1291 .init = initMRuby, 1292 .close = closeMRuby, 1293 .tick = callMRubyTick, 1294 .boot = callMRubyBoot, 1295 1296 .callback = 1297 { 1298 .scanline = callMRubyScanline, 1299 .border = callMRubyBorder, 1300 .menu = callMRubyMenu, 1301 }, 1302 1303 .getOutline = getMRubyOutline, 1304 .eval = evalMRuby, 1305 1306 .blockCommentStart = "=begin", 1307 .blockCommentEnd = "=end", 1308 .blockCommentStart2 = NULL, 1309 .blockCommentEnd2 = NULL, 1310 .singleComment = "#", 1311 .blockStringStart = NULL, 1312 .blockStringEnd = NULL, 1313 .blockEnd = "end", 1314 1315 .keywords = MRubyKeywords, 1316 .keywordsCount = COUNT_OF(MRubyKeywords), 1317 1318 .demo = {DemoRom, sizeof DemoRom}, 1319 .mark = {MarkRom, sizeof MarkRom, "rubymark.tic"}, 1320};