A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 798 lines 14 kB view raw
1/* process.c - Interpreter loop and program control 2 * Copyright (c) 1995-1997 Stefan Jokisch 3 * 4 * This file is part of Frotz. 5 * 6 * Frotz is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * Frotz is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 19 */ 20 21#include "frotz.h" 22 23#ifdef DJGPP 24#include "djfrotz.h" 25#endif 26 27 28zword zargs[8]; 29int zargc; 30 31static int finished = 0; 32 33static void __extended__ (void); 34static void __illegal__ (void); 35 36void (*op0_opcodes[0x10]) (void) = { 37 z_rtrue, 38 z_rfalse, 39 z_print, 40 z_print_ret, 41 z_nop, 42 z_save, 43 z_restore, 44 z_restart, 45 z_ret_popped, 46 z_catch, 47 z_quit, 48 z_new_line, 49 z_show_status, 50 z_verify, 51 __extended__, 52 z_piracy 53}; 54 55void (*op1_opcodes[0x10]) (void) = { 56 z_jz, 57 z_get_sibling, 58 z_get_child, 59 z_get_parent, 60 z_get_prop_len, 61 z_inc, 62 z_dec, 63 z_print_addr, 64 z_call_s, 65 z_remove_obj, 66 z_print_obj, 67 z_ret, 68 z_jump, 69 z_print_paddr, 70 z_load, 71 z_call_n 72}; 73 74void (*var_opcodes[0x40]) (void) = { 75 __illegal__, 76 z_je, 77 z_jl, 78 z_jg, 79 z_dec_chk, 80 z_inc_chk, 81 z_jin, 82 z_test, 83 z_or, 84 z_and, 85 z_test_attr, 86 z_set_attr, 87 z_clear_attr, 88 z_store, 89 z_insert_obj, 90 z_loadw, 91 z_loadb, 92 z_get_prop, 93 z_get_prop_addr, 94 z_get_next_prop, 95 z_add, 96 z_sub, 97 z_mul, 98 z_div, 99 z_mod, 100 z_call_s, 101 z_call_n, 102 z_set_colour, 103 z_throw, 104 __illegal__, 105 __illegal__, 106 __illegal__, 107 z_call_s, 108 z_storew, 109 z_storeb, 110 z_put_prop, 111 z_read, 112 z_print_char, 113 z_print_num, 114 z_random, 115 z_push, 116 z_pull, 117 z_split_window, 118 z_set_window, 119 z_call_s, 120 z_erase_window, 121 z_erase_line, 122 z_set_cursor, 123 z_get_cursor, 124 z_set_text_style, 125 z_buffer_mode, 126 z_output_stream, 127 z_input_stream, 128 z_sound_effect, 129 z_read_char, 130 z_scan_table, 131 z_not, 132 z_call_n, 133 z_call_n, 134 z_tokenise, 135 z_encode_text, 136 z_copy_table, 137 z_print_table, 138 z_check_arg_count 139}; 140 141void (*ext_opcodes[0x1d]) (void) = { 142 z_save, 143 z_restore, 144 z_log_shift, 145 z_art_shift, 146 z_set_font, 147 z_draw_picture, 148 z_picture_data, 149 z_erase_picture, 150 z_set_margins, 151 z_save_undo, 152 z_restore_undo, 153 z_print_unicode, 154 z_check_unicode, 155 __illegal__, 156 __illegal__, 157 __illegal__, 158 z_move_window, 159 z_window_size, 160 z_window_style, 161 z_get_wind_prop, 162 z_scroll_window, 163 z_pop_stack, 164 z_read_mouse, 165 z_mouse_window, 166 z_push_stack, 167 z_put_wind_prop, 168 z_print_form, 169 z_make_menu, 170 z_picture_table 171}; 172 173 174/* 175 * init_process 176 * 177 * Initialize process variables. 178 * 179 */ 180 181void init_process (void) 182{ 183 finished = 0; 184} /* init_process */ 185 186 187/* 188 * load_operand 189 * 190 * Load an operand, either a variable or a constant. 191 * 192 */ 193 194static void load_operand (zbyte type) 195{ 196 zword value; 197 198 if (type & 2) { /* variable */ 199 200 zbyte variable; 201 202 CODE_BYTE (variable) 203 204 if (variable == 0) 205 value = *sp++; 206 else if (variable < 16) 207 value = *(fp - variable); 208 else { 209 zword addr = h_globals + 2 * (variable - 16); 210 LOW_WORD (addr, value) 211 } 212 213 } else if (type & 1) { /* small constant */ 214 215 zbyte bvalue; 216 217 CODE_BYTE (bvalue) 218 value = bvalue; 219 220 } else CODE_WORD (value) /* large constant */ 221 222 zargs[zargc++] = value; 223 224}/* load_operand */ 225 226/* 227 * load_all_operands 228 * 229 * Given the operand specifier byte, load all (up to four) operands 230 * for a VAR or EXT opcode. 231 * 232 */ 233 234static void load_all_operands (zbyte specifier) 235{ 236 int i; 237 238 for (i = 6; i >= 0; i -= 2) { 239 240 zbyte type = (specifier >> i) & 0x03; 241 242 if (type == 3) 243 break; 244 245 load_operand (type); 246 247 } 248 249}/* load_all_operands */ 250 251/* 252 * interpret 253 * 254 * Z-code interpreter main loop 255 * 256 */ 257 258void interpret (void) 259{ 260 261 do { 262 263 zbyte opcode; 264 265 CODE_BYTE (opcode) 266 267 zargc = 0; 268 269 if (opcode < 0x80) { /* 2OP opcodes */ 270 271 load_operand ((zbyte) (opcode & 0x40) ? 2 : 1); 272 load_operand ((zbyte) (opcode & 0x20) ? 2 : 1); 273 274 var_opcodes[opcode & 0x1f] (); 275 276 } else if (opcode < 0xb0) { /* 1OP opcodes */ 277 278 load_operand ((zbyte) (opcode >> 4)); 279 280 op1_opcodes[opcode & 0x0f] (); 281 282 } else if (opcode < 0xc0) { /* 0OP opcodes */ 283 284 op0_opcodes[opcode - 0xb0] (); 285 286 } else { /* VAR opcodes */ 287 288 zbyte specifier1; 289 zbyte specifier2; 290 291 if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */ 292 CODE_BYTE (specifier1) /* and 0xfa are */ 293 CODE_BYTE (specifier2) /* call opcodes */ 294 load_all_operands (specifier1); /* with up to 8 */ 295 load_all_operands (specifier2); /* arguments */ 296 } else { 297 CODE_BYTE (specifier1) 298 load_all_operands (specifier1); 299 } 300 301 var_opcodes[opcode - 0xc0] (); 302 303 } 304 305#if defined(DJGPP) && defined(SOUND_SUPPORT) 306 if (end_of_sound_flag) 307 end_of_sound (); 308#endif 309 310 } while (finished == 0); 311 312 finished--; 313 314}/* interpret */ 315 316/* 317 * call 318 * 319 * Call a subroutine. Save PC and FP then load new PC and initialise 320 * new stack frame. Note that the caller may legally provide less or 321 * more arguments than the function actually has. The call type "ct" 322 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call). 323 * 324 */ 325 326void call (zword routine, int argc, zword *args, int ct) 327{ 328 long pc; 329 zword value; 330 zbyte count; 331 int i; 332 333 if (sp - stack < 4) 334 runtime_error (ERR_STK_OVF); 335 336 GET_PC (pc) 337 338 *--sp = (zword) (pc >> 9); 339 *--sp = (zword) (pc & 0x1ff); 340 *--sp = (zword) (fp - stack - 1); 341 *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8))); 342 343 fp = sp; 344 frame_count++; 345 346 /* Calculate byte address of routine */ 347 348 if (h_version <= V3) 349 pc = (long) routine << 1; 350 else if (h_version <= V5) 351 pc = (long) routine << 2; 352 else if (h_version <= V7) 353 pc = ((long) routine << 2) + ((long) h_functions_offset << 3); 354 else /* h_version == V8 */ 355 pc = (long) routine << 3; 356 357 if (pc >= story_size) 358 runtime_error (ERR_ILL_CALL_ADDR); 359 360 SET_PC (pc) 361 362 /* Initialise local variables */ 363 364 CODE_BYTE (count) 365 366 if (count > 15) 367 runtime_error (ERR_CALL_NON_RTN); 368 if (sp - stack < count) 369 runtime_error (ERR_STK_OVF); 370 371 if (f_setup.save_quetzal) 372 fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ 373 374 value = 0; 375 376 for (i = 0; i < count; i++) { 377 378 if (h_version <= V4) /* V1 to V4 games provide default */ 379 CODE_WORD (value) /* values for all local variables */ 380 381 *--sp = (zword) ((argc-- > 0) ? args[i] : value); 382 383 } 384 385 /* Start main loop for direct calls */ 386 387 if (ct == 2) 388 interpret (); 389 390}/* call */ 391 392/* 393 * ret 394 * 395 * Return from the current subroutine and restore the previous stack 396 * frame. The result may be stored (0), thrown away (1) or pushed on 397 * the stack (2). In the latter case a direct call has been finished 398 * and we must exit the interpreter loop. 399 * 400 */ 401 402void ret (zword value) 403{ 404 long pc; 405 int ct; 406 407 if (sp > fp) 408 runtime_error (ERR_STK_UNDF); 409 410 sp = fp; 411 412 ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8); 413 frame_count--; 414 fp = stack + 1 + *sp++; 415 pc = *sp++; 416 pc = ((long) *sp++ << 9) | pc; 417 418 SET_PC (pc) 419 420 /* Handle resulting value */ 421 422 if (ct == 0) 423 store (value); 424 if (ct == 2) 425 *--sp = value; 426 427 /* Stop main loop for direct calls */ 428 429 if (ct == 2) 430 finished++; 431 432}/* ret */ 433 434/* 435 * branch 436 * 437 * Take a jump after an instruction based on the flag, either true or 438 * false. The branch can be short or long; it is encoded in one or two 439 * bytes respectively. When bit 7 of the first byte is set, the jump 440 * takes place if the flag is true; otherwise it is taken if the flag 441 * is false. When bit 6 of the first byte is set, the branch is short; 442 * otherwise it is long. The offset occupies the bottom 6 bits of the 443 * first byte plus all the bits in the second byte for long branches. 444 * Uniquely, an offset of 0 means return false, and an offset of 1 is 445 * return true. 446 * 447 */ 448 449void branch (bool flag) 450{ 451 long pc; 452 zword offset; 453 zbyte specifier; 454 zbyte off1; 455 zbyte off2; 456 457 CODE_BYTE (specifier) 458 459 off1 = specifier & 0x3f; 460 461 if (!flag) 462 specifier ^= 0x80; 463 464 if (!(specifier & 0x40)) { /* it's a long branch */ 465 466 if (off1 & 0x20) /* propagate sign bit */ 467 off1 |= 0xc0; 468 469 CODE_BYTE (off2) 470 471 offset = (off1 << 8) | off2; 472 473 } else offset = off1; /* it's a short branch */ 474 475 if (specifier & 0x80) { 476 477 if (offset > 1) { /* normal branch */ 478 479 GET_PC (pc) 480 pc += (short) offset - 2; 481 SET_PC (pc) 482 483 } else ret (offset); /* special case, return 0 or 1 */ 484 } 485 486}/* branch */ 487 488/* 489 * store 490 * 491 * Store an operand, either as a variable or pushed on the stack. 492 * 493 */ 494 495void store (zword value) 496{ 497 zbyte variable; 498 499 CODE_BYTE (variable) 500 501 if (variable == 0) 502 *--sp = value; 503 else if (variable < 16) 504 *(fp - variable) = value; 505 else { 506 zword addr = h_globals + 2 * (variable - 16); 507 SET_WORD (addr, value) 508 } 509 510}/* store */ 511 512/* 513 * direct_call 514 * 515 * Call the interpreter loop directly. This is necessary when 516 * 517 * - a sound effect has been finished 518 * - a read instruction has timed out 519 * - a newline countdown has hit zero 520 * 521 * The interpreter returns the result value on the stack. 522 * 523 */ 524 525int direct_call (zword addr) 526{ 527 zword saved_zargs[8]; 528 int saved_zargc; 529 int i; 530 531 /* Calls to address 0 return false */ 532 533 if (addr == 0) 534 return 0; 535 536 /* Save operands and operand count */ 537 538 for (i = 0; i < 8; i++) 539 saved_zargs[i] = zargs[i]; 540 541 saved_zargc = zargc; 542 543 /* Call routine directly */ 544 545 call (addr, 0, 0, 2); 546 547 /* Restore operands and operand count */ 548 549 for (i = 0; i < 8; i++) 550 zargs[i] = saved_zargs[i]; 551 552 zargc = saved_zargc; 553 554 /* Resulting value lies on top of the stack */ 555 556 return (short) *sp++; 557 558}/* direct_call */ 559 560/* 561 * __extended__ 562 * 563 * Load and execute an extended opcode. 564 * 565 */ 566 567static void __extended__ (void) 568{ 569 zbyte opcode; 570 zbyte specifier; 571 572 CODE_BYTE (opcode) 573 CODE_BYTE (specifier) 574 575 load_all_operands (specifier); 576 577 if (opcode < 0x1d) /* extended opcodes from 0x1d on */ 578 ext_opcodes[opcode] (); /* are reserved for future spec' */ 579 580}/* __extended__ */ 581 582/* 583 * __illegal__ 584 * 585 * Exit game because an unknown opcode has been hit. 586 * 587 */ 588 589static void __illegal__ (void) 590{ 591 592 runtime_error (ERR_ILL_OPCODE); 593 594}/* __illegal__ */ 595 596/* 597 * z_catch, store the current stack frame for later use with z_throw. 598 * 599 * no zargs used 600 * 601 */ 602 603void z_catch (void) 604{ 605 606 store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack)); 607 608}/* z_catch */ 609 610/* 611 * z_throw, go back to the given stack frame and return the given value. 612 * 613 * zargs[0] = value to return 614 * zargs[1] = stack frame 615 * 616 */ 617 618void z_throw (void) 619{ 620 621 if (f_setup.save_quetzal) { 622 if (zargs[1] > frame_count) 623 runtime_error (ERR_BAD_FRAME); 624 625 /* Unwind the stack a frame at a time. */ 626 for (; frame_count > zargs[1]; --frame_count) 627 fp = stack + 1 + fp[1]; 628 } else { 629 if (zargs[1] > STACK_SIZE) 630 runtime_error (ERR_BAD_FRAME); 631 632 fp = stack + zargs[1]; 633 } 634 635 ret (zargs[0]); 636 637}/* z_throw */ 638 639/* 640 * z_call_n, call a subroutine and discard its result. 641 * 642 * zargs[0] = packed address of subroutine 643 * zargs[1] = first argument (optional) 644 * ... 645 * zargs[7] = seventh argument (optional) 646 * 647 */ 648 649void z_call_n (void) 650{ 651 652 if (zargs[0] != 0) 653 call (zargs[0], zargc - 1, zargs + 1, 1); 654 655}/* z_call_n */ 656 657/* 658 * z_call_s, call a subroutine and store its result. 659 * 660 * zargs[0] = packed address of subroutine 661 * zargs[1] = first argument (optional) 662 * ... 663 * zargs[7] = seventh argument (optional) 664 * 665 */ 666 667void z_call_s (void) 668{ 669 670 if (zargs[0] != 0) 671 call (zargs[0], zargc - 1, zargs + 1, 0); 672 else 673 store (0); 674 675}/* z_call_s */ 676 677/* 678 * z_check_arg_count, branch if subroutine was called with >= n arg's. 679 * 680 * zargs[0] = number of arguments 681 * 682 */ 683 684void z_check_arg_count (void) 685{ 686 687 if (fp == stack + STACK_SIZE) 688 branch (zargs[0] == 0); 689 else 690 branch (zargs[0] <= (*fp & 0xff)); 691 692}/* z_check_arg_count */ 693 694/* 695 * z_jump, jump unconditionally to the given address. 696 * 697 * zargs[0] = PC relative address 698 * 699 */ 700 701void z_jump (void) 702{ 703 long pc; 704 705 GET_PC (pc) 706 707 pc += (short) zargs[0] - 2; 708 709 if (pc >= story_size) 710 runtime_error (ERR_ILL_JUMP_ADDR); 711 712 SET_PC (pc) 713 714}/* z_jump */ 715 716/* 717 * z_nop, no operation. 718 * 719 * no zargs used 720 * 721 */ 722 723void z_nop (void) 724{ 725 726 /* Do nothing */ 727 728}/* z_nop */ 729 730/* 731 * z_quit, stop game and exit interpreter. 732 * 733 * no zargs used 734 * 735 */ 736 737void z_quit (void) 738{ 739 740 finished = 9999; 741 742}/* z_quit */ 743 744/* 745 * z_ret, return from a subroutine with the given value. 746 * 747 * zargs[0] = value to return 748 * 749 */ 750 751void z_ret (void) 752{ 753 754 ret (zargs[0]); 755 756}/* z_ret */ 757 758/* 759 * z_ret_popped, return from a subroutine with a value popped off the stack. 760 * 761 * no zargs used 762 * 763 */ 764 765void z_ret_popped (void) 766{ 767 768 ret (*sp++); 769 770}/* z_ret_popped */ 771 772/* 773 * z_rfalse, return from a subroutine with false (0). 774 * 775 * no zargs used 776 * 777 */ 778 779void z_rfalse (void) 780{ 781 782 ret (0); 783 784}/* z_rfalse */ 785 786/* 787 * z_rtrue, return from a subroutine with true (1). 788 * 789 * no zargs used 790 * 791 */ 792 793void z_rtrue (void) 794{ 795 796 ret (1); 797 798}/* z_rtrue */