                               Zoom - A Z-Machine
                               ----// 8888 \\----

by Andrew Hunter

   Zoom was originally designed as an experiment into writing fast
   bytecode interpreters, just over 3 years ago (first checkin into
   the CVS repository was on 19/11/2000). Last time I timed it, it
   could run up to around 2x faster than frotz, depending on what
   code was being executed. It's changed a lot since then, as the
   emphasis is now on designing a good user interface for the
   interpreter. As I changed my main machine from a Linux PC to a
   PowerMac about a year ago, and for another reason that will be
   revealed later in the year, the recent development focus has been
   on producing a Cocoa interface.

   Send any bug reports to andrew@logicalshift.co.uk, and anything
   else to andrew@logicalshift.co.uk. Zoom's homepage can be
   located at http://www.logicalshift.co.uk/unix/zoom/

   You can get the latest version of the source code at any time
   using git:

      git clone http://code.logicalshift.co.uk/zoom/zoom.git
      git submodule init
      git submodule update
   
Versions
========

   This is version 1.1.5 of Zoom. As the components have gained an 
   increasing tendency to change at different rates, I've given them 
   separate versions:
   
   Zoom interpreter core: 1.1.5
   X11 interface: 1.1.5
   Windows interface: 0.9.99beta5
   Carbon interface: 1.0.1
   Cocoa interface: 2.1.5
   ZoomPlugins: 1.1.5
   Spotlight importer: 1.5
   QuickLook plugin: 1.1.5
   CocoaGlk version: 1.0.8
   Glulxe version: 0.4.7
   
   Note that Zoom's display system underwent a fairly major change
   between 1.0.0 and 1.0.1 due to the addition of version 6 support. This
   means that the Windows version of Zoom will not compile any longer.
   The 2.0 version of the Cocoa interface refers to the version number
   displayed in the About box for the Macintosh version of Zoom.

Compiling Zoom
==============

   (See later for Cocoa)

   To get a full-featured interpreter under X windows or Carbon, 
   you'll want to have at least libpng installed. For X, having an
   X server with Xrender and Xft is a good idea. T1Lib is optional, 
   but appears to provide improved rendering quality over Xft (which
   seems to have great difficulties with hinting).

   With all the libraries installed, ./configure && make should be
   all that's required to build Zoom for either X or Mac OS X (Carbon).
   Please report any build problems to the bugs address listed 
   above: Zoom should work on a reasonably wide variety of systems.
   
   You can also build the Carbon version of Zoom by opening the
   Zoom.pbproj file in Project Builder. The Cocoa version of Zoom has
   to be built with XCode, using the ZoomCocoa.xcode file. Building
   with project builder may cause problems with yacc generating corrupt 
   files, producing many spurious errors (XCode does not appear to have
   these problems). If this occurs, delete the relevant files from
   the build/Zoom.build/DerivedSources directory.

   You may want to try the --enable-new-terp option to configure:
   this enables an experimental new interpreter design. This gives
   gcc's optimiser a much easier time of things (improving compile
   times considerably). This style of interpreter produces a slight
   (almost insignificant) performance hit.

   I've experienced problems with gcc 3's optimiser: specifically,
   it reduces the speed of some instructions by a factor of up to
   6 (0OPs, usually, for some reason - see the results of NopMark in 
   zmark). No idea why at all - this effect is most marked with the
   new interpreter style. Note that compiling the interpreter can be
   a great workout for your system; on my Mac OS X machine, it needs
   nearly 1Gb of memory to compile successfully with gcc 3.3. 
   Earlier versions of gcc require much less memory; I have 
   successfully compiled under x86 linux on a machine with 32Mb
   of memory and gcc 2.95; the PPC version of gcc seems to require
   much more memory for optimisation than the standard version, too.
   Regardless, at least 256Mb of memory is recommended for those
   that wish to compile Zoom.

Compiling Zoom (Cocoa)
======================

   Before compiling, you will need to have a version of the expat
   library unpacked into the directory containing the Zoom source
   directory. Use a symbolic link, or rename this directory 'expat'.
   Currently, the Zoom binaries link against expat 1.95.8: later
   versions may also work. (Zoom is statically linked against expat
   as it was an optional install on versions of OS X prior to 10.3)

   You will also need the babel utility and the Sparkle library in the
   directory containing the Zoom source code.

   Finally, you will need the dependencies in the depend directory: this 
   means CocoaGlk at the moment. The easiest way to get these is to use git:
   in the depends directory type this:

      git clone http://code.logicalshift.co.uk/zoom/cocoaglk.git CocoaGlk
   
   If you want to use the versions that correspond to a specific release, 
   use git checkout with the version number. For example, git checkout 
   v1.1.5. Note that CocoaGlk requires you to download versions of the
   git and glulxe interpreters to compile it.

   Rather than use the configure/automake process for this, I have
   elected to use Xcode: to compile the new version of Zoom, load the
   ZoomCocoa.xcode project into Xcode and hit build. Note that Xcode
   seems to have a hard time running Yacc; if the build fails with
   many weird errors, you will have to manually delete the relevant
   DerivedSources (hidden in build/ZoomCocoa.build/ZoomServer.build). 
   There also seems to be an occasional problem with precompiled headers 
   that will produce similar problems (errors in Carbon headers this 
   time), but simply restarting the build process will fix this - you 
   are much more likely to see problems with building the Yacc sources.

Using Zoom (X-Windows)
======================

   Running Zoom is fairly simple - zoom <filename> should do the
   trick, where <filename> is a Z-Code file (modern files created by
   Inform tend to have a .z5 or .z8 filename, original Infocom files
   tend to have a .dat or .zip filename - note that the .zip stands
   for 'Z-Code Interpreter', not PKZip as you might think). Assuming
   you haven't tinkered with zmachine.h, Zoom will support Z-Code
   versions 3, 4, 5, 6, 7 and 8. As of version 1.0.1beta1, version
   6 is fully supported, as is the Standard v1.1draft6. Unicode
   support is also much improved from this release.

   Zoom needs a configuration file called '.zoomrc' in your home
   directory. This file tells Zoom about the colours and fonts it
   should use, and also identifies game titles. It consists of many
   entries of the form 'game "mygame" 12.345678 { options }', where
   'mygame' is the text that appears in the title bar, and { options } 
   is the options for this game (in curly brackets). You can omit the
   options altogether if you want the options to be the same as the
   default. There is also exactly one default block, which has the
   form 'default "%s (%i.%.6s)" { options }'. The default block must
   define the interpreter number and revision, at least 4 fonts, and
   the default set of colours:

      # Standard settings - applies to all interpreters
      # (Note, font 4 must always be fixed-pitch)
      default "%s (%i.%.6s)"
      {
        interpreter 1
        revision    Z
      
        font 1 "-*-helvetica-medium-r-*-*-14-*-*-*-*-*-*-*" roman
        font 2 "-*-helvetica-bold-r-*-*-14-*-*-*-*-*-*-*"   bold
        font 3 "-*-helvetica-medium-o-*-*-14-*-*-*-*-*-*-*" italic
        font 4 "-*-courier-medium-r-*-*-14-*-*-*-*-*-*-*"   fixed
        font 5 "-*-courier-bold-r-*-*-14-*-*-*-*-*-*-*"     fixed-bold
        font 6 "-*-courier-medium-o-*-*-14-*-*-*-*-*-*-*"   fixed-italic
        font 7 "-*-courier-bold-o-*-*-14-*-*-*-*-*-*-*"     fixed-bold-italic
        font 8 "-*-helvetica-bold-o-*-*-14-*-*-*-*-*-*-*"   bold-italic
        font 9 "font3"                                      symbolic
      
        colours (0,0,0), (255,0,0), (0,255,0), (255,255,0), (0,0,255),
                (255,0,255), (0,255,255), (255,255,204),
      	        # These are the colours provided by DOS interpreters
      	        (187, 187, 187), (136, 136, 136), (68, 68, 68)

        size 80, 30

        gamedir "/usr/local/share/games/zcode"
        savedir "/home/me/zcode/save"
      }

   This illustrates most of the available options. Unless you have a
   particular reason for setting the interpreter number and revision, 
   the values given should do a reasonable job (version 6 games, and
   Beyond Zork benefit from setting the interpreter number).

   Fonts are defined using statements of the form:

      font <num> "<name>" <style>

   Where <num> specifies the font number (note that font 4 *must* be
   a fixed-pitch font), and <style> is a list of style attributes
   (attributes can be seperated by commas). A style attribute is one
   of 'roman', 'bold', 'italic', 'fixed' or 'symbolic', or a combination
   made by combining attributes with hyphens, for example 'fixed-bold'. If
   you don't specify a font with a given combination of style
   attributes, Zoom will use font 1 (for normal text) or font 4 (for
   fixed pitch text). You can work out font names with the aid of
   xfontsel. The special font name 'font3' refers to the built-in
   symbolic font.

   Colours are defined using a list of values of the form (R, G,
   B). There should be at least 8 colours, coresponding to the
   Infocom colour scheme, and there can be up to 11 (corresponding
   to the colour scheme allowed by various interpreter versions):

      0  = black
      1  = red
      2  = green
      3  = yellow
      4  = blue
      5  = magenta
      6	 = cyan
      7  = white
   (I prefer cream to white, but that's a matter of personal preference)
      8  = light grey (amiga) dark grey (DOS)
      9  = medium grey (amiga)
      10 = dark grey (amiga)

   Colour 0 is the first specified in the list, and the rest follow
   in sequence.

   size <n>, <m> sets the size of the display to (n x m)
   characters. These characters are the size of the fixed-pitch font
   (font 4). For a version 6 game, the graphics file can be specified
   using a command like

      resources "/home/me/infocom/zorkzero/ZORK0/ZORK0.blb"

   Presently only Blorb graphics files are supported. While I'm aware
   of Blorb 1.1, I am somewhat hesitant to include JPEG support until 
   the current patent unpleasentness is resolved.

   Zoom will load sound effects, but won't really do anything with them
   at the moment.

   If Zoom is run without a game specified, it will try to look in the
   directory specified by 'gamedir', and list all of the games it finds
   in there (games are assumed to be in files with a .z? extension) as
   a menu, giving a choice as to which one to run. 'savedir' gives the
   default directory for saving and restoring files.

   Each game can be specified with a block like this, or the block
   can be omitted. If you do specify a block for a game, the options
   there override the defaults. For example:

      game "Planetfall (Solid Gold edition)" 10.880531

   might have no options, so it runs as the default - but,

       game "Beyond Zork" 47.870915, 49.870917, 51.870923, 57.871221
       {
         interpreter 5
         revision    Z

	 savedir "/home/me/zcode/beyond"
       }

   specifies that Beyond Zork requires a different intepreter number
   and should save its games into '/home/me/zcode/beyond' by default
   - note that you can also specify different colour schemes and
   fonts here (with fonts you can give a partial specification, so
   just adding, say, 'font 1 "-weird-font" roman' would cause Zoom to 
   use that font instead of the default font 1, but keep the defaults 
   for the rest)

   An example .zoomrc file can be found in the source distribution as 
   'zoomrc'

Xft problems
============

   XRender and Xft might well provide nice anti-aliased fonts, but 
   unfortunately, Xft doesn't provide any 'font not found' mechanism,
   instead choosing a default font. When Xft is not setup, in particular
   when there is no XftConfig file, this can result in the same font
   being used for anything. The troubleshooting section of the manual
   describes how to create an XftConfig file. The poor man's solution
   is simply to specify 'antialias no' in .zoomrc.

Zoom and Windows
================

  *** THE WINDOWS DRIVER IS DEAD ***

  Feel free to volunteer to resurrect it...

  This version of Zoom now supports windows through the mingw32 library
  (and to a lesser extent with Borland's C compiler). The windows display
  library is a complete rewrite, and uses the techniques I discussed in
  an raif posting to allow the window to be resized while a game is running.
  These techniques are not without their penalty, however, and this version
  of Zoom cannot be compiled with v6 support, and tends to redraw some
  displays much slower than the X version. It also starts v3 games in the
  top left of the screen instead of the bottom left, which is strictly
  speaking against the standard, but saves me some fiddling.

  The zoomrc has the format as described above. A windows font is described
  using a string such as:

    'Arial' 10 b

  Which is Arial, 10pt, bold. The font name MUST be in single quotes ('). The
  optional specifiers at the end can be left out entirely, or can be a
  combination of:

    b - bold
    B - extra bold
    i - italic
    u - underline
    f - fixed-pitch
  
  The font specifiers in the example could then be:

        font 1 "'Arial' 10"           roman
        font 2 "'Arial' 10 b"         bold
        font 3 "'Arial' 10 i"         italic
        font 4 "'Courier New' 10 f"   fixed
        font 5 "'Courier New' 10 fb"  fixed-bold
        font 6 "'Courier New' 10 fi"  fixed-italic
        font 7 "'Courier New' 10 fbi" fixed-bold-italic
        font 8 "'Arial' bi"           bold-italic
        font 9 "font3"                symbolic

  (Note that the special font name 'font3' still specifies the built-in
  version of font 3)

Zoom and Mac OS X
=================

   This has been variously fixed and updated, along with everything else.
   The Quartz renderer is to be preferred, for the higher-quality
   rendering and best support for Unicode. Version 6 games (and blorb)
   are supported in this port: images are *always* rendered using
   Quartz.

Why it goes fast
================

   Specialisation is a formal name for an optimisation strategy that
   removes layers of interpretation from a program. A classic example
   is the standard C printf statement:

     printf("Foo %i bar %s baz\n", i, s);

   The usual technique is for the runtime system to parse the string
   and insert the values of i and s appropriately. A specialised
   version of this statement would note that the format string is
   /constant/, and never changes, so a specialised version of that
   printf statement would evaluate the format string at compile time
   and output simpler routines that just print 'Foo ', then i, then '
   bar ', followed by s and ' baz\n'.

   Zoom's particular specialisation is to note that many of the
   bytecodes can only appear in limited combinations, so instead of
   testing for the opcodes and then working out what the various bits
   mean, Zoom simply tests for all possible values in an enormous
   switch statement (or three) - this process is applied to the
   opcodes themselves and their arguments. In addition, various
   opcodes can be 'branch' opcodes or 'store' opcodes, etc. A custom
   decoding routine is written for each and every opcode.

   This would obviously take rather a long time to do by hand, but the 
   code is actually generated automatically by a helper program
   (general purpose programs like this are known as 'partial
   evaluators', but I'm not sure if the name applies to helper
   programs such as the one used here). The code has to be generated
   individually for different ZCode versions, and a consequence of
   this is the rather large size of the Zoom executable (it's strongly 
   recommended you strip it to get rid of the excessively large
   debugging tables :-)

   Note that Zoom also makes extensive use of function inlining
   (supported in C by gcc) to give an extra speed boost (actually
   only an extra few %). If you compile in support for many Z-Code
   versions, you may find that turning it off (by uncommenting the
   '#define inline' in interp.c) will decrease the executable size
   somewhat - as a side note, Zoom also depends on the compiler having
   a few sensible optimisation strategies for its speed. Turning
   optimisation off is probably never a good idea, as (under gcc under 
   x86 Linux) that increases the size of the executable and gives
   quite a performance hit on those switch statements.

   That's actually about it. Zoom is probably actually slower than
   frotz in a few areas, I'll get round to writing a few benchmarks to 
   check them out and see what can be improved.

Z-Machine extensions
====================

   Zoom provides a selection of extensions to the Z-Machine:

start_timer (EXT:128) (no arguments, neither branch nor store)
    This makes a note of the time this instruction is used. Normally this
    will be the CPU clock time returned by clock().

stop_timer (EXT:129)
    This makes a note of the time this instruction is used, storing it
    seperately from the time marked by start_timer.

read_timer (EXT:130) (store)
    This stores the difference between the start time and end time, as
    defined by using the instruction above, in the variable. This time is
    in centiseconds.

print_timer (EXT:131)
    This displays the difference between the start time and end time as a
    decimal number of seconds, to centisecond precision.

   If you want to add your own extensions for some nefarious purpose, all
   opcodes are defined in src/zcode.ops, with definitions like:

OPCODE "print_unicode" EXT:0x0b ARGS:1        VERSION 5,7,8
%{
  stream_printc(argblock.arg[0]);
%}

   Extended ops are only available in v4+ games and the Z-Machine
   specification suggests that you only use extended opcodes that are greater
   than 0x80 to add new instructions.
