| ----------------------------------------------- | |
| A collection of Zip bugs and missing features | |
| ----------------------------------------------- | |
| (in the light of Graham Nelson's specification V0.2) | |
| Last update: 12-JAN-96 | |
| This is a collection of bugs, missing features and possible improvements | |
| for Mark Howell's Zip V2.0. Everything below, especially the term "Zip | |
| port", refers to this distribution only. The numerous ports which have | |
| evolved from this version may have less (or more) bugs. Nevertheless, I | |
| hope this list is useful for anyone involved in Z-code interpretation. | |
| Many thanks to the people who sent me bug reports. If you find a bug | |
| which is not covered by this list send it to | |
| Stefan Jokisch (jokisch@ls7.informatik.uni-dortmund.de) | |
| This is the third edition. All passages which have been modified or | |
| added in this edition are marked [3]. | |
| (i) MORE prompts | |
| -> If top_margin > 0 then the MORE prompt might appear before the | |
| output reaches the bottom line (e.g. beginning of 'Beyond Zork'). | |
| -> Even if top_margin = 0 the first MORE prompt in 'Curses' appears | |
| too early. | |
| -> When timeouts display some lines of text in the lower window, the | |
| MORE prompt is printed too late. | |
| I propose the following method: | |
| 1) The line counter is set to zero when the text window or the | |
| entire screen is erased. 2) The line counter is increased after | |
| every newline in the lower window. 3) The line counter is set to | |
| top_margin after read or read_char instructions (but only if these | |
| instructions weren't terminated by time-out). 4) The MORE prompt | |
| is printed when the line_counter equals the height of the lower | |
| window minus 1. The line counter is reset to top_margin. | |
| (ii) Fonts | |
| -> The runes in Beyond Zork are not displayed. | |
| -> Zip does not return 0 if a font is unavailable. | |
| -> Zip does not implement font 0 (ie. keep current font). | |
| [3] Font changes are allowed in the middle of a word so it is | |
| necessary to store font changes in a buffer. | |
| (iii) Beep sounds | |
| -> The DOS (V2.0) executable at ftp.gmd.de prints character 7 | |
| instead of playing a beep sound. | |
| -> The display_char routine in UNIXIO.C adds 1 to current_col | |
| whenever a beep sound is played. | |
| -> The sound routine in AMIGAIO.C plays two beep sound instead | |
| of playing a low-pitched beep sound (sound_effect #2). | |
| The sound routine in OSDEPEND.C passes beep sounds to the general | |
| character output routine which is not a very wise thing to do. | |
| Preferably, there shoud be an interface function called "beep" | |
| that plays a high- or low-pitched beep sound. | |
| (iv) Sample sounds | |
| -> The generic sound routine in OSDEPEND.C plays a beep sound when | |
| a sample should be played. It should better remain silent. | |
| -> The sound routine in AMIGAIO.C misunderstands the 4th argument | |
| of the sound instruction and implements it as fade-in/fade-out. | |
| -> The same routine ignores the volume information in V5 games. | |
| -> The same routine gets the sound file format completely wrong. | |
| -> The same routine seems to put the sounds in a queue (I think), | |
| but this is wrong. | |
| (Note: "The Lurking Horror" sometimes plays several sample sounds | |
| during a single turn. Even worse, it can happen that a "stop sound" | |
| instruction follows closely. This isn't a major problem if the game | |
| is played on an Amiga 500 by Infocom's own interpreter, since the | |
| interpreter is so slow that most of each sample will be played. On | |
| a fast machine, however, a click noise is all that can be heard. | |
| This is annoying because it means that roughly 200KB of sample data | |
| are simply wasted. One way to work around this is to put the sounds | |
| in a queue and play them one after another. However, this is not in | |
| line with the specification (and wouldn't work very well for | |
| "Sherlock"). I suggest the following fix: If a sound instruction is | |
| issued by the game, see if a sample sound has been started in the | |
| same turn (ie. since the player's last input). If so, wait until | |
| the sound finishes then execute the new sound instruction. Of | |
| course, if the current sound is looping forever you have to stop it | |
| sooner or later.) | |
| I suggest that Zip should not leave all the work to the interface. | |
| For instance, a generic sound routine could execute a sound_effect | |
| by calling one of several interface routines: beep, prepare_sample, | |
| start_sample, stop_sample and finish_with_sample. It could reject | |
| the sound instruction if the interface declared itself unable to | |
| play sample sounds. To some extent it could also handle the 4th | |
| parameter of sound_effect. | |
| Another thought: The end of a sample is usually signalled by a | |
| hardware interrupt. This means that the interpreter main loop may | |
| be called at any time, making life _much_ more interesting. This | |
| requires very careful coding, especially whenever global variables | |
| are used. | |
| (v) Encoding and tokenising | |
| -> Zip uses a list of word separators that apply to all Infocom games | |
| (in addition to the word separators from the game dictionary). This | |
| list is obsolete if not illegal. | |
| -> Alternate alphabets ("Zork 1 German") are copied to an array. This | |
| is slightly illegal since the specification claims that all tables | |
| can be altered. | |
| -> [3] The tokenise opcode ignores byte 1 of the text buffer (which | |
| holds the number of characters in V5+) and translates the text up | |
| to the first null character. | |
| Tokenising and encoding are implemented sloppily: the routines | |
| access the memory of the Z-machine directly (without using the | |
| memory access macros). Furthermore, the binary search on the | |
| dictionary is more complicated than it had to be. | |
| The text encoding routine could be simplified if one assumes | |
| that shift-lock characters do not occur in dictionary entries. | |
| (vi) Function keys | |
| -> The screen output gets into disorder when function keys are | |
| pressed which are not supported by the current game. | |
| -> Transscription does not work properly if function keys are | |
| used in "Beyond Zork". | |
| -> Transscription does not work properly when function keys are | |
| defined in "Beyond Zork" (ie. when the player types DEFINE). | |
| -> Under certain circumstances (to do with function keys), Zip | |
| does not lowercase the first part of the text buffer. | |
| Zip should check the terminating keys table before it stops the | |
| current input action. This task should not be left to the IO | |
| interface. For instance, Zip could offer a routine which can be | |
| used by the interface to check the terminating keys table. | |
| Transcription problem no 1 can only be solved by removing the last | |
| n characters from the transscript file whenever a read instruction | |
| starts with n characters in its text buffer. The one exception are | |
| read instructions that take place in the upper window (type DEFINE | |
| in "Beyond Zork"). These should not affect the transscript file at | |
| all (which would also solve transscription problem no 2). | |
| (vii) Cursor | |
| -> Some ports turn off the cursor in the upper window which leads | |
| to problems in "Bureaucracy", for instance. | |
| It has been suggested that the interpreter should never move the | |
| cursor in the lower window. Naturally, the interface would have | |
| to be more involved in implementing split_window, set_window and | |
| erase_window, but it would be easy to implement scroll-back. | |
| (viii) Buffer mode | |
| -> In V3, Zip turns off buffering in the upper window (correct | |
| behaviour is to use buffering all the time). | |
| -> In V4, Zip turns off buffering in the upper window (correct | |
| behaviour is to apply the buffer mode ("formatting", as Zip | |
| calls it) to both windows). | |
| -> In V5, Zip correctly turns off buffering in the upper window, | |
| but turns on buffering when it returns to the lower window | |
| (correct behaviour is to remember the previous state of buffer | |
| mode). | |
| (ix) Emergencies | |
| -> The Unix port crashes when the story file does not exist. | |
| The "fatal" routine should make sure the screen is initialised | |
| before it calls reset_screen (or reset_screen could check this | |
| condition itself). | |
| (x) Erasing windows | |
| -> The cursor does not move to the top left after erasing the upper | |
| window. | |
| -> The screen is not unsplit after erase_window -1. | |
| -> erase_window -2 is not implemented. | |
| -> In a V5 game, the cursor is moved to the upper window after erasing | |
| the lower window. | |
| Note that the erase_window opcode is specified in the "screen model" | |
| section of the specification. The brief description in the "dictionary | |
| of opcodes" is less precise. | |
| (xi) Input routine | |
| -> Some ports ignore the return key when the input buffer is full. | |
| -> Cursor and function keys confuse the input routine of UNIXIO.C. | |
| -> In some ports, the input can exceed the end of line when the input | |
| prompt is longer than ">". | |
| -> The input routine does not always read the correct number of | |
| characters (see the details about read in the specification). | |
| -> [3] In V5+ the interpreter should not add a null character to | |
| the text buffer. | |
| Similar to the tokenising and encoding routines, the input routine | |
| accesses the memory of the Z-machine directly (ignoring the memory | |
| access macros). A better design would use a local array. The | |
| interface would read the input into this array, and the interpreter | |
| would copy the contents of this array to the text-buffer of the | |
| read instruction afterwards. | |
| (xii) Colours and text styles | |
| -> The text style is reset to Roman (or "normal") when the lower | |
| window is selected but this is wrong. | |
| -> Some ports erase to the foreground colour in reverse mode. | |
| -> Some ports produce entirely reversed lines when the screen | |
| scrolls in reverse mode. | |
| -> Some ports think that reverse plus reverse is Roman. | |
| -> Some ports reset the text style when the colours change. | |
| -> Zip does not implement colour 0 (ie. keep same colour). | |
| -> The default colours are not written into the header. | |
| If an interface implements text styles via colour changes then it | |
| should keep track of the current text style and colours. If any of | |
| these are altered by the game, a routine should be called that | |
| calculates the new screen colours by combining the current text | |
| style and colours. | |
| (xiii) Time-outs | |
| -> The time argument of the read opcode is interpreted as the delay | |
| in seconds (although it gives the delay in 1/10s of a second). | |
| -> The time-out routine is called with one argument. | |
| -> The return value of the read opcode is -1 if it is terminated by | |
| a time-out (should be 0). | |
| -> The Amiga version gets confused when a timeout occurs while the | |
| cursor is placed in the middle of the input line. | |
| -> All Zip ports fail to redraw the input line when it is destroyed | |
| by a time-out routine. | |
| The last bug is not easily fixed. I suggest to use a flag that is | |
| cleared when the time-out routine is called and that is set when | |
| the game prints a newline to the lower window of the screen. After | |
| the time-out routine has finished, the interpreter can check the | |
| flag to find out whether the input line needs redrawing. | |
| The Amiga problem is even more serious. My own "DOS Frotz" rigidly | |
| ignores all time-outs and function keys unless the cursor is placed | |
| at the end of the input line. | |
| (xiv) Printing | |
| -> The fourth parameter of print_table is not supported. | |
| -> Character 0 does not behave as specified. | |
| -> Character 13 does not behave as specified. | |
| The decode_text routine reads bytes from the instruction stream | |
| without using the GET_CODE_*** macros. | |
| (xv) Flags | |
| -> The "boldface available" flag is called CONFIG_MAX_DATA and | |
| is completely misunderstood. | |
| -> The "fixed font available" flag is missing. | |
| -> The "variable-pitch font is default" flag is missing. | |
| -> The "timed keyboard input available" flag is missing (well, it was | |
| just recently defined). | |
| -> The "game wants to use mouse" flag is missing. | |
| -> The "screen-splitting available" flag is set in V4 and V5. | |
| It is tiresome to check the fixed font and scripting flags after | |
| every character printed. Since the only sensible way to change | |
| these flags is through the storeb and storew opcodes, it is easy | |
| to recognise changing flags immediately. The interpreter should | |
| react by (de-)selecting fixed-font or transscription respectively. | |
| [3] The specification claims that an interpreter should not change | |
| the state of the transscription flag. However, this policy does not | |
| work for AMFV so the specification will have to be corrected. | |
| (xvi) Catch and throw | |
| -> Throw does not work (the safety check at the start of the routine | |
| "unwind" prevents all _legal_ attempts to use the opcode). | |
| (xvii) Save and restore | |
| -> The optional parameters are not supported. | |
| -> Restore fails to initialise certain header fields which are marked | |
| "Rst" in the specification. | |
| -> [2] According to the specification, restore should not print any | |
| "file not found" messages. | |
| One suggestion to improve the save/restore routines is to use an | |
| OS independent format (ie. to store all 16 bit words in the order | |
| most significant/least significant byte). Another idea is to save | |
| only the differences between the current and the initial state of | |
| memory (thus saving a lot of disk space). | |
| Another suggestion is to include a command-line option for loading | |
| saved game files (like pinfocom does). | |
| (xviii) Token buffer overflow | |
| -> When too many words are typed, Zip reads (tbuf[0]+1) instead of | |
| (tbuf[0]) tokens... | |
| -> ...prints the "Too many words..." message more than once... | |
| -> ...and sets (tbuf[1]) to a value bigger than (tbuf[0]). | |
| (xix) UNDO | |
| -> Too much memory is allocated for UNDO (enough to save the entire | |
| resident or "static" memory). | |
| [2] It has been suggested that multiple UNDO should be possible. It | |
| is, and it is also possible to make this feature part of the core | |
| of Zip. Various strategies can be used to reduce the (huge) amount | |
| of memory required for multiple UNDO: see the comment on "Save and | |
| restore" for an idea. [3] However, these strategies tend to consume | |
| considerable CPU time, at least for slow machines. | |
| (xx) Mice | |
| -> Except for AMIGAIO.C, Zip does not offer mouse support. | |
| It would be good if the interpreter itself (ie. not the interface | |
| routines) would write the mouse position into the mouse data table. | |
| (xxi) European characters | |
| -> Currently only German characters are supported (all other | |
| character codes have been defined in the latest specification). | |
| -> Scripting does not work properly when European characters are | |
| used (this results from using the isprint macro). | |
| -> The MS-DOS port terminates the input when European characters | |
| are typed. | |
| -> European characters are not converted to lower case. | |
| -> European characters can confuse word-wrapping (once again, this | |
| results from using the isprint macro). | |
| -> There is a problem with interpreter number 6 and "Beyond Zork". | |
| Read the specification, section "format of the header", for more | |
| information about the "Beyond Zork" problem. | |
| Currently, the translation of European characters takes place very | |
| early in the output process. I suggest that this should happen at a | |
| low level. For instance, the interface itself can convert European | |
| character codes to appropriate characters of its own font just | |
| before it displays the codes on the screen. The transscription | |
| routines can replace European character codes with plain ASCII | |
| substitutes before they are written to the transscript file. And | |
| finally, if input recording is turned on then the codes can be | |
| converted to strings (eg. "[155]" or "\ae" for a-umlaut) before | |
| they are passed to the command file. | |
| (xxii) Missing opcodes | |
| -> GET_CURSOR is not implemented. | |
| -> NOP makes the interpreter crash. | |
| (xxiii) Byte-sized properties | |
| -> [3] Byte-sized properties are not supported by get_prop and put_prop | |
| in V4 and above. | |
| [3] Neither Infocom nor Inform games use byte-sized properties in V4 | |
| or V5 so this is not really serious. (Although V6 games use byte-sized | |
| properties they never apply get_prop or put_prop to these properties.) | |
| [3] Currently, the specification also defines the behaviour for | |
| properties that are longer than two bytes. | |
| (xxiv) Word-wrapping | |
| -> The generic fit_line routine counts style changes like normal | |
| characters (so lines with style changes are wrapped to early). | |
| -> The line wrapping algorithm is a strange mixture of fixed-width | |
| and proportional-width line splitting. Even if a proportional font | |
| is used, each line may contain only as many characters as a fixed | |
| font would allow. | |
| -> The proportional-width line splitting doesn't work if the buffer | |
| is flushed in the middle of the screen (eg. when the font changes). | |
| The word-wrapping algorithm is highly inefficient. I suggest to | |
| modify the algorithm such that it tries to collect a single word | |
| (instead of a complete line). Whenever a word is complete, an | |
| interface routine is called to calculate the width of this word in | |
| screen units. If it doesn't fit into the current line, a newline | |
| must be printed first. | |
| It has been suggested that hyphenated words could be split, too. | |
| (xxv) Random numbers | |
| -> The ANSI random function does not always produce proper random | |
| numbers. | |
| It would be helpful if some-one could write a generic random number | |
| generator. Also note that Zip does not support the predictable mode | |
| which is suggested by the specification. | |
| (xxvi) Newlines | |
| -> The Unix port calculates a wrong cursor position when newline | |
| characters are printed in the upper window. | |
| -> When function keys are used, "Beyond Zork" sometimes prints | |
| newlines to the lower window while buffering is turned off. | |
| Zip does not handle this situation properly and passes the | |
| newline to display_char instead of scroll_line. | |
| To fix the latter bug, it suffices to move the (formatting == ON) | |
| condition to the next if-clause in output_new_line. | |
| (xxvii) Recording and playback | |
| -> Zip cannot handle function keys, mouse clicks or timeouts when | |
| input recording is turned on. | |
| -> Zip does not implement "input_stream 0" ie. every command file | |
| is played up to the end-of-file. | |
| The behaviour described in the specification (ignore input lines | |
| unless they are terminated by the return key) is quite helpful. | |
| However, to cope with games like "Beyond Zork" and "Border Zone" | |
| it would be ideal to store all input lines plus their terminating | |
| characters (plus coordinates for mouse clicks) in a command file. | |
| (xxviii) Screen dimensions | |
| -> [2] Zip sets certain header values to the left, right, top and | |
| bottom coordinates. These values should actually be written with | |
| the screen width and height in units. | |
| (xxix) The Beyond Zork fix | |
| -> [2] A bug in "Beyond Zork" can cause serious trouble. Some Zip | |
| ports might crash close to the end of the game. | |
| When the player types "BLOW CIRCLET", the game tries to | |
| get_prop_addr 41460... (or similar) | |
| which results from confusing the address of the dictionary entry | |
| "circlet" with the object id of "circlet". The interpreter may | |
| simply return 0, but it should not crash. | |
| (xxx) copy_table | |
| -> [3] If the 3rd argument of copy_table is positive then the | |
| opcode behaves like the memmove function. Zip should copy | |
| forwards if source > dest, and it should copy backwards if | |
| source < dest. (This feature wasn't used until Journey.) | |
| (xxxi) Arithmetic | |
| -> [3] The div opcode should perform a signed division (contrary | |
| to the specification)! Otherwise the microwave oven in "Lurking | |
| Horror" behaves strangely: If its timer is set to x minutes | |
| plus y <> 0 seconds (eg. 2:30 or 0:01) then it turns all food | |
| radioactive! | |
| -> [3] Similarly, mod is the remainder after signed division. | |
| (xxxii) More thoughts | |
| As G. Nelson says, it is very helpful to name routines internally | |
| after the opcodes in the specification. In my source code I added | |
| the prefix "z_" to these routine names which makes it very easy | |
| to distinguish opcode implementations from other routines. This | |
| can be pushed even further by adding the prefix "os_" to the names | |
| of all OS depending interface routines. | |
| To make Zip faster, one should consider removing the virtual memory | |
| system. This can result in a Zip version which is twice as fast as | |
| the original. However, the gain depends very much on the OS and the | |
| machine Zip runs on. Basically, fast machines spend most of the CPU | |
| time with output operations, whereas slow machines need a lot of | |
| time for the thinking process. Naturally, small machines may still | |
| require virtual memory as the story files grow in size. | |
| [2] The memory management is not very clever. Zip allocates as much | |
| memory as possible to load the story file. In some cases, this does | |
| not leave sufficient memory for opening files, so saving the game | |
| becomes impossible. | |
| [2] The specification asks interpreter writers to make the addition | |
| of new characters, fonts and opcodes easy. | |
| [2] See also Graham Nelson's notes on error messages (appendix A). | |
Xet Storage Details
- Size:
- 20.6 kB
- Xet hash:
- a23d2e7657d1ff8be5906cc86640fc4052873783c0faee423b553e17822b74f3
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.