Sunday, April 21, 2019

Chapter 16. Pardon the Interruptions (part 1)

16.1 Introduction

An interrupt is a special routine that is called after a certain number of turns has lapsed. They can be used to monitor objects and variables in the background. Interrupts will then change other variables and objects or call other routines and actions. Naming of these interrupt routines is done by adding a “I-” prefix to the routine name. The array to hold interrupt entries is a 180 byte (90 word) table where each entry had 3 words: enable flag, number of turns (TICKS), and routine address. This meant the table could only hold 30 entries. Only 3 routines are used to manage this ingenious system: INT to create or retrieve interrupt entries, QUEUE to set the number of turns before the interrupt is called, and CLOCKER which checks all the interrupts and finds those that need to be executed.
One thing to note is that there is no way to delete or replace an interrupt entry. It can be disabled by clearing the enable flag though. So there is a limited number of total interrupts that can be created.

16.2 Creating and Storing interrupts with INT

  • Arguments: Routine address
  • Returns: Address to interrupt entry
INT typically uses the 180 byte interrupt table to manage up to 30 interrupt entries. Some games like Deadline and The Witness use 300 bytes while Cutthroats’s table was 246 bytes in size. The table is filled like a stack, from the highest address to the lowest. So the oldest routines are located in the higher address, or the bottom of the table. The pointer to the newest interrupt entry (C-INTS) then moves toward the front of the buffer. After a routine address is passed to INT,
  1. The routine address in each entry in the interrupt table is checked (starting with the newest entry) with the requested one until there is a match or no more entries exist (when the pointer to the current entry reaches the end of the table).
  2. If there is a match, then the address to this interrupt entry is returned.
  3. If there is no match, then pointer to the newest entry is moved up (decrease by 6 bytes) and now points to a new blank entry. The requested routine address is stored in the appropriate location in the new entry. The address to this new entry is returned.

16.3 QUEUE - Setting Up the Interrupts

  • Arguments: Routine address, Number of turns
  • Returns: Address of interrupt entry
With the first generation interrupt routines, QUEUE was a separate routine to set the number of turns, or TICKS. It would call INT to get the address to the interrupt entry for the request routine address (creating the entry if necessary). Then. it would store the number of ticks for that interrupt in the 2nd word value of that entry and return the address to this interrupt entry. The setting of the enable flag was not done and have to be done using a separate STORE command. The last few ZIP 3 games (Stationfall and The Lurking Horror) and most EZIP games (all except AMFV) and all XZIP games did not use QUEUE. INT would automatically queue those entries or the games would do it without a separate routine.

16.4 CLOCKER - Running Interrupts

  • Arguments: None
  • Returns: TRUE if an interrupt was executed, FALSE if no interrupt was executed
CLOCKER is the main routine that checks each interrupt entry and decreases the number of turns for any enabled entry. It will call the routine at the address stored in any interrupt entry with only 1 TICK left or -1 TICKS which indicates the entry will always be executed. CLOCKER is always called at the end of the MAIN-LOOP.
  • If the given command was valid, CLOCKER will search through the interrupt table for entries with their enable flag set and extract the number of turns left.
    • The search begins with the newest interrupt entry and proceeds to the oldest entry.
  • If the TICKS is zero, CLOCKER goes to the next entry.
  • If the TICKS is not zero (a negative, 1, or greater than 1) then it will be decreased by 1 and saved back into the entry.
  • If the TICKS is still greater than 1, then CLOCKER will then go to the next entry.
  • At this point, CLOCKER will call the routine at the addr stored in the interrupt entry. TICKS will be 1 or a negative number at this point.
If no routine is called, CLOCKER will return FALSE. Otherwise, it will return TRUE no matter how many routines are called. The actual return value of the routine is not returned.

16.5 What about DEMONs?

INT allowed an initially set of interrupts to be added to the interrupt table that are always checked regardless of the PARSER outcome. This is marked by by the C-DEMONS pointer which moves from the end of the interrupt table when new entries are created with the DEMON flag set. C-INTS also moves when an entry is added when the DEMON flag is set. In game design, these important interrupts are added first and end up at the bottom of the interrupt table. All other interrupts but be added above these entries. If PARSER fails on a given command, CLOCKER will this and start checking the interrupts starting with C-DEMON and not C-INT.
Zork 1’s first use of these special, all-run, interrupts were used for the demons and monsters in the game. This term is very similar to “daemons” in operating systems where background processes can run on their own. However, Infocom’s use of the word “demon” came after “daemon” was already used by other programmers. It is probably just a happy coincidence.

No comments:

Post a Comment