]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
fixing packages.el
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 17 Apr 2014 08:32:58 +0000 (10:32 +0200)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Thu, 17 Apr 2014 08:32:58 +0000 (10:32 +0200)
adding malyon

Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
custom.el
elpa/archives/gnu/archive-contents
elpa/archives/marmalade/archive-contents
init.el
malyon.el [new file with mode: 0644]
malyon.elc [new file with mode: 0644]

index cc32ada7919133011dd445830a9f8398a1659294..4d957f1f9e8afeaacfaf53f16f7dd7156322820a 100644 (file)
--- a/custom.el
+++ b/custom.el
@@ -9,7 +9,7 @@
  '(magit-commit-signoff t)
  '(magit-repo-dirs (quote ("~/git")))
  '(org-agenda-files (quote ("~/org/emacs.org" "~/org/work.org" "~/org/private.org" "~/org/it.org")))
- '(package-archives (quote (("gnu" . "http://elpa.gnu.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("melpa" . "http://melpa.milkbox.net/packages"))))
+ '(package-archives (quote (("gnu" . "http://elpa.gnu.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("melpa" . "http://melpa.milkbox.net/packages/"))))
  '(package-user-dir "~/.xemacs/elpa")
  '(recentf-max-saved-items 42)
  '(savehist-mode t nil (savehist))
index 87dadb146a29e1f09dfd7bf60a8f0f3a460e1eac..9fd5de5a2ae7cd654b371a02cf82b9820fbb4273 100644 (file)
        ((:url . "http://elpa.gnu.org/packages/all.html")
        (:keywords "matching"))])
  (ascii-art-to-unicode .
-                      [(1 4)
+                      [(1 5)
                        nil "a small artist adjunct" single
                        ((:url . "http://elpa.gnu.org/packages/ascii-art-to-unicode.html")
                         (:keywords))])
  (auctex .
-        [(11 87 3)
+        [(11 87 4)
          nil "Integrated environment for *TeX*" tar
          ((:url . "http://www.gnu.org/software/auctex/"))])
  (cl-lib .
           nil "Info-like viewer for DocBook" single
           ((:url . "http://elpa.gnu.org/packages/docbook.html")
            (:keywords "docs" "help"))])
+ (easy-kill .
+           [(0 9 2)
+            ((emacs
+              (24))
+             (cl-lib
+              (0 5)))
+            "kill & mark things easily" single
+            ((:url . "https://github.com/leoliu/easy-kill")
+             (:keywords "killing" "convenience"))])
  (ediprolog .
            [(1 0)
             nil "Emacs Does Interactive Prolog" single
           ((:url . "http://elpa.gnu.org/packages/flylisp.html")
            (:keywords))])
  (ggtags .
-        [(0 8 0)
+        [(0 8 3)
          ((emacs
            (24))
           (cl-lib
           (:keywords "tools" "convenience"))])
  (gnugo .
        [(2 3 1)
-        nil "play GNU Go in a buffer" tar
+        ((ascii-art-to-unicode
+          (1 5)))
+        "play GNU Go in a buffer" tar
         ((:url . "http://elpa.gnu.org/packages/gnugo.html"))])
  (heap .
        [(0 3)
            ((:url . "http://elpa.gnu.org/packages/omn-mode.html")
             (:keywords))])
  (org .
-      [(20140324)
+      [(20140414)
        nil "Outline-based notes management and organizer" tar nil])
  (quarter-plane .
                [(0 1)
index 8fdce9a9bb9647619a5d446547db4ccb327a831f..96ca44c6a017735446d1adf80432e3f5fdfcca51 100644 (file)
@@ -1 +1 @@
-(1(haml-mode .[(3 1 5)((ruby-mode (1 0)))"Major mode for editing Haml files"single])(paredit .[(22)()"minor mode for editing parentheses  -*- Mode: Emacs-Lisp -*-"single])(perspective .[(1 9)()"switch between named \"perspectives-theme .[(1 0 35)()"Ujelly theme for GNU Emacs 24 (deftheme)"single])(zen-and-art-theme .[(1 0 1)()"zen and art color theme for GNU Emacs 24"single])(git-auto-commit-mode .[(4 2 2)()"Emacs Minor mode to automatically commit and push"single])(haste .[(1)((json (1 2)))"Emacs client for hastebin (http://hastebin.com/about.md)"single])(my-packages .[(0 1 0)()"Package Initialization."single])(zen-mode .[(20120627)()"remove/restore Emacs distractions quickly"tar])(heroku .[(1 1 0)()"Interface to Heroku apps."single])(php-extras .[(2 1 1 20140312)((php-mode (1 5 0)))"Extra features for `php-mode'"tar])(repository-te SPARQL queries."tar])(puppet-mode .[(0 3)((emacs (24 1)) (cl-lib (0 5)) (pkg-info (0 4)))"Major mode for Puppet manifests  -*- lexical-binding: t; -*-"single])(epl .[(0 7)((cl-lib (0 3)))"Emacs Package Library -*- lexical-binding: t; -*-"single]))
\ No newline at end of file
+(1(haml-mode .[(3 1 5)((ruby-mode (1 0)))"Major mode for editing Haml files"single])(paredit .[(22)()"minor mode for editing parentheses  -*- Mode: Emacs-Lisp -*-"single])(perspective .[(1 9)()"switch between named \"perspectives\" of the editor"single])(slime .[(20100404 1)()&quonbsp;24 (deftheme)"single])(zen-and-art-theme .[(1 0 1)()"zen and art color theme for GNU Emacs 24"single])(git-auto-commit-mode .[(4 2 2)()"Emacs Minor mode to automatically commit and push"single])(haste .[(1)((json (1 2)))"Emacs client for hastebin (http://hastebin.com/about.md)"single])(my-packages .[(0 1 0)()"Package Initialization."single])(zen-mode .[(20120627)()"remove/restore Emacs distractions quickly"tar])(heroku .[(1 1 0)()"Interface to Heroku apps."single])(repository-root .[(1 0 4)()"deduce the repository root directory for a given file"single])(grep-a-lot .[(1 0 7)()"manages multiple search results&nbinnish national and Christian holidays for calendar"tar])(iregister .[(0 2 1)()"Interactive register commands for Emacs."single])(flycheck-haskell .[(0 4)((flycheck (0 16)) (haskell-mode (13 7)) (dash (2 4 0)) (f (0 11 0)))"Flycheck: Cabal projects and sandboxes"tar]))
\ No newline at end of file
diff --git a/init.el b/init.el
index 8c01ebcc83b879a65f97f4a04b5bf404f481a109..378bc45d3fdb7a7296a6e9a9324ce0b8f2ab8fbf 100644 (file)
--- a/init.el
+++ b/init.el
@@ -223,6 +223,11 @@ load-path))
 ;; (require 'bbdb)
 ;; (bbdb-initialize)
 
+;;; * Malyon
+;; http://www.emacswiki.org/emacs/MalyonMode
+
+(require 'malyon)
+
 ;;; * TemplateToolkit
 
 (add-to-list 'auto-mode-alist '("\\.tt2$" . html-mode))
diff --git a/malyon.el b/malyon.el
new file mode 100644 (file)
index 0000000..03ef759
--- /dev/null
+++ b/malyon.el
@@ -0,0 +1,3236 @@
+; malyon.el --- mode to execute z code files version 3, 5, 8
+
+;; Maintainer: Peter Ilberg <peter.ilbergNOSPAMgmail.com>
+;; (I am unable to continue supporting malyon.el. Please send me an
+;;  email if you are interested in taking over the project. Thanks.)
+
+;; Copyright (C) 1999-2011 Peter Ilberg
+
+;; Permission is hereby granted, free of charge, to any person obtaining a
+;; copy of this software and associated documentation files (the "Software"),
+;; to deal in the Software without restriction, including without limitation
+;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
+;; and/or sell copies of the Software, and to permit persons to whom the
+;; Software is furnished to do so, subject to the following conditions:
+
+;; The above copyright notice and this permission notice shall be included in
+;; all copies or substantial portions of the Software.
+
+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;; DEALINGS IN THE SOFTWARE.
+
+;;; Credits:
+
+;;   The author would like to thank the following people for reporting
+;;   bugs, testing, suggesting and/or contributing improvements:
+;;     Bernhard Barde, Jonathan Craven, Alberto Petrofsky, Alan Shutko
+
+;;; Commentary:
+
+;; This package provides a basic interpreter for version 3, 5, 8 z code
+;; story files as generated by Inform (C) Graham Nelson and Infocom.
+
+;; If you encounter a bug please send a report to Peter Ilberg at 
+;; peter.ilbergNOSPAMgmail.com. Thank you!
+
+;; To play a story file simple type M-x malyon and enter the path to the
+;; story file. If anything goes wrong and you want to manually clean
+;; up type M-x malyon-quit. In addition, you can switch back to a game in
+;; progress by typing M-x malyon-restore.
+
+;; A note on the format of saved game states:
+
+;; As of version 1.0, Malyon supports the quetzal file format for saved 
+;; games. Support for this format required changes to several internal
+;; data structures (stack frames and catch-throw) that are incompatible
+;; with the old implementation. Unfortunately, the old file format for
+;; saved games cannot be converted into quetzal.
+
+;; For backwards compatibility, however, Malyon still supports the old
+;; file format. And you can continue to play your old game states.
+
+;; Because of the incompatibility of the two file formats, Malyon now 
+;; runs, as follows, in either of two modes: quetzal and compatibility.
+
+;; - in quetzal mode, game states are saved in quetzal format
+;; - in compatibility mode, games states are saved in the old format
+;; - loading a game state in quetzal format switches to quetzal mode
+;; - loading an old game state switches to compatibility mode
+;; - quetzal mode is the default setting
+
+;; In other words, Malyon will only use the old file format if you've
+;; restored a game state saved in the old file format.
+
+;; Enjoy!
+
+;;; Code:
+
+;; global variables - moved here to appease the byte-code compiler
+
+;; story file information
+
+(defvar malyon-story-file-name nil
+  "The name of the story file being executed.")
+
+(defvar malyon-story-file nil
+  "The story file which is currently being run.")
+
+(defvar malyon-story-version nil
+  "The story file version.")
+
+(defvar malyon-supported-versions '(3 5 8)
+  "A list of supported story file versions.")
+
+;; status and transcript buffers
+
+(defvar malyon-transcript-buffer nil
+  "The main transcript buffer of the story file execution.")
+
+(defvar malyon-transcript-buffer-buffered nil
+  "Is output in the transcript buffer buffered?")
+
+(defvar malyon-status-buffer nil
+  "The status bar buffer of the story file execution.")
+
+(defvar malyon-status-buffer-lines nil
+  "The number of lines in the status bar buffer.")
+
+(defvar malyon-status-buffer-delayed-split nil
+  "If the number of lines in the status buffer is reduced,
+the window configuration is not changed immediately. It
+is changed after the next turn (read or read_char).")
+
+(defvar malyon-status-buffer-point nil
+  "The point location in the status bar buffer.")
+
+(defvar malyon-max-column 72
+  "Maximum column for text display.")
+
+;; window management
+
+(defvar malyon-window-configuration nil
+  "The current window configuration of the malyon interpreter.")
+
+(defvar malyon-current-window nil
+  "The currently active window for text output.")
+
+;; z machine registers
+
+(defvar malyon-stack nil
+  "The stack of the z machine.")
+
+(defvar malyon-stack-pointer nil
+  "The stack pointer of the z machine.")
+
+(defvar malyon-frame-pointer nil
+  "The frame pointer of the z machine.")
+
+(defvar malyon-instruction-pointer nil
+  "The instruction pointer of the z machine.")
+
+;; game file related global variables
+
+(defvar malyon-score-game nil
+  "A flag indicating whether this story uses score or time.")
+
+(defvar malyon-packed-multiplier nil
+  "The amount by which packed addresses are multiplied to get byte
+addresses.")
+
+(defvar malyon-global-variables nil
+  "A pointer to the global variable section in the story file.")
+
+(defvar malyon-abbreviations nil
+  "A pointer to the abbreviations in the story file.")
+
+(defvar malyon-alphabet nil
+  "The z machine's text alphabet.")
+
+(defvar malyon-whitespace nil
+  "A string of whitespace characters recognized by the interpreter.")
+
+;; object tables
+
+(defvar malyon-object-table nil
+  "A pointer to the object table in the story file.")
+
+(defvar malyon-object-table-entry-size nil
+  "The size of one entry in the object table.")
+
+(defvar malyon-object-properties nil
+  "The number of properties per object minus one.")
+
+(defvar malyon-object-property-offset nil
+  "The byte offset of the properties table in the object.")
+
+;; dictionaries
+
+(defvar malyon-dictionary nil
+  "A pointer to the dictionary of the story file.")
+
+(defvar malyon-dictionary-entry-length nil
+  "The length of a dictionary entry.")
+
+(defvar malyon-dictionary-num-entries nil
+  "The number of dictionary entries.")
+
+(defvar malyon-dictionary-entries nil
+  "A pointer to the first dictionary entry.")
+
+(defvar malyon-dictionary-word-length nil
+  "The length of a dictionary word.")
+
+;; game state information
+
+(defvar malyon-game-state-restart nil
+  "The machine state for implementing restart.")
+
+(defvar malyon-game-state-undo nil
+  "The machine state for implementing undo.")
+
+(defvar malyon-game-state-quetzal t
+  "Store game state information for quetzal.")
+
+;; various
+
+(defvar malyon-current-face nil
+  "The current face in which to display text.")
+
+(defvar malyon-last-cursor-position-after-input nil
+  "The last cursor position after reading input from the keyboard.")
+
+;; interactive functions
+
+(defun malyon (file-name)
+  "Major mode for playing z3/5/8 story files.
+This mode allows execution of version 3, 5, 8 z code story files."
+  (interactive "fStory file name: ")
+  (if malyon-story-file
+      (message "You are already playing a game.")
+    (if (not (string-match ".*\.z[358]$" file-name))
+       (message "%s is not a version 3, 5, or 8 story file." file-name)
+      (condition-case nil
+         (malyon-load-story-file file-name)
+       (error
+        (malyon-fatal-error "loading of story file failed.")))
+      (setq malyon-story-version (aref malyon-story-file 0))
+      (cond ((memq malyon-story-version malyon-supported-versions)
+            (condition-case nil
+                (malyon-initialize)
+              (error
+               (malyon-fatal-error "initialization of interpreter failed.")))
+            (malyon-interpreter))
+           (t
+            (message "%s is not a version 3, 5, or 8 story file." file-name)
+            (malyon-cleanup))))))
+
+(defun malyon-restore ()
+  "Restore the save window configuration for the interpreter."
+  (interactive)
+  (condition-case nil
+      (progn
+       (malyon-restore-window-configuration)
+       (malyon-adjust-transcript))
+    (error
+     (malyon-fatal-error "restoring window configuration failed."))))
+
+(defun malyon-quit ()
+  "Exit the malyon interpreter."
+  (interactive)
+  (if malyon-story-file
+      (progn
+       (malyon-restore)
+       (if (malyon-yes-or-no-p-minibuf "Do you really want to quit? ")
+           (malyon-cleanup)))))
+
+(defun malyon-mode ()
+  "This mode provides a basic interpreter for version 3, 5, 8 z code
+story files as generated by Inform (C) Graham Nelson and Infocom. 
+
+Note that this package is by no means complete and bug free.
+If you encounter a bug please send a report to Peter Ilberg at 
+peter.ilbergNOSPAMnatinst.com. Thank you!
+
+To play a story file simple type M-x malyon and enter the path to the
+story file. If anything goes wrong and you want to manually clean
+up type M-x malyon-quit. In addition, you can switch back to a game in
+progress by typing M-x malyon-restore.
+
+The author would like to thank the following people for reporting
+bugs, testing, suggesting and/or contributing improvements:
+    Bernhard Barde, Jonathan Craven, Alberto Petrofsky, Alan Shutko"
+  (message "Use M-x malyon if you want to play a zcode game."))
+
+;; compatibility functions for GNU emacs
+
+(if (fboundp 'cadr)
+    (defalias 'malyon-cadr 'cadr)
+  (defun malyon-cadr (list)
+    "Take the cadr of the list."
+    (car (cdr list))))
+
+(if (fboundp 'caddr)
+    (defalias 'malyon-caddr 'caddr)
+  (defun malyon-caddr (list)
+    "Take the caddr of the list."
+    (car (cdr (cdr list)))))
+
+(if (fboundp 'cdddr)
+    (defalias 'malyon-cdddr 'cdddr)
+  (defun malyon-cdddr (list)
+    "Take the cdddr of the list."
+    (cdr (cdr (cdr list)))))
+
+(if (fboundp 'char-before)
+    (defalias 'malyon-char-before 'char-before)
+  (defun malyon-char-before ()
+    "Return the character before the point."
+    (char-after (- (point) 1))))
+
+(if (fboundp 'char-to-int)
+    (defalias 'malyon-char-to-int 'char-to-int)
+  (defun malyon-char-to-int (c)
+    "Convert a character into an integer."
+    c))
+
+(if (fboundp 'characterp)
+    (defalias 'malyon-characterp 'characterp)
+  (defun malyon-characterp (x)
+    "Test for a character."
+    (and (numberp x) (<= 0 x) (< x 256))))
+
+(defun malyon-disable-multibyte ()
+  "Disable multibyte support in the current buffer."
+  (condition-case nil (set-buffer-multibyte nil) (error)))
+
+(defun malyon-erase-buffer (&optional buffer)
+  "Erase the given buffer."
+  (save-excursion
+    (if buffer (set-buffer buffer))
+    (if (and buffer (eq buffer malyon-transcript-buffer))
+       (malyon-begin-section)
+      (erase-buffer))))
+
+(if (fboundp 'int-to-char)
+    (defalias 'malyon-int-to-char 'int-to-char)
+  (defun malyon-int-to-char (i)
+    "Convert an integer into a character."
+    i))
+
+(if (fboundp 'mapc)
+    (defalias 'malyon-mapc 'mapc)
+  (defun malyon-mapc (function list)
+    "Apply fun to every element of args ignoring the results."
+    (if (null list)
+       '()
+      (funcall function (car list))
+      (malyon-mapc function (cdr list)))))
+
+(if (fboundp 'mapcan)
+    (defalias 'malyon-mapcan 'mapcan)
+  (defun malyon-mapcan (function list)
+    "Apply fun to every element of args nconc'ing the result."
+    (if (null list)
+       '()
+      (nconc (funcall function (car list))
+            (malyon-mapcan function (cdr list))))))
+
+; Do not use the built-in conversion via 'multibyte-char-to-unibyte.
+(defun malyon-multibyte-char-to-unibyte (char)
+  "Convert a multibyte character to unibyte."
+  char)
+
+(defun malyon-point-max (&optional buffer)
+  "Get the point-max of the given buffer."
+  (save-excursion
+    (if buffer (set-buffer buffer))
+    (point-max)))
+
+(if (fboundp 'redisplay-frame)
+    (defalias 'malyon-redisplay-frame 'redisplay-frame)
+  (defun malyon-redisplay-frame (frame &rest ignore)
+    "Redisplay the given frame."))
+
+(if (fboundp 'remove)
+    (defalias 'malyon-remove 'remove)
+  (defun malyon-remove (element list)
+    "Remove the element from the list."
+    (cond ((null list)
+          '())
+         ((eq element (car list))
+          (malyon-remove element (cdr list)))
+         ((equal element (car list))
+          (malyon-remove element (cdr list)))
+         (t
+          (cons (car list)
+                (malyon-remove element (cdr list)))))))
+
+(if (fboundp 'set-keymap-name)
+    (defalias 'malyon-set-keymap-name 'set-keymap-name)
+  (defun malyon-set-keymap-name (keymap name)
+    "Set the name of the keymap."))
+
+(if (fboundp 'string-to-list)
+    (defalias 'malyon-string-to-list 'string-to-list)
+  (defun malyon-string-to-list (s)
+    "Convert a string into a list of characters."
+    (let ((i (- (length s) 1)) (l '()))
+      (while (<= 0 i)
+       (setq l (cons (aref s i) l)
+             i (- i 1)))
+      l)))
+
+(if (fboundp 'string-to-vector)
+    (defalias 'malyon-string-to-vector 'string-to-vector)
+  (defun malyon-string-to-vector (s)
+    "Convert a string into a vector of characters."
+    (let* ((i 0) (l (length s)) (v (make-vector l 0)))
+      (while (< i l)
+       (aset v i (aref s i))
+       (setq i (+ 1 i)))
+      v)))
+
+; Do not use the built-in conversion via 'unibyte-char-to-multibyte.
+(defun malyon-unibyte-char-to-multibyte (char)
+  "Convert a unibyte character to multibyte."
+  char)
+
+(defun malyon-vector-to-list (v begin end)
+  "Return a list of elements in v in the range [begin, end)."
+  (let ((result '()))
+    (while (< begin end)
+      (setq result (cons (aref v begin) result))
+      (setq begin (+ 1 begin)))
+    (reverse result)))
+
+(if (fboundp 'window-displayed-height)
+    (defalias 'malyon-window-displayed-height 'window-displayed-height)
+  (defun malyon-window-displayed-height (&optional window)
+    "Get the height of the window's displayed region."
+    (- (window-height) 1)))
+
+(if (fboundp 'yes-or-no-p-minibuf)
+    (defalias 'malyon-yes-or-no-p-minibuf 'yes-or-no-p-minibuf)
+  (defun malyon-yes-or-no-p-minibuf (prompt)
+    "Ask a yes or no question."
+    (yes-or-no-p prompt)))
+
+;; global variables for the malyon mode
+
+(defvar malyon-syntax-table nil
+  "Syntax table used while in malyon mode (same as in text-mode).")
+
+(if malyon-syntax-table
+    '()
+  (setq malyon-syntax-table (make-syntax-table))
+  (modify-syntax-entry ?\" ".   " malyon-syntax-table)
+  (modify-syntax-entry ?\\ ".   " malyon-syntax-table)
+  (modify-syntax-entry ?'  "w   " malyon-syntax-table))
+
+(defvar malyon-keymap-read nil
+  "Keymap for malyon mode for reading input into a buffer.")
+
+(defvar malyon-history-saved-up nil
+  "The saved binding for the up arrow key.")
+
+(defvar malyon-history-saved-down nil
+  "The saved binding for the down arrow key.")
+
+(if malyon-keymap-read
+    '()
+  (setq malyon-keymap-read (make-sparse-keymap))
+  (malyon-set-keymap-name malyon-keymap-read 'malyon-keymap-read)
+  (setq malyon-history-saved-up   (global-key-binding [up]))
+  (setq malyon-history-saved-down (global-key-binding [down]))
+  (define-key malyon-keymap-read "\r"        'malyon-end-input)
+  (define-key malyon-keymap-read [up]        'malyon-history-previous-char)
+  (define-key malyon-keymap-read [down]      'malyon-history-next-char)
+  (define-key malyon-keymap-read "\M-p"      'malyon-history-previous-char)
+  (define-key malyon-keymap-read "\M-n"      'malyon-history-next-char)
+  (define-key malyon-keymap-read "\C-a"      'malyon-beginning-of-line)
+  (define-key malyon-keymap-read "\C-w"      'malyon-kill-region)
+  (define-key malyon-keymap-read "\C-k"      'malyon-kill-line)
+  (define-key malyon-keymap-read "\M-d"      'malyon-kill-word)
+  (define-key malyon-keymap-read "\C-y"      'malyon-yank)
+  (define-key malyon-keymap-read "\M-y"      'malyon-yank-pop)
+  (define-key malyon-keymap-read "\C-d"      'malyon-delete-char)
+  (define-key malyon-keymap-read "\d"        'malyon-backward-delete-char)
+  (define-key malyon-keymap-read [del]       'malyon-delete-char)
+  (define-key malyon-keymap-read [backspace] 'malyon-backward-delete-char)
+  (substitute-key-definition (lookup-key (current-global-map) "a")
+                            'malyon-self-insert-command
+                            malyon-keymap-read (current-global-map)))
+
+(defvar malyon-keymap-readchar nil
+  "Keymap for malyon mode for waiting for input.")
+
+(if malyon-keymap-readchar
+    '()
+  (setq malyon-keymap-readchar (make-sparse-keymap))
+  (malyon-set-keymap-name malyon-keymap-readchar 'malyon-keymap-readchar)
+  (define-key malyon-keymap-readchar "\r" 'malyon-wait-char)
+  (substitute-key-definition (lookup-key (current-global-map) "a")
+                            'malyon-wait-char
+                            malyon-keymap-readchar (current-global-map)))
+
+(defvar malyon-keymap-more nil
+  "Keymap for malyon mode for browsing through text.")
+
+(if malyon-keymap-more
+    '()
+  (setq malyon-keymap-more (make-sparse-keymap))
+  (malyon-set-keymap-name malyon-keymap-more 'malyon-keymap-more)
+  (define-key malyon-keymap-more "\r" 'malyon-more-char)
+  (substitute-key-definition (lookup-key (current-global-map) "a")
+                            'malyon-more-char
+                            malyon-keymap-more (current-global-map)))
+
+(defvar malyon-keymap-more-status nil
+  "Keymap for malyon mode for browsing through the status buffer.")
+
+(if malyon-keymap-more-status
+    '()
+  (setq malyon-keymap-more-status (make-sparse-keymap))
+  (malyon-set-keymap-name malyon-keymap-more-status 'malyon-keymap-more-status)
+  (define-key malyon-keymap-more-status "\r" 'malyon-more-char-status)
+  (substitute-key-definition (lookup-key (current-global-map) "a")
+                            'malyon-more-char-status
+                            malyon-keymap-more-status (current-global-map)))
+
+(defvar malyon-faces nil
+  "An association list of text faces used by the malyon mode.")
+
+(defun malyon-initialize-faces ()
+  (copy-face 'default  'malyon-face-plain)
+  (copy-face 'bold     'malyon-face-reverse)
+  (copy-face 'bold     'malyon-face-bold)
+  (copy-face 'italic   'malyon-face-italic)
+  (copy-face 'default  'malyon-face-error)
+  (set-face-foreground 'malyon-face-error "red")
+  (setq malyon-faces '((0 . malyon-face-plain)
+                      (1 . malyon-face-reverse)
+                      (2 . malyon-face-bold)
+                      (4 . malyon-face-italic)
+                      (8 . malyon-face-plain))))
+
+(defvar malyon-print-separator nil
+  "A flag indicating whether to print the * * * separator.")
+
+(defun malyon-begin-section ()
+  "Print a section divider and begin a new section."
+  (if malyon-print-separator
+      (progn
+       (malyon-mapc 'malyon-putchar-transcript '(?\n ?\n ?* ?  ?* ?  ?*))
+       (center-line)
+       (malyon-mapc 'malyon-putchar-transcript '(?\n ?\n))
+       (setq malyon-print-separator nil)))
+  (narrow-to-region (point-max) (point-max)))
+
+(if malyon-whitespace
+    '()
+  (setq malyon-whitespace (list (malyon-char-to-int ? )
+                               (malyon-char-to-int ?\t)
+                               (malyon-char-to-int ?\n)
+                               (malyon-char-to-int ?\r))))
+
+;; memory utilities
+
+(defsubst malyon-read-byte (address)
+  "Read a byte at address in the story file."
+  (if (<= 0 address)
+      (aref malyon-story-file address)
+    (aref malyon-story-file (+ 65536 address))))
+
+(defsubst malyon-store-byte (address value)
+  "Store a byte at address in the story file."
+  (if (<= 0 address)
+      (aset malyon-story-file address (logand 255 value))
+    (aset malyon-story-file (+ 65536 address) (logand 255 value))))
+
+(defsubst malyon-read-word (address)
+  "Read a word at address in the story file."
+  (if (<= 0 address)
+      (logior (lsh (aref malyon-story-file address) 8)
+             (aref malyon-story-file (+ 1 address)))
+    (logior (lsh (aref malyon-story-file (+ 65536 address)) 8)
+           (aref malyon-story-file (+ 65537 address)))))
+       
+(defsubst malyon-store-word (address value)
+  "Store a word at address in the story file."
+  (if (<= 0 address)
+      (progn
+       (aset malyon-story-file address (logand 255 (lsh value -8)))
+       (aset malyon-story-file (+ 1 address) (logand 255 value)))
+    (aset malyon-story-file (+ 65536 address) (logand 255 (lsh value -8)))
+    (aset malyon-story-file (+ 65537 address) (logand 255 value))))
+
+(defsubst malyon-read-code-byte ()
+  "Read the next byte at the program counter location."
+  (setq malyon-instruction-pointer (+ malyon-instruction-pointer 1))
+  (malyon-read-byte (- malyon-instruction-pointer 1)))
+
+(defsubst malyon-read-code-word ()
+  "Read the next word at the program counter location."
+  (setq malyon-instruction-pointer (+ malyon-instruction-pointer 2))
+  (malyon-read-word (- malyon-instruction-pointer 2)))
+
+(defsubst malyon-pop-stack ()
+  "Pop a value off the stack."
+  (if (> 0 malyon-stack-pointer)
+      (malyon-fatal-error "stack underflow."))
+  (setq malyon-stack-pointer (- malyon-stack-pointer 1))
+  (aref malyon-stack (+ malyon-stack-pointer 1)))
+
+(defsubst malyon-read-local-variable (variable)
+  "Read a local variable."
+  (aref malyon-stack (+ variable malyon-frame-pointer)))
+
+(defsubst malyon-read-global-variable (variable)
+  "Read a global variable."
+  (malyon-read-word (+ malyon-global-variables (* 2 variable))))
+
+(defsubst malyon-read-variable (variable)
+  "Read a variable."
+  (cond ((= variable 0)  (malyon-pop-stack))
+       ((< variable 16) (malyon-read-local-variable variable))
+       (t               (malyon-read-global-variable (- variable 16)))))
+
+(defsubst malyon-push-stack (value)
+  "Push a value onto the stack."
+  (setq malyon-stack-pointer (+ malyon-stack-pointer 1))
+  (aset malyon-stack malyon-stack-pointer value))
+
+(defsubst malyon-store-local-variable (variable value)
+  "Store a value in a local variable."
+  (aset malyon-stack (+ variable malyon-frame-pointer) value))
+
+(defsubst malyon-store-global-variable (variable value)
+  "Store a value in a global variable."
+  (malyon-store-word (+ malyon-global-variables (* 2 variable)) value))
+
+(defsubst malyon-store-variable (var value)
+  "Store the value in a variable."
+  (setq value (logand 65535 value))
+  (cond ((= var 0)  (malyon-push-stack value))
+       ((< var 16) (malyon-store-local-variable var value))
+       (t          (malyon-store-global-variable (- var 16) value))))
+
+;; list of opcodes
+
+(defvar malyon-opcodes
+  [malyon-opcode-nop
+   malyon-opcode-je             malyon-opcode-jl
+   malyon-opcode-jg              malyon-opcode-dec-chk
+   malyon-opcode-inc-chk         malyon-opcode-jin
+   malyon-opcode-test            malyon-opcode-or
+   malyon-opcode-and             malyon-opcode-test-attr
+   malyon-opcode-set-attr        malyon-opcode-clear-attr
+   malyon-opcode-store           malyon-opcode-insert-obj
+   malyon-opcode-loadw           malyon-opcode-loadb
+   malyon-opcode-get-prop        malyon-opcode-get-prop-addr
+   malyon-opcode-get-next-prop   malyon-opcode-add
+   malyon-opcode-sub             malyon-opcode-mul
+   malyon-opcode-div             malyon-opcode-mod
+   malyon-opcode-calls           malyon-opcode-calln
+   malyon-opcode-set-color       malyon-opcode-throw
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-je              malyon-opcode-jl
+   malyon-opcode-jg              malyon-opcode-dec-chk
+   malyon-opcode-inc-chk         malyon-opcode-jin
+   malyon-opcode-test            malyon-opcode-or
+   malyon-opcode-and             malyon-opcode-test-attr
+   malyon-opcode-set-attr        malyon-opcode-clear-attr
+   malyon-opcode-store           malyon-opcode-insert-obj
+   malyon-opcode-loadw           malyon-opcode-loadb
+   malyon-opcode-get-prop        malyon-opcode-get-prop-addr
+   malyon-opcode-get-next-prop   malyon-opcode-add
+   malyon-opcode-sub             malyon-opcode-mul
+   malyon-opcode-div             malyon-opcode-mod
+   malyon-opcode-calls           malyon-opcode-calln
+   malyon-opcode-set-color       malyon-opcode-throw
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-je              malyon-opcode-jl 
+   malyon-opcode-jg              malyon-opcode-dec-chk
+   malyon-opcode-inc-chk         malyon-opcode-jin
+   malyon-opcode-test            malyon-opcode-or
+   malyon-opcode-and             malyon-opcode-test-attr
+   malyon-opcode-set-attr        malyon-opcode-clear-attr
+   malyon-opcode-store           malyon-opcode-insert-obj
+   malyon-opcode-loadw           malyon-opcode-loadb
+   malyon-opcode-get-prop        malyon-opcode-get-prop-addr
+   malyon-opcode-get-next-prop   malyon-opcode-add
+   malyon-opcode-sub             malyon-opcode-mul
+   malyon-opcode-div             malyon-opcode-mod
+   malyon-opcode-calls           malyon-opcode-calln
+   malyon-opcode-set-color       malyon-opcode-throw
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-je              malyon-opcode-jl
+   malyon-opcode-jg              malyon-opcode-dec-chk
+   malyon-opcode-inc-chk         malyon-opcode-jin
+   malyon-opcode-test            malyon-opcode-or
+   malyon-opcode-and             malyon-opcode-test-attr
+   malyon-opcode-set-attr        malyon-opcode-clear-attr
+   malyon-opcode-store           malyon-opcode-insert-obj
+   malyon-opcode-loadw           malyon-opcode-loadb
+   malyon-opcode-get-prop        malyon-opcode-get-prop-addr
+   malyon-opcode-get-next-prop   malyon-opcode-add
+   malyon-opcode-sub             malyon-opcode-mul
+   malyon-opcode-div             malyon-opcode-mod
+   malyon-opcode-calls           malyon-opcode-calln
+   malyon-opcode-set-color       malyon-opcode-throw
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop             malyon-opcode-jz
+   malyon-opcode-get-sibling     malyon-opcode-get-child
+   malyon-opcode-get-parent      malyon-opcode-get-prop-len
+   malyon-opcode-inc             malyon-opcode-dec
+   malyon-opcode-print-addr      malyon-opcode-calls
+   malyon-opcode-remove-obj      malyon-opcode-print-obj
+   malyon-opcode-ret             malyon-opcode-jump
+   malyon-opcode-print-paddr     malyon-opcode-load
+   malyon-opcode-calln           malyon-opcode-jz
+   malyon-opcode-get-sibling     malyon-opcode-get-child
+   malyon-opcode-get-parent      malyon-opcode-get-prop-len
+   malyon-opcode-inc             malyon-opcode-dec
+   malyon-opcode-print-addr      malyon-opcode-calls
+   malyon-opcode-remove-obj      malyon-opcode-print-obj
+   malyon-opcode-ret             malyon-opcode-jump
+   malyon-opcode-print-paddr     malyon-opcode-load
+   malyon-opcode-calln           malyon-opcode-jz
+   malyon-opcode-get-sibling     malyon-opcode-get-child
+   malyon-opcode-get-parent      malyon-opcode-get-prop-len
+   malyon-opcode-inc             malyon-opcode-dec
+   malyon-opcode-print-addr      malyon-opcode-calls
+   malyon-opcode-remove-obj      malyon-opcode-print-obj
+   malyon-opcode-ret             malyon-opcode-jump
+   malyon-opcode-print-paddr     malyon-opcode-load
+   malyon-opcode-calln           malyon-opcode-rtrue
+   malyon-opcode-rfalse          malyon-opcode-print
+   malyon-opcode-print-ret       malyon-opcode-nop
+   malyon-opcode-illegal         malyon-opcode-illegal
+   malyon-opcode-restart         malyon-opcode-ret-popped
+   malyon-opcode-catch           malyon-opcode-quit
+   malyon-opcode-new-line        malyon-opcode-illegal
+   malyon-opcode-verify          malyon-opcode-illegal
+   malyon-opcode-piracy          malyon-opcode-nop
+   malyon-opcode-je              malyon-opcode-jl
+   malyon-opcode-jg              malyon-opcode-dec-chk
+   malyon-opcode-inc-chk         malyon-opcode-jin
+   malyon-opcode-test            malyon-opcode-or
+   malyon-opcode-and             malyon-opcode-test-attr
+   malyon-opcode-set-attr        malyon-opcode-clear-attr
+   malyon-opcode-store           malyon-opcode-insert-obj
+   malyon-opcode-loadw           malyon-opcode-loadb
+   malyon-opcode-get-prop        malyon-opcode-get-prop-addr
+   malyon-opcode-get-next-prop   malyon-opcode-add
+   malyon-opcode-sub             malyon-opcode-mul
+   malyon-opcode-div             malyon-opcode-mod
+   malyon-opcode-calls           malyon-opcode-calln
+   malyon-opcode-set-color       malyon-opcode-throw
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop             malyon-opcode-calls
+   malyon-opcode-storew          malyon-opcode-storeb
+   malyon-opcode-put-prop        malyon-opcode-aread
+   malyon-opcode-print-char      malyon-opcode-print-num
+   malyon-opcode-random          malyon-opcode-push
+   malyon-opcode-pull            malyon-opcode-split-window
+   malyon-opcode-set-window      malyon-opcode-calls
+   malyon-opcode-erase-window    malyon-opcode-erase-line
+   malyon-opcode-set-cursor      malyon-opcode-get-cursor
+   malyon-opcode-set-text-style  malyon-opcode-buffer-mode
+   malyon-opcode-output-stream   malyon-opcode-input-stream
+   malyon-opcode-nop             malyon-opcode-read-char
+   malyon-opcode-scan-table      malyon-opcode-not
+   malyon-opcode-calln           malyon-opcode-calln
+   malyon-opcode-tokenise        malyon-opcode-encode-text
+   malyon-opcode-copy-table      malyon-opcode-print-table
+   malyon-opcode-check-arg-count malyon-opcode-save
+   malyon-opcode-restore         malyon-opcode-log-shift
+   malyon-opcode-art-shift       malyon-opcode-set-font
+   malyon-opcode-illegal         malyon-opcode-illegal
+   malyon-opcode-illegal         malyon-opcode-illegal
+   malyon-opcode-save-undo       malyon-opcode-restore-undo
+   malyon-opcode-print-unicode   malyon-opcode-check-unicode
+   malyon-opcode-nop             malyon-opcode-nop
+   malyon-opcode-nop]
+  "A vector of all known legal z code opcodes.")
+
+;; initialization
+
+(defun malyon-load-story-file (file-name)
+  "Load a z code story file into an internal vector."
+  (save-excursion
+    (set-buffer (create-file-buffer file-name))
+    (malyon-disable-multibyte)
+    (malyon-erase-buffer)
+    (let ((coding-system-for-read 'binary))
+      (insert-file-contents file-name))
+    (setq malyon-story-file-name file-name)
+    (setq malyon-story-file (buffer-substring-no-properties (point-min)
+                                                           (point-max)))
+    (setq malyon-story-file (malyon-string-to-vector malyon-story-file))
+    (if (not (eq ?\^A 1))
+       (let ((i 0))
+         (while (< i (length malyon-story-file))
+           (aset malyon-story-file
+                 i
+                 (malyon-char-to-int (aref malyon-story-file i)))
+           (setq i (+ 1 i)))))
+    (kill-buffer nil)))
+
+(defun malyon-initialize ()
+  "Initialize the z code interpreter."
+;  (malyon-trace-file)
+  (setq malyon-game-state-quetzal t)
+  (malyon-initialize-faces)
+  (malyon-initialize-status)
+  (malyon-initialize-transcript)
+  (malyon-initialize-windows)
+  (malyon-initialize-story-header)
+  (malyon-initialize-registers)
+  (malyon-initialize-opcodes)
+  (malyon-history-clear)
+  (setq malyon-game-state-restart (malyon-current-game-state))
+  (malyon-print-header))
+
+(defun malyon-initialize-status ()
+  "Initialize the status buffer."
+  (setq malyon-status-buffer (get-buffer-create "Malyon Status"))
+  (switch-to-buffer malyon-status-buffer)
+  (malyon-erase-buffer)
+  (kill-all-local-variables)
+  (setq malyon-status-buffer-point (point))
+  (setq malyon-status-buffer-lines 0)
+  (setq malyon-status-buffer-delayed-split nil)
+  (use-local-map malyon-keymap-read)
+  (set-syntax-table malyon-syntax-table)
+  (setq mode-name "Malyon")
+  (setq major-mode 'malyon-mode)
+  (run-hooks 'malyon-mode-hook))
+
+(defun malyon-initialize-transcript ()
+  "Initialize the transcript buffer."
+  (setq malyon-transcript-buffer (get-buffer-create "Malyon Transcript"))
+  (switch-to-buffer malyon-transcript-buffer)
+  (malyon-erase-buffer)
+  (kill-all-local-variables)
+  (setq malyon-last-cursor-position-after-input
+       (malyon-point-max malyon-transcript-buffer))
+  (use-local-map malyon-keymap-read)
+  (set-syntax-table malyon-syntax-table)
+  (setq fill-column malyon-max-column)
+  (auto-fill-mode 1)
+  (setq mode-name "Malyon")
+  (setq major-mode 'malyon-mode)
+  (run-hooks 'malyon-mode-hook))
+
+(defun malyon-initialize-windows ()
+  "Initialize the window configuration for the z machine."
+  (setq window-min-height 3)
+  (setq malyon-transcript-buffer-buffered t)
+  (malyon-set-window-configuration 0)
+  (malyon-opcode-set-window 0))
+
+(defun malyon-initialize-story-header ()
+  "Initializes the header section of the story file."
+  (malyon-store-byte 1  
+                    (if (>= malyon-story-version 5)
+                        28
+                      (logior 48 (malyon-read-byte 1))))
+  (malyon-store-byte 16 (logand 440 (malyon-read-byte 16)))
+  (malyon-store-byte 30 1)
+  (malyon-store-byte 31 65)
+  (malyon-store-byte 32 255)
+  (malyon-store-byte 33 (- malyon-max-column 1))
+  (malyon-store-word 34 (- malyon-max-column 1))
+  (malyon-store-word 36 255)
+  (malyon-store-word 38 1)
+  (malyon-store-word 39 1)
+  (malyon-store-byte 44 0)
+  (malyon-store-byte 45 0)
+  (malyon-store-byte 50 1)
+  (malyon-store-byte 51 0))
+
+(defun malyon-initialize-registers ()
+  "Initialize the interpreter's internal registers."
+  (setq malyon-stack (make-vector 1024 0))
+  (setq malyon-stack-pointer -1)
+  (malyon-push-initial-frame)
+  (setq malyon-frame-pointer malyon-stack-pointer)
+  (setq malyon-instruction-pointer (malyon-read-word 6))
+  (setq malyon-global-variables (malyon-read-word 12))
+  (setq malyon-object-table (malyon-read-word 10))
+  (cond ((< malyon-story-version 5)
+        (setq malyon-object-table-entry-size 9)
+        (setq malyon-object-properties       31)
+        (setq malyon-object-property-offset  7))
+       (t
+        (setq malyon-object-table-entry-size 14)
+        (setq malyon-object-properties       63)
+        (setq malyon-object-property-offset  12)))
+  (setq malyon-abbreviations (malyon-read-word 24))
+  (if (< malyon-story-version 5)
+      (setq malyon-score-game (zerop (logand 2 (malyon-read-byte 1)))))
+  (setq malyon-packed-multiplier
+       (malyon-cadr (assq malyon-story-version '((3 2) (5 4) (8 8)))))
+  (if (or (< malyon-story-version 5) (zerop (malyon-read-word 52)))
+      (setq malyon-alphabet (concat "abcdefghijklmnopqrstuvwxyz"
+                                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                                   " \n0123456789.,!?_#'\"/\\-:()"))
+    (setq malyon-alphabet (make-string 78 ? ))
+    (let ((i 0))
+      (while (< i 78)
+       (aset malyon-alphabet i
+             (malyon-read-byte (+ i (malyon-read-word 52))))
+       (setq i (+ 1 i)))))
+  (malyon-initialize-unicode-table)
+  (setq malyon-dictionary (malyon-read-word 8))
+  (setq malyon-dictionary-entry-length
+       (malyon-read-byte
+        (+ 1 malyon-dictionary (malyon-read-byte malyon-dictionary))))
+  (setq malyon-dictionary-num-entries
+       (malyon-read-word
+        (+ 2 malyon-dictionary (malyon-read-byte malyon-dictionary))))
+  (setq malyon-dictionary-entries
+       (+ 4 malyon-dictionary (malyon-read-byte malyon-dictionary)))
+  (setq malyon-dictionary-word-length (if (< malyon-story-version 5) 3 5))
+  (setq malyon-current-face 'malyon-face-plain)
+  (setq malyon-print-separator nil)
+  (malyon-initialize-output-streams))
+
+(defun malyon-initialize-opcodes ()
+  "Initialize the opcode table used by the story file."
+  (cond ((< malyon-story-version 5)
+        (aset malyon-opcodes 143 'malyon-opcode-not)
+        (aset malyon-opcodes 181 'malyon-opcode-save)
+        (aset malyon-opcodes 182 'malyon-opcode-restore)
+        (aset malyon-opcodes 185 'malyon-opcode-pop)
+        (aset malyon-opcodes 188 'malyon-opcode-show-status))
+       (t
+        (aset malyon-opcodes 143 'malyon-opcode-calln)
+        (aset malyon-opcodes 181 'malyon-opcode-illegal)
+        (aset malyon-opcodes 182 'malyon-opcode-illegal)
+        (aset malyon-opcodes 185 'malyon-opcode-catch)
+        (aset malyon-opcodes 188 'malyon-opcode-illegal))))
+
+(defun malyon-print-header ()
+  "Print malyon mode header information."
+  (malyon-opcode-set-text-style 2)
+  (malyon-print "Malyon V 1.0.3")
+  (malyon-opcode-set-text-style 0)
+  (malyon-newline)
+  (malyon-print "A z-code interpreter for version 3, 5, and 8 games.")
+  (malyon-newline)
+  (malyon-print "(c) 1999-2011 by Peter Ilberg <peter.ilbergNOSPAMgmail.com>")
+  (malyon-newline)
+  (malyon-newline))
+
+;; cleanup
+
+(defun malyon-cleanup ()
+  "Clean up the z code interpreter."
+  (condition-case nil
+      (progn
+       (setq malyon-story-file nil)
+       (setq malyon-window-configuration nil)
+       (setq malyon-game-state-restart nil)
+       (setq malyon-game-state-undo nil)
+       (if (get-buffer "Malyon Status")
+           (kill-buffer (get-buffer "Malyon Status")))
+       (if (get-buffer "Malyon Transcript")
+           (progn
+             (switch-to-buffer (get-buffer "Malyon Transcript"))
+             (malyon-redisplay-frame (selected-frame) t)
+             (delete-other-windows (get-buffer-window (current-buffer)))
+             (widen)
+             (text-mode)))
+       (setq malyon-status-buffer nil)
+       (setq malyon-transcript-buffer nil))
+    (error
+     (malyon-fatal-error "cleanup failed."))))
+
+;; error handling
+
+(defun malyon-fatal-error (message)
+  "Print error message and abort."
+  (setq message (concat "Malyon fatal error: " message))
+  (unwind-protect
+      (save-excursion
+       (set-buffer malyon-transcript-buffer)
+       (goto-char (point-max))
+       (newline)
+       (newline)
+       (put-text-property 0
+                          (length message)
+                          'face
+                          'malyon-face-error
+                          message)
+       (insert message)
+       (newline))
+    (malyon-cleanup)
+    (malyon-redisplay-frame (selected-frame) t)
+    (error message)))
+
+;; conversion of zscii to ascii
+
+(defvar malyon-unicode-table nil
+  "An array mapping zscii characters to latin-1 ones.")
+
+(defvar malyon-default-unicode-table nil
+  "The default array mapping zscii characters to latin-1 ones.")
+
+(if malyon-default-unicode-table
+    '()
+  (setq malyon-default-unicode-table
+       [32 
+        0   0   0   0   0   0   0       ;   1 -   7
+        8   0   0   0   0   10  0   0   ;   8 -  15
+        0   0   0   0   0   0   0   0   ;  16 -  23
+        0   0   0   39  0   0   0   0   ;  24 -  31
+        32  33  34  35  36  37  38  39  ;  32 -  39
+        40  41  42  43  44  45  46  47  ;  40 -  47
+        48  49  50  51  52  53  54  55  ;  48 -  55
+        56  57  58  59  60  61  62  63  ;  56 -  63
+        64  65  66  67  68  69  70  71  ;  64 -  71
+        72  73  74  75  76  77  78  79  ;  72 -  79
+        80  81  82  83  84  85  86  87  ;  80 -  87
+        88  89  90  91  92  93  94  95  ;  88 -  95
+        96  97  98  99  100 101 102 103 ;  96 - 103
+        104 105 106 107 108 109 110 111 ; 104 - 111
+        112 113 114 115 116 117 118 119 ; 112 - 119
+        120 121 122 123 124 125 126 0   ; 120 - 127
+        0   0   0   0   0   0   0   0   ; 128 - 135
+        0   0   0   0   0   0   0   0   ; 136 - 143
+        0   48  49  50  51  52  53  54  ; 144 - 151
+        55  56  57  228 246 252 196 214 ; 152 - 159
+        220 223 187 171 235 239 255 203 ; 160 - 167
+        207 225 233 237 243 250 253 193 ; 168 - 175
+        201 205 211 218 221 224 232 236 ; 176 - 183
+        242 249 192 200 204 210 217 226 ; 184 - 191
+        234 238 244 251 194 202 206 212 ; 192 - 199
+        219 229 197 248 216 227 241 245 ; 200 - 207
+        195 209 213 230 198 231 199 254 ; 208 - 215
+        240 222 208 163 63  63  161 191 ; 216 - 223
+        0   0   0   0   0   0   0   0   ; 224 - 231
+        0   0   0   0   0   0   0   0   ; 232 - 239
+        0   0   0   0   0   0   0   0   ; 240 - 247
+        0   0   0   0   0   0   0   0   ; 248 - 255
+        ]))
+
+(defun malyon-initialize-unicode-table ()
+  "Initializes the zscii-to-unicode conversion table."
+  (setq malyon-unicode-table
+       (copy-sequence malyon-default-unicode-table))
+  (let* ((ext   (malyon-read-word 54))
+        (len   (if (zerop ext) 0 (malyon-read-word ext)))
+        (table (if (< len 3)   0 (malyon-read-word (+ ext 6)))))
+    (if (or (< malyon-story-version 5) (zerop table))
+       '()
+      (let ((i 0))
+       (while (< i 96)
+         (aset malyon-unicode-table (+ 155 i) (malyon-char-to-int ??))
+         (setq i (+ 1 i))))
+      (setq len (malyon-read-byte table))
+      (let ((i 0))
+       (while (< i len)
+         (aset malyon-unicode-table (+ 155 i)
+               (malyon-read-word (+ table 1 i)))
+         (setq i (+ 1 i)))))))
+
+(defsubst malyon-zscii-to-unicode (char)
+  "Converts a zscii character to unicode."
+  (if (or (< char 0) (> char 255))
+      ??
+    (let ((uni (aref malyon-unicode-table char)))
+      (if (zerop uni)
+         ??
+       (malyon-unibyte-char-to-multibyte (malyon-int-to-char uni))))))
+
+(defsubst malyon-unicode-to-zscii (char)
+  "Converts a unicode character to zscii."
+  (setq char (malyon-multibyte-char-to-unibyte char))
+  (setq char (if (malyon-characterp char) (malyon-char-to-int char) char))
+  (if (= 13 char)
+      ?\r
+    (let ((i 1) (found 0))
+      (while (and (< i 255) (zerop found))
+       (if (= char (aref malyon-unicode-table i))
+           (setq found i))
+       (setq i (+ i 1)))
+      (malyon-int-to-char found))))
+
+;; output streams
+
+(defvar malyon-output-streams nil
+  "Valid output streams for the interpreter.")
+
+(defvar malyon-output-streams-tables nil
+  "A list of active tables for stream 3.")
+
+(defun malyon-initialize-output-streams ()
+  "Initializes the output streams."
+  (setq malyon-output-streams '())
+  (setq malyon-output-streams-tables '())
+  (malyon-add-output-stream 1 0))
+
+(defun malyon-output-stream-function (stream)
+  "Returns the output function representing the given stream."
+  (cond ((= 1 stream) (if (zerop malyon-current-window)
+                         'malyon-putchar-transcript
+                       'malyon-putchar-status))
+       ((= 2 stream) 'malyon-putchar-printer)))
+
+(defun malyon-add-output-stream (stream table)
+  "Add a new output stream."
+  (if (= stream 3)
+      (progn
+       (setq malyon-output-streams-tables
+             (cons table malyon-output-streams-tables))
+       (malyon-store-word table 0))
+    (let ((function (malyon-output-stream-function stream)))
+      (setq malyon-output-streams
+           (if (member function malyon-output-streams)
+               malyon-output-streams
+             (cons function malyon-output-streams))))))
+
+(defun malyon-remove-output-stream (stream)
+  "Remove an output stream."
+  (if (= stream 3)
+      (setq malyon-output-streams-tables (cdr malyon-output-streams-tables))
+    (setq malyon-output-streams
+         (malyon-remove (malyon-output-stream-function stream)
+                        malyon-output-streams))))
+
+(defun malyon-update-output-streams ()
+  "Update output streams when the output window has changed."
+  (let ((one (or (member 'malyon-putchar-transcript malyon-output-streams)
+                (member 'malyon-putchar-status     malyon-output-streams))))
+    (setq malyon-output-streams
+         (malyon-remove 'malyon-putchar-transcript
+                        (malyon-remove 'malyon-putchar-status
+                                       malyon-output-streams)))
+    (if one
+       (malyon-add-output-stream 1 0))))
+
+(defsubst malyon-output-character (char)
+  "Output a single character on all active streams."
+  (setq char (malyon-zscii-to-unicode char))
+  (if malyon-output-streams-tables
+      (malyon-putchar-table char (car malyon-output-streams-tables))
+    (malyon-mapc (lambda (s) (funcall s char)) malyon-output-streams)))
+
+;; printing text
+
+(defsubst malyon-abbrev (abbrev x)
+  "Print an abbreviation."
+  (malyon-print-ztext
+   (* 2 (malyon-read-word (+ malyon-abbreviations
+                            (* 2 (+ x (* 32 (1- abbrev)))))))))
+
+(defun malyon-newline ()
+  "Print a newline."
+  (if (eq malyon-status-buffer (current-buffer))
+      (goto-char malyon-status-buffer-point)
+    (goto-char (point-max)))
+  (malyon-output-character ?\r)
+  (if (eq malyon-status-buffer (current-buffer))
+      (setq malyon-status-buffer-point (point))
+    (goto-char malyon-last-cursor-position-after-input))
+  (malyon-redisplay-frame (selected-frame) nil))
+
+(defun malyon-print (object)
+  "Print text." 
+  (let ((text (if (malyon-characterp object) (char-to-string object) object))
+       (start))
+    (if (eq malyon-transcript-buffer (current-buffer))
+       (goto-char (point-max))
+      (goto-char malyon-status-buffer-point))
+    (setq start (point))
+    (malyon-print-characters (malyon-string-to-list text))
+    (put-text-property start (point) 'face malyon-current-face)
+    (if (eq malyon-status-buffer (current-buffer))
+       (setq malyon-status-buffer-point (point))
+      (goto-char malyon-last-cursor-position-after-input))))
+
+(defun malyon-print-characters (text)
+  "Print a list of characters."
+  (malyon-mapc 'malyon-output-character text))
+
+(defsubst malyon-print-state-new (char shift abbr zscii zcode)
+  "Generate a new print state."
+  (list char shift abbr zscii zcode))
+
+(defsubst malyon-print-state-initial ()
+  "Returns an initial state for the ztext decoder."
+  (malyon-print-state-new nil -6 0 0 0))
+
+(defsubst malyon-print-state-next (x ignore shift abbr zscii z)
+  "Print state transition function."
+  (cond ((= zscii 2)
+        (malyon-print-state-new (+ z x) -6 0 0 0))
+       ((= zscii 1)
+        (malyon-print-state-new nil     -6 0 2 (* 32 x)))
+       ((> abbr 0)
+        (malyon-abbrev abbr x)
+        (malyon-print-state-initial))
+       ((= x 0)
+        (malyon-print-state-new ?       -6 0 0 0))
+       ((< x 4)
+        (malyon-print-state-new nil     -6 x 0 0))
+       ((= x 4)
+        (malyon-print-state-new nil     20 0 0 0))
+       ((= x 5)
+        (malyon-print-state-new nil     46 0 0 0))
+       ((and (= shift 46) (= x 6))
+        (malyon-print-state-new nil     -6 0 1 0))
+       ((and (= shift 46) (= x 7))
+        (malyon-print-state-new ?\r     -6 0 0 0))
+       (t
+        (malyon-print-state-new
+         (aref malyon-alphabet (+ shift x)) -6 0 0 0))))
+
+(defun malyon-print-text (address)
+  "Print text at address and return the address of the following byte."
+  (let ((start))
+    (if (eq malyon-transcript-buffer (current-buffer))
+       (goto-char (point-max))
+      (goto-char malyon-status-buffer-point))
+    (setq start (point))
+    (setq address (malyon-print-ztext address))
+    (put-text-property start (point) 'face malyon-current-face)
+    (if (eq malyon-status-buffer (current-buffer))
+       (setq malyon-status-buffer-point (point))
+      (goto-char malyon-last-cursor-position-after-input))
+    (malyon-redisplay-frame (selected-frame) nil)
+    address))
+
+(defun malyon-print-ztext (address)
+  "Print the ztext stored at the given address."
+  (let ((high 0) (low) (a) (b) (c) (state (malyon-print-state-initial)))
+    (while (zerop (logand 128 high))
+      (setq high (malyon-read-byte address))
+      (setq low  (malyon-read-byte (+ 1 address)))
+      (setq a    (logand 31 (lsh high -2)))
+      (setq b    (logand 31 (logior (lsh high 3) (lsh low -5))))
+      (setq c    (logand 31 low))
+      (setq state (apply 'malyon-print-state-next a state))
+      (if (car state) (malyon-output-character (car state)))
+      (setq state (apply 'malyon-print-state-next b state))
+      (if (car state) (malyon-output-character (car state)))
+      (setq state (apply 'malyon-print-state-next c state))
+      (if (car state) (malyon-output-character (car state)))
+      (setq address (+ 2 address)))
+    address))
+
+(defun malyon-putchar-transcript (char)
+  "Print a single character in the transcript window."
+  (if (char-equal char ?\n)
+      (newline 1)
+    (insert char)
+    (setq malyon-print-separator (null (member char malyon-whitespace))))
+  (if (and malyon-transcript-buffer-buffered
+          (> (current-column) (current-fill-column)))
+      (progn
+       (end-of-line)
+       (forward-word -1)
+       (if (< 0 (current-column))
+           (newline 1))
+       (end-of-line))))
+
+(defun malyon-putchar-status (char)
+  "Print a single character in the status window."
+  (if malyon-status-buffer-delayed-split
+      (progn
+       (malyon-split-buffer-windows malyon-status-buffer-delayed-split)
+       (other-window 1)))
+  (if (char-equal char ?\n)
+      (progn
+       (beginning-of-line)
+       (forward-line 1)
+       (if (= (point) (point-max))
+           (forward-line -1)))
+    (if (> (current-column) (current-fill-column))
+       '()
+      (insert char)
+      (delete-char 1))))
+
+(defun malyon-putchar-table (char table)
+  "Print a single character into a table."
+  (setq char (malyon-unicode-to-zscii char))
+  (malyon-store-byte (+ 2 table (malyon-read-word table)) char)
+  (malyon-store-word table (+ 1 (malyon-read-word table))))
+
+(defun malyon-putchar-printer (char)
+  "Print a single character onto a printer."); not yet implemented
+
+;; more
+
+(defvar malyon-more-continue-keymap nil
+  "The keymap with which to continue after More has finished.")
+
+(defun malyon-more (keymap)
+  "Enter More mode."
+  (if (eq malyon-status-buffer (current-buffer))
+      (use-local-map keymap)
+    (if (< malyon-story-version 5) (malyon-opcode-show-status))
+    (if (< (count-lines malyon-last-cursor-position-after-input (point-max))
+          (malyon-window-displayed-height))
+       (progn
+         (malyon-adjust-transcript)
+         (use-local-map keymap))
+      (goto-char malyon-last-cursor-position-after-input)
+      (beginning-of-line)
+      (recenter 1)
+      (setq malyon-more-continue-keymap keymap)
+      (use-local-map malyon-keymap-more)
+      (message "[More]"))))
+
+(defun malyon-more-status-buffer ()
+  "Enter More mode for the status buffer."
+  (setq malyon-more-continue-keymap (current-local-map))
+  (use-local-map malyon-keymap-more-status)
+  (message "[More]")
+  (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-input))
+
+;; input history
+
+(defvar malyon-history nil
+  "The input history.")
+
+(defun malyon-history-previous ()
+  "Move one entry up in the input history."
+  (let ((prev (aref malyon-history 0))
+       (curr (aref malyon-history 1))
+       (next (aref malyon-history 2)))
+    (if (null prev)
+       curr
+      (aset malyon-history 2 (if curr (cons curr next) next))
+      (aset malyon-history 0 (cdr prev))
+      (aset malyon-history 1 (car prev)))))
+
+(defun malyon-history-next ()
+  "Move one entry down in the input history."
+  (let ((prev (aref malyon-history 0))
+       (curr (aref malyon-history 1))
+       (next (aref malyon-history 2)))
+    (if (null next)
+       curr
+      (aset malyon-history 0 (if curr (cons curr prev) prev))
+      (aset malyon-history 2 (cdr next))
+      (aset malyon-history 1 (car next)))))
+
+(defun malyon-history-clear ()
+  "Clear the input history."
+  (setq malyon-history (vector '() nil '())))
+
+(defun malyon-history-insert (entry)
+  "Insert an entry into the input history."
+  (let* ((prev (aref malyon-history 0))
+        (curr (aref malyon-history 1))
+        (next (aref malyon-history 2))
+        (l    (malyon-remove entry
+                             (append (nreverse prev)
+                                     (if curr (cons curr next) next))))
+        (cut  (- (length l) 19)))
+    (while (> cut 0)
+      (setq l   (cdr l)
+           cut (- cut 1)))
+    (aset malyon-history 0
+         (malyon-remove nil (malyon-remove "" (cons entry (nreverse l)))))
+    (aset malyon-history 1 nil)
+    (aset malyon-history 2 '())))
+
+;; dictionary lookup
+
+(defun malyon-dictionary-word (chars)
+  "Convert a list of characters into a dictionary word."
+  (list (car (car chars))
+       (length chars)
+       (malyon-encode-dictionary-word (append (malyon-mapcan 'cdr chars)
+                                              '(5 5 5 5 5 5 5 5)))))
+
+(defsubst malyon-join-characters (stop list)
+  "Joins three ztext characters into two bytes."
+  (let ((a (car          list))
+       (b (malyon-cadr  list))
+       (c (malyon-caddr list))
+       (x (if (zerop stop) 0 128)))
+    (list (logior x (logand 255 (logior (lsh a 2) (lsh b -3))))
+         (logand 255 (logior (lsh b 5) c)))))
+
+(defun malyon-encode-dictionary-word (l)
+  "Converts a list of ztext characters into a dictionary word."
+  (let* ((first  l)
+        (second (malyon-cdddr first))
+        (third  (malyon-cdddr second)))
+    (apply 'vector
+          (if (< malyon-story-version 5)
+              (append (malyon-join-characters 0 first)
+                      (malyon-join-characters 1 second))
+            (append (malyon-join-characters 0 first)
+                    (malyon-join-characters 0 second)
+                    (malyon-join-characters 1 third))))))
+
+(defun malyon-lookup (dict code)
+  "Look for the given code in the dictionary and return its address."
+  (cond ((not code)                 0)
+       ((not dict)                 (malyon-binary-search code))
+       ((= dict malyon-dictionary) (malyon-binary-search code))
+       (t                          (malyon-linear-search dict code))))
+
+(defsubst malyon-compare-words (word address)
+  "Compares the given word to the word stored at address."
+  (let* ((i 0)
+        (j address)
+        (x (aref word i))
+        (y (malyon-read-byte j)))
+    (while (not (or (/= x y) (= i malyon-dictionary-word-length)))
+      (setq i (+ 1 i)
+           j (+ 1 j)
+           x (aref word i)
+           y (malyon-read-byte j)))
+    (- x y)))
+
+;; search functions
+
+(defun malyon-binary-search (code)
+  "Binary search through the main dictionary."
+  (let* ((lower   0)
+        (upper   (- malyon-dictionary-num-entries 1))
+        (median  (/ (+ lower upper) 2))
+        (entry   (+ malyon-dictionary-entries
+                    (* malyon-dictionary-entry-length median)))
+        (looking (malyon-compare-words code entry)))
+    (while (not (or (> lower upper) (zerop looking)))
+      (setq lower   (if (< 0 looking) (+ median 1) lower)
+            upper   (if (> 0 looking) (- median 1) upper)
+           median  (/ (+ lower upper) 2)
+           entry   (+ malyon-dictionary-entries
+                      (* malyon-dictionary-entry-length median))
+           looking (malyon-compare-words code entry)))
+    (if (zerop looking) entry 0)))
+
+(defun malyon-linear-search (dictionary code)
+  "Linear search through the given dictionary."
+  (let* ((length  (malyon-read-byte (+ dictionary 1
+                                      (malyon-read-byte dictionary))))
+        (number  (malyon-read-word (+ dictionary 2
+                                      (malyon-read-byte dictionary))))
+        (entries (+ dictionary 4 (malyon-read-byte dictionary)))
+        (i       0)
+        (entry   (+ entries (* length i)))
+        (looking (malyon-compare-words code entry)))
+    (while (not (or (>= i number) (zerop looking)))
+      (setq i       (+ 1 i)
+           entry   (+ entries (* length i))
+           looking (malyon-compare-words code entry)))
+    (if (zerop looking) entry 0)))
+
+;; encoding text and lexical analysis
+
+(defun malyon-split-list (sep list &optional x)
+  "Split a list into sublists as indicated by the separators."
+  (cond ((null list)
+        (list (nreverse x)))
+       ((eq sep (car list))
+        (cons (nreverse x) (malyon-split-list sep (cdr list) '())))
+       (t
+        (malyon-split-list sep (cdr list) (cons (car list) x)))))
+
+(defun malyon-characters-to-words (list)
+  "Turn the list of characters into a list of words."
+  (mapcar 'malyon-dictionary-word
+         (delete '() (malyon-split-list 'malyon-word-separator list))))
+
+(defsubst malyon-char-in-string (c s)
+  "Returns the index of c in s if found, or length of s."
+  (let ((i 0))
+    (while (not (or (= i (length s)) (= c (aref s i))))
+      (setq i (+ 1 i)))
+    i))
+
+(defsubst malyon-encode-into-ztext (c)
+  "Convert a character into ztext."
+  (let* ((index (malyon-char-in-string c malyon-alphabet))
+        (shift (floor index 26))
+        (char  (+ 6 (mod index 26))))
+    (cond ((> shift 2) (list 5 6 (logand 31 (lsh c -5)) (logand 31 c)))
+         ((= shift 2) (list 5 char))
+         ((= shift 1) (list 4 char))
+         (t           (list char)))))
+
+(defun malyon-encode-single-character (terminating-characters char)
+  "Encode a character into ztext."
+  (let ((pos (car char))
+       (c   (cdr char)))
+    (cond ((member c malyon-whitespace)
+          (list 'malyon-word-separator))
+         ((member c terminating-characters)
+          (list 'malyon-word-separator
+                (cons pos (malyon-encode-into-ztext c))
+                'malyon-word-separator))
+         (t (list (cons pos (malyon-encode-into-ztext c)))))))
+
+(defun malyon-encode-character-list (dict list)
+  "Encode the list of characters into ztext."
+  (let ((l '())
+       (i 0))
+    (while (< i (malyon-read-byte dict))
+      (setq l (cons (malyon-read-byte (+ dict 1 i)) l)
+           i (+ 1 i)))
+    (malyon-mapcan (lambda (x) (malyon-encode-single-character l x)) list)))
+
+(defun malyon-text-length (address)
+  "Return the length of the input text."
+  (if (>= malyon-story-version 5)
+      (malyon-read-byte (+ 1 address))
+    (let ((i 0))
+      (while (not (zerop (malyon-read-byte (+ i 1 address))))
+       (setq i (+ i 1)))
+      i)))
+
+(defun malyon-text-to-character-list (address)
+  "Convert the input text into a list of characters."
+  (let ((i    (malyon-text-length address))
+       (text '()))
+    (while (< 0 i)
+      (setq text (cons
+                 (cons (if (< malyon-story-version 5) i (+ 1 i))
+                       (malyon-read-byte
+                        (+ i address (if (< malyon-story-version 5) 0 1))))
+                 text)
+           i    (- i 1)))
+    text))
+
+(defun malyon-text-to-words (address dictionary)
+  "Turn ztext into a list of dictionary words."
+  (malyon-characters-to-words
+   (malyon-encode-character-list (if dictionary dictionary malyon-dictionary)
+                                (malyon-text-to-character-list address))))
+
+;; window management
+
+(defvar malyon-status-buffer-grew-this-turn nil
+  "A flag signalling if the status buffer grew this turn.")
+
+(defun malyon-adjust-transcript ()
+  "Adjust the position of the transcript text."
+  (save-excursion
+    (setq malyon-status-buffer-grew-this-turn nil)
+    (set-buffer malyon-transcript-buffer)
+    (goto-char (point-max))
+    (recenter (- (malyon-window-displayed-height) 2))))
+
+(defun malyon-prepare-status-buffer (status)
+  "Fill the status buffer with empty lines."
+  (save-excursion
+    (set-buffer malyon-status-buffer)
+    (let ((lines (count-lines (point-min) (point-max)))
+         (new   status))
+      (if (zerop lines)
+         (newline 1))
+      (goto-char (point-max))
+      (setq status (- status lines -1))
+      (while (> status 0)
+       (insert (make-string (+ 3 malyon-max-column) ? ))
+       (newline 1)
+       (setq status (- status 1)))
+      (goto-char (point-min))
+      (forward-line (+ 1 new))
+      (kill-region (point) (point-max))
+      (insert (make-string (+ 3 malyon-max-column) ? ))
+      (newline 1))))
+
+(defun malyon-restore-window-configuration ()
+  "Restore the saved window configuration."
+  (let ((buffer (window-buffer (selected-window))))
+    (if malyon-window-configuration
+       (set-window-configuration malyon-window-configuration))
+    (cond ((eq malyon-status-buffer buffer)     (other-window 1))
+         ((eq malyon-transcript-buffer buffer) (goto-char (point-max))))))
+
+(defun malyon-set-window-configuration (status)
+  "Set up the new window configuration."
+  (cond ((< status malyon-status-buffer-lines)
+        (setq malyon-status-buffer-delayed-split status)
+        (if malyon-status-buffer-grew-this-turn
+            (malyon-more-status-buffer)))
+       ((> status malyon-status-buffer-lines)
+        (malyon-split-buffer-windows status)
+        (setq malyon-status-buffer-grew-this-turn t))
+       ((not malyon-window-configuration)
+        (malyon-split-buffer-windows status))))
+
+(defun malyon-split-buffer-windows (status)
+  "Split the buffer windows.
+The status buffer gets 'status' lines while the transcript buffer
+gets the remaining lines."
+  (delete-other-windows (get-buffer-window (current-buffer)))
+  (setq malyon-status-buffer-lines status)
+  (setq malyon-status-buffer-delayed-split nil)
+  (if (zerop status)
+      '()
+    (split-window (get-buffer-window (current-buffer)) (+ status 3))
+    (switch-to-buffer malyon-status-buffer)
+    (malyon-prepare-status-buffer status)
+    (malyon-opcode-set-cursor 1 1)
+    (other-window 1))
+  (switch-to-buffer malyon-transcript-buffer)
+  (setq malyon-window-configuration (current-window-configuration)))
+
+;; getting and setting the machine state
+
+(defun malyon-current-game-state ()
+  "Return the current state of the interpreter."
+  (vector malyon-instruction-pointer
+         malyon-stack-pointer
+          malyon-frame-pointer
+         (copy-sequence malyon-stack)
+         (copy-sequence malyon-story-file)
+         malyon-game-state-quetzal))
+
+(defun malyon-set-game-state (state)
+  "Installs the given state as the new state of the interpreter."
+  (setq malyon-instruction-pointer       (aref state 0))
+  (setq malyon-stack-pointer             (aref state 1))
+  (setq malyon-frame-pointer             (aref state 2))
+  (setq malyon-stack (copy-sequence      (aref state 3)))
+  (setq malyon-story-file (copy-sequence (aref state 4)))
+  (setq malyon-game-state-quetzal        (aref state 5))
+  (save-excursion
+    (malyon-erase-buffer malyon-status-buffer)
+    (malyon-split-buffer-windows 0)
+    (setq malyon-last-cursor-position-after-input
+         (malyon-point-max malyon-transcript-buffer))))
+
+;; file utilities
+
+(defsubst malyon-write-byte-to-file (byte)
+  "Write a byte to a file."
+  (insert-char (logand 255 byte) 1))
+
+(defsubst malyon-write-word-to-file (word)
+  "Write a word to the last opened file."
+  (insert-char (logand 255 (lsh word -8)) 1)
+  (insert-char (logand 255 word) 1))
+
+(defsubst malyon-write-dword-to-file (dword)
+  "Write a dword to the last opened file."
+  (insert-char (logand 255 (lsh dword -24)) 1)
+  (insert-char (logand 255 (lsh dword -16)) 1)
+  (insert-char (logand 255 (lsh dword -8)) 1)
+  (insert-char (logand 255 dword) 1))
+
+(defsubst malyon-write-chunk-id-to-file (id)
+  "Write a quetzal chunk id to the last opened file."
+  (insert id))
+
+(defsubst malyon-read-byte-from-file ()
+  "Read the next byte from a file."
+  (if (= (point) (point-max))
+      0
+    (forward-char 1)
+    (malyon-char-to-int (malyon-char-before))))
+
+(defsubst malyon-read-word-from-file ()
+  "Read the next word from the last opened file."
+  (logior (lsh (malyon-read-byte-from-file) 8) (malyon-read-byte-from-file)))
+
+(defsubst malyon-read-dword-from-file ()
+  "Read the next dword from the last opened file."
+  (logior (lsh (malyon-read-byte-from-file) 24)
+         (lsh (malyon-read-byte-from-file) 16)
+         (lsh (malyon-read-byte-from-file) 8)
+         (malyon-read-byte-from-file)))
+
+(defsubst malyon-read-chunk-id-from-file ()
+  "Read a quetzal chunk id from the last opened file."
+  (string (malyon-int-to-char (malyon-read-byte-from-file))
+         (malyon-int-to-char (malyon-read-byte-from-file))
+         (malyon-int-to-char (malyon-read-byte-from-file))
+         (malyon-int-to-char (malyon-read-byte-from-file))))
+
+(defun malyon-get-file-name (address)
+  "Retrieves the file name stored at address."
+  (let ((name (make-string (malyon-read-byte address) ? ))
+       (i    0))
+    (while (< i (length name))
+      (aset name i (malyon-read-byte (+ address 1 i)))
+      (setq i (+ 1 i)))
+    name))
+
+;; saving data to disk
+
+(defun malyon-save-file (file &optional table length)
+  "Save the current game state or a memory section to disk."
+  (interactive "FSave file: ")
+  (condition-case nil
+      (save-excursion
+       (set-buffer (create-file-buffer file))
+       (malyon-disable-multibyte)
+       (malyon-erase-buffer)
+       (cond (table (malyon-save-table table length))
+             (malyon-game-state-quetzal
+              (malyon-save-quetzal-state (malyon-current-game-state)))
+             (t
+              (malyon-save-game-state (malyon-current-game-state))))
+       (let ((coding-system-for-write 'binary))
+         (write-file file))
+       (kill-buffer nil)
+       1)
+    (error 0)))
+
+(defun malyon-save-table (table length)
+  "Save the given section of memory to the file."
+  (let ((i 0)
+       (j table))
+    (while (< i length)
+      (malyon-write-byte-to-file (malyon-read-byte j))
+      (setq i (+ 1 i)
+           j (+ 1 j)))))
+
+(defun malyon-save-game-state (state)
+  "Saves the game state to disk."
+  (let ((ip    (aref state 0))
+       (sp    (aref state 1))
+       (fp    (aref state 2))
+       (stack (aref state 3))
+       (mem   (aref state 4))
+       (dyn   (malyon-read-word 14))
+       (i     0))
+    (malyon-write-word-to-file (length malyon-story-file-name))
+    (while (< i (length malyon-story-file-name))
+      (malyon-write-byte-to-file (aref malyon-story-file-name i))
+      (setq i (+ 1 i)))
+    (malyon-write-dword-to-file ip)
+    (malyon-write-word-to-file  sp)
+    (malyon-write-word-to-file  fp)
+    (malyon-write-word-to-file  dyn)
+    (setq i 0)
+    (while (<= i sp)
+      (malyon-write-dword-to-file (aref stack i))
+      (setq i (+ 1 i)))
+    (setq i 0)
+    (while (< i dyn)
+      (malyon-write-byte-to-file (aref mem i))
+      (setq i (+ 1 i)))))
+
+(defun malyon-save-quetzal-state (state)
+  "Saves the game state to disk in quetzal format."
+  (goto-char (point-min))
+  (malyon-save-quetzal-ifhd state)
+  (malyon-save-quetzal-cmem state)
+  (malyon-save-quetzal-stks state)
+  (goto-char (point-min))
+  (malyon-write-chunk-id-to-file "IFZS")
+  (goto-char (point-min))
+  (malyon-write-dword-to-file (- (point-max) (point-min)))
+  (goto-char (point-min))
+  (malyon-write-chunk-id-to-file "FORM"))
+
+(defun malyon-save-quetzal-ifhd (state)
+  "Saves the IFhd chunk of the quetzal format."
+  (malyon-write-chunk-id-to-file "IFhd")
+  (malyon-write-dword-to-file 13)
+  (malyon-write-word-to-file (malyon-read-word 2))
+  (malyon-write-word-to-file (malyon-read-word 18))
+  (malyon-write-word-to-file (malyon-read-word 20))
+  (malyon-write-word-to-file (malyon-read-word 22))
+  (malyon-write-word-to-file (malyon-read-word 28))
+  (malyon-write-byte-to-file (lsh (aref state 0) -16))
+  (malyon-write-byte-to-file (lsh (aref state 0) -8))
+  (malyon-write-byte-to-file (aref state 0))
+  (malyon-write-byte-to-file 0))
+
+(defun malyon-save-quetzal-cmem (state)
+  "Saves the CMem chunk of the quetzal format."
+  (let ((beginning (point-max))
+       (original  (aref malyon-game-state-restart 4))
+       (current   (aref state 4))
+       (size      (malyon-read-word 14))
+       (byte      0)
+       (count     0)
+       (i         0))
+    (goto-char (point-max))
+    (while (< i size)
+      (setq byte (logxor (aref current i) (aref original i)))
+      (if (zerop byte)
+         (setq count (+ 1 count))
+       (while (> count 0)
+         (malyon-write-byte-to-file 0)
+         (setq count (- count 1))
+         (malyon-write-byte-to-file (min 255 count))
+         (setq count (- count (min 255 count))))
+       (malyon-write-byte-to-file byte))
+      (setq i (+ 1 i)))
+    (setq size (- (point-max) beginning))
+    (if (zerop (mod size 2)) '() (malyon-write-byte-to-file 0))
+    (goto-char beginning)
+    (malyon-write-chunk-id-to-file "CMem")
+    (malyon-write-dword-to-file size)))
+
+(defun malyon-save-quetzal-stks (state)
+  "Saves the Stks chunk of the quetzal format."
+  (let ((beginning (point-max))
+       (size      0))
+    (goto-char (point-max))
+    (malyon-save-quetzal-stack-frame (- (aref state 2) 4)
+                                    (aref state 1)
+                                    (aref state 3))
+    (setq size (- (point-max) beginning))
+    (if (zerop (mod size 2)) '() (malyon-write-byte-to-file 0))
+    (goto-char beginning)
+    (malyon-write-chunk-id-to-file "Stks")
+    (malyon-write-dword-to-file size)))
+
+(defun malyon-save-quetzal-stack-frame (fp sp stack)
+  "Saves the stack frames for the Stks chunk."
+  (let* ((frame       (malyon-get-stack-frame fp sp stack))
+        (frame-id    (aref frame 0))
+        (previous-fp (aref frame 1))
+        (previous-sp (aref frame 2))
+        (return-addr (aref frame 3))
+        (result-addr (aref frame 4))
+        (local-vars  (aref frame 5))
+        (num-args    (aref frame 6))
+        (eval-stack  (aref frame 7)))
+    (if (> frame-id 0)
+       (malyon-save-quetzal-stack-frame previous-fp previous-sp stack))
+    (malyon-write-byte-to-file (lsh return-addr -16))
+    (malyon-write-byte-to-file (lsh return-addr -8))
+    (malyon-write-byte-to-file return-addr)
+    (if (zerop frame-id)
+       (malyon-write-byte-to-file 0)
+      (malyon-write-byte-to-file (logior (if result-addr 0 16)
+                                        (length local-vars))))
+    (malyon-write-byte-to-file (if result-addr result-addr 0))
+    (malyon-write-byte-to-file (- (lsh 1 num-args) 1))
+    (malyon-write-word-to-file (length eval-stack))
+    (while (not (null local-vars))
+      (malyon-write-word-to-file (car local-vars))
+      (setq local-vars (cdr local-vars)))
+    (while (not (null eval-stack))
+      (malyon-write-word-to-file (car eval-stack))
+      (setq eval-stack (cdr eval-stack)))))
+
+;; restoring data from disk
+
+(defvar malyon-restore-data-error nil
+  "An error message if restoring data from a file failed.")
+
+(defvar malyon-restore-quetzal-stack nil
+  "A temporary stack for restoring quetzal game states.")
+
+(defvar malyon-restore-quetzal-stack-pointer nil
+  "A temporary stack pointer for restoring quetzal game states.")
+
+(defvar malyon-restore-quetzal-frame-pointer nil
+  "A temporary frame-pointer for restoring quetzal game states.")
+
+(defun malyon-restore-file (file &optional table length)
+  "Restore a game state or a memory section from disk."
+  (interactive "fLoad file: ")
+  (if (not (and (file-exists-p file) (file-readable-p file)))
+      0
+    (condition-case nil
+       (save-excursion
+         (setq malyon-restore-data-error nil)
+         (set-buffer (create-file-buffer file))
+         (malyon-disable-multibyte)
+         (malyon-erase-buffer)
+         (let ((coding-system-for-read 'binary))
+           (insert-file-contents file))
+         (goto-char (point-min))
+         (if table
+             (malyon-restore-table table length)
+           (let* ((first  (malyon-read-chunk-id-from-file))
+                  (second (malyon-read-dword-from-file))
+                  (third  (malyon-read-chunk-id-from-file)))
+             (if (and (string= "FORM" first) (string= "IFZS" third))
+                 (malyon-restore-quetzal-state (+ 8 second))
+               (goto-char (point-min))
+               (malyon-restore-game-state))))
+         (kill-buffer nil)
+         (if (null malyon-restore-data-error)
+             2
+           (message malyon-restore-data-error)
+           0))
+      (error 0))))
+
+(defun malyon-restore-table (table length)
+  "Restore the given section of memory from a file."
+  (let ((i 0)
+       (j table))
+    (while (< i length)
+      (malyon-store-byte j (malyon-read-byte-from-file))
+      (setq i (+ 1 i)
+           j (+ 1 j)))))
+
+(defun malyon-restore-game-state ()
+  "Restore a saved game state from disk."
+  (let ((len   0)
+       (name  0)
+       (story 0)
+       (ip    0)
+       (sp    0)
+       (fp    0)
+       (dyn   0)
+       (stack (copy-sequence malyon-stack))
+       (mem   (copy-sequence malyon-story-file))
+       (i     0))
+    (setq len (malyon-read-word-from-file))
+    (setq name (make-string len ? ))
+    (while (< i len)
+      (aset name i (malyon-read-byte-from-file))
+      (setq i (+ 1 i)))
+    (setq ip  (malyon-read-dword-from-file))
+    (setq sp  (malyon-read-word-from-file))
+    (setq fp  (malyon-read-word-from-file))
+    (setq dyn (malyon-read-word-from-file))
+    (setq i 0)
+    (while (<= i sp)
+      (aset stack i (malyon-read-dword-from-file))
+      (setq i (+ 1 i)))
+    (setq i 0)
+    (while (< i dyn)
+      (aset mem i (malyon-read-byte-from-file))
+      (setq i (+ 1 i)))
+    (setq name  (file-name-nondirectory name))
+    (setq story (file-name-nondirectory malyon-story-file-name))
+    (if (or (string-match name story) (string-match story name))
+       (malyon-set-game-state (vector ip sp fp stack mem nil))
+      (setq malyon-restore-data-error "Invalid save file."))))
+
+(defun malyon-restore-quetzal-state (size)
+  "Restore a saved quetzal game state from disk."
+  (let ((chunk-id  nil)
+       (chunk-len 0)
+       (ip        0)
+       (memory    nil)
+       (stack     nil)
+       (beginning 0))
+    (while (< (point) size)
+      (setq chunk-id  (malyon-read-chunk-id-from-file))
+      (setq chunk-len (malyon-read-dword-from-file))
+      (setq beginning (point))
+      (cond ((string= chunk-id "IFhd")
+            (setq ip (malyon-restore-quetzal-ifhd chunk-len)))
+           ((string= chunk-id "CMem")
+            (setq memory (malyon-restore-quetzal-cmem chunk-len)))
+           ((string= chunk-id "UMem")
+            (setq memory (malyon-restore-quetzal-umem chunk-len)))
+           ((string= chunk-id "Stks")
+            (setq stack (malyon-restore-quetzal-stks chunk-len))))
+      (if (zerop (mod chunk-len 2)) '() (setq chunk-len (+ 1 chunk-len)))
+      (goto-char (+ beginning chunk-len)))
+    (cond ((and ip memory stack)
+          (malyon-set-game-state (vector ip
+                                         (aref stack 0)
+                                         (aref stack 1)
+                                         (aref stack 2)
+                                         memory
+                                         t)))
+         ((null malyon-restore-data-error)
+          (setq malyon-restore-data-error "invalid quetzal file.")))))
+
+(defun malyon-restore-quetzal-ifhd (size)
+  "Restore an IFhd chunk from disk. Return the instruction pointer."
+  (if (and (= (malyon-read-word-from-file) (malyon-read-word 2))
+          (= (malyon-read-word-from-file) (malyon-read-word 18))
+          (= (malyon-read-word-from-file) (malyon-read-word 20))
+          (= (malyon-read-word-from-file) (malyon-read-word 22))
+          (= (malyon-read-word-from-file) (malyon-read-word 28)))
+      (logior (lsh (malyon-read-byte-from-file) 16)
+             (lsh (malyon-read-byte-from-file) 8)
+             (malyon-read-byte-from-file))
+    (setq malyon-restore-data-error "quetzal file doesn't belong to game.")
+    nil))
+
+(defun malyon-restore-quetzal-cmem (size)
+  "Restore a CMem chunk from disk. Return the entire memory layout."
+  (let ((memory   (copy-sequence (aref malyon-game-state-restart 4)))
+       (max-size (+ (point) size))
+       (byte     0)
+       (i        0))
+    (while (< (point) max-size)
+      (setq byte (malyon-read-byte-from-file))
+      (if (zerop byte)
+         (setq i (+ 1 i (malyon-read-byte-from-file)))
+       (aset memory i (logxor byte (aref memory i)))
+       (setq i (+ 1 i))))
+    memory))
+
+(defun malyon-restore-quetzal-umem (size)
+  "Restore a UMem chunk from disk. Return the entire memory layout."
+  (let ((memory (copy-sequence (aref malyon-game-state-restart 4)))
+       (i      0))
+    (while (< i size)
+      (aset memory i (malyon-read-byte-from-file))
+      (setq i (+ 1 i)))
+    memory))
+
+(defun malyon-restore-quetzal-stks (size)
+  "Restore a Stks chunk from disk. Return a vector containing the 
+stack pointer, the frame pointer, and the stack itself."
+  (let ((i 0) (frame-id 0))
+    (setq malyon-restore-quetzal-stack
+         (copy-sequence (aref malyon-game-state-restart 3)))
+    (setq malyon-restore-quetzal-stack-pointer -1)
+    (setq malyon-restore-quetzal-frame-pointer 2)
+    (while (< i size)
+      (let* ((beginning     (point))
+            (return3       (malyon-read-byte-from-file))
+            (return2       (malyon-read-byte-from-file))
+            (return1       (malyon-read-byte-from-file))
+            (return-addr   (logior (lsh return3 16) (lsh return2 8) return1))
+            (result-locals (malyon-read-byte-from-file))
+            (has-result    (zerop (logand 16 result-locals)))
+            (num-locals    (logand 15 result-locals))
+            (result-addr   (malyon-read-byte-from-file))
+            (arg-flags     (+ 1 (malyon-read-byte-from-file)))
+            (num-args      0)
+            (eval-size     (malyon-read-word-from-file))
+            (local-vars    '())
+            (eval-stack    '()))
+       (while (> num-locals 0)
+         (setq local-vars (cons (malyon-read-word-from-file) local-vars))
+         (setq num-locals (- num-locals 1)))
+       (while (> eval-size 0)
+         (setq eval-stack (cons (malyon-read-word-from-file) eval-stack))
+         (setq eval-size (- eval-size 1)))
+       (while (> arg-flags 1)
+         (setq arg-flags (lsh arg-flags -1))
+         (setq num-args (+ num-args 1)))
+       (malyon-push-stack-frame frame-id
+                                return-addr
+                                (if (zerop frame-id)
+                                    nil
+                                  (if has-result result-addr nil))
+                                (reverse local-vars)
+                                num-args
+                                (reverse eval-stack))
+       (setq frame-id (+ 1 frame-id))
+       (setq i (+ i (- (point) beginning)))))
+    (vector malyon-restore-quetzal-stack-pointer
+           malyon-restore-quetzal-frame-pointer
+           malyon-restore-quetzal-stack)))
+
+;; object table management
+
+(defsubst malyon-object-address (object)
+  "Compute the address at which the object is stored."
+  (+ malyon-object-table
+     (* 2 malyon-object-properties)
+     (* malyon-object-table-entry-size (- object 1))))
+
+(defsubst malyon-object-read-parent (address)
+  "Return the parent."
+  (if (< malyon-story-version 5)
+      (malyon-read-byte (+ 4 address))
+    (malyon-read-word (+ 6 address))))
+
+(defsubst malyon-object-read-sibling (address)
+  "Return the next sibling."
+  (if (< malyon-story-version 5)
+      (malyon-read-byte (+ 5 address))
+    (malyon-read-word (+ 8 address))))
+
+(defsubst malyon-object-read-child (address)
+  "Return the first child."
+  (if (< malyon-story-version 5)
+      (malyon-read-byte (+ 6 address))
+    (malyon-read-word (+ 10 address))))
+
+(defsubst malyon-object-store-parent (address value)
+  "Set the parent."
+  (if (< malyon-story-version 5)
+      (malyon-store-byte (+ 4 address) value)
+    (malyon-store-word (+ 6 address) value)))
+
+(defsubst malyon-object-store-sibling (address value)
+  "Set the next sibling."
+  (if (< malyon-story-version 5)
+      (malyon-store-byte (+ 5 address) value)
+    (malyon-store-word (+ 8 address) value)))
+
+(defsubst malyon-object-store-child (address value)
+  "Set the first child."
+  (if (< malyon-story-version 5)
+      (malyon-store-byte (+ 6 address) value)
+    (malyon-store-word (+ 10 address) value)))
+
+(defun malyon-find-property (object property)
+  "Return the address of the object's property, or 0 if it doesn't exist."
+  (let ((next (malyon-first-property object))
+       (number 0))
+    (setq number (logand (malyon-read-byte next) malyon-object-properties))
+    (while (> number property)
+      (setq next (malyon-next-property next))
+      (setq number (logand (malyon-read-byte next) malyon-object-properties)))
+    (if (= number property) next 0)))
+
+(defun malyon-first-property (object)
+  "Get the address of the object's first property."
+  (let ((header (malyon-read-word (+ malyon-object-property-offset
+                                    (malyon-object-address object)))))
+    (+ header 1 (* 2 (malyon-read-byte header)))))
+
+(defun malyon-next-property (property)
+  "Get the address of the following property."
+  (let ((size (malyon-read-byte property))
+       (addr (+ property 1)))
+    (+ 1 addr (cond ((< malyon-story-version 5) (lsh size -5))
+                   ((zerop (logand 128 size))  (lsh size -6))
+                   (t
+                    (let ((second (logand 63 (malyon-read-byte addr))))
+                      (if (= 0 second) 64 second)))))))
+
+(defun malyon-remove-object (object)
+  "Remove the object from the children list of its parent."
+  (let* ((address (malyon-object-address object))
+        (parent  (malyon-object-read-parent address))
+        (sibling (malyon-object-read-sibling address)))
+    (malyon-object-store-parent address 0)
+    (malyon-object-store-sibling address 0)
+    (if (/= parent 0)
+       (let ((parent-addr (malyon-object-address parent)))
+         (let ((children (malyon-object-read-child parent-addr)))
+           (if (or (= children 0) (= children object))
+               (malyon-object-store-child parent-addr sibling)
+             (let ((this (malyon-object-address children)))
+               (let ((next (malyon-object-read-sibling this)))
+                 (while (/= next object)
+                   (setq this (malyon-object-address next))
+                   (setq next (malyon-object-read-sibling this)))
+                 (malyon-object-store-sibling this sibling)))))))))
+
+;; function calls and code branches
+
+(defun malyon-call-routine (routine arguments &optional result)
+  "Call a routine with the given arguments and return its result."
+  (if (= routine 0)
+      (if result (malyon-store-variable result 0) 0)
+    (malyon-push-stack (if result 0 1))
+    (malyon-push-stack (if result result 0))
+    (malyon-push-stack malyon-instruction-pointer)
+    (malyon-push-stack
+     (logior (lsh (- malyon-stack-pointer malyon-frame-pointer) 8)
+            (length arguments)))
+    (setq malyon-instruction-pointer (* malyon-packed-multiplier routine))
+    (let ((args (malyon-read-code-byte)) (value nil))
+      (if malyon-game-state-quetzal
+         (let ((id (lsh (aref malyon-stack malyon-frame-pointer) -8)))
+           (malyon-push-stack (logior (lsh (+ 1 id) 8) args))))
+      (setq malyon-frame-pointer malyon-stack-pointer)
+      (while (> args 0)
+       (setq value (if (< malyon-story-version 5) (malyon-read-code-word) 0))
+       (malyon-push-stack (if (null arguments) value (car arguments)))
+       (setq arguments (cdr arguments))
+       (setq args (- args 1))))))
+
+(defun malyon-jump-if (condition)
+  "Jump depending on the condition and the following jump data."
+  (let ((byte   (malyon-read-code-byte))
+       (offset nil)
+       (iftrue nil))
+    (setq iftrue (/= 0 (logand byte 128)))
+    (setq offset (logand byte 63))
+    (if (= 0 (logand byte 64)) 
+       (progn
+         (setq offset (logior (lsh offset 8) (malyon-read-code-byte)))
+         (if (>= offset 8192) (setq offset (- offset 16384)))))
+    (if (or (and iftrue condition) (and (not iftrue) (not condition)))
+       (progn
+         (cond ((= offset 0) (malyon-opcode-rfalse))
+               ((= offset 1) (malyon-opcode-rtrue))
+               (t            (setq
+                              malyon-instruction-pointer 
+                              (+ malyon-instruction-pointer offset -2))))))))
+
+(defun malyon-return (value)
+  "Return from a routine."
+  (setq malyon-stack-pointer malyon-frame-pointer)
+  (if malyon-game-state-quetzal (malyon-pop-stack))
+  (setq malyon-frame-pointer
+       (- malyon-stack-pointer 1 (lsh (malyon-pop-stack) -8)))
+  (setq malyon-instruction-pointer (malyon-pop-stack))
+  (let ((result (malyon-pop-stack))
+       (store  (malyon-pop-stack)))
+    (if (zerop store)
+       (malyon-return-store result value)
+      (malyon-return-ignore result value))))
+
+(defun malyon-return-ignore (where value)
+  "Return from a routine ignoring the result.")
+
+(defun malyon-return-store (where value)
+  "Return from a routine storing the result."
+  (malyon-store-variable where value))
+
+(defun malyon-push-initial-frame ()
+  "Push the initial stack frame required in quetzal mode."
+  (if malyon-game-state-quetzal
+      (progn
+       (malyon-push-stack 1)
+       (malyon-push-stack 0)
+       (malyon-push-stack 0)
+       (malyon-push-stack 0)
+       (malyon-push-stack 0))))
+
+(defun malyon-get-stack-frame (fp sp stack)
+  "Return a decoded stack frame in quetzal mode.
+The result is a vector containing the frame id, the fp of the
+previous frame, the sp of the previous frame, the return address,
+the result variable if any, a list of local variables, the number
+of arguments, and a list of the evaluation stack elements."
+  (let* ((has-result   (zerop (aref stack fp)))
+        (result-addr  (if has-result (aref stack (+ 1 fp)) nil))
+        (return-addr  (aref stack (+ 2 fp)))
+        (offset       (lsh (aref stack (+ 3 fp)) -8))
+        (num-args     (logand 255 (aref stack (+ 3 fp))))
+        (frame-id     (lsh (aref stack (+ 4 fp)) -8))
+        (num-locals   (logand 255 (aref stack (+ 4 fp))))
+        (start-locals (+ 5 fp))
+        (start-eval   (+ 5 fp num-locals))
+        (local-vars   '())
+        (eval-stack   '()))
+    (if (not (zerop num-locals))
+       (setq local-vars 
+             (malyon-vector-to-list stack start-locals start-eval)))
+    (if (> sp start-eval)
+       (setq eval-stack 
+             (malyon-vector-to-list stack start-eval (+ 1 sp))))
+    (vector frame-id
+           (- fp offset 2)
+           (- fp 1)
+           return-addr
+           result-addr
+           local-vars
+           num-args
+           eval-stack)))
+
+(defsubst malyon-restore-quetzal-push-stack (value)
+  "Push a value onto the restore quetzal stack."
+  (setq malyon-restore-quetzal-stack-pointer
+       (+ malyon-restore-quetzal-stack-pointer 1))
+  (aset malyon-restore-quetzal-stack
+       malyon-restore-quetzal-stack-pointer
+       value))
+
+(defun malyon-push-stack-frame
+  (frame-id return-addr result local-vars num-args eval-stack)
+  "Pushes a new stack frame in quetzal mode."
+  (malyon-restore-quetzal-push-stack (if result 0 1))
+  (malyon-restore-quetzal-push-stack (if result result 0))
+  (malyon-restore-quetzal-push-stack return-addr)
+  (malyon-restore-quetzal-push-stack
+   (logior (lsh (- malyon-restore-quetzal-stack-pointer
+                  malyon-restore-quetzal-frame-pointer) 8)
+          num-args))
+  (malyon-restore-quetzal-push-stack
+   (logior (lsh frame-id 8) (length local-vars)))
+  (setq malyon-restore-quetzal-frame-pointer
+       malyon-restore-quetzal-stack-pointer)
+  (while (not (null local-vars))
+    (malyon-restore-quetzal-push-stack (car local-vars))
+    (setq local-vars (cdr local-vars)))
+  (while (not (null eval-stack))
+    (malyon-restore-quetzal-push-stack (car eval-stack))
+    (setq eval-stack (cdr eval-stack))))
+
+;; other stuff
+
+(defvar malyon-aread-text nil
+  "Text buffer for user input.")
+
+(defvar malyon-aread-parse nil
+  "Parse buffer for user input.")
+
+(defvar malyon-aread-beginning-of-line nil
+  "The beginning of the input line.")
+
+;; execution
+
+(defun malyon-interpreter ()
+  "Run the z code interpreter on the given story file."
+  (condition-case nil
+      (progn
+       (malyon-restore-window-configuration)
+       (if malyon-story-file
+           (catch 'malyon-end-of-interpreter-loop
+             (setq malyon-last-cursor-position-after-input
+                   (malyon-point-max malyon-transcript-buffer))
+             (malyon-execute))))
+    (error
+     (malyon-fatal-error "unspecified internal runtime error."))))
+
+(defsubst malyon-fetch-variable-operands (specifier)
+  "Fetch a variable number of operands based on the specifier argument."
+  (let ((var  (logand specifier 49152))
+       (op   '()))
+    (setq specifier (logand 65535 specifier))
+    (while (/= 0 specifier)
+      (cond ((= var 0)     (setq op (cons (malyon-read-code-word) op)))
+           ((= var 16384) (setq op (cons (malyon-read-code-byte) op)))
+           ((= var 32768) (setq op (cons (malyon-read-variable
+                                          (malyon-read-code-byte)) op)))
+           (t             (setq specifier 0)))
+      (setq specifier (logand 65535 (lsh specifier 2)))
+      (setq var  (logand specifier 49152)))
+    (nreverse op)))
+
+(defsubst malyon-fetch-extended (opcode)
+  "Fetch operands for an extended instruction."
+  (malyon-fetch-variable-operands
+   (logior (lsh (malyon-read-code-byte) 8) 255)))
+
+(defsubst malyon-fetch-variable (opcode)
+  "Fetch operands for a variable instruction."
+  (malyon-fetch-variable-operands
+   (if (or (= opcode 236) (= opcode 250))
+       (malyon-read-code-word)
+     (logior (lsh (malyon-read-code-byte) 8) 255))))
+
+(defsubst malyon-fetch-short (opcode)
+  "Fetch operands for a short instruction."
+  (let ((op (logand opcode 48)))
+    (cond ((= op 0)  (list (malyon-read-code-word)))
+         ((= op 16) (list (malyon-read-code-byte)))
+         ((= op 32) (list (malyon-read-variable (malyon-read-code-byte)))))))
+
+(defsubst malyon-fetch-long (instr)
+  "Fetch operands for a long instruction."
+  (let ((byte1 (malyon-read-code-byte))
+       (byte2 (malyon-read-code-byte)))
+    (list (if (= (logand instr 64) 0) byte1 (malyon-read-variable byte1))
+         (if (= (logand instr 32) 0) byte2 (malyon-read-variable byte2)))))
+
+(defun malyon-execute ()
+  "Execute z code instructions.
+Load the next instruction opcode and its operands and execute it.
+Repeat ad infinitum."
+  (let ((opcode) (operands)); (pc))
+    (while t
+;      (setq pc malyon-instruction-pointer)
+      (setq opcode (malyon-read-code-byte))
+      (setq operands (cond ((=  opcode 190) 
+                           (setq opcode (+ 256 (malyon-read-code-byte)))
+                           (malyon-fetch-extended opcode))
+                          ((>= opcode 192)
+                           (malyon-fetch-variable opcode))
+                          ((>= opcode 128)
+                           (malyon-fetch-short opcode))
+                          (t
+                           (malyon-fetch-long opcode))))
+;      (malyon-trace-opcode pc opcode operands)
+      (apply (aref malyon-opcodes opcode) operands))))
+
+;; opcodes
+
+(defsubst malyon-number (n)
+  "Convert an unsigned number into a signed one."
+  (if (< n 32768) n (- n 65536)))
+
+(defun malyon-opcode-add (a b)
+  "Addition."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (+ (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-and (a b)
+  "Bitwise and."
+  (malyon-store-variable (malyon-read-code-byte) (logand a b)))
+
+(defun malyon-opcode-aread (text parse &optional time routine)
+  "Read input text."
+  (setq malyon-aread-text text)
+  (setq malyon-aread-parse parse)
+  (goto-char (point-max))
+  (setq malyon-aread-beginning-of-line (point))
+; Some games violate these assumptions for the "Quit" question.
+;  (if (> 3 (malyon-read-byte text))
+;      (malyon-fatal-error "text buffer less than 3 bytes."))
+;  (if (and (not (zerop parse)) (> 2 (malyon-read-byte parse)))
+;      (malyon-fatal-error "parse buffer less than 2 bytes."))
+  (malyon-more malyon-keymap-read)
+  (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-input))
+
+(defun malyon-opcode-art-shift (value places)
+  "Arithmetic shift."
+  (malyon-store-variable (malyon-read-code-byte) (ash value places)))
+
+(defun malyon-opcode-buffer-mode (mode)
+  "Toggles buffering of text in the transcript window."
+  (setq malyon-transcript-buffer-buffered (/= 0 mode)))
+
+(defun malyon-opcode-calln (routine &rest arguments)
+  "Call a routine and ignore the result."
+  (malyon-call-routine routine arguments))
+
+(defun malyon-opcode-calls (routine &rest arguments)
+  "Call a routine and store the result."
+  (malyon-call-routine routine arguments (malyon-read-code-byte)))
+
+(defun malyon-opcode-catch ()
+  "Return the current stack frame."
+  (malyon-store-variable
+   (malyon-read-code-byte)
+   (if malyon-game-state-quetzal
+       (lsh (aref malyon-stack malyon-frame-pointer) -8)
+     malyon-frame-pointer)))
+
+(defun malyon-opcode-check-arg-count (count)
+  "Tests the number of arguments passed to routine."
+  (malyon-jump-if
+   (<= count (logand 255 (aref malyon-stack 
+                              (if malyon-game-state-quetzal
+                                  (- malyon-frame-pointer 1)
+                                malyon-frame-pointer))))))
+
+(defun malyon-opcode-check-unicode (char)
+  "Check whether the given character is valid for input/output."
+  (malyon-store-variable (malyon-read-code-byte) 0))
+
+(defun malyon-opcode-clear-attr (object attribute)
+  "Clear the given attribute in the given object."
+  (let ((attributes (malyon-object-address object))
+       (byte       (lsh attribute -3)))
+    (malyon-store-byte (+ attributes byte)
+                      (logand (malyon-read-byte (+ attributes byte))
+                              (logxor (lsh 128 (- (logand attribute 7)))
+                                      255)))))
+
+(defun malyon-opcode-copy-table (first second size)
+  "Copies first table onto second one."
+  (let* ((length  (abs (malyon-number size)))
+        (zero    (zerop second))
+        (forward (or (< (malyon-number size) 0) (> first second)))
+        (i       0)
+        (a       (if forward first (+ first length -1)))
+        (b       (if forward (if zero first second) (+ second length -1))))
+    (while (< i length)
+      (malyon-store-byte b (if zero 0 (malyon-read-byte a)))
+      (setq i (+ i 1)
+           a (if forward (+ a 1) (- a 1))
+           b (if forward (+ b 1) (- b 1))))))
+
+(defun malyon-opcode-dec (var)
+  "Decrement variable."
+  (malyon-store-variable var
+                        (- (malyon-number (malyon-read-variable var)) 1)))
+
+(defun malyon-opcode-dec-chk (variable threshold)
+  "Decrement variable and jump if it's less than the given value."
+  (let ((value (malyon-number (malyon-read-variable variable))))
+    (malyon-store-variable variable (- value 1))
+    (malyon-jump-if (< (- value 1) (malyon-number threshold)))))
+
+(defun malyon-opcode-div (a b)
+  "Division."
+  (if (zerop b) (malyon-fatal-error "division by 0."))
+  (malyon-store-variable (malyon-read-code-byte)
+                        (/ (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-encode-text (text length from encoded)
+  "Encode the zscii text starting at from with the given length.
+The result is stored at encoded."
+  (let* ((i     length)
+        (j     encoded)
+        (l     '())
+        (word  '()))
+    (while (< 0 i)
+      (setq l (cons (malyon-read-byte (+ text from i -1)) l)
+           i (- i 1)))
+    (setq word (malyon-encode-dictionary-word
+               (append (malyon-mapcan 'malyon-encode-into-ztext l)
+                       '(5 5 5 5 5 5 5 5))))
+    (while (< i 6)
+      (malyon-store-byte j (car l))
+      (setq i (+ 1 i)
+           j (+ 1 j)
+           l (cdr word)))))
+
+(defun malyon-opcode-erase-line (value)
+  "Erases the rest of the line."
+  (if (= value 1)
+      (if (eq malyon-transcript-buffer (current-buffer))
+         (kill-line nil)
+       (save-excursion
+         (let ((i (current-column)))
+           (while (<= i malyon-max-column)
+             (insert ? )
+             (delete-char 1)
+             (setq i (+ 1 i))))))))
+
+(defun malyon-opcode-erase-window (window)
+  "Erase the contents of the given window."
+  (save-excursion
+    (let ((w (malyon-number window)))
+      (if (or (= w 0) (= w -1) (= w -2))
+         (malyon-erase-buffer malyon-transcript-buffer))
+      (if (or (= w 1) (= w -1) (= w -2))
+         (malyon-erase-buffer malyon-status-buffer))
+      (if (= w -1)
+         (malyon-split-buffer-windows 0)))
+    (setq malyon-last-cursor-position-after-input
+         (malyon-point-max malyon-transcript-buffer))))
+
+(defun malyon-opcode-get-child (object)
+  "Get the first child of the given object and jump."
+  (let ((child (malyon-object-read-child (malyon-object-address object))))
+    (malyon-store-variable (malyon-read-code-byte) child)
+    (malyon-jump-if (/= 0 child))))
+
+(defun malyon-opcode-get-cursor (array)
+  "Retrieves the current cursor position."
+  (save-excursion
+    (set-buffer malyon-status-buffer)
+    (malyon-store-word array (- (count-lines (point-min) (point)) 1))
+    (malyon-store-word (+ 2 array) (+ 1 (current-column)))))
+
+(defun malyon-opcode-get-next-prop (object property)
+  "Retrieve the first or next property id of object."
+  (let ((next (malyon-first-property object))
+       (number 0))
+    (if (zerop property)
+       '()
+      (setq number (logand (malyon-read-byte next)
+                          malyon-object-properties))
+      (setq next (malyon-next-property next))
+      (while (> number property)
+       (setq number (logand (malyon-read-byte next)
+                            malyon-object-properties))
+       (setq next (malyon-next-property next)))
+      (if (/= number property)
+         (malyon-fatal-error "property does not exist.")))
+    (setq number (logand (malyon-read-byte next) malyon-object-properties))
+    (malyon-store-variable (malyon-read-code-byte) number)))
+    
+(defun malyon-opcode-get-parent (object)
+  "Get the parent of the given object."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (malyon-object-read-parent
+                         (malyon-object-address object))))
+
+(defun malyon-opcode-get-prop (object property)
+  "Get the value of the object's property."
+  (let* ((address (malyon-find-property object property))
+        (size    (malyon-read-byte address)))
+    (malyon-store-variable 
+     (malyon-read-code-byte)
+     (cond ((zerop address)
+           (malyon-read-word (+ malyon-object-table (* 2 (- property 1)))))
+          ((and (<  malyon-story-version 5) (zerop (lsh size -5)))
+           (malyon-read-byte (+ address 1)))
+          ((and (>= malyon-story-version 5) (zerop (logand 192 size)))
+           (malyon-read-byte (+ address 1)))
+          (t
+           (malyon-read-word (+ address 1)))))))
+
+(defun malyon-opcode-get-prop-addr (object property)
+  "Get the address of the object's property."
+  (let* ((address (malyon-find-property object property))
+        (size    (malyon-read-byte address))
+        (offset  (if (< malyon-story-version 5)
+                     1
+                   (if (zerop (logand 128 size)) 1 2))))
+    (malyon-store-variable (malyon-read-code-byte)
+                          (if (zerop address) 0 (+ address offset)))))
+
+(defun malyon-opcode-get-prop-len (property)
+  "Get the length of the object's property."
+  (let ((size (malyon-read-byte (- property 1))))
+    (malyon-store-variable
+     (malyon-read-code-byte)
+     (cond ((< malyon-story-version 5) (+ 1 (lsh size -5)))
+          ((zerop (logand 128 size))  (+ 1 (lsh size -6)))
+          ((zerop (logand  63 size))  64)
+          (t                          (logand 63 size))))))
+
+(defun malyon-opcode-get-sibling (object)
+  "Get the next object in the tree and jump."
+  (let ((sibling (malyon-object-read-sibling (malyon-object-address object))))
+    (malyon-store-variable (malyon-read-code-byte) sibling)
+    (malyon-jump-if (/= 0 sibling))))
+
+(defun malyon-opcode-illegal (&rest ignore)
+  "Print an error message and exit the interpreter."
+  (malyon-fatal-error "illegal opcode."))
+
+(defun malyon-opcode-inc (var)
+  "Increment variable."
+  (malyon-store-variable var
+                        (+ (malyon-number (malyon-read-variable var)) 1)))
+
+(defun malyon-opcode-inc-chk (variable threshold)
+  "Increment variable and jump if it's greater than the given value."
+  (let ((value (malyon-number (malyon-read-variable variable))))
+    (malyon-store-variable variable (+ value 1))
+    (malyon-jump-if (> (+ value 1) (malyon-number threshold)))))
+
+(defun malyon-opcode-input-stream (number)
+  "Select the given input stream. Only the keyboard is supported."
+  (if (zerop (malyon-number number))
+      '()
+    (message "Only the keyboard is supported as an input stream.")))
+
+(defun malyon-opcode-insert-obj (object destination)
+  "Insert an object into the children list of another."
+  (let ((child  (malyon-object-address object))
+       (parent (malyon-object-address destination)))
+    (malyon-remove-object object)
+    (malyon-object-store-parent  child destination)
+    (malyon-object-store-sibling child (malyon-object-read-child parent))
+    (malyon-object-store-child   parent object)))
+
+(defun malyon-opcode-je (a &rest rest)
+  "Jump if first operand equals any of the following."
+  (malyon-jump-if (member (malyon-number a) (mapcar 'malyon-number rest))))
+
+(defun malyon-opcode-jg (a b)
+  "Jump if first operand > second operand."
+  (malyon-jump-if (> (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-jin (child parent)
+  "Jump if second object is parent of the first one."
+    (malyon-jump-if
+     (= parent (malyon-object-read-parent (malyon-object-address child)))))
+
+(defun malyon-opcode-jl (a b)
+  "Jump if first operand < second operand."
+  (malyon-jump-if (< (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-jump (offset)
+  "Jump unconditionally."
+  (setq malyon-instruction-pointer (+ malyon-instruction-pointer
+                                     (malyon-number offset) -2)))
+
+(defun malyon-opcode-jz (a)
+  "Jump if operand = 0."
+  (malyon-jump-if (zerop a)))
+
+(defun malyon-opcode-load (variable)
+  "Load a variable."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (malyon-read-variable variable)))
+
+(defun malyon-opcode-loadb (array index)
+  "Load an array element into a variable."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (malyon-read-byte (+ array index))))
+
+(defun malyon-opcode-loadw (array index)
+  "Load an array element into a variable."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (malyon-read-word (+ array (* 2 index)))))
+
+(defun malyon-opcode-log-shift (value places)
+  "Logical shift."
+  (malyon-store-variable (malyon-read-code-byte) (lsh value places)))
+
+(defun malyon-opcode-mod (a b)
+  "Modulo."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (mod (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-mul (a b)
+  "Multiplication."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (* (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-new-line ()
+  "Print a newline."
+  (malyon-newline))
+
+(defun malyon-opcode-nop (&rest ignore)
+  "Do nothing.")
+
+(defun malyon-opcode-not (a)
+  "Bitwise not."
+  (malyon-store-variable (malyon-read-code-byte) (logand 65535 (lognot a))))
+
+(defun malyon-opcode-or (a b)
+  "Bitwise or."
+  (malyon-store-variable (malyon-read-code-byte) (logior a b)))
+
+(defun malyon-opcode-output-stream (stream &optional table)
+  "Select an output stream."
+  (let ((stream (malyon-number stream)))
+    (cond ((< 0 stream) (malyon-add-output-stream stream table))
+         ((> 0 stream) (malyon-remove-output-stream (- stream))))))
+
+(defun malyon-opcode-piracy ()
+  "Piracy check, effectively an unconditional jump."
+  (malyon-jump-if 1))
+
+(defun malyon-opcode-pop ()
+  "Pop a value off the stack."
+  (malyon-pop-stack))
+
+(defun malyon-opcode-print ()
+  "Print a string."
+  (setq malyon-instruction-pointer
+       (malyon-print-text malyon-instruction-pointer)))
+
+(defun malyon-opcode-print-addr (address)
+  "Print a string."
+  (malyon-print-text address))
+
+(defun malyon-opcode-print-char (c)
+  "Print a character."
+  (malyon-print (char-to-string c)))
+
+(defun malyon-opcode-print-num (n)
+  "Print a number."
+  (malyon-print (number-to-string (malyon-number n))))
+
+(defun malyon-opcode-print-obj (obj)
+  "Print the short name of the object."
+  (malyon-print-text
+   (+ 1 (malyon-read-word (+ malyon-object-property-offset
+                            (malyon-object-address obj))))))
+
+(defun malyon-opcode-print-paddr (address)
+  "Print a string."
+  (malyon-print-text (* malyon-packed-multiplier address)))
+
+(defun malyon-opcode-print-ret ()
+  "Print a string, print a newline, return true/1."
+  (setq malyon-instruction-pointer
+       (malyon-print-text malyon-instruction-pointer))
+  (malyon-newline)
+  (malyon-return 1))
+
+(defun malyon-opcode-print-table (text width &optional height skip)
+  "Print the given table."
+  (if (not height) (setq height 1))
+  (if (not skip)   (setq skip 0))
+  (let ((column  (current-column))
+       (address text)
+       (y       0)
+       (x       0))
+    (while (< y height)
+      (if (zerop y)
+         '()
+       (malyon-newline)
+       (malyon-print-characters (make-string column ? )))
+      (setq x 0)
+      (while (< x width)
+       (malyon-output-character (malyon-read-byte address))
+       (setq address (+ 1 address))
+       (setq x (+ 1 x)))
+      (setq address (+ skip address))
+      (setq y (+ 1 y)))))
+
+(defun malyon-opcode-print-unicode (char)
+  "Prints a unicode character.")
+
+(defun malyon-opcode-pull (variable)
+  "Pull value off stack."
+  (malyon-store-variable variable (malyon-pop-stack)))
+
+(defun malyon-opcode-push (value)
+  "Push value onto stack."
+  (malyon-push-stack value))
+
+(defun malyon-opcode-put-prop (object property value)
+  "Set the object's property to the given value."
+  (let* ((address (malyon-find-property object property))
+        (size    (malyon-read-byte address)))
+    (cond ((= address 0)
+          (malyon-fatal-error "property does not exist."))
+         ((and (<  malyon-story-version 5) (zerop (lsh size -5)))
+          (malyon-store-byte (+ 1 address) (logand 255 value)))
+         ((and (>= malyon-story-version 5) (zerop (logand size 192)))
+          (malyon-store-byte (+ 1 address) (logand 255 value)))
+         (t
+          (malyon-store-word (+ 1 address) value)))))
+
+(defun malyon-opcode-quit ()
+  "End the game immediately."
+  (malyon-adjust-transcript)
+  (malyon-cleanup)
+  (throw 'malyon-end-of-interpreter-loop 'malyon-opcode-quit))
+
+(defun malyon-opcode-random (limit)
+  "Generate a random number or set the seed value."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (if (>= 0 (malyon-number limit))
+                            0
+                          (+ 1 (random (malyon-number limit))))))
+
+(defun malyon-opcode-read-char (&optional device &rest ignore)
+  "Read a character."
+  (if (and device (/= 1 device))
+      (malyon-fatal-error "illegal device specified in read_char."))
+  (if (eq malyon-transcript-buffer (current-buffer))
+      (goto-char (point-max)))
+  (message "[Press a key.]")
+  (malyon-more malyon-keymap-readchar)
+  (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-character))
+
+(defun malyon-opcode-remove-obj (object)
+  "Remove an object from its parent's children list."
+  (malyon-remove-object object))
+
+(defun malyon-opcode-restart ()
+  "Restart the game."
+  (malyon-set-game-state malyon-game-state-restart))
+
+(defun malyon-opcode-restore (&optional table bytes name)
+  "Restore a saved game state or a section of memory from a file."
+  (let ((result (if name
+                   (malyon-restore-file
+                    (malyon-get-file-name name) table bytes)
+                 (call-interactively 'malyon-restore-file))))
+    (if (< malyon-story-version 5)
+       (malyon-jump-if (not (zerop result)))
+      (malyon-store-variable (malyon-read-code-byte) result))))
+
+(defun malyon-opcode-restore-undo ()
+  "Restore game state for undo."
+  (if malyon-game-state-undo
+      (malyon-set-game-state malyon-game-state-undo))
+  (malyon-store-variable (malyon-read-code-byte) 2))
+
+(defun malyon-opcode-ret (value)
+  "Return a value."
+  (malyon-return value))
+
+(defun malyon-opcode-ret-popped ()
+  "Return top of stack."
+  (malyon-return (malyon-pop-stack)))
+
+(defun malyon-opcode-rfalse ()
+  "Return false/0."
+  (malyon-return 0))
+
+(defun malyon-opcode-rtrue ()
+  "Return true/1."
+  (malyon-return 1))
+
+(defun malyon-opcode-save (&optional table bytes name)
+  "Save the current game state or a section of memory to a file."
+  (let ((result (if name
+                   (malyon-save-file (malyon-get-file-name name) table bytes)
+                 (call-interactively 'malyon-save-file))))
+    (if (< malyon-story-version 5)
+       (malyon-jump-if (not (zerop result)))
+      (malyon-store-variable (malyon-read-code-byte) result))))
+
+(defun malyon-opcode-save-undo ()
+  "Save game state for undo."
+  (setq malyon-game-state-undo (malyon-current-game-state))
+  (malyon-store-byte (malyon-read-code-byte) 1))
+
+(defun malyon-opcode-scan-table (x table len &optional form)
+  "Scan the given table for the first occurrence of x."
+  (if (not form) (setq form 130))
+  (let ((inc (logand 127 form))
+       (byte (zerop (logand 128 form)))
+       (addr table)
+       (found 0)
+       (index 0))
+    (while (and (zerop found) (< index len))
+      (setq found
+           (if byte
+               (if (= x (malyon-read-byte addr)) addr 0)
+             (if (= x (malyon-read-word addr)) addr 0)))
+      (setq addr (+ addr inc))
+      (setq index (+ index 1)))
+    (malyon-store-variable (malyon-read-code-byte) found)
+    (malyon-jump-if (not (zerop found)))))
+
+(defun malyon-opcode-set-attr (object attribute)
+  "Set the given attribute in the given object."
+  (let ((attributes (malyon-object-address object))
+       (byte       (lsh attribute -3)))
+    (malyon-store-byte (+ attributes byte)
+                      (logior (malyon-read-byte (+ attributes byte))
+                              (lsh 128 (- (logand attribute 7)))))))
+
+(defun malyon-opcode-set-color (foreground background)
+  "Sets the fore- and background colors ie. does nothing.")
+
+(defun malyon-opcode-set-cursor (&optional line column)
+  "Set the cursor."
+  (if (eq malyon-transcript-buffer (current-buffer))
+      (goto-char (point-max))
+    (if malyon-status-buffer-delayed-split
+       (progn
+         (malyon-split-buffer-windows malyon-status-buffer-delayed-split)
+         (other-window 1)))
+    (if line   '() (setq line   (count-lines (point-min) (point))))
+    (if column '() (setq column (current-column)))
+    (if (> line malyon-status-buffer-lines)
+       (progn
+         (malyon-split-buffer-windows line)
+         (other-window 1)))
+    (goto-char (point-min))
+    (if (and (<= 1 line) (<= line malyon-status-buffer-lines))
+       (forward-line line)
+      (beginning-of-line))
+    (if (and (<= 1 column) (<= column malyon-max-column))
+       (forward-char (- column 1))
+      (beginning-of-line))
+    (setq malyon-status-buffer-point (point))))
+
+(defun malyon-opcode-set-font (font)
+  "Sets the font if available or 0 otherwise."
+  (malyon-store-variable (malyon-read-code-byte) 0))
+
+(defun malyon-opcode-set-text-style (style)
+  "Set the text style/face."
+  (let ((face (assq style malyon-faces)))
+    (setq malyon-current-face (if face (cdr face) 'malyon-face-plain))))
+
+(defun malyon-opcode-set-window (window)
+  "Set the current window."
+  (malyon-restore-window-configuration)
+  (setq malyon-current-window window)
+  (malyon-update-output-streams)
+  (if (zerop window)
+      (if (not (eq malyon-transcript-buffer (current-buffer)))
+         (other-window 1))
+    (if (not (eq malyon-status-buffer (current-buffer)))
+       (other-window 1))
+    (malyon-opcode-set-cursor 1 1)))
+
+(defun malyon-opcode-show-status ()
+  "Display the status line."
+  (save-excursion
+    (malyon-opcode-split-window 1)
+    (malyon-restore-window-configuration)
+    (malyon-opcode-set-window 1)
+    (malyon-prepare-status-buffer 1)
+    (malyon-opcode-set-cursor 1 1)
+    (malyon-opcode-print-obj (malyon-read-global-variable 0))
+    (if (<= (current-column) (- (current-fill-column) 10))
+       (let* ((x     (malyon-read-global-variable 1))
+              (y     (malyon-read-global-variable 2))
+              (hours (if (> x 12) (- x 12) x))
+              (ampm  (if (> x 12) "PM" "AM"))
+              (score (format "%4d/%4d" x y))
+              (time  (format "%02d:%02d%s" hours y ampm)))
+         (malyon-opcode-set-cursor 1 (- (current-fill-column) 10))
+         (malyon-print (if malyon-score-game score time))))
+    (malyon-opcode-set-window 0)
+    (malyon-adjust-transcript)))
+
+(defun malyon-opcode-split-window (size)
+  "Split upper and lower window."
+  (malyon-set-window-configuration size))
+
+(defun malyon-opcode-store (variable value)
+  "Store a value in a variable."
+  (malyon-store-variable variable value))
+
+(defun malyon-opcode-storeb (array index value)
+  "Store a value in an array at the given index."
+  (malyon-store-byte (+ array index) value))
+
+(defun malyon-opcode-storew (array index value)
+  "Store a value in an array at the given index."
+  (malyon-store-word (+ array (* 2 index)) value))
+
+(defun malyon-opcode-sub (a b)
+  "Subtraction."
+  (malyon-store-variable (malyon-read-code-byte)
+                        (- (malyon-number a) (malyon-number b))))
+
+(defun malyon-opcode-test (bitmap flags)
+  "Test if all of the flags are set in the bitmap."
+  (malyon-jump-if (= flags (logand bitmap flags))))
+
+(defun malyon-opcode-test-attr (object attribute)
+  "Jump depending on the given attribute in the given object."
+  (malyon-jump-if 
+   (/= 0 (logand (malyon-read-byte (+ (malyon-object-address object)
+                                     (lsh attribute -3)))
+                (lsh 128 (- (logand attribute 7)))))))
+
+(defun malyon-opcode-throw (value frame)
+  "Return from the given stack frame."
+  (if malyon-game-state-quetzal
+      (let ((id (lsh (aref malyon-stack malyon-frame-pointer) -8)))
+       (while (/= frame id)
+         (setq malyon-stack-pointer malyon-frame-pointer)
+         (malyon-pop-stack)
+         (setq malyon-frame-pointer
+               (- malyon-stack-pointer 1 (lsh (malyon-pop-stack) -8)))
+         (malyon-pop-stack)
+         (malyon-pop-stack)
+         (setq id (lsh (aref malyon-stack malyon-frame-pointer) -8))))
+    (setq malyon-frame-pointer frame))
+  (malyon-return value))
+
+(defun malyon-opcode-tokenise (text parse &optional dict flag)
+  "Perform lexical analysis on the text buffer."
+  (let* ((words (malyon-text-to-words text dict))
+        (word  (car           words))
+        (start (car           word))
+        (len   (malyon-cadr   word))
+        (code  (malyon-caddr  word))
+        (entry (malyon-lookup dict code))
+        (i     0))
+    (while (not (or (null words) (= i (malyon-read-byte parse))))
+      (if (and (zerop entry) flag (/= 0 flag))
+         '()
+       (malyon-store-word (+ 2 parse (* 4 i)) entry)
+       (malyon-store-byte (+ 4 parse (* 4 i)) len)
+       (malyon-store-byte (+ 5 parse (* 4 i)) start))
+      (setq words (cdr           words)
+           word  (car           words)
+           start (car           word)
+           len   (malyon-cadr   word)
+           code  (malyon-caddr  word)
+           entry (malyon-lookup dict code)
+           i     (+ 1 i)))
+    (malyon-store-byte (+ 1 parse) i)))
+
+(defun malyon-opcode-verify ()
+  "Verify the correctness of the story file."
+  (let ((length (+ 1 (* malyon-packed-multiplier (malyon-read-word 26))))
+       (sum    0)
+       (i      64))
+    (while (< i length)
+      (setq sum (mod (+ sum (malyon-read-byte i)) 65536)
+           i   (+ 1 i)))
+    (malyon-jump-if (= (malyon-read-word 28) sum))))
+
+;; keymap utilities
+
+(defun malyon-end-input ()
+  "Store the input line in a text buffer and perform lexical analysis."
+  (interactive)
+  (condition-case nil
+      (progn
+       (malyon-adjust-transcript)
+       (switch-to-buffer malyon-transcript-buffer)
+       (goto-char (point-max))
+       (let* ((input (downcase
+                      (buffer-substring-no-properties
+                       (if (< malyon-aread-beginning-of-line (point))
+                           malyon-aread-beginning-of-line
+                         (point))
+                       (point))))
+              (vec  (malyon-string-to-vector input))
+              (text (apply 'vector (mapcar 'malyon-unicode-to-zscii vec)))
+              (len  (min (malyon-read-byte malyon-aread-text) (length text)))
+              (i    0))
+         (malyon-history-insert input)
+         (if (>= malyon-story-version 5)
+             (malyon-store-byte (+ malyon-aread-text 1) len))
+         (while (< i len)
+           (malyon-store-byte
+            (+ malyon-aread-text (if (< malyon-story-version 5) 1 2) i)
+            (malyon-char-to-int (aref text i)))
+           (setq i (+ 1 i)))
+         (if (< malyon-story-version 5)
+             (malyon-store-byte (+ malyon-aread-text 1 len) 0)))
+       (if (/= 0 malyon-aread-parse)
+           (malyon-opcode-tokenise malyon-aread-text malyon-aread-parse))
+       (newline)
+       (if (>= malyon-story-version 5)
+           (malyon-store-variable (malyon-read-code-byte) 10))
+       (malyon-interpreter))
+    (error
+     (malyon-fatal-error "unspecified internal runtime error."))))
+
+(defun malyon-more-char ()
+  "Page down in More mode."
+  (interactive)
+  (condition-case nil
+      (scroll-up)
+    (error))
+  (if (>= (count-lines (point) (point-max))
+          (malyon-window-displayed-height))
+      (message "[More]")
+    (goto-char (point-max))
+    (malyon-adjust-transcript)
+    (use-local-map malyon-more-continue-keymap)))
+
+(defun malyon-more-char-status ()
+  "Wait for a key then continue."
+  (interactive)
+  (condition-case nil
+      (progn
+       (malyon-adjust-transcript)
+       (use-local-map malyon-more-continue-keymap)
+       (malyon-interpreter))
+    (error
+     (malyon-fatal-error "unspecified internal runtime error."))))
+
+(defun malyon-wait-char ()
+  "Store the input character in a variable and resume execution."
+  (interactive)
+  (condition-case nil
+      (progn
+       (malyon-store-variable 
+        (malyon-read-code-byte)
+        (malyon-char-to-int (malyon-unicode-to-zscii last-command-char)))
+       (use-local-map malyon-keymap-read)
+       (malyon-interpreter))
+    (error
+     (malyon-fatal-error "unspecified internal runtime error."))))
+
+(defun malyon-history-previous-char (arg)
+  "Display the previous item in the input history."
+  (interactive "p")
+  (let ((input (malyon-history-previous)))
+    (cond ((> malyon-aread-beginning-of-line (point))
+          (funcall malyon-history-saved-up arg))
+         (input
+          (save-excursion
+            (set-buffer malyon-transcript-buffer)
+            (delete-region malyon-aread-beginning-of-line (point-max)))
+          (goto-char (point-max))
+          (insert input)
+          (malyon-adjust-transcript)))))
+
+(defun malyon-history-next-char (arg)
+  "Display the next item in the input history."
+  (interactive "p")
+  (let ((input (malyon-history-next)))
+    (cond ((> malyon-aread-beginning-of-line (point))
+          (funcall malyon-history-saved-down arg))
+         (input
+          (save-excursion
+            (set-buffer malyon-transcript-buffer)
+            (delete-region malyon-aread-beginning-of-line (point-max)))
+          (goto-char (point-max))
+          (insert input)
+          (malyon-adjust-transcript)))))
+
+(defun malyon-beginning-of-line (arg)
+  "Go to the beginning of the line."
+  (interactive "p")
+  (if (> malyon-aread-beginning-of-line (point))
+      (beginning-of-line)
+    (goto-char malyon-aread-beginning-of-line)))
+
+(defun malyon-kill-region (arg)
+  "Kill region."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (kill-region (point) (mark))
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-kill-line (arg)
+  "Kill rest of the current line."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (kill-line)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-kill-word (arg)
+  "Kill the current word."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (kill-word 1)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-yank (arg)
+  "Yank."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (yank)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-yank-pop (arg)
+  "Yank pop."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (yank-pop 1)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-delete-char (arg)
+  "Delete a character."
+  (interactive "p")
+  (if (<= malyon-aread-beginning-of-line (point))
+      (delete-char 1)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-backward-delete-char (arg)
+  "Delete a character backwards."
+  (interactive "p")
+  (if (< malyon-aread-beginning-of-line (point))
+      (backward-delete-char-untabify 1)
+    (message "Editing is restricted to the input prompt.")))
+
+(defun malyon-self-insert-command (arg)
+  "Insert a character."
+  (interactive "p")
+  (if (> malyon-aread-beginning-of-line (point))
+      (goto-char (point-max)))
+  (self-insert-command 1))
+
+;; tracing utility
+
+(defun malyon-trace-file ()
+  "Turn tracing on for a particular file."
+  (let ((trace
+        (get-buffer-create
+         (concat "Malyon Trace " malyon-story-file-name))))
+    (if trace
+       (save-excursion
+         (set-buffer trace)
+         (malyon-erase-buffer)
+         (insert (concat "Tracing " malyon-story-file-name "..."))
+         (newline)))))
+  
+(defun malyon-trace-newline ()
+  "Output tracing newline."
+  (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
+    (if trace
+       (save-excursion
+         (set-buffer trace)
+         (goto-char (point-max))
+         (newline)))))
+
+(defun malyon-trace-opcode (pc opcode operands)
+  "Output a z code instruction."
+  (malyon-trace-string
+   (format "%8d   %-3d %-25s %s\n"
+          pc
+          opcode
+          (symbol-name (aref malyon-opcodes opcode))
+          (apply 'concat (malyon-mapcan
+                          (lambda (x)
+                            (list " "
+                                  (number-to-string
+                                   (if (malyon-characterp x)
+                                       (malyon-char-to-int x)
+                                     x))))
+                          operands)))))
+
+(defun malyon-trace-string (s)
+  "Output tracing string."
+  (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
+    (if (and trace s)
+       (save-excursion
+         (set-buffer trace)
+         (goto-char (point-max))
+         (insert s)))))
+
+(defun malyon-trace-object (o)
+  "Output tracing object."
+  (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
+    (if (and trace o)
+       (save-excursion
+         (set-buffer trace)
+         (goto-char (point-max))
+         (prin1 o trace)))))
+
+;;; announce malyon-mode
+
+(provide 'malyon-mode)
+(provide 'malyon)
+
+;;; malyon-mode.el ends here
diff --git a/malyon.elc b/malyon.elc
new file mode 100644 (file)
index 0000000..aa357e8
Binary files /dev/null and b/malyon.elc differ