I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

Glulx.i6t

Glulx contents

Summary.

This segment closely parallels ZMachine.i6t, which provides exactly equivalent functionality (indeed, usually the same-named functions and in the same order) for the Z-machine VM. This is intended to make the rest of the template code independent of the choice of VM, although that is more of an ideal than a reality, because there are so many fiddly differences in some of the grammar and dictionary tables that it is not really practical for the parser (for instance) to call VM-neutral routines to get the data it wants out of these arrays.

Variables and Arrays.

21Array gg_event --> 4; 22Array gg_arguments buffer 28; 23Global gg_mainwin = 0; 24Global gg_statuswin = 0; 25Global gg_quotewin = 0; 26Global gg_scriptfref = 0; 27Global gg_scriptstr = 0; 28Global gg_savestr = 0; 29Global gg_commandstr = 0; 30Global gg_command_reading = 0; ! true if gg_commandstr is being replayed 31Global gg_foregroundchan = 0; 32Global gg_backgroundchan = 0; 33 34Constant GLK_NULL 0; 35 36Constant INPUT_BUFFER_LEN = 260; ! No extra byte necessary 37Constant MAX_BUFFER_WORDS = 20; 38Constant PARSE_BUFFER_LEN = 61; 39 40Array buffer buffer INPUT_BUFFER_LEN; 41Array buffer2 buffer INPUT_BUFFER_LEN; 42Array buffer3 buffer INPUT_BUFFER_LEN; 43Array parse --> PARSE_BUFFER_LEN; 44Array parse2 --> PARSE_BUFFER_LEN;

Infglk.

This section is a verbatim copy of an invaluable I6 header file originally put together by John Cater but now maintained by Andrew Plotkin. The routines are convenient to have on hand, and also provide a canonical set of I6 names for the many gestalt and other codes.

53! infglk.h -- auto-generated by parse_dispatch.py. 54! Generated for Glk API version 0.7.4 55 56Constant evtype_Arrange = 5; 57Constant evtype_CharInput = 2; 58Constant evtype_Hyperlink = 8; 59Constant evtype_LineInput = 3; 60Constant evtype_MouseInput = 4; 61Constant evtype_None = 0; 62Constant evtype_Redraw = 6; 63Constant evtype_SoundNotify = 7; 64Constant evtype_Timer = 1; 65Constant evtype_VolumeNotify = 9; 66Constant filemode_Read = 2; 67Constant filemode_ReadWrite = 3; 68Constant filemode_Write = 1; 69Constant filemode_WriteAppend = 5; 70Constant fileusage_BinaryMode = 0; 71Constant fileusage_Data = 0; 72Constant fileusage_InputRecord = 3; 73Constant fileusage_SavedGame = 1; 74Constant fileusage_TextMode = 256; 75Constant fileusage_Transcript = 2; 76Constant fileusage_TypeMask = 15; 77Constant gestalt_CharInput = 1; 78Constant gestalt_CharOutput = 3; 79Constant gestalt_CharOutput_ApproxPrint = 1; 80Constant gestalt_CharOutput_CannotPrint = 0; 81Constant gestalt_CharOutput_ExactPrint = 2; 82Constant gestalt_DateTime = 20; 83Constant gestalt_DrawImage = 7; 84Constant gestalt_Graphics = 6; 85Constant gestalt_GraphicsCharInput = 23; 86Constant gestalt_GraphicsTransparency = 14; 87Constant gestalt_HyperlinkInput = 12; 88Constant gestalt_Hyperlinks = 11; 89Constant gestalt_LineInput = 2; 90Constant gestalt_LineInputEcho = 17; 91Constant gestalt_LineTerminatorKey = 19; 92Constant gestalt_LineTerminators = 18; 93Constant gestalt_MouseInput = 4; 94Constant gestalt_ResourceStream = 22; 95Constant gestalt_Sound = 8; 96Constant gestalt_Sound2 = 21; 97Constant gestalt_SoundMusic = 13; 98Constant gestalt_SoundNotify = 10; 99Constant gestalt_SoundVolume = 9; 100Constant gestalt_Timer = 5; 101Constant gestalt_Unicode = 15; 102Constant gestalt_UnicodeNorm = 16; 103Constant gestalt_Version = 0; 104Constant imagealign_InlineCenter = 3; 105Constant imagealign_InlineDown = 2; 106Constant imagealign_MarginLeft = 4; 107Constant imagealign_MarginRight = 5; 108Constant imagealign_InlineUp = 1; 109Constant keycode_Delete = 4294967289; 110Constant keycode_Down = 4294967291; 111Constant keycode_End = 4294967283; 112Constant keycode_Escape = 4294967288; 113Constant keycode_Func1 = 4294967279; 114Constant keycode_Func10 = 4294967270; 115Constant keycode_Func11 = 4294967269; 116Constant keycode_Func12 = 4294967268; 117Constant keycode_Func2 = 4294967278; 118Constant keycode_Func3 = 4294967277; 119Constant keycode_Func4 = 4294967276; 120Constant keycode_Func5 = 4294967275; 121Constant keycode_Func6 = 4294967274; 122Constant keycode_Func7 = 4294967273; 123Constant keycode_Func8 = 4294967272; 124Constant keycode_Func9 = 4294967271; 125Constant keycode_Home = 4294967284; 126Constant keycode_Left = 4294967294; 127Constant keycode_MAXVAL = 28; 128Constant keycode_PageDown = 4294967285; 129Constant keycode_PageUp = 4294967286; 130Constant keycode_Return = 4294967290; 131Constant keycode_Right = 4294967293; 132Constant keycode_Tab = 4294967287; 133Constant keycode_Unknown = 4294967295; 134Constant keycode_Up = 4294967292; 135Constant seekmode_Current = 1; 136Constant seekmode_End = 2; 137Constant seekmode_Start = 0; 138Constant style_Alert = 5; 139Constant style_BlockQuote = 7; 140Constant style_Emphasized = 1; 141Constant style_Header = 3; 142Constant style_Input = 8; 143Constant style_NUMSTYLES = 11; 144Constant style_Normal = 0; 145Constant style_Note = 6; 146Constant style_Preformatted = 2; 147Constant style_Subheader = 4; 148Constant style_User1 = 9; 149Constant style_User2 = 10; 150Constant stylehint_BackColor = 8; 151Constant stylehint_Indentation = 0; 152Constant stylehint_Justification = 2; 153Constant stylehint_NUMHINTS = 10; 154Constant stylehint_Oblique = 5; 155Constant stylehint_ParaIndentation = 1; 156Constant stylehint_Proportional = 6; 157Constant stylehint_ReverseColor = 9; 158Constant stylehint_Size = 3; 159Constant stylehint_TextColor = 7; 160Constant stylehint_Weight = 4; 161Constant stylehint_just_Centered = 2; 162Constant stylehint_just_LeftFlush = 0; 163Constant stylehint_just_LeftRight = 1; 164Constant stylehint_just_RightFlush = 3; 165Constant winmethod_Above = 2; 166Constant winmethod_Below = 3; 167Constant winmethod_Border = 0; 168Constant winmethod_BorderMask = 256; 169Constant winmethod_DirMask = 15; 170Constant winmethod_DivisionMask = 240; 171Constant winmethod_Fixed = 16; 172Constant winmethod_Left = 0; 173Constant winmethod_NoBorder = 256; 174Constant winmethod_Proportional = 32; 175Constant winmethod_Right = 1; 176Constant wintype_AllTypes = 0; 177Constant wintype_Blank = 2; 178Constant wintype_Graphics = 5; 179Constant wintype_Pair = 1; 180Constant wintype_TextBuffer = 3; 181Constant wintype_TextGrid = 4; 182 183[ glk_exit _vararg_count; 184  ! glk_exit() 185  @glk 1 _vararg_count 0; 186  return 0; 187]; 188 189[ glk_tick _vararg_count; 190  ! glk_tick() 191  @glk 3 _vararg_count 0; 192  return 0; 193]; 194 195[ glk_gestalt _vararg_count ret; 196  ! glk_gestalt(uint, uint) => uint 197  @glk 4 _vararg_count ret; 198  return ret; 199]; 200 201[ glk_gestalt_ext _vararg_count ret; 202  ! glk_gestalt_ext(uint, uint, uintarray, arraylen) => uint 203  @glk 5 _vararg_count ret; 204  return ret; 205]; 206 207[ glk_window_iterate _vararg_count ret; 208  ! glk_window_iterate(window, &uint) => window 209  @glk 32 _vararg_count ret; 210  return ret; 211]; 212 213[ glk_window_get_rock _vararg_count ret; 214  ! glk_window_get_rock(window) => uint 215  @glk 33 _vararg_count ret; 216  return ret; 217]; 218 219[ glk_window_get_root _vararg_count ret; 220  ! glk_window_get_root() => window 221  @glk 34 _vararg_count ret; 222  return ret; 223]; 224 225[ glk_window_open _vararg_count ret; 226  ! glk_window_open(window, uint, uint, uint, uint) => window 227  @glk 35 _vararg_count ret; 228  return ret; 229]; 230 231[ glk_window_close _vararg_count; 232  ! glk_window_close(window, &{uint, uint}) 233  @glk 36 _vararg_count 0; 234  return 0; 235]; 236 237[ glk_window_get_size _vararg_count; 238  ! glk_window_get_size(window, &uint, &uint) 239  @glk 37 _vararg_count 0; 240  return 0; 241]; 242 243[ glk_window_set_arrangement _vararg_count; 244  ! glk_window_set_arrangement(window, uint, uint, window) 245  @glk 38 _vararg_count 0; 246  return 0; 247]; 248 249[ glk_window_get_arrangement _vararg_count; 250  ! glk_window_get_arrangement(window, &uint, &uint, &window) 251  @glk 39 _vararg_count 0; 252  return 0; 253]; 254 255[ glk_window_get_type _vararg_count ret; 256  ! glk_window_get_type(window) => uint 257  @glk 40 _vararg_count ret; 258  return ret; 259]; 260 261[ glk_window_get_parent _vararg_count ret; 262  ! glk_window_get_parent(window) => window 263  @glk 41 _vararg_count ret; 264  return ret; 265]; 266 267[ glk_window_clear _vararg_count; 268  ! glk_window_clear(window) 269  @glk 42 _vararg_count 0; 270  return 0; 271]; 272 273[ glk_window_move_cursor _vararg_count; 274  ! glk_window_move_cursor(window, uint, uint) 275  @glk 43 _vararg_count 0; 276  return 0; 277]; 278 279[ glk_window_get_stream _vararg_count ret; 280  ! glk_window_get_stream(window) => stream 281  @glk 44 _vararg_count ret; 282  return ret; 283]; 284 285[ glk_window_set_echo_stream _vararg_count; 286  ! glk_window_set_echo_stream(window, stream) 287  @glk 45 _vararg_count 0; 288  return 0; 289]; 290 291[ glk_window_get_echo_stream _vararg_count ret; 292  ! glk_window_get_echo_stream(window) => stream 293  @glk 46 _vararg_count ret; 294  return ret; 295]; 296 297[ glk_set_window _vararg_count; 298  ! glk_set_window(window) 299  @glk 47 _vararg_count 0; 300  return 0; 301]; 302 303[ glk_window_get_sibling _vararg_count ret; 304  ! glk_window_get_sibling(window) => window 305  @glk 48 _vararg_count ret; 306  return ret; 307]; 308 309[ glk_stream_iterate _vararg_count ret; 310  ! glk_stream_iterate(stream, &uint) => stream 311  @glk 64 _vararg_count ret; 312  return ret; 313]; 314 315[ glk_stream_get_rock _vararg_count ret; 316  ! glk_stream_get_rock(stream) => uint 317  @glk 65 _vararg_count ret; 318  return ret; 319]; 320 321[ glk_stream_open_file _vararg_count ret; 322  ! glk_stream_open_file(fileref, uint, uint) => stream 323  @glk 66 _vararg_count ret; 324  return ret; 325]; 326 327[ glk_stream_open_memory _vararg_count ret; 328  ! glk_stream_open_memory(nativechararray, arraylen, uint, uint) => stream 329  @glk 67 _vararg_count ret; 330  return ret; 331]; 332 333[ glk_stream_close _vararg_count; 334  ! glk_stream_close(stream, &{uint, uint}) 335  @glk 68 _vararg_count 0; 336  return 0; 337]; 338 339[ glk_stream_set_position _vararg_count; 340  ! glk_stream_set_position(stream, int, uint) 341  @glk 69 _vararg_count 0; 342  return 0; 343]; 344 345[ glk_stream_get_position _vararg_count ret; 346  ! glk_stream_get_position(stream) => uint 347  @glk 70 _vararg_count ret; 348  return ret; 349]; 350 351[ glk_stream_set_current _vararg_count; 352  ! glk_stream_set_current(stream) 353  @glk 71 _vararg_count 0; 354  return 0; 355]; 356 357[ glk_stream_get_current _vararg_count ret; 358  ! glk_stream_get_current() => stream 359  @glk 72 _vararg_count ret; 360  return ret; 361]; 362 363[ glk_stream_open_resource _vararg_count ret; 364  ! glk_stream_open_resource(uint, uint) => stream 365  @glk 73 _vararg_count ret; 366  return ret; 367]; 368 369[ glk_fileref_create_temp _vararg_count ret; 370  ! glk_fileref_create_temp(uint, uint) => fileref 371  @glk 96 _vararg_count ret; 372  return ret; 373]; 374 375[ glk_fileref_create_by_name _vararg_count ret; 376  ! glk_fileref_create_by_name(uint, string, uint) => fileref 377  @glk 97 _vararg_count ret; 378  return ret; 379]; 380 381[ glk_fileref_create_by_prompt _vararg_count ret; 382  ! glk_fileref_create_by_prompt(uint, uint, uint) => fileref 383  @glk 98 _vararg_count ret; 384  return ret; 385]; 386 387[ glk_fileref_destroy _vararg_count; 388  ! glk_fileref_destroy(fileref) 389  @glk 99 _vararg_count 0; 390  return 0; 391]; 392 393[ glk_fileref_iterate _vararg_count ret; 394  ! glk_fileref_iterate(fileref, &uint) => fileref 395  @glk 100 _vararg_count ret; 396  return ret; 397]; 398 399[ glk_fileref_get_rock _vararg_count ret; 400  ! glk_fileref_get_rock(fileref) => uint 401  @glk 101 _vararg_count ret; 402  return ret; 403]; 404 405[ glk_fileref_delete_file _vararg_count; 406  ! glk_fileref_delete_file(fileref) 407  @glk 102 _vararg_count 0; 408  return 0; 409]; 410 411[ glk_fileref_does_file_exist _vararg_count ret; 412  ! glk_fileref_does_file_exist(fileref) => uint 413  @glk 103 _vararg_count ret; 414  return ret; 415]; 416 417[ glk_fileref_create_from_fileref _vararg_count ret; 418  ! glk_fileref_create_from_fileref(uint, fileref, uint) => fileref 419  @glk 104 _vararg_count ret; 420  return ret; 421]; 422 423[ glk_put_char _vararg_count; 424  ! glk_put_char(uchar) 425  @glk 128 _vararg_count 0; 426  return 0; 427]; 428 429[ glk_put_char_stream _vararg_count; 430  ! glk_put_char_stream(stream, uchar) 431  @glk 129 _vararg_count 0; 432  return 0; 433]; 434 435[ glk_put_string _vararg_count; 436  ! glk_put_string(string) 437  @glk 130 _vararg_count 0; 438  return 0; 439]; 440 441[ glk_put_string_stream _vararg_count; 442  ! glk_put_string_stream(stream, string) 443  @glk 131 _vararg_count 0; 444  return 0; 445]; 446 447[ glk_put_buffer _vararg_count; 448  ! glk_put_buffer(nativechararray, arraylen) 449  @glk 132 _vararg_count 0; 450  return 0; 451]; 452 453[ glk_put_buffer_stream _vararg_count; 454  ! glk_put_buffer_stream(stream, nativechararray, arraylen) 455  @glk 133 _vararg_count 0; 456  return 0; 457]; 458 459[ glk_set_style _vararg_count; 460  ! glk_set_style(uint) 461  @glk 134 _vararg_count 0; 462  return 0; 463]; 464 465[ glk_set_style_stream _vararg_count; 466  ! glk_set_style_stream(stream, uint) 467  @glk 135 _vararg_count 0; 468  return 0; 469]; 470 471[ glk_get_char_stream _vararg_count ret; 472  ! glk_get_char_stream(stream) => int 473  @glk 144 _vararg_count ret; 474  return ret; 475]; 476 477[ glk_get_line_stream _vararg_count ret; 478  ! glk_get_line_stream(stream, nativechararray, arraylen) => uint 479  @glk 145 _vararg_count ret; 480  return ret; 481]; 482 483[ glk_get_buffer_stream _vararg_count ret; 484  ! glk_get_buffer_stream(stream, nativechararray, arraylen) => uint 485  @glk 146 _vararg_count ret; 486  return ret; 487]; 488 489[ glk_char_to_lower _vararg_count ret; 490  ! glk_char_to_lower(uchar) => uchar 491  @glk 160 _vararg_count ret; 492  return ret; 493]; 494 495[ glk_char_to_upper _vararg_count ret; 496  ! glk_char_to_upper(uchar) => uchar 497  @glk 161 _vararg_count ret; 498  return ret; 499]; 500 501[ glk_stylehint_set _vararg_count; 502  ! glk_stylehint_set(uint, uint, uint, int) 503  @glk 176 _vararg_count 0; 504  return 0; 505]; 506 507[ glk_stylehint_clear _vararg_count; 508  ! glk_stylehint_clear(uint, uint, uint) 509  @glk 177 _vararg_count 0; 510  return 0; 511]; 512 513[ glk_style_distinguish _vararg_count ret; 514  ! glk_style_distinguish(window, uint, uint) => uint 515  @glk 178 _vararg_count ret; 516  return ret; 517]; 518 519[ glk_style_measure _vararg_count ret; 520  ! glk_style_measure(window, uint, uint, &uint) => uint 521  @glk 179 _vararg_count ret; 522  return ret; 523]; 524 525[ glk_select _vararg_count; 526  ! glk_select(&{uint, window, uint, uint}) 527  @glk 192 _vararg_count 0; 528  return 0; 529]; 530 531[ glk_select_poll _vararg_count; 532  ! glk_select_poll(&{uint, window, uint, uint}) 533  @glk 193 _vararg_count 0; 534  return 0; 535]; 536 537[ glk_request_line_event _vararg_count; 538  ! glk_request_line_event(window, nativechararray, arraylen, uint) 539  @glk 208 _vararg_count 0; 540  return 0; 541]; 542 543[ glk_cancel_line_event _vararg_count; 544  ! glk_cancel_line_event(window, &{uint, window, uint, uint}) 545  @glk 209 _vararg_count 0; 546  return 0; 547]; 548 549[ glk_request_char_event _vararg_count; 550  ! glk_request_char_event(window) 551  @glk 210 _vararg_count 0; 552  return 0; 553]; 554 555[ glk_cancel_char_event _vararg_count; 556  ! glk_cancel_char_event(window) 557  @glk 211 _vararg_count 0; 558  return 0; 559]; 560 561[ glk_request_mouse_event _vararg_count; 562  ! glk_request_mouse_event(window) 563  @glk 212 _vararg_count 0; 564  return 0; 565]; 566 567[ glk_cancel_mouse_event _vararg_count; 568  ! glk_cancel_mouse_event(window) 569  @glk 213 _vararg_count 0; 570  return 0; 571]; 572 573[ glk_request_timer_events _vararg_count; 574  ! glk_request_timer_events(uint) 575  @glk 214 _vararg_count 0; 576  return 0; 577]; 578 579[ glk_image_get_info _vararg_count ret; 580  ! glk_image_get_info(uint, &uint, &uint) => uint 581  @glk 224 _vararg_count ret; 582  return ret; 583]; 584 585[ glk_image_draw _vararg_count ret; 586  ! glk_image_draw(window, uint, int, int) => uint 587  @glk 225 _vararg_count ret; 588  return ret; 589]; 590 591[ glk_image_draw_scaled _vararg_count ret; 592  ! glk_image_draw_scaled(window, uint, int, int, uint, uint) => uint 593  @glk 226 _vararg_count ret; 594  return ret; 595]; 596 597[ glk_window_flow_break _vararg_count; 598  ! glk_window_flow_break(window) 599  @glk 232 _vararg_count 0; 600  return 0; 601]; 602 603[ glk_window_erase_rect _vararg_count; 604  ! glk_window_erase_rect(window, int, int, uint, uint) 605  @glk 233 _vararg_count 0; 606  return 0; 607]; 608 609[ glk_window_fill_rect _vararg_count; 610  ! glk_window_fill_rect(window, uint, int, int, uint, uint) 611  @glk 234 _vararg_count 0; 612  return 0; 613]; 614 615[ glk_window_set_background_color _vararg_count; 616  ! glk_window_set_background_color(window, uint) 617  @glk 235 _vararg_count 0; 618  return 0; 619]; 620 621[ glk_schannel_iterate _vararg_count ret; 622  ! glk_schannel_iterate(schannel, &uint) => schannel 623  @glk 240 _vararg_count ret; 624  return ret; 625]; 626 627[ glk_schannel_get_rock _vararg_count ret; 628  ! glk_schannel_get_rock(schannel) => uint 629  @glk 241 _vararg_count ret; 630  return ret; 631]; 632 633[ glk_schannel_create _vararg_count ret; 634  ! glk_schannel_create(uint) => schannel 635  @glk 242 _vararg_count ret; 636  return ret; 637]; 638 639[ glk_schannel_destroy _vararg_count; 640  ! glk_schannel_destroy(schannel) 641  @glk 243 _vararg_count 0; 642  return 0; 643]; 644 645[ glk_schannel_create_ext _vararg_count ret; 646  ! glk_schannel_create_ext(uint, uint) => schannel 647  @glk 244 _vararg_count ret; 648  return ret; 649]; 650 651[ glk_schannel_play_multi _vararg_count ret; 652  ! glk_schannel_play_multi(schannelarray, arraylen, uintarray, arraylen, uint) => uint 653  @glk 247 _vararg_count ret; 654  return ret; 655]; 656 657[ glk_schannel_play _vararg_count ret; 658  ! glk_schannel_play(schannel, uint) => uint 659  @glk 248 _vararg_count ret; 660  return ret; 661]; 662 663[ glk_schannel_play_ext _vararg_count ret; 664  ! glk_schannel_play_ext(schannel, uint, uint, uint) => uint 665  @glk 249 _vararg_count ret; 666  return ret; 667]; 668 669[ glk_schannel_stop _vararg_count; 670  ! glk_schannel_stop(schannel) 671  @glk 250 _vararg_count 0; 672  return 0; 673]; 674 675[ glk_schannel_set_volume _vararg_count; 676  ! glk_schannel_set_volume(schannel, uint) 677  @glk 251 _vararg_count 0; 678  return 0; 679]; 680 681[ glk_sound_load_hint _vararg_count; 682  ! glk_sound_load_hint(uint, uint) 683  @glk 252 _vararg_count 0; 684  return 0; 685]; 686 687[ glk_schannel_set_volume_ext _vararg_count; 688  ! glk_schannel_set_volume_ext(schannel, uint, uint, uint) 689  @glk 253 _vararg_count 0; 690  return 0; 691]; 692 693[ glk_schannel_pause _vararg_count; 694  ! glk_schannel_pause(schannel) 695  @glk 254 _vararg_count 0; 696  return 0; 697]; 698 699[ glk_schannel_unpause _vararg_count; 700  ! glk_schannel_unpause(schannel) 701  @glk 255 _vararg_count 0; 702  return 0; 703]; 704 705[ glk_set_hyperlink _vararg_count; 706  ! glk_set_hyperlink(uint) 707  @glk 256 _vararg_count 0; 708  return 0; 709]; 710 711[ glk_set_hyperlink_stream _vararg_count; 712  ! glk_set_hyperlink_stream(stream, uint) 713  @glk 257 _vararg_count 0; 714  return 0; 715]; 716 717[ glk_request_hyperlink_event _vararg_count; 718  ! glk_request_hyperlink_event(window) 719  @glk 258 _vararg_count 0; 720  return 0; 721]; 722 723[ glk_cancel_hyperlink_event _vararg_count; 724  ! glk_cancel_hyperlink_event(window) 725  @glk 259 _vararg_count 0; 726  return 0; 727]; 728 729[ glk_buffer_to_lower_case_uni _vararg_count ret; 730  ! glk_buffer_to_lower_case_uni(uintarray, arraylen, uint) => uint 731  @glk 288 _vararg_count ret; 732  return ret; 733]; 734 735[ glk_buffer_to_upper_case_uni _vararg_count ret; 736  ! glk_buffer_to_upper_case_uni(uintarray, arraylen, uint) => uint 737  @glk 289 _vararg_count ret; 738  return ret; 739]; 740 741[ glk_buffer_to_title_case_uni _vararg_count ret; 742  ! glk_buffer_to_title_case_uni(uintarray, arraylen, uint, uint) => uint 743  @glk 290 _vararg_count ret; 744  return ret; 745]; 746 747[ glk_buffer_canon_decompose_uni _vararg_count ret; 748  ! glk_buffer_canon_decompose_uni(uintarray, arraylen, uint) => uint 749  @glk 291 _vararg_count ret; 750  return ret; 751]; 752 753[ glk_buffer_canon_normalize_uni _vararg_count ret; 754  ! glk_buffer_canon_normalize_uni(uintarray, arraylen, uint) => uint 755  @glk 292 _vararg_count ret; 756  return ret; 757]; 758 759[ glk_put_char_uni _vararg_count; 760  ! glk_put_char_uni(uint) 761  @glk 296 _vararg_count 0; 762  return 0; 763]; 764 765[ glk_put_string_uni _vararg_count; 766  ! glk_put_string_uni(unicode) 767  @glk 297 _vararg_count 0; 768  return 0; 769]; 770 771[ glk_put_buffer_uni _vararg_count; 772  ! glk_put_buffer_uni(uintarray, arraylen) 773  @glk 298 _vararg_count 0; 774  return 0; 775]; 776 777[ glk_put_char_stream_uni _vararg_count; 778  ! glk_put_char_stream_uni(stream, uint) 779  @glk 299 _vararg_count 0; 780  return 0; 781]; 782 783[ glk_put_string_stream_uni _vararg_count; 784  ! glk_put_string_stream_uni(stream, unicode) 785  @glk 300 _vararg_count 0; 786  return 0; 787]; 788 789[ glk_put_buffer_stream_uni _vararg_count; 790  ! glk_put_buffer_stream_uni(stream, uintarray, arraylen) 791  @glk 301 _vararg_count 0; 792  return 0; 793]; 794 795[ glk_get_char_stream_uni _vararg_count ret; 796  ! glk_get_char_stream_uni(stream) => int 797  @glk 304 _vararg_count ret; 798  return ret; 799]; 800 801[ glk_get_buffer_stream_uni _vararg_count ret; 802  ! glk_get_buffer_stream_uni(stream, uintarray, arraylen) => uint 803  @glk 305 _vararg_count ret; 804  return ret; 805]; 806 807[ glk_get_line_stream_uni _vararg_count ret; 808  ! glk_get_line_stream_uni(stream, uintarray, arraylen) => uint 809  @glk 306 _vararg_count ret; 810  return ret; 811]; 812 813[ glk_stream_open_file_uni _vararg_count ret; 814  ! glk_stream_open_file_uni(fileref, uint, uint) => stream 815  @glk 312 _vararg_count ret; 816  return ret; 817]; 818 819[ glk_stream_open_memory_uni _vararg_count ret; 820  ! glk_stream_open_memory_uni(uintarray, arraylen, uint, uint) => stream 821  @glk 313 _vararg_count ret; 822  return ret; 823]; 824 825[ glk_stream_open_resource_uni _vararg_count ret; 826  ! glk_stream_open_resource_uni(uint, uint) => stream 827  @glk 314 _vararg_count ret; 828  return ret; 829]; 830 831[ glk_request_char_event_uni _vararg_count; 832  ! glk_request_char_event_uni(window) 833  @glk 320 _vararg_count 0; 834  return 0; 835]; 836 837[ glk_request_line_event_uni _vararg_count; 838  ! glk_request_line_event_uni(window, uintarray, arraylen, uint) 839  @glk 321 _vararg_count 0; 840  return 0; 841]; 842 843[ glk_set_echo_line_event _vararg_count; 844  ! glk_set_echo_line_event(window, uint) 845  @glk 336 _vararg_count 0; 846  return 0; 847]; 848 849[ glk_set_terminators_line_event _vararg_count; 850  ! glk_set_terminators_line_event(window, uintarray, arraylen) 851  @glk 337 _vararg_count 0; 852  return 0; 853]; 854 855[ glk_current_time _vararg_count; 856  ! glk_current_time(&{int, uint, int}) 857  @glk 352 _vararg_count 0; 858  return 0; 859]; 860 861[ glk_current_simple_time _vararg_count ret; 862  ! glk_current_simple_time(uint) => int 863  @glk 353 _vararg_count ret; 864  return ret; 865]; 866 867[ glk_time_to_date_utc _vararg_count; 868  ! glk_time_to_date_utc(&{int, uint, int}, &{int, int, int, int, int, int, int, int}) 869  @glk 360 _vararg_count 0; 870  return 0; 871]; 872 873[ glk_time_to_date_local _vararg_count; 874  ! glk_time_to_date_local(&{int, uint, int}, &{int, int, int, int, int, int, int, int}) 875  @glk 361 _vararg_count 0; 876  return 0; 877]; 878 879[ glk_simple_time_to_date_utc _vararg_count; 880  ! glk_simple_time_to_date_utc(int, uint, &{int, int, int, int, int, int, int, int}) 881  @glk 362 _vararg_count 0; 882  return 0; 883]; 884 885[ glk_simple_time_to_date_local _vararg_count; 886  ! glk_simple_time_to_date_local(int, uint, &{int, int, int, int, int, int, int, int}) 887  @glk 363 _vararg_count 0; 888  return 0; 889]; 890 891[ glk_date_to_time_utc _vararg_count; 892  ! glk_date_to_time_utc(&{int, int, int, int, int, int, int, int}, &{int, uint, int}) 893  @glk 364 _vararg_count 0; 894  return 0; 895]; 896 897[ glk_date_to_time_local _vararg_count; 898  ! glk_date_to_time_local(&{int, int, int, int, int, int, int, int}, &{int, uint, int}) 899  @glk 365 _vararg_count 0; 900  return 0; 901]; 902 903[ glk_date_to_simple_time_utc _vararg_count ret; 904  ! glk_date_to_simple_time_utc(&{int, int, int, int, int, int, int, int}, uint) => int 905  @glk 366 _vararg_count ret; 906  return ret; 907]; 908 909[ glk_date_to_simple_time_local _vararg_count ret; 910  ! glk_date_to_simple_time_local(&{int, int, int, int, int, int, int, int}, uint) => int 911  @glk 367 _vararg_count ret; 912  return ret; 913];

Rocks.

These are unique ID codes used to mark resources; think of them as inedible cookies.

920Constant GG_MAINWIN_ROCK 201; 921Constant GG_STATUSWIN_ROCK 202; 922Constant GG_QUOTEWIN_ROCK 203; 923Constant GG_SAVESTR_ROCK 301; 924Constant GG_SCRIPTSTR_ROCK 302; 925Constant GG_COMMANDWSTR_ROCK 303; 926Constant GG_COMMANDRSTR_ROCK 304; 927Constant GG_SCRIPTFREF_ROCK 401; 928Constant GG_FOREGROUNDCHAN_ROCK 410; 929Constant GG_BACKGROUNDCHAN_ROCK 411;

Stubs.

These are I6 library-style entry point routines, not used by I7, but retained in case I7 extensions want to do interesting things with Glulx.

936#Stub HandleGlkEvent 2; 937#Stub IdentifyGlkObject 4; 938#Stub InitGlkWindow 1;

Starting Up.

VM_Initialise() is almost the first routine called, except that the "starting the virtual machine" activity is allowed to go first; and, come to think of it, memory allocation has to be set up before even that, and that in turn calls VM_PreInitialise() to do the absolute minimum.

Arrangements are a little different here from on the Z-machine, because some data is retained in the case of a restart.

(Many thanks are due to Eliuk Blau, who found several tricky timing errors here and elsewhere in the Glulx-specific code. Frankly, I feel like hanging a sign on the following routines which reads "Congratulations on bringing light to the Dark Room.")

955[ VM_PreInitialise res; 956    @gestalt 4 2 res; ! Test if this interpreter has Glk... 957    if (res == 0) quit; ! ...without which there would be nothing we could do 958 959    unicode_gestalt_ok = false; 960    if (glk_gestalt(gestalt_Unicode, 0)) 961        unicode_gestalt_ok = true; 962 963    ! Set the VM's I/O system to be Glk. 964    @setiosys 2 0; 965]; 966 967[ VM_Initialise res sty i; 968    @gestalt 4 2 res; ! Test if this interpreter has Glk... 969    if (res == 0) quit; ! ...without which there would be nothing we could do 970 971    ! First, we must go through all the Glk objects that exist, and see 972    ! if we created any of them. One might think this strange, since the 973    ! program has just started running, but remember that the player might 974    ! have just typed "restart". 975 976    GGRecoverObjects(); 977 978    ! Sound channel initialisation, and RNG fixing, must be done now rather 979    ! than later in case InitGlkWindow() returns a non-zero value. 980 981    if (glk_gestalt(gestalt_Sound, 0)) { 982        if (gg_foregroundchan == 0) 983            gg_foregroundchan = glk_schannel_create(GG_FOREGROUNDCHAN_ROCK); 984        if (gg_backgroundchan == 0) 985            gg_backgroundchan = glk_schannel_create(GG_BACKGROUNDCHAN_ROCK); 986    } 987 988    #ifdef FIX_RNG; 989    @random 10000 i; 990    i = -i-2000; 991    print "[Random number generator seed is ", i, "]^"; 992    @setrandom i; 993    #endif; ! FIX_RNG 994 995    res = InitGlkWindow(0); 996    if (res ~= 0) return; 997 998    ! Now, gg_mainwin and gg_storywin might already be set. If not, set them. 999 1000    if (gg_mainwin == 0) { 1001        ! Open the story window. 1002        res = InitGlkWindow(GG_MAINWIN_ROCK); 1003        if (res == 0) { 1004            ! Left-justify the header style 1005            glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Justification, 0); 1006            ! Try to make emphasized type in italics and not boldface 1007            glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Weight, 0); 1008            glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Oblique, 1); 1009            gg_mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, GG_MAINWIN_ROCK); 1010        } 1011        if (gg_mainwin == 0) quit; ! If we can't even open one window, give in 1012    } else { 1013        ! There was already a story window. We should erase it. 1014        glk_window_clear(gg_mainwin); 1015    } 1016 1017    if (gg_statuswin == 0) { 1018        res = InitGlkWindow(GG_STATUSWIN_ROCK); 1019        if (res == 0) { 1020            statuswin_cursize = statuswin_size; 1021            for (sty=0: sty<style_NUMSTYLES: sty++) 1022                glk_stylehint_set(wintype_TextGrid, sty, stylehint_ReverseColor, 1); 1023            gg_statuswin = 1024                glk_window_open(gg_mainwin, winmethod_Fixed + winmethod_Above, 1025                    statuswin_cursize, wintype_TextGrid, GG_STATUSWIN_ROCK); 1026        } 1027    } 1028    ! It's possible that the status window couldn't be opened, in which case 1029    ! gg_statuswin is now zero. We must allow for that later on. 1030 1031    glk_set_window(gg_mainwin); 1032 1033    InitGlkWindow(1); 1034     1035    ! Empty the parse buffer (see bug 0001451) 1036    buffer3-->0 = 0; 1037]; 1038 1039[ GGRecoverObjects id; 1040    ! If GGRecoverObjects() has been called, all these stored IDs are 1041    ! invalid, so we start by clearing them all out. 1042    ! (In fact, after a restoreundo, some of them may still be good. 1043    ! For simplicity, though, we assume the general case.) 1044    gg_mainwin = 0; 1045    gg_statuswin = 0; 1046    gg_quotewin = 0; 1047    gg_scriptfref = 0; 1048    gg_scriptstr = 0; 1049    gg_savestr = 0; 1050    statuswin_cursize = 0; 1051    gg_foregroundchan = 0; 1052    gg_backgroundchan = 0; 1053    #Ifdef DEBUG; 1054    gg_commandstr = 0; 1055    gg_command_reading = false; 1056    #Endif; ! DEBUG 1057    ! Also tell the game to clear its object references. 1058    IdentifyGlkObject(0); 1059 1060    id = glk_stream_iterate(0, gg_arguments); 1061    while (id) { 1062        switch (gg_arguments-->0) { 1063            GG_SAVESTR_ROCK: gg_savestr = id; 1064            GG_SCRIPTSTR_ROCK: gg_scriptstr = id; 1065            #Ifdef DEBUG; 1066            GG_COMMANDWSTR_ROCK: gg_commandstr = id; 1067                                 gg_command_reading = false; 1068            GG_COMMANDRSTR_ROCK: gg_commandstr = id; 1069                                 gg_command_reading = true; 1070            #Endif; ! DEBUG 1071            default: IdentifyGlkObject(1, 1, id, gg_arguments-->0); 1072        } 1073        id = glk_stream_iterate(id, gg_arguments); 1074    } 1075 1076    id = glk_window_iterate(0, gg_arguments); 1077    while (id) { 1078        switch (gg_arguments-->0) { 1079            GG_MAINWIN_ROCK: gg_mainwin = id; 1080            GG_STATUSWIN_ROCK: gg_statuswin = id; 1081            GG_QUOTEWIN_ROCK: gg_quotewin = id; 1082            default: IdentifyGlkObject(1, 0, id, gg_arguments-->0); 1083        } 1084        id = glk_window_iterate(id, gg_arguments); 1085    } 1086 1087    id = glk_fileref_iterate(0, gg_arguments); 1088    while (id) { 1089        switch (gg_arguments-->0) { 1090            GG_SCRIPTFREF_ROCK: gg_scriptfref = id; 1091            default: IdentifyGlkObject(1, 2, id, gg_arguments-->0); 1092        } 1093        id = glk_fileref_iterate(id, gg_arguments); 1094    } 1095 1096    if (glk_gestalt(gestalt_Sound, 0)) { 1097        id = glk_schannel_iterate(0, gg_arguments); 1098        while (id) { 1099            switch (gg_arguments-->0) { 1100                GG_FOREGROUNDCHAN_ROCK: gg_foregroundchan = id; 1101                GG_BACKGROUNDCHAN_ROCK: gg_backgroundchan = id; 1102                default: IdentifyGlkObject(1, 3, id, gg_arguments-->0); 1103            } 1104            id = glk_schannel_iterate(id, gg_arguments); 1105        } 1106        if (gg_foregroundchan ~= 0) { glk_schannel_stop(gg_foregroundchan); } 1107        if (gg_backgroundchan ~= 0) { glk_schannel_stop(gg_backgroundchan); } 1108    } 1109 1110    ! Tell the game to tie up any loose ends. 1111    IdentifyGlkObject(2); 1112];

Enable Acceleration.

This enables use of March 2009 extension to Glulx which optimises the speed of Inform-compiled story files by moving the work of I6 veneer routines into the interpreter itself. It should have no effect on earlier versions of the Glulx VM, which will lack the gestalt for this feature, but nor should it do any harm.

1122[ ENABLE_GLULX_ACCEL_R addr res; 1123    @gestalt 9 0 res; 1124    if (res == 0) return; 1125    addr = #classes_table; 1126    @accelparam 0 addr; 1127    @accelparam 1 INDIV_PROP_START; 1128    @accelparam 2 Class; 1129    @accelparam 3 Object; 1130    @accelparam 4 Routine; 1131    @accelparam 5 String; 1132    addr = #globals_array + WORDSIZE * #g$self; 1133    @accelparam 6 addr; 1134    @accelparam 7 NUM_ATTR_BYTES; 1135    addr = #cpv__start; 1136    @accelparam 8 addr; 1137    @accelfunc 1 Z__Region; 1138    @accelfunc 2 CP__Tab; 1139    @accelfunc 3 RA__Pr; 1140    @accelfunc 4 RL__Pr; 1141    @accelfunc 5 OC__Cl; 1142    @accelfunc 6 RV__Pr; 1143    @accelfunc 7 OP__Pr; 1144    rfalse; 1145];

Release Number.

Like all software, IF story files have release numbers to mark revised versions being circulated: unlike most software, and partly for traditional reasons, the version number is recorded not in some print statement or variable but is branded on, so to speak, in a specific memory location of the story file header.

VM_Describe_Release() describes the release and is used as part of the "banner", IF's equivalent to a title page.

1158[ VM_Describe_Release i; 1159    print "Release "; 1160    @aloads ROM_GAMERELEASE 0 i; 1161    print i; 1162    print " / Serial number "; 1163    for (i=0 : i<6 : i++) print (char) ROM_GAMESERIAL->i; 1164];

Keyboard Input.

The VM must provide three routines for keyboard input:

(a) VM_KeyChar() waits for a key to be pressed and then returns the character chosen as a ZSCII character. (b) VM_KeyDelay(N) waits up to N/10 seconds for a key to be pressed, returning the ZSCII character if so, or 0 if not. (c) VM_ReadKeyboard(b, t) reads a whole newline-terminated command into the buffer b, then parses it into a word stream in the table t.

There are elaborations to due with mouse clicks, but this isn't the place to document all of that.

1180[ VM_KeyChar win nostat done res ix jx ch; 1181    jx = ch; ! squash compiler warnings 1182    if (win == 0) win = gg_mainwin; 1183    if (gg_commandstr ~= 0 && gg_command_reading ~= false) { 1184        done = glk_get_line_stream(gg_commandstr, gg_arguments, 31); 1185        if (done == 0) { 1186            glk_stream_close(gg_commandstr, 0); 1187            gg_commandstr = 0; 1188            gg_command_reading = false; 1189            ! fall through to normal user input. 1190        } else { 1191            ! Trim the trailing newline 1192            if (gg_arguments->(done-1) == 10) done = done-1; 1193            res = gg_arguments->0; 1194            if (res == '\') { 1195                res = 0; 1196                for (ix=1 : ix<done : ix++) { 1197                    ch = gg_arguments->ix; 1198                    if (ch >= '0' && ch <= '9') { 1199                        @shiftl res 4 res; 1200                        res = res + (ch-'0'); 1201                    } else if (ch >= 'a' && ch <= 'f') { 1202                        @shiftl res 4 res; 1203                        res = res + (ch+10-'a'); 1204                    } else if (ch >= 'A' && ch <= 'F') { 1205                        @shiftl res 4 res; 1206                        res = res + (ch+10-'A'); 1207                    } 1208                } 1209            } 1210               jump KCPContinue; 1211        } 1212    } 1213    done = false; 1214    glk_request_char_event(win); 1215    while (~~done) { 1216        glk_select(gg_event); 1217        switch (gg_event-->0) { 1218          5: ! evtype_Arrange 1219            if (nostat) { 1220                glk_cancel_char_event(win); 1221                res = $80000000; 1222                done = true; 1223                break; 1224            } 1225            DrawStatusLine(); 1226          2: ! evtype_CharInput 1227            if (gg_event-->1 == win) { 1228                res = gg_event-->2; 1229                done = true; 1230                } 1231        } 1232        ix = HandleGlkEvent(gg_event, 1, gg_arguments); 1233        if (ix == 2) { 1234            res = gg_arguments-->0; 1235            done = true; 1236        } else if (ix == -1) done = false; 1237    } 1238    if (gg_commandstr ~= 0 && gg_command_reading == false) { 1239        if (res < 32 || res >= 256 || (res == '\' or ' ')) { 1240            glk_put_char_stream(gg_commandstr, '\'); 1241            done = 0; 1242            jx = res; 1243            for (ix=0 : ix<8 : ix++) { 1244                @ushiftr jx 28 ch; 1245                @shiftl jx 4 jx; 1246                ch = ch & $0F; 1247                if (ch ~= 0 || ix == 7) done = 1; 1248                if (done) { 1249                    if (ch >= 0 && ch <= 9) ch = ch + '0'; 1250                    else ch = (ch - 10) + 'A'; 1251                    glk_put_char_stream(gg_commandstr, ch); 1252                } 1253            } 1254        } else { 1255            glk_put_char_stream(gg_commandstr, res); 1256        } 1257        glk_put_char_stream(gg_commandstr, 10); ! newline 1258    } 1259  .KCPContinue; 1260    return res; 1261]; 1262 1263[ VM_KeyDelay tenths key done ix; 1264    glk_request_char_event(gg_mainwin); 1265    glk_request_timer_events(tenths*100); 1266    while (~~done) { 1267        glk_select(gg_event); 1268        ix = HandleGlkEvent(gg_event, 1, gg_arguments); 1269        if (ix == 2) { 1270            key = gg_arguments-->0; 1271            done = true; 1272        } 1273        else if (ix >= 0 && gg_event-->0 == 1 or 2) { 1274            key = gg_event-->2; 1275            done = true; 1276        } 1277    } 1278    glk_cancel_char_event(gg_mainwin); 1279    glk_request_timer_events(0); 1280    return key; 1281]; 1282 1283[ VM_ReadKeyboard a_buffer a_table done ix; 1284    if (gg_commandstr ~= 0 && gg_command_reading ~= false) { 1285        done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE, 1286            (INPUT_BUFFER_LEN-WORDSIZE)-1); 1287        if (done == 0) { 1288            glk_stream_close(gg_commandstr, 0); 1289            gg_commandstr = 0; 1290            gg_command_reading = false; 1291        } 1292        else { 1293            ! Trim the trailing newline 1294            if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1; 1295            a_buffer-->0 = done; 1296            VM_Style(INPUT_VMSTY); 1297            glk_put_buffer(a_buffer+WORDSIZE, done); 1298            VM_Style(NORMAL_VMSTY); 1299            print "^"; 1300            jump KPContinue; 1301        } 1302    } 1303    done = false; 1304    glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); 1305    while (~~done) { 1306        glk_select(gg_event); 1307        switch (gg_event-->0) { 1308          5: ! evtype_Arrange 1309            DrawStatusLine(); 1310          3: ! evtype_LineInput 1311            if (gg_event-->1 == gg_mainwin) { 1312                a_buffer-->0 = gg_event-->2; 1313                done = true; 1314            } 1315        } 1316        ix = HandleGlkEvent(gg_event, 0, a_buffer); 1317        if (ix == 2) done = true; 1318        else if (ix == -1) done = false; 1319    } 1320    if (gg_commandstr ~= 0 && gg_command_reading == false) { 1321        glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0); 1322        glk_put_char_stream(gg_commandstr, 10); ! newline 1323    } 1324  .KPContinue; 1325    VM_Tokenise(a_buffer,a_table); 1326    ! It's time to close any quote window we've got going. 1327    if (gg_quotewin) { 1328        glk_window_close(gg_quotewin, 0); 1329        gg_quotewin = 0; 1330    } 1331    #ifdef ECHO_COMMANDS; 1332    print "** "; 1333    for (ix=WORDSIZE: ix<(a_buffer-->0)+WORDSIZE: ix++) print (char) a_buffer->ix; 1334    print "^"; 1335    #endif; ! ECHO_COMMANDS 1336];

Buffer Functions.

A "buffer", in this sense, is an array containing a stream of characters typed from the keyboard; a "parse buffer" is an array which resolves this into individual words, pointing to the relevant entries in the dictionary structure. Because each VM has its own format for each of these arrays (not to mention the dictionary), we have to provide some standard operations needed by the rest of the template as routines for each VM.

VM_CopyBuffer(to, from) copies one buffer into another.

VM_Tokenise(buff, parse_buff) takes the text in the buffer buff and produces the corresponding data in the parse buffer parse_buff – this is called tokenisation since the characters are divided into words: in traditional computing jargon, such clumps of characters treated syntactically as units are called tokens.

LTI_Insert is documented in the DM4 and the LTI prefix stands for "Language To Informese": it's used only by translations into non-English languages of play, and is not called in the template.

1359[ VM_CopyBuffer bto bfrom i; 1360    for (i=0: i<INPUT_BUFFER_LEN: i++) bto->i = bfrom->i; 1361]; 1362 1363[ VM_PrintToBuffer buf len a b c; 1364    if (b) { 1365        if (metaclass(a) == Object && a.#b == WORDSIZE 1366            && metaclass(a.b) == String) 1367            buf-->0 = Glulx_PrintAnyToArray(buf+WORDSIZE, len, a.b); 1368        else if (metaclass(a) == Routine) 1369            buf-->0 = Glulx_PrintAnyToArray(buf+WORDSIZE, len, a, b, c); 1370        else 1371            buf-->0 = Glulx_PrintAnyToArray(buf+WORDSIZE, len, a, b); 1372    } 1373    else if (metaclass(a) == Routine) 1374        buf-->0 = Glulx_PrintAnyToArray(buf+WORDSIZE, len, a, b, c); 1375    else 1376        buf-->0 = Glulx_PrintAnyToArray(buf+WORDSIZE, len, a); 1377    if (buf-->0 > len) buf-->0 = len; 1378    return buf-->0; 1379]; 1380 1381[ VM_Tokenise buf tab 1382    cx numwords len bx ix wx wpos wlen val res dictlen entrylen; 1383    len = buf-->0; 1384    buf = buf+WORDSIZE; 1385 1386    ! First, split the buffer up into words. We use the standard Infocom 1387    ! list of word separators (comma, period, double-quote). 1388    cx = 0; 1389    numwords = 0; 1390    while (cx < len) { 1391        while (cx < len && buf->cx == ' ') cx++; 1392        if (cx >= len) break; 1393        bx = cx; 1394        if (buf->cx == '.' or ',' or '') cx++; 1395        else { 1396            while (cx < len && buf->cx ~= ' ' or '.' or ',' or '') cx++; 1397        } 1398        tab-->(numwords*3+2) = (cx-bx); 1399        tab-->(numwords*3+3) = WORDSIZE+bx; 1400        numwords++; 1401        if (numwords >= MAX_BUFFER_WORDS) break; 1402    } 1403    tab-->0 = numwords; 1404    ! Now we look each word up in the dictionary. 1405 1406    dictlen = #dictionary_table-->0; 1407    entrylen = DICT_WORD_SIZE + 7; 1408 1409    for (wx=0 : wx<numwords : wx++) { 1410        wlen = tab-->(wx*3+2); 1411        wpos = tab-->(wx*3+3); 1412 1413        ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE 1414        ! characters and lower case. 1415        if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE; 1416        cx = wpos - WORDSIZE; 1417        for (ix=0 : ix<wlen : ix++) gg_tokenbuf->ix = VM_UpperToLowerCase(buf->(cx+ix)); 1418        for (: ix<DICT_WORD_SIZE : ix++) gg_tokenbuf->ix = 0; 1419 1420        val = #dictionary_table + WORDSIZE; 1421        @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res; 1422        tab-->(wx*3+1) = res; 1423    } 1424]; 1425 1426[ LTI_Insert i ch b y; 1427 1428    ! Protect us from strict mode, as this isn't an array in quite the 1429    ! sense it expects 1430    b = buffer; 1431 1432    ! Insert character ch into buffer at point i. 1433    ! Being careful not to let the buffer possibly overflow: 1434    y = b-->0; 1435    if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN; 1436 1437    ! Move the subsequent text along one character: 1438    for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1); 1439    b->i = ch; 1440 1441    ! And the text is now one character longer: 1442    if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++; 1443];

Dictionary Functions.

Again, the dictionary structure is differently arranged on the different VMs. This is a data structure containing, in compressed form, the text of all the words to be recognised by tokenisation (above). In I6 for Glulx, a dictionary word is represented at run-time by its record's address in the dictionary.

VM_InvalidDictionaryAddress(A) tests whether A is a valid record address in the dictionary data structure. In Glulx, dictionary records might in theory be anywhere in the 2 GB or so of possible memory, but we can rule out negative addresses. (This allows -1, say, to be used as a value meaning "not a valid dictionary word".)

VM_DictionaryAddressToNumber(A) and VM_NumberToDictionaryAddress(N) convert between word addresses and their run-time representations: since, on Glulx, they are the same, these are each the identity function.

1462[ VM_InvalidDictionaryAddress addr; 1463    if (addr < 0) rtrue; 1464    rfalse; 1465]; 1466 1467[ VM_DictionaryAddressToNumber w; return w; ]; 1468[ VM_NumberToDictionaryAddress n; return n; ]; 1469 1470Array gg_tokenbuf -> DICT_WORD_SIZE; 1471 1472[ GGWordCompare str1 str2 ix jx; 1473    for (ix=0 : ix<DICT_WORD_SIZE : ix++) { 1474        jx = (str1->ix) - (str2->ix); 1475        if (jx ~= 0) return jx; 1476    } 1477    return 0; 1478];

SHOWVERB support.

Further VM-specific tables cover actions and attributes, and these are used by the SHOWVERB testing command.

1485#Ifdef DEBUG; 1486[ DebugAction a str; 1487    if (a >= 4096) { print "<fake action ", a-4096, ">"; return; } 1488    if (a < 0 || a >= #identifiers_table-->7) print "<invalid action ", a, ">"; 1489    else { 1490        str = #identifiers_table-->6; 1491        str = str-->a; 1492        if (str) print (string) str; else print "<unnamed action ", a, ">"; 1493    } 1494]; 1495 1496[ DebugAttribute a str; 1497    if (a < 0 || a >= NUM_ATTR_BYTES*8) print "<invalid attribute ", a, ">"; 1498    else { 1499        str = #identifiers_table-->4; 1500        str = str-->a; 1501        if (str) print (string) str; else print "<unnamed attribute ", a, ">"; 1502    } 1503]; 1504#Endif;

Command Tables.

The VM is also generated containing a data structure for the grammar produced by I6's Verb and Extend directives: this is essentially a list of command verbs such as DROP or PUSH, together with a list of synonyms, and then the grammar for the subsequent commands to be recognised by the parser.

1514[ VM_CommandTableAddress i; 1515    return (#grammar_table)-->(i+1); 1516]; 1517 1518[ VM_PrintCommandWords i wd j dictlen entrylen; 1519    dictlen = #dictionary_table-->0; 1520    entrylen = DICT_WORD_SIZE + 7; 1521    for (j=0 : j<dictlen : j++) { 1522        wd = #dictionary_table + WORDSIZE + entrylen*j; 1523        if (DictionaryWordToVerbNum(wd) == i) 1524            print "", (address) wd, " "; 1525    } 1526];

Random Number Generator.

No routine is needed for extracting a random number, since I6's built-in random function does that, but it's useful to abstract the process of seeding the RNG so that it produces a repeatable sequence of "random" numbers from here on: the necessary opcodes are different for the two VMs.

1535[ VM_Seed_RNG n; 1536    @setrandom n; 1537];

Memory Allocation.

This is dynamic memory allocation: something which is never practicable in the Z-machine, because the whole address range is already claimed, but which is viable on recent revisions of Glulx.

1545[ VM_AllocateMemory amount i; 1546    @gestalt 7 0 i; 1547    if (i == 0) return i; 1548    @malloc amount i; 1549    return i; 1550]; 1551 1552[ VM_FreeMemory address i; 1553    @gestalt 7 0 i; 1554    if (i == 0) return; 1555    @mfree address; 1556];

Audiovisual Resources.

The Z-machine only barely supports figures and sound effects, so Glulx is the preferred VM to choose if they are wanted. Properly speaking, it's not Glulx which supports these, but its I/O layer Glk, and implementations of Glk are free to support them or not as they please: "cheapglk", a dumb terminal version, does not, for instance. We therefore have to investigate the "gestalt" to find out.

1567[ VM_Picture resource_ID; 1568    if (glk_gestalt(gestalt_Graphics, 0)) { 1569        glk_image_draw(gg_mainwin, resource_ID, imagealign_InlineCenter, 0); 1570    } else { 1571        print "[Picture number ", resource_ID, " here.]^"; 1572    } 1573]; 1574 1575[ VM_SoundEffect resource_ID; 1576    if (glk_gestalt(gestalt_Sound, 0)) { 1577        glk_schannel_play(gg_foregroundchan, resource_ID); 1578    } else { 1579        print "[Sound effect number ", resource_ID, " here.]^"; 1580    } 1581];

Typography.

Glk makes an attempt to present typographic styles as being a matter of semantic markup rather than controlling the actual appearance of text: the idea is that the story file should want to print something in a heading kind of way, and then the interpreter – guided by the player's reading preferences – might set that in bold, or larger type, or red ink, or any combination of the three, or with other effects entirely. This is not the place to discuss whether that was a wise decision for Glk to take (it really, really, really wasn't): we can only play along.

1594[ VM_Style sty; 1595    switch (sty) { 1596        NORMAL_VMSTY: glk_set_style(style_Normal); 1597        HEADER_VMSTY: glk_set_style(style_Header); 1598        SUBHEADER_VMSTY: glk_set_style(style_Subheader); 1599        NOTE_VMSTY: glk_set_style(style_Note); 1600        ALERT_VMSTY: glk_set_style(style_Alert); 1601        BLOCKQUOTE_VMSTY: glk_set_style(style_BlockQuote); 1602        INPUT_VMSTY: glk_set_style(style_Input); 1603    } 1604];

Character Casing.

The following are the equivalent of tolower and toupper, the traditional C library functions for forcing letters into lower and upper case form, for the ZSCII character set. Note that Glulx can also use Unicode characters for some purposes (Unicode was a relatively late addition to the Glulx standard), and we make good use of this when storing text.

1614[ VM_UpperToLowerCase c; return glk_char_to_lower(c); ]; 1615[ VM_LowerToUpperCase c; return glk_char_to_upper(c); ];

Glulx-Only Printing Routines.

Partly because of the smallness of the range of representable values in the Z-machine, there is little run-time type-checking that can be done: for instance a dictionary address cannot be distinguished from a function address because they are encoded differently, so that a function address (which is packed) could well coincide with that of a dictionary word (which is not). On Glulx these restrictions are somewhat lifted, so that it's possible to write a routine which can look at a value, work out what it must mean, and print it suitably. This is only possible up to a point – for instance, it can't distinguish an integer from a function address – and in I7 the use of this sort of trick is much less important because type-checking in the NI compiler handles the problem much better. Still, we retain some Glulx-only features because they are convenient for writing external files to disc, for instance, something which the Z-machine can't do in any case.

Glulx_PrintAnything handles strings, functions (with optional arguments), objects, object properties (with optional arguments), and dictionary words.

Glulx_PrintAnyToArray does the same, but the output is sent to a byte array in memory. The first two arguments must be the array address and length; subsequent arguments are as for Glulx_PrintAnything. The return value is the number of characters output. If the output is longer than the array length given, the extra characters are discarded, so the array does not overflow. (However, the return value is the total length of the output, including discarded characters.) The character set stored here is ZSCII, not Unicode.

Glulx_ChangeAnyToCString calls Glulx_PrintAnyToArray on a particular array, then amends the result to make it a C-style string – that is, a sequence of byte-sized characters which are null terminated. The character set stored here is once again ZSCII, not Unicode.

1651! Glulx_PrintAnything() <nothing printed> 1652! Glulx_PrintAnything(0) <nothing printed> 1653! Glulx_PrintAnything("string"); print (string) "string"; 1654! Glulx_PrintAnything('word') print (address) 'word'; 1655! Glulx_PrintAnything(obj) print (name) obj; 1656! Glulx_PrintAnything(obj, prop) obj.prop(); 1657! Glulx_PrintAnything(obj, prop, args...) obj.prop(args...); 1658! Glulx_PrintAnything(func) func(); 1659! Glulx_PrintAnything(func, args...) func(args...); 1660 1661[ Glulx_PrintAnything _vararg_count obj mclass; 1662    if (_vararg_count == 0) return; 1663    @copy sp obj; 1664    _vararg_count--; 1665    if (obj == 0) return; 1666 1667    if (obj->0 == $60) { 1668        ! Dictionary word. Metaclass() can't catch this case, so we do it manually 1669        print (address) obj; 1670        return; 1671    } 1672 1673    mclass = metaclass(obj); 1674    switch (mclass) { 1675      nothing: 1676        return; 1677      String: 1678        print (string) obj; 1679        return; 1680      Routine: 1681        ! Call the function with all the arguments which are already 1682        ! on the stack. 1683        @call obj _vararg_count 0; 1684        return; 1685      Object: 1686        if (_vararg_count == 0) { 1687            print (name) obj; 1688        } 1689        else { 1690            ! Push the object back onto the stack, and call the 1691            ! veneer routine that handles obj.prop() calls. 1692            @copy obj sp; 1693            _vararg_count++; 1694            @call CA__Pr _vararg_count 0; 1695        } 1696        return; 1697    } 1698]; 1699 1700[ Glulx_PrintAnyToArray _vararg_count arr arrlen str oldstr len; 1701    @copy sp arr; 1702    @copy sp arrlen; 1703    _vararg_count = _vararg_count - 2; 1704 1705    oldstr = glk_stream_get_current(); 1706    str = glk_stream_open_memory(arr, arrlen, 1, 0); 1707    if (str == 0) return 0; 1708 1709    glk_stream_set_current(str); 1710 1711    @call Glulx_PrintAnything _vararg_count 0; 1712 1713    glk_stream_set_current(oldstr); 1714    @copy $ffffffff sp; 1715    @copy str sp; 1716    @glk $0044 2 0; ! stream_close 1717    @copy sp len; 1718    @copy sp 0; 1719    return len; 1720]; 1721 1722Constant GG_ANYTOSTRING_LEN 66; 1723Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1; 1724 1725[ Glulx_ChangeAnyToCString _vararg_count ix len; 1726    ix = GG_ANYTOSTRING_LEN-2; 1727    @copy ix sp; 1728    ix = AnyToStrArr+1; 1729    @copy ix sp; 1730    ix = _vararg_count+2; 1731    @call Glulx_PrintAnyToArray ix len; 1732    AnyToStrArr->0 = $E0; 1733    if (len >= GG_ANYTOSTRING_LEN) 1734        len = GG_ANYTOSTRING_LEN-1; 1735    AnyToStrArr->(len+1) = 0; 1736    return AnyToStrArr; 1737];

The Screen.

Our generic screen model is that the screen is made up of windows: we tend to refer only to two of these, the main window and the status line, but others may also exist from time to time. Windows have unique ID numbers: the special window ID -1 means "all windows" or "the entire screen", which usually amounts to the same thing.

Screen height and width are measured in characters, with respect to the fixed-pitch font used for the status line. The main window normally contains variable-pitch text which may even have been kerned, and character dimensions make little sense there.

1752[ VM_ClearScreen window; 1753    if (window == WIN_ALL or WIN_MAIN) { 1754        glk_window_clear(gg_mainwin); 1755        if (gg_quotewin) { 1756            glk_window_close(gg_quotewin, 0); 1757            gg_quotewin = 0; 1758        } 1759    } 1760    if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk_window_clear(gg_statuswin); 1761]; 1762 1763[ VM_ScreenWidth id; 1764    id=gg_mainwin; 1765    if (gg_statuswin && statuswin_current) id = gg_statuswin; 1766    glk_window_get_size(id, gg_arguments, 0); 1767    return gg_arguments-->0; 1768]; 1769 1770[ VM_ScreenHeight; 1771    glk_window_get_size(gg_mainwin, 0, gg_arguments); 1772    return gg_arguments-->0; 1773];

Window Colours.

Our generic screen model is that the screen is made up of windows, each of which can have its own foreground and background colours.

The colour of individual letters or words of type is not controllable in Glulx, to the frustration of many, and so the template layer of I7 has no framework for handling this (even though it is controllable on the Z-machine, which is greatly superior in this respect).

1785[ VM_SetWindowColours f b window doclear i fwd bwd swin; 1786    if (clr_on && f && b) { 1787        if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer 1788 1789        fwd = MakeColourWord(f); 1790        bwd = MakeColourWord(b); 1791        for (i=0 : i<style_NUMSTYLES: i++) { 1792            if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints 1793                glk_stylehint_clear(swin, i, stylehint_TextColor); 1794                glk_stylehint_clear(swin, i, stylehint_BackColor); 1795            } else { 1796                glk_stylehint_set(swin, i, stylehint_TextColor, fwd); 1797                glk_stylehint_set(swin, i, stylehint_BackColor, bwd); 1798            } 1799        } 1800 1801        ! Now re-open the windows to apply the hints 1802        if (gg_statuswin) glk_window_close(gg_statuswin, 0); 1803        gg_statuswin = 0; 1804 1805        if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) { 1806            glk_window_close(gg_mainwin, 0); 1807            gg_mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, GG_MAINWIN_ROCK); 1808            if (gg_scriptstr ~= 0) 1809                glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); 1810        } 1811 1812        gg_statuswin = 1813            glk_window_open(gg_mainwin, winmethod_Fixed + winmethod_Above, 1814                statuswin_cursize, wintype_TextGrid, GG_STATUSWIN_ROCK); 1815        if (statuswin_current && gg_statuswin) VM_MoveCursorInStatusLine(); else VM_MainWindow(); 1816 1817        if (window ~= 2) { 1818            clr_fgstatus = f; 1819            clr_bgstatus = b; 1820        } 1821        if (window ~= 1) { 1822            clr_fg = f; 1823            clr_bg = b; 1824        } 1825    } 1826]; 1827 1828[ VM_RestoreWindowColours; ! used after UNDO: compare I6 patch L61007 1829    if (clr_on) { ! check colour has been used 1830        VM_SetWindowColours(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored 1831        VM_SetWindowColours(clr_fgstatus, clr_bgstatus, 1, true); 1832        VM_ClearScreen(); 1833    } 1834]; 1835 1836[ MakeColourWord c; 1837    if (c > 9) return c; 1838    c = c-2; 1839    return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0); 1840];

Main Window.

The part of the screen on which commands and responses are printed, which ordinarily occupies almost all of the screen area.

VM_MainWindow() switches printing back from another window, usually the status line, to the main window.

1850[ VM_MainWindow; 1851    glk_set_window(gg_mainwin); ! set_window 1852    statuswin_current=0; 1853];

Status Line.

Despite the name, the status line need not be a single line at the top of the screen: that's only the conventional default arrangement. It can expand to become the equivalent of an old-fashioned VT220 terminal, with menus and grids and mazes displayed lovingly in character graphics, or it can close up to invisibility.

VM_StatusLineHeight(n) sets the status line to have a height of n lines of type. (The width of the status line is always the width of the whole screen, and the position is always at the top, so the height is the only controllable aspect.) The n=0 case makes the status line disappear.

VM_MoveCursorInStatusLine(x, y) switches printing to the status line, positioning the "cursor" – the position at which printing will begin – at the given character grid position (x, y). Line 1 represents the top line; line 2 is underneath, and so on; columns are similarly numbered from 1 at the left.

1874[ VM_StatusLineHeight hgt; 1875    if (gg_statuswin == 0) return; 1876    if (hgt == statuswin_cursize) return; 1877    glk_window_set_arrangement(glk_window_get_parent(gg_statuswin), $12, hgt, 0); 1878    statuswin_cursize = hgt; 1879]; 1880 1881[ VM_MoveCursorInStatusLine line column; 1882    if (gg_statuswin == 0) return; 1883    glk_set_window(gg_statuswin); 1884    if (line == 0) { line = 1; column = 1; } 1885    glk_window_move_cursor(gg_statuswin, column-1, line-1); 1886    statuswin_current=1; 1887];

Quotation Boxes.

On the Z-machine, quotation boxes are produced by stretching the status line, but on Glulx they usually occupy windows of their own. If it isn't possible to create such a window, so that gg_quotewin is zero below, the quotation text just appears in the main window.

1896[ Box__Routine maxwid arr ix lines lastnl parwin; 1897    maxwid = 0; ! squash compiler warning 1898    lines = arr-->0; 1899 1900    if (gg_quotewin == 0) { 1901        gg_arguments-->0 = lines; 1902        ix = InitGlkWindow(GG_QUOTEWIN_ROCK); 1903        if (ix == 0) 1904            gg_quotewin = 1905                glk_window_open(gg_mainwin, winmethod_Fixed + winmethod_Above, 1906                    lines, wintype_TextBuffer, GG_QUOTEWIN_ROCK); 1907    } else { 1908        parwin = glk_window_get_parent(gg_quotewin); 1909        glk_window_set_arrangement(parwin, $12, lines, 0); 1910    } 1911 1912    lastnl = true; 1913    if (gg_quotewin) { 1914        glk_window_clear(gg_quotewin); 1915        glk_set_window(gg_quotewin); 1916        lastnl = false; 1917    } 1918 1919    VM_Style(BLOCKQUOTE_VMSTY); 1920    for (ix=0 : ix<lines : ix++) { 1921        print (string) arr-->(ix+1); 1922        if (ix < lines-1 || lastnl) new_line; 1923    } 1924    VM_Style(NORMAL_VMSTY); 1925 1926    if (gg_quotewin) glk_set_window(gg_mainwin); 1927];

GlkList Command.

GLKLIST is a testing command best used by those who understand Glulx and its ways: it isn't documented in the I7 manual, because it is pretty inscrutable for "real" users, but it's probably worth keeping just the same.

1935#Ifdef DEBUG; 1936[ GlkListSub id val; 1937    id = glk_window_iterate(0, gg_arguments); 1938    while (id) { 1939        print "Window ", id, " (", gg_arguments-->0, "): "; 1940        val = glk_window_get_type(id); 1941        switch (val) { 1942          1: print "pair"; 1943          2: print "blank"; 1944          3: print "textbuffer"; 1945          4: print "textgrid"; 1946          5: print "graphics"; 1947          default: print "unknown"; 1948        } 1949        val = glk_window_get_parent(id); 1950        if (val) print ", parent is window ", val; 1951        else print ", no parent (root)"; 1952        val = glk_window_get_stream(id); 1953        print ", stream ", val; 1954        val = glk_window_get_echo_stream(id); 1955        if (val) print ", echo stream ", val; 1956        print "^"; 1957        id = glk_window_iterate(id, gg_arguments); 1958    } 1959    id = glk_stream_iterate(0, gg_arguments); 1960    while (id) { 1961        print "Stream ", id, " (", gg_arguments-->0, ")^"; 1962        id = glk_stream_iterate(id, gg_arguments); 1963    } 1964    id = glk_fileref_iterate(0, gg_arguments); 1965    while (id) { 1966        print "Fileref ", id, " (", gg_arguments-->0, ")^"; 1967        id = glk_fileref_iterate(id, gg_arguments); 1968    } 1969    if (glk_gestalt(gestalt_Sound, 0)) { 1970        id = glk_schannel_iterate(0, gg_arguments); 1971        while (id) { 1972            print "Soundchannel ", id, " (", gg_arguments-->0, ")^"; 1973            id = glk_schannel_iterate(id, gg_arguments); 1974        } 1975    } 1976]; 1977 1978{-testing-command:glklist} 1979    * -> Glklist; 1980#Endif;

Undo.

These are really emulations of the Z-machine's conventions on UNDO: Glulx's undo opcodes used different result codes while providing essentially the same functionality, for reasons which are opaque, but no trouble is caused thereby.

1988[ VM_Undo result_code; 1989    @restoreundo result_code; 1990    return (~~result_code); 1991]; 1992 1993[ VM_Save_Undo result_code; 1994    @saveundo result_code; 1995    if (result_code == -1) { GGRecoverObjects(); return 2; } 1996    return (~~result_code); 1997];

Quit The Game Rule.

2002[ QUIT_THE_GAME_R; 2003    if (actor ~= player) rfalse; 2004    if ((actor == player) && (untouchable_silence == false)) 2005        QUIT_THE_GAME_RM('A'); 2006    if (YesOrNo()~=0) quit; 2007];

Restart The Game Rule.

2012[ RESTART_THE_GAME_R; 2013    if (actor ~= player) rfalse; 2014    RESTART_THE_GAME_RM('A'); 2015    if (YesOrNo()~=0) { 2016        @restart; 2017        RESTART_THE_GAME_RM('B'); new_line; 2018    } 2019];

Restore The Game Rule.

2024[ RESTORE_THE_GAME_R res fref; 2025    if (actor ~= player) rfalse; 2026    fref = glk_fileref_create_by_prompt($01, $02, 0); 2027    if (fref == 0) jump RFailed; 2028    gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK); 2029    glk_fileref_destroy(fref); 2030    if (gg_savestr == 0) jump RFailed; 2031    @restore gg_savestr res; 2032    glk_stream_close(gg_savestr, 0); 2033    gg_savestr = 0; 2034    .RFailed; 2035    RESTORE_THE_GAME_RM('A'); new_line; 2036];

Save The Game Rule.

2041[ SAVE_THE_GAME_R res fref; 2042    if (actor ~= player) rfalse; 2043    fref = glk_fileref_create_by_prompt($01, $01, 0); 2044    if (fref == 0) jump SFailed; 2045    gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK); 2046    glk_fileref_destroy(fref); 2047    if (gg_savestr == 0) jump SFailed; 2048    @save gg_savestr res; 2049    if (res == -1) { 2050        ! The player actually just typed "restore". We first have to recover 2051        ! all the Glk objects; the values in our global variables are all wrong. 2052        GGRecoverObjects(); 2053        glk_stream_close(gg_savestr, 0); ! stream_close 2054        gg_savestr = 0; 2055        RESTORE_THE_GAME_RM('B'); new_line; 2056        rtrue; 2057    } 2058    glk_stream_close(gg_savestr, 0); ! stream_close 2059    gg_savestr = 0; 2060    if (res == 0) { SAVE_THE_GAME_RM('B'); new_line; rtrue; } 2061    .SFailed; 2062    SAVE_THE_GAME_RM('A'); new_line; 2063];

Verify The Story File Rule.

This is a fossil now, really, but in the days of Infocom, the 110K story file occupying an entire disc was a huge data set: floppy discs were by no means a reliable medium, and cheap hardware often used hit-and-miss components, as on the notorious Commodore 64 disc controller. If somebody experienced an apparent bug in play, it could easily be that he had a corrupt disc or was unable to read data of that density. So the VERIFY command, which took up to ten minutes on some early computers, would chug through the entire story file and compute a checksum, compare it against a known result in the header, and determine that the story file could or could not properly be read. The Z-machine provided this service as an opcode, and so Glulx followed suit.

2079[ VERIFY_THE_STORY_FILE_R res; 2080    if (actor ~= player) rfalse; 2081    @verify res; 2082    if (res == 0) { VERIFY_THE_STORY_FILE_RM('A'); new_line; rtrue; } 2083    VERIFY_THE_STORY_FILE_RM('B'); new_line; 2084];

Switch Transcript On Rule.

2089[ SWITCH_TRANSCRIPT_ON_R; 2090    if (actor ~= player) rfalse; 2091    if (gg_scriptstr ~= 0) { SWITCH_TRANSCRIPT_ON_RM('A'); new_line; rtrue; } 2092    if (gg_scriptfref == 0) { 2093        gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK); 2094        if (gg_scriptfref == 0) jump S1Failed; 2095    } 2096    ! stream_open_file 2097    gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK); 2098    if (gg_scriptstr == 0) jump S1Failed; 2099    glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); 2100    SWITCH_TRANSCRIPT_ON_RM('B'); new_line; 2101    VersionSub(); 2102    return; 2103    .S1Failed; 2104    SWITCH_TRANSCRIPT_ON_RM('C'); new_line; 2105];

Switch Transcript Off Rule.

2110[ SWITCH_TRANSCRIPT_OFF_R; 2111    if (actor ~= player) rfalse; 2112    if (gg_scriptstr == 0) { SWITCH_TRANSCRIPT_OFF_RM('A'); new_line; rtrue; } 2113    SWITCH_TRANSCRIPT_OFF_RM('B'); new_line; 2114    glk_stream_close(gg_scriptstr, 0); ! stream_close 2115    gg_scriptstr = 0; 2116];

Announce Story File Version Rule.

2121[ ANNOUNCE_STORY_FILE_VERSION_R ix; 2122    if (actor ~= player) rfalse; 2123    Banner(); 2124    print "Identification number: "; 2125    for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix; 2126    print "^"; 2127    @gestalt 1 0 ix; 2128    print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100, 2129    ".", ix & $FF, " / "; 2130    @gestalt 0 0 ix; 2131    print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; 2132    print "Library serial number ", (string) LibSerial, "^"; 2133    #Ifdef LanguageVersion; 2134    print (string) LanguageVersion, "^"; 2135    #Endif; ! LanguageVersion 2136    ShowExtensionVersions(); 2137    say__p = 1; 2138];

Descend To Specific Action Rule.

There are 100 or so actions, typically, and this rule is for efficiency's sake: rather than perform 100 or so comparisons to see which routine to call, we indirect through a jump table. The routines called are the -Sub routines: thus, for instance, if action is ##Wait then WaitSub is called. It is essential that this routine not be called for fake actions: in I7 use this is guaranteed, since fake actions are not allowed into the action machinery at all.

Strangely, Glulx's action routines table is numbered in an off-by-one way compared to the Z-machine's: hence the +1.

2153[ DESCEND_TO_SPECIFIC_ACTION_R; 2154    indirect(#actions_table-->(action+1)); 2155    rtrue; 2156];

Veneer.

2161[ Unsigned__Compare x y; 2162    @jleu x y ?lesseq; 2163    return 1; 2164    .lesseq; 2165    @jeq x y ?equal; 2166    return -1; 2167    .equal; 2168    return 0; 2169]; 2170 2171[ RT__ChLDW x y; 2172    @aload x y sp; 2173    @return sp; 2174]; 2175 2176[ RT__ChLDB x y; 2177    @aloadb x y sp; 2178    @return sp; 2179];