Sunday, April 21, 2019

Chapter 16. Pardon the Interruptions (part 2)

16.6 Update: New INT and Interrupt Entry

All of the ZIP 1 and 2 games and most of the ZIP 3 games use the same INT routine as seen in Zork 1. CutthroatsHGTG, and Suspect used a slightly modified INT where DEMON pointer is omitted. The first major change was with AMFV where the size of the interrupt entry shrank to 2 words by removing the ENABLE flag. Now, each entry with non-zero TICKS is considered enabled. Most of the ZIP 3 games since LGOP also used this more compact interrupt entry structure. AMFV’s version also kept track of the most recent blank entry which would be used when storing a new entry instead of creating a new one. If new entry needs to be created beyond the limits of the table, an error message is displayed but the entry is still created.
LGOP also introduced a smarter INT routine that build off the one from AMFV. It would “remove” any entries at the top of the table with no more TICKS left by moving C-INT and skipping over these completed entries. Any entries with no more TICKS surrounded by entries that were still enable were not removed. This smarter INT also modified how interrupts during the game initialization are stored. It would convert their TICK numbers (by increasing it by 3 and making it negative) to label these interrupts so they would not be called until CLOCKER had already been executed. So, the first call of CLOCKER would convert these entries back to normal entries (converting the TICKS back to positive values and subtracting 3) and then be checked on the next call to CLOCKER. So, these initial interrupts would be bypassed at the start.
Only a few subsequent Infocom games modified their INT routines beyond what was mentioned. A few added special flag checks that would quit out of INT. Borderzone and Sherlock both used time more than ticks and had modified INT routines to reflect that.  

16.7 Update: New CLOCKER

CLOCKER also went through multiple modifications over successive games. The ZIP 2 version added the CLOCK-WAIT flag which skips checking any interrupts (including DEMON ones) when set and is cleared. This feature is only important for the WAIT command which already calls CLOCKER 3 times by default. Without this flag, the MAIN-LOOP will also call CLOCKER after WAIT is completed. So there would be 4 calls to CLOCKER for a WAIT command which is seen in ZIP 1. So the flag helps clear up that confusion.
Some CLOCKER routines (like in Deadline, Trinity, Bureaucracy, Borderzone, and Sherlock) would also increment separate time variables (seconds, minutes, hours, and possibly days) that the game would use for various situations. These routines (like in Deadline and Moonmist) check the number of turns or the elapsed time to see if the game should end prematurely or reset specific counters. Others like Wishbringer would check specific flags that would cause CLOCKER to execute routines of specific entries if their TICK values were below a certain threshold. Later versions (Suspended, Infidel, Enchanter, Zork1, Zork 2, Sorcerer, Moonmist, Ballyhoo, Mini-Zork) increase the number of turns in CLOCKER instead of MAIN-LOOP. The Witness, Seastalker, and Cutthroats return the first non-zero interrupt routine result. However, if one of the routine returns M-FATAL, then that will always be returned.
Planetfall corrected one logic error in the original INT routine regarding entries with negative TICKS. Previously, any entry with a negative TICK value would have its routine execute with the TICK value decreasing as well. So subsequent calls to this entry would make the TICK value more negative. Theoretically, that value could then flip into the positive range because of the way negative numbers are represented. Hexidecimal values of $0001 to $7FFF are positive (1 to 32767) while $8000 to $FFFF are negative (-32768 to -1). If a TICK value of $8000 (-32768) is decreased by 1 again, the new value $7FFF now is 32767 and will not be executed on the next CLOCKER cycle. Planetfall added a specific check for the $FFFF (-1) TICK value which would still execute the routine at the entry’s address but would not decrease the TICK value.
As mentioned before, AMFV uses the shortened 2 word entry. Its CLOCKER also will clear out the routine address of any entry that also has zero TICKS. This allows INT to use these blank entries for new entries. LGOP’s CLOCKER had the ability to “delete” entries that were at the top of the interrupt table. It would lower the starting point of the interrupt table to just past any blank or recently completed entries. If any blank or completed entries were below a still enabled entry, they could not be deleted. Stationfall and Sherlock could decreased TICKS/time value by a different amount for all entries with each call of CLOCKER. This allowed parts of the game to cause interrupts to be executed soon than expected as each call of CLOCKER will more rapidly drop the TICK counts.
HGTG still has the PARSER valid check to decide which pointer to use. If the PARSER fails, the entire table is checked (#00 as pointer) instead of from the newest entry.

16.8 Removing Interrupts with DEQUEUE

  • Arguments: Routine address
  • Return: TRUE if successful, FALSE if unable to find interrupt
DEQUEUE was introduced in LGOP and clears the routine address of the entry with that address. Any dequeued entry would eventually be “removed” by CLOCKER.

16.9 New Predicates for the Interrupts

  • ENABLED?
    • Arguments: Routine address
    • Return: TRUE if matching interrupt is enabled, FALSE if no match found or interrupt is not enabled
  • RUNNING?
    • Arguments: Routine address
    • Return: TRUE if matching interrupt has at least 1 TICK left, FALSE if no TICKS left or no match found
Two new predicates were added, ENABLED? And RUNNING? in Cutthroats to help find the status of specific interrupts. ENABLED? returned TRUE if the ENABLED word in a matching interrupt entry was set. RUNNING? returned TRUE if the TICKS in a matching entry was not zero, including -1. In LGOP, ENABLED? was changed as no ENABLED word exists in its entries. It would check the TICKS value and return TRUE if it was non-zero including -1. LGOP’s version of RUNNING? would return TRUE if the matching entry’s TICK value was 1 or -1. So only entries that will be executed on the next call of CLOCKER are considered running.

No comments:

Post a Comment