1 ; malyon.el --- mode to execute z code files version 3, 5, 8
3 ;; Copyright (C) 1999-2009 Peter Ilberg
5 ;; Maintainer: Peter Ilberg <peter.ilberg@gmail.com>
8 ;; The author would like to thank the following people for reporting
9 ;; bugs, testing, suggesting and/or contributing improvements:
10 ;; Bernhard Barde, Jonathan Craven, Alberto Petrofsky, Alan Shutko
14 ;; This package provides a basic interpreter for version 3, 5, 8 z code
15 ;; story files as generated by Inform (C) Graham Nelson and Infocom.
17 ;; If you encounter a bug please send a report to Peter Ilberg at
18 ;; peter.ilberg@gmail.com. Thank you!
20 ;; To play a story file simple type M-x malyon and enter the path to the
21 ;; story file. If anything goes wrong and you want to manually clean
22 ;; up type M-x malyon-quit. In addition, you can switch back to a game in
23 ;; progress by typing M-x malyon-restore.
25 ;; A note on the format of saved game states:
27 ;; As of version 1.0, Malyon supports the quetzal file format for saved
28 ;; games. Support for this format required changes to several internal
29 ;; data structures (stack frames and catch-throw) that are incompatible
30 ;; with the old implementation. Unfortunately, the old file format for
31 ;; saved games cannot be converted into quetzal.
33 ;; For backwards compatibility, however, Malyon still supports the old
34 ;; file format. And you can continue to play your old game states.
36 ;; Because of the incompatibility of the two file formats, Malyon now
37 ;; runs, as follows, in either of two modes: quetzal and compatibility.
39 ;; - in quetzal mode, game states are saved in quetzal format
40 ;; - in compatibility mode, games states are saved in the old format
41 ;; - loading a game state in quetzal format switches to quetzal mode
42 ;; - loading an old game state switches to compatibility mode
43 ;; - quetzal mode is the default setting
45 ;; In other words, Malyon will only use the old file format if you've
46 ;; restored a game state saved in the old file format.
52 ;; global variables - moved here to appease the byte-code compiler
54 ;; story file information
56 (defvar malyon-story-file-name nil
57 "The name of the story file being executed.")
59 (defvar malyon-story-file nil
60 "The story file which is currently being run.")
62 (defvar malyon-story-version nil
63 "The story file version.")
65 (defvar malyon-supported-versions '(3 5 8)
66 "A list of supported story file versions.")
68 ;; status and transcript buffers
70 (defvar malyon-transcript-buffer nil
71 "The main transcript buffer of the story file execution.")
73 (defvar malyon-transcript-buffer-buffered nil
74 "Is output in the transcript buffer buffered?")
76 (defvar malyon-status-buffer nil
77 "The status bar buffer of the story file execution.")
79 (defvar malyon-status-buffer-lines nil
80 "The number of lines in the status bar buffer.")
82 (defvar malyon-status-buffer-delayed-split nil
83 "If the number of lines in the status buffer is reduced,
84 the window configuration is not changed immediately. It
85 is changed after the next turn (read or read_char).")
87 (defvar malyon-status-buffer-point nil
88 "The point location in the status bar buffer.")
90 (defvar malyon-max-column 72
91 "Maximum column for text display.")
95 (defvar malyon-window-configuration nil
96 "The current window configuration of the malyon interpreter.")
98 (defvar malyon-current-window nil
99 "The currently active window for text output.")
101 ;; z machine registers
103 (defvar malyon-stack nil
104 "The stack of the z machine.")
106 (defvar malyon-stack-pointer nil
107 "The stack pointer of the z machine.")
109 (defvar malyon-frame-pointer nil
110 "The frame pointer of the z machine.")
112 (defvar malyon-instruction-pointer nil
113 "The instruction pointer of the z machine.")
115 ;; game file related global variables
117 (defvar malyon-score-game nil
118 "A flag indicating whether this story uses score or time.")
120 (defvar malyon-packed-multiplier nil
121 "The amount by which packed addresses are multiplied to get byte
124 (defvar malyon-global-variables nil
125 "A pointer to the global variable section in the story file.")
127 (defvar malyon-abbreviations nil
128 "A pointer to the abbreviations in the story file.")
130 (defvar malyon-alphabet nil
131 "The z machine's text alphabet.")
133 (defvar malyon-whitespace nil
134 "A string of whitespace characters recognized by the interpreter.")
138 (defvar malyon-object-table nil
139 "A pointer to the object table in the story file.")
141 (defvar malyon-object-table-entry-size nil
142 "The size of one entry in the object table.")
144 (defvar malyon-object-properties nil
145 "The number of properties per object minus one.")
147 (defvar malyon-object-property-offset nil
148 "The byte offset of the properties table in the object.")
152 (defvar malyon-dictionary nil
153 "A pointer to the dictionary of the story file.")
155 (defvar malyon-dictionary-entry-length nil
156 "The length of a dictionary entry.")
158 (defvar malyon-dictionary-num-entries nil
159 "The number of dictionary entries.")
161 (defvar malyon-dictionary-entries nil
162 "A pointer to the first dictionary entry.")
164 (defvar malyon-dictionary-word-length nil
165 "The length of a dictionary word.")
167 ;; game state information
169 (defvar malyon-game-state-restart nil
170 "The machine state for implementing restart.")
172 (defvar malyon-game-state-undo nil
173 "The machine state for implementing undo.")
175 (defvar malyon-game-state-quetzal t
176 "Store game state information for quetzal.")
180 (defvar malyon-current-face nil
181 "The current face in which to display text.")
183 (defvar malyon-last-cursor-position-after-input nil
184 "The last cursor position after reading input from the keyboard.")
186 ;; interactive functions
188 (defun malyon (file-name)
189 "Major mode for playing z3/5/8 story files.
190 This mode allows execution of version 3, 5, 8 z code story files."
191 (interactive "fStory file name: ")
192 (if malyon-story-file
193 (message "You are already playing a game.")
194 (if (not (string-match ".*\.z[358]$" file-name))
195 (message "%s is not a version 3, 5, or 8 story file." file-name)
197 (malyon-load-story-file file-name)
199 (malyon-fatal-error "loading of story file failed.")))
200 (setq malyon-story-version (aref malyon-story-file 0))
201 (cond ((memq malyon-story-version malyon-supported-versions)
205 (malyon-fatal-error "initialization of interpreter failed.")))
206 (malyon-interpreter))
208 (message "%s is not a version 3, 5, or 8 story file." file-name)
209 (malyon-cleanup))))))
211 (defun malyon-restore ()
212 "Restore the save window configuration for the interpreter."
216 (malyon-restore-window-configuration)
217 (malyon-adjust-transcript))
219 (malyon-fatal-error "restoring window configuration failed."))))
221 (defun malyon-quit ()
222 "Exit the malyon interpreter."
224 (if malyon-story-file
227 (if (malyon-yes-or-no-p-minibuf "Do you really want to quit? ")
230 (defun malyon-mode ()
231 "This mode provides a basic interpreter for version 3, 5, 8 z code
232 story files as generated by Inform (C) Graham Nelson and Infocom.
234 Note that this package is by no means complete and bug free.
235 If you encounter a bug please send a report to Peter Ilberg at
236 peter.ilberg@natinst.com. Thank you!
238 To play a story file simple type M-x malyon and enter the path to the
239 story file. If anything goes wrong and you want to manually clean
240 up type M-x malyon-quit. In addition, you can switch back to a game in
241 progress by typing M-x malyon-restore.
243 The author would like to thank the following people for reporting
244 bugs, testing, suggesting and/or contributing improvements:
245 Bernhard Barde, Jonathan Craven, Alberto Petrofsky, Alan Shutko"
246 (message "Use M-x malyon if you want to play a zcode game."))
248 ;; compatibility functions for GNU emacs
251 (defalias 'malyon-cadr 'cadr)
252 (defun malyon-cadr (list)
253 "Take the cadr of the list."
257 (defalias 'malyon-caddr 'caddr)
258 (defun malyon-caddr (list)
259 "Take the caddr of the list."
260 (car (cdr (cdr list)))))
263 (defalias 'malyon-cdddr 'cdddr)
264 (defun malyon-cdddr (list)
265 "Take the cdddr of the list."
266 (cdr (cdr (cdr list)))))
268 (if (fboundp 'char-before)
269 (defalias 'malyon-char-before 'char-before)
270 (defun malyon-char-before ()
271 "Return the character before the point."
272 (char-after (- (point) 1))))
274 (if (fboundp 'char-to-int)
275 (defalias 'malyon-char-to-int 'char-to-int)
276 (defun malyon-char-to-int (c)
277 "Convert a character into an integer."
280 (if (fboundp 'characterp)
281 (defalias 'malyon-characterp 'characterp)
282 (defun malyon-characterp (x)
283 "Test for a character."
284 (and (numberp x) (<= 0 x) (< x 256))))
286 (defun malyon-disable-multibyte ()
287 "Disable multibyte support in the current buffer."
288 (condition-case nil (set-buffer-multibyte nil) (error)))
290 (defun malyon-erase-buffer (&optional buffer)
291 "Erase the given buffer."
293 (if buffer (set-buffer buffer))
294 (if (and buffer (eq buffer malyon-transcript-buffer))
295 (malyon-begin-section)
298 (if (fboundp 'int-to-char)
299 (defalias 'malyon-int-to-char 'int-to-char)
300 (defun malyon-int-to-char (i)
301 "Convert an integer into a character."
305 (defalias 'malyon-mapc 'mapc)
306 (defun malyon-mapc (function list)
307 "Apply fun to every element of args ignoring the results."
310 (funcall function (car list))
311 (malyon-mapc function (cdr list)))))
313 (if (fboundp 'mapcan)
314 (defalias 'malyon-mapcan 'mapcan)
315 (defun malyon-mapcan (function list)
316 "Apply fun to every element of args nconc'ing the result."
319 (nconc (funcall function (car list))
320 (malyon-mapcan function (cdr list))))))
322 ; Do not use the built-in conversion via 'multibyte-char-to-unibyte.
323 (defun malyon-multibyte-char-to-unibyte (char)
324 "Convert a multibyte character to unibyte."
327 (defun malyon-point-max (&optional buffer)
328 "Get the point-max of the given buffer."
330 (if buffer (set-buffer buffer))
333 (if (fboundp 'redisplay-frame)
334 (defalias 'malyon-redisplay-frame 'redisplay-frame)
335 (defun malyon-redisplay-frame (frame &rest ignore)
336 "Redisplay the given frame."))
338 (if (fboundp 'remove)
339 (defalias 'malyon-remove 'remove)
340 (defun malyon-remove (element list)
341 "Remove the element from the list."
344 ((eq element (car list))
345 (malyon-remove element (cdr list)))
346 ((equal element (car list))
347 (malyon-remove element (cdr list)))
350 (malyon-remove element (cdr list)))))))
352 (if (fboundp 'set-keymap-name)
353 (defalias 'malyon-set-keymap-name 'set-keymap-name)
354 (defun malyon-set-keymap-name (keymap name)
355 "Set the name of the keymap."))
357 (if (fboundp 'string-to-list)
358 (defalias 'malyon-string-to-list 'string-to-list)
359 (defun malyon-string-to-list (s)
360 "Convert a string into a list of characters."
361 (let ((i (- (length s) 1)) (l '()))
363 (setq l (cons (aref s i) l)
367 (if (fboundp 'string-to-vector)
368 (defalias 'malyon-string-to-vector 'string-to-vector)
369 (defun malyon-string-to-vector (s)
370 "Convert a string into a vector of characters."
371 (let* ((i 0) (l (length s)) (v (make-vector l 0)))
373 (aset v i (aref s i))
377 ; Do not use the built-in conversion via 'unibyte-char-to-multibyte.
378 (defun malyon-unibyte-char-to-multibyte (char)
379 "Convert a unibyte character to multibyte."
382 (defun malyon-vector-to-list (v begin end)
383 "Return a list of elements in v in the range [begin, end)."
386 (setq result (cons (aref v begin) result))
387 (setq begin (+ 1 begin)))
390 (if (fboundp 'window-displayed-height)
391 (defalias 'malyon-window-displayed-height 'window-displayed-height)
392 (defun malyon-window-displayed-height (&optional window)
393 "Get the height of the window's displayed region."
394 (- (window-height) 1)))
396 (if (fboundp 'yes-or-no-p-minibuf)
397 (defalias 'malyon-yes-or-no-p-minibuf 'yes-or-no-p-minibuf)
398 (defun malyon-yes-or-no-p-minibuf (prompt)
399 "Ask a yes or no question."
400 (yes-or-no-p prompt)))
402 ;; global variables for the malyon mode
404 (defvar malyon-syntax-table nil
405 "Syntax table used while in malyon mode (same as in text-mode).")
407 (if malyon-syntax-table
409 (setq malyon-syntax-table (make-syntax-table))
410 (modify-syntax-entry ?\" ". " malyon-syntax-table)
411 (modify-syntax-entry ?\\ ". " malyon-syntax-table)
412 (modify-syntax-entry ?' "w " malyon-syntax-table))
414 (defvar malyon-keymap-read nil
415 "Keymap for malyon mode for reading input into a buffer.")
417 (defvar malyon-history-saved-up nil
418 "The saved binding for the up arrow key.")
420 (defvar malyon-history-saved-down nil
421 "The saved binding for the down arrow key.")
423 (if malyon-keymap-read
425 (setq malyon-keymap-read (make-sparse-keymap))
426 (malyon-set-keymap-name malyon-keymap-read 'malyon-keymap-read)
427 (setq malyon-history-saved-up (global-key-binding [up]))
428 (setq malyon-history-saved-down (global-key-binding [down]))
429 (define-key malyon-keymap-read "\r" 'malyon-end-input)
430 (define-key malyon-keymap-read [up] 'malyon-history-previous-char)
431 (define-key malyon-keymap-read [down] 'malyon-history-next-char)
432 (define-key malyon-keymap-read "\M-p" 'malyon-history-previous-char)
433 (define-key malyon-keymap-read "\M-n" 'malyon-history-next-char)
434 (define-key malyon-keymap-read "\C-a" 'malyon-beginning-of-line)
435 (define-key malyon-keymap-read "\C-w" 'malyon-kill-region)
436 (define-key malyon-keymap-read "\C-k" 'malyon-kill-line)
437 (define-key malyon-keymap-read "\M-d" 'malyon-kill-word)
438 (define-key malyon-keymap-read "\C-y" 'malyon-yank)
439 (define-key malyon-keymap-read "\M-y" 'malyon-yank-pop)
440 (define-key malyon-keymap-read "\C-d" 'malyon-delete-char)
441 (define-key malyon-keymap-read "\d" 'malyon-backward-delete-char)
442 (define-key malyon-keymap-read [del] 'malyon-delete-char)
443 (define-key malyon-keymap-read [backspace] 'malyon-backward-delete-char)
444 (substitute-key-definition (lookup-key (current-global-map) "a")
445 'malyon-self-insert-command
446 malyon-keymap-read (current-global-map)))
448 (defvar malyon-keymap-readchar nil
449 "Keymap for malyon mode for waiting for input.")
451 (if malyon-keymap-readchar
453 (setq malyon-keymap-readchar (make-sparse-keymap))
454 (malyon-set-keymap-name malyon-keymap-readchar 'malyon-keymap-readchar)
455 (define-key malyon-keymap-readchar "\r" 'malyon-wait-char)
456 (substitute-key-definition (lookup-key (current-global-map) "a")
458 malyon-keymap-readchar (current-global-map)))
460 (defvar malyon-keymap-more nil
461 "Keymap for malyon mode for browsing through text.")
463 (if malyon-keymap-more
465 (setq malyon-keymap-more (make-sparse-keymap))
466 (malyon-set-keymap-name malyon-keymap-more 'malyon-keymap-more)
467 (define-key malyon-keymap-more "\r" 'malyon-more-char)
468 (substitute-key-definition (lookup-key (current-global-map) "a")
470 malyon-keymap-more (current-global-map)))
472 (defvar malyon-keymap-more-status nil
473 "Keymap for malyon mode for browsing through the status buffer.")
475 (if malyon-keymap-more-status
477 (setq malyon-keymap-more-status (make-sparse-keymap))
478 (malyon-set-keymap-name malyon-keymap-more-status 'malyon-keymap-more-status)
479 (define-key malyon-keymap-more-status "\r" 'malyon-more-char-status)
480 (substitute-key-definition (lookup-key (current-global-map) "a")
481 'malyon-more-char-status
482 malyon-keymap-more-status (current-global-map)))
484 (defvar malyon-faces nil
485 "An association list of text faces used by the malyon mode.")
487 (defun malyon-initialize-faces ()
488 (copy-face 'default 'malyon-face-plain)
489 (copy-face 'bold 'malyon-face-reverse)
490 (copy-face 'bold 'malyon-face-bold)
491 (copy-face 'italic 'malyon-face-italic)
492 (copy-face 'default 'malyon-face-error)
493 (set-face-foreground 'malyon-face-error "red")
494 (setq malyon-faces '((0 . malyon-face-plain)
495 (1 . malyon-face-reverse)
496 (2 . malyon-face-bold)
497 (4 . malyon-face-italic)
498 (8 . malyon-face-plain))))
500 (defvar malyon-print-separator nil
501 "A flag indicating whether to print the * * * separator.")
503 (defun malyon-begin-section ()
504 "Print a section divider and begin a new section."
505 (if malyon-print-separator
507 (malyon-mapc 'malyon-putchar-transcript '(?\n ?\n ?* ? ?* ? ?*))
509 (malyon-mapc 'malyon-putchar-transcript '(?\n ?\n))
510 (setq malyon-print-separator nil)))
511 (narrow-to-region (point-max) (point-max)))
513 (if malyon-whitespace
515 (setq malyon-whitespace (list (malyon-char-to-int ? )
516 (malyon-char-to-int ?\t)
517 (malyon-char-to-int ?\n)
518 (malyon-char-to-int ?\r))))
522 (defsubst malyon-read-byte (address)
523 "Read a byte at address in the story file."
525 (aref malyon-story-file address)
526 (aref malyon-story-file (+ 65536 address))))
528 (defsubst malyon-store-byte (address value)
529 "Store a byte at address in the story file."
531 (aset malyon-story-file address (logand 255 value))
532 (aset malyon-story-file (+ 65536 address) (logand 255 value))))
534 (defsubst malyon-read-word (address)
535 "Read a word at address in the story file."
537 (logior (lsh (aref malyon-story-file address) 8)
538 (aref malyon-story-file (+ 1 address)))
539 (logior (lsh (aref malyon-story-file (+ 65536 address)) 8)
540 (aref malyon-story-file (+ 65537 address)))))
542 (defsubst malyon-store-word (address value)
543 "Store a word at address in the story file."
546 (aset malyon-story-file address (logand 255 (lsh value -8)))
547 (aset malyon-story-file (+ 1 address) (logand 255 value)))
548 (aset malyon-story-file (+ 65536 address) (logand 255 (lsh value -8)))
549 (aset malyon-story-file (+ 65537 address) (logand 255 value))))
551 (defsubst malyon-read-code-byte ()
552 "Read the next byte at the program counter location."
553 (setq malyon-instruction-pointer (+ malyon-instruction-pointer 1))
554 (malyon-read-byte (- malyon-instruction-pointer 1)))
556 (defsubst malyon-read-code-word ()
557 "Read the next word at the program counter location."
558 (setq malyon-instruction-pointer (+ malyon-instruction-pointer 2))
559 (malyon-read-word (- malyon-instruction-pointer 2)))
561 (defsubst malyon-pop-stack ()
562 "Pop a value off the stack."
563 (if (> 0 malyon-stack-pointer)
564 (malyon-fatal-error "stack underflow."))
565 (setq malyon-stack-pointer (- malyon-stack-pointer 1))
566 (aref malyon-stack (+ malyon-stack-pointer 1)))
568 (defsubst malyon-read-local-variable (variable)
569 "Read a local variable."
570 (aref malyon-stack (+ variable malyon-frame-pointer)))
572 (defsubst malyon-read-global-variable (variable)
573 "Read a global variable."
574 (malyon-read-word (+ malyon-global-variables (* 2 variable))))
576 (defsubst malyon-read-variable (variable)
578 (cond ((= variable 0) (malyon-pop-stack))
579 ((< variable 16) (malyon-read-local-variable variable))
580 (t (malyon-read-global-variable (- variable 16)))))
582 (defsubst malyon-push-stack (value)
583 "Push a value onto the stack."
584 (setq malyon-stack-pointer (+ malyon-stack-pointer 1))
585 (aset malyon-stack malyon-stack-pointer value))
587 (defsubst malyon-store-local-variable (variable value)
588 "Store a value in a local variable."
589 (aset malyon-stack (+ variable malyon-frame-pointer) value))
591 (defsubst malyon-store-global-variable (variable value)
592 "Store a value in a global variable."
593 (malyon-store-word (+ malyon-global-variables (* 2 variable)) value))
595 (defsubst malyon-store-variable (var value)
596 "Store the value in a variable."
597 (setq value (logand 65535 value))
598 (cond ((= var 0) (malyon-push-stack value))
599 ((< var 16) (malyon-store-local-variable var value))
600 (t (malyon-store-global-variable (- var 16) value))))
604 (defvar malyon-opcodes
606 malyon-opcode-je malyon-opcode-jl
607 malyon-opcode-jg malyon-opcode-dec-chk
608 malyon-opcode-inc-chk malyon-opcode-jin
609 malyon-opcode-test malyon-opcode-or
610 malyon-opcode-and malyon-opcode-test-attr
611 malyon-opcode-set-attr malyon-opcode-clear-attr
612 malyon-opcode-store malyon-opcode-insert-obj
613 malyon-opcode-loadw malyon-opcode-loadb
614 malyon-opcode-get-prop malyon-opcode-get-prop-addr
615 malyon-opcode-get-next-prop malyon-opcode-add
616 malyon-opcode-sub malyon-opcode-mul
617 malyon-opcode-div malyon-opcode-mod
618 malyon-opcode-calls malyon-opcode-calln
619 malyon-opcode-set-color malyon-opcode-throw
620 malyon-opcode-nop malyon-opcode-nop
621 malyon-opcode-nop malyon-opcode-nop
622 malyon-opcode-je malyon-opcode-jl
623 malyon-opcode-jg malyon-opcode-dec-chk
624 malyon-opcode-inc-chk malyon-opcode-jin
625 malyon-opcode-test malyon-opcode-or
626 malyon-opcode-and malyon-opcode-test-attr
627 malyon-opcode-set-attr malyon-opcode-clear-attr
628 malyon-opcode-store malyon-opcode-insert-obj
629 malyon-opcode-loadw malyon-opcode-loadb
630 malyon-opcode-get-prop malyon-opcode-get-prop-addr
631 malyon-opcode-get-next-prop malyon-opcode-add
632 malyon-opcode-sub malyon-opcode-mul
633 malyon-opcode-div malyon-opcode-mod
634 malyon-opcode-calls malyon-opcode-calln
635 malyon-opcode-set-color malyon-opcode-throw
636 malyon-opcode-nop malyon-opcode-nop
637 malyon-opcode-nop malyon-opcode-nop
638 malyon-opcode-je malyon-opcode-jl
639 malyon-opcode-jg malyon-opcode-dec-chk
640 malyon-opcode-inc-chk malyon-opcode-jin
641 malyon-opcode-test malyon-opcode-or
642 malyon-opcode-and malyon-opcode-test-attr
643 malyon-opcode-set-attr malyon-opcode-clear-attr
644 malyon-opcode-store malyon-opcode-insert-obj
645 malyon-opcode-loadw malyon-opcode-loadb
646 malyon-opcode-get-prop malyon-opcode-get-prop-addr
647 malyon-opcode-get-next-prop malyon-opcode-add
648 malyon-opcode-sub malyon-opcode-mul
649 malyon-opcode-div malyon-opcode-mod
650 malyon-opcode-calls malyon-opcode-calln
651 malyon-opcode-set-color malyon-opcode-throw
652 malyon-opcode-nop malyon-opcode-nop
653 malyon-opcode-nop malyon-opcode-nop
654 malyon-opcode-je malyon-opcode-jl
655 malyon-opcode-jg malyon-opcode-dec-chk
656 malyon-opcode-inc-chk malyon-opcode-jin
657 malyon-opcode-test malyon-opcode-or
658 malyon-opcode-and malyon-opcode-test-attr
659 malyon-opcode-set-attr malyon-opcode-clear-attr
660 malyon-opcode-store malyon-opcode-insert-obj
661 malyon-opcode-loadw malyon-opcode-loadb
662 malyon-opcode-get-prop malyon-opcode-get-prop-addr
663 malyon-opcode-get-next-prop malyon-opcode-add
664 malyon-opcode-sub malyon-opcode-mul
665 malyon-opcode-div malyon-opcode-mod
666 malyon-opcode-calls malyon-opcode-calln
667 malyon-opcode-set-color malyon-opcode-throw
668 malyon-opcode-nop malyon-opcode-nop
669 malyon-opcode-nop malyon-opcode-jz
670 malyon-opcode-get-sibling malyon-opcode-get-child
671 malyon-opcode-get-parent malyon-opcode-get-prop-len
672 malyon-opcode-inc malyon-opcode-dec
673 malyon-opcode-print-addr malyon-opcode-calls
674 malyon-opcode-remove-obj malyon-opcode-print-obj
675 malyon-opcode-ret malyon-opcode-jump
676 malyon-opcode-print-paddr malyon-opcode-load
677 malyon-opcode-calln malyon-opcode-jz
678 malyon-opcode-get-sibling malyon-opcode-get-child
679 malyon-opcode-get-parent malyon-opcode-get-prop-len
680 malyon-opcode-inc malyon-opcode-dec
681 malyon-opcode-print-addr malyon-opcode-calls
682 malyon-opcode-remove-obj malyon-opcode-print-obj
683 malyon-opcode-ret malyon-opcode-jump
684 malyon-opcode-print-paddr malyon-opcode-load
685 malyon-opcode-calln malyon-opcode-jz
686 malyon-opcode-get-sibling malyon-opcode-get-child
687 malyon-opcode-get-parent malyon-opcode-get-prop-len
688 malyon-opcode-inc malyon-opcode-dec
689 malyon-opcode-print-addr malyon-opcode-calls
690 malyon-opcode-remove-obj malyon-opcode-print-obj
691 malyon-opcode-ret malyon-opcode-jump
692 malyon-opcode-print-paddr malyon-opcode-load
693 malyon-opcode-calln malyon-opcode-rtrue
694 malyon-opcode-rfalse malyon-opcode-print
695 malyon-opcode-print-ret malyon-opcode-nop
696 malyon-opcode-illegal malyon-opcode-illegal
697 malyon-opcode-restart malyon-opcode-ret-popped
698 malyon-opcode-catch malyon-opcode-quit
699 malyon-opcode-new-line malyon-opcode-illegal
700 malyon-opcode-verify malyon-opcode-illegal
701 malyon-opcode-piracy malyon-opcode-nop
702 malyon-opcode-je malyon-opcode-jl
703 malyon-opcode-jg malyon-opcode-dec-chk
704 malyon-opcode-inc-chk malyon-opcode-jin
705 malyon-opcode-test malyon-opcode-or
706 malyon-opcode-and malyon-opcode-test-attr
707 malyon-opcode-set-attr malyon-opcode-clear-attr
708 malyon-opcode-store malyon-opcode-insert-obj
709 malyon-opcode-loadw malyon-opcode-loadb
710 malyon-opcode-get-prop malyon-opcode-get-prop-addr
711 malyon-opcode-get-next-prop malyon-opcode-add
712 malyon-opcode-sub malyon-opcode-mul
713 malyon-opcode-div malyon-opcode-mod
714 malyon-opcode-calls malyon-opcode-calln
715 malyon-opcode-set-color malyon-opcode-throw
716 malyon-opcode-nop malyon-opcode-nop
717 malyon-opcode-nop malyon-opcode-calls
718 malyon-opcode-storew malyon-opcode-storeb
719 malyon-opcode-put-prop malyon-opcode-aread
720 malyon-opcode-print-char malyon-opcode-print-num
721 malyon-opcode-random malyon-opcode-push
722 malyon-opcode-pull malyon-opcode-split-window
723 malyon-opcode-set-window malyon-opcode-calls
724 malyon-opcode-erase-window malyon-opcode-erase-line
725 malyon-opcode-set-cursor malyon-opcode-get-cursor
726 malyon-opcode-set-text-style malyon-opcode-buffer-mode
727 malyon-opcode-output-stream malyon-opcode-input-stream
728 malyon-opcode-nop malyon-opcode-read-char
729 malyon-opcode-scan-table malyon-opcode-not
730 malyon-opcode-calln malyon-opcode-calln
731 malyon-opcode-tokenise malyon-opcode-encode-text
732 malyon-opcode-copy-table malyon-opcode-print-table
733 malyon-opcode-check-arg-count malyon-opcode-save
734 malyon-opcode-restore malyon-opcode-log-shift
735 malyon-opcode-art-shift malyon-opcode-set-font
736 malyon-opcode-illegal malyon-opcode-illegal
737 malyon-opcode-illegal malyon-opcode-illegal
738 malyon-opcode-save-undo malyon-opcode-restore-undo
739 malyon-opcode-print-unicode malyon-opcode-check-unicode
740 malyon-opcode-nop malyon-opcode-nop
742 "A vector of all known legal z code opcodes.")
746 (defun malyon-load-story-file (file-name)
747 "Load a z code story file into an internal vector."
749 (set-buffer (create-file-buffer file-name))
750 (malyon-disable-multibyte)
751 (malyon-erase-buffer)
752 (let ((coding-system-for-read 'binary))
753 (insert-file-contents file-name))
754 (setq malyon-story-file-name file-name)
755 (setq malyon-story-file (buffer-substring-no-properties (point-min)
757 (setq malyon-story-file (malyon-string-to-vector malyon-story-file))
758 (if (not (eq ?\^A 1))
760 (while (< i (length malyon-story-file))
761 (aset malyon-story-file
763 (malyon-char-to-int (aref malyon-story-file i)))
767 (defun malyon-initialize ()
768 "Initialize the z code interpreter."
769 ; (malyon-trace-file)
770 (setq malyon-game-state-quetzal t)
771 (malyon-initialize-faces)
772 (malyon-initialize-status)
773 (malyon-initialize-transcript)
774 (malyon-initialize-windows)
775 (malyon-initialize-story-header)
776 (malyon-initialize-registers)
777 (malyon-initialize-opcodes)
778 (malyon-history-clear)
779 (setq malyon-game-state-restart (malyon-current-game-state))
780 (malyon-print-header))
782 (defun malyon-initialize-status ()
783 "Initialize the status buffer."
784 (setq malyon-status-buffer (get-buffer-create "Malyon Status"))
785 (switch-to-buffer malyon-status-buffer)
786 (malyon-erase-buffer)
787 (kill-all-local-variables)
788 (setq malyon-status-buffer-point (point))
789 (setq malyon-status-buffer-lines 0)
790 (setq malyon-status-buffer-delayed-split nil)
791 (use-local-map malyon-keymap-read)
792 (set-syntax-table malyon-syntax-table)
793 (setq mode-name "Malyon")
794 (setq major-mode 'malyon-mode)
795 (run-hooks 'malyon-mode-hook))
797 (defun malyon-initialize-transcript ()
798 "Initialize the transcript buffer."
799 (setq malyon-transcript-buffer (get-buffer-create "Malyon Transcript"))
800 (switch-to-buffer malyon-transcript-buffer)
801 (malyon-erase-buffer)
802 (kill-all-local-variables)
803 (setq malyon-last-cursor-position-after-input
804 (malyon-point-max malyon-transcript-buffer))
805 (use-local-map malyon-keymap-read)
806 (set-syntax-table malyon-syntax-table)
807 (setq fill-column malyon-max-column)
809 (setq mode-name "Malyon")
810 (setq major-mode 'malyon-mode)
811 (run-hooks 'malyon-mode-hook))
813 (defun malyon-initialize-windows ()
814 "Initialize the window configuration for the z machine."
815 (setq window-min-height 3)
816 (setq malyon-transcript-buffer-buffered t)
817 (malyon-set-window-configuration 0)
818 (malyon-opcode-set-window 0))
820 (defun malyon-initialize-story-header ()
821 "Initializes the header section of the story file."
823 (if (>= malyon-story-version 5)
825 (logior 48 (malyon-read-byte 1))))
826 (malyon-store-byte 16 (logand 440 (malyon-read-byte 16)))
827 (malyon-store-byte 30 1)
828 (malyon-store-byte 31 65)
829 (malyon-store-byte 32 255)
830 (malyon-store-byte 33 (- malyon-max-column 1))
831 (malyon-store-word 34 (- malyon-max-column 1))
832 (malyon-store-word 36 255)
833 (malyon-store-word 38 1)
834 (malyon-store-word 39 1)
835 (malyon-store-byte 44 0)
836 (malyon-store-byte 45 0)
837 (malyon-store-byte 50 1)
838 (malyon-store-byte 51 0))
840 (defun malyon-initialize-registers ()
841 "Initialize the interpreter's internal registers."
842 (setq malyon-stack (make-vector 1024 0))
843 (setq malyon-stack-pointer -1)
844 (malyon-push-initial-frame)
845 (setq malyon-frame-pointer malyon-stack-pointer)
846 (setq malyon-instruction-pointer (malyon-read-word 6))
847 (setq malyon-global-variables (malyon-read-word 12))
848 (setq malyon-object-table (malyon-read-word 10))
849 (cond ((< malyon-story-version 5)
850 (setq malyon-object-table-entry-size 9)
851 (setq malyon-object-properties 31)
852 (setq malyon-object-property-offset 7))
854 (setq malyon-object-table-entry-size 14)
855 (setq malyon-object-properties 63)
856 (setq malyon-object-property-offset 12)))
857 (setq malyon-abbreviations (malyon-read-word 24))
858 (if (< malyon-story-version 5)
859 (setq malyon-score-game (zerop (logand 2 (malyon-read-byte 1)))))
860 (setq malyon-packed-multiplier
861 (malyon-cadr (assq malyon-story-version '((3 2) (5 4) (8 8)))))
862 (if (or (< malyon-story-version 5) (zerop (malyon-read-word 52)))
863 (setq malyon-alphabet (concat "abcdefghijklmnopqrstuvwxyz"
864 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
865 " \n0123456789.,!?_#'\"/\\-:()"))
866 (setq malyon-alphabet (make-string 78 ? ))
869 (aset malyon-alphabet i
870 (malyon-read-byte (+ i (malyon-read-word 52))))
872 (malyon-initialize-unicode-table)
873 (setq malyon-dictionary (malyon-read-word 8))
874 (setq malyon-dictionary-entry-length
876 (+ 1 malyon-dictionary (malyon-read-byte malyon-dictionary))))
877 (setq malyon-dictionary-num-entries
879 (+ 2 malyon-dictionary (malyon-read-byte malyon-dictionary))))
880 (setq malyon-dictionary-entries
881 (+ 4 malyon-dictionary (malyon-read-byte malyon-dictionary)))
882 (setq malyon-dictionary-word-length (if (< malyon-story-version 5) 3 5))
883 (setq malyon-current-face 'malyon-face-plain)
884 (setq malyon-print-separator nil)
885 (malyon-initialize-output-streams))
887 (defun malyon-initialize-opcodes ()
888 "Initialize the opcode table used by the story file."
889 (cond ((< malyon-story-version 5)
890 (aset malyon-opcodes 143 'malyon-opcode-not)
891 (aset malyon-opcodes 181 'malyon-opcode-save)
892 (aset malyon-opcodes 182 'malyon-opcode-restore)
893 (aset malyon-opcodes 185 'malyon-opcode-pop)
894 (aset malyon-opcodes 188 'malyon-opcode-show-status))
896 (aset malyon-opcodes 143 'malyon-opcode-calln)
897 (aset malyon-opcodes 181 'malyon-opcode-illegal)
898 (aset malyon-opcodes 182 'malyon-opcode-illegal)
899 (aset malyon-opcodes 185 'malyon-opcode-catch)
900 (aset malyon-opcodes 188 'malyon-opcode-illegal))))
902 (defun malyon-print-header ()
903 "Print malyon mode header information."
904 (malyon-opcode-set-text-style 2)
905 (malyon-print "Malyon V 1.0.2")
906 (malyon-opcode-set-text-style 0)
908 (malyon-print "A z-code interpreter for version 3, 5, and 8 games.")
910 (malyon-print "(c) 1999-2009 by Peter Ilberg <peter.ilberg@gmail.com>")
916 (defun malyon-cleanup ()
917 "Clean up the z code interpreter."
920 (setq malyon-story-file nil)
921 (setq malyon-window-configuration nil)
922 (setq malyon-game-state-restart nil)
923 (setq malyon-game-state-undo nil)
924 (if (get-buffer "Malyon Status")
925 (kill-buffer (get-buffer "Malyon Status")))
926 (if (get-buffer "Malyon Transcript")
928 (switch-to-buffer (get-buffer "Malyon Transcript"))
929 (malyon-redisplay-frame (selected-frame) t)
930 (delete-other-windows (get-buffer-window (current-buffer)))
933 (setq malyon-status-buffer nil)
934 (setq malyon-transcript-buffer nil))
936 (malyon-fatal-error "cleanup failed."))))
940 (defun malyon-fatal-error (message)
941 "Print error message and abort."
942 (setq message (concat "Malyon fatal error: " message))
945 (set-buffer malyon-transcript-buffer)
946 (goto-char (point-max))
957 (malyon-redisplay-frame (selected-frame) t)
960 ;; conversion of zscii to ascii
962 (defvar malyon-unicode-table nil
963 "An array mapping zscii characters to latin-1 ones.")
965 (defvar malyon-default-unicode-table nil
966 "The default array mapping zscii characters to latin-1 ones.")
968 (if malyon-default-unicode-table
970 (setq malyon-default-unicode-table
972 0 0 0 0 0 0 0 ; 1 - 7
973 8 0 0 0 0 10 0 0 ; 8 - 15
974 0 0 0 0 0 0 0 0 ; 16 - 23
975 0 0 0 39 0 0 0 0 ; 24 - 31
976 32 33 34 35 36 37 38 39 ; 32 - 39
977 40 41 42 43 44 45 46 47 ; 40 - 47
978 48 49 50 51 52 53 54 55 ; 48 - 55
979 56 57 58 59 60 61 62 63 ; 56 - 63
980 64 65 66 67 68 69 70 71 ; 64 - 71
981 72 73 74 75 76 77 78 79 ; 72 - 79
982 80 81 82 83 84 85 86 87 ; 80 - 87
983 88 89 90 91 92 93 94 95 ; 88 - 95
984 96 97 98 99 100 101 102 103 ; 96 - 103
985 104 105 106 107 108 109 110 111 ; 104 - 111
986 112 113 114 115 116 117 118 119 ; 112 - 119
987 120 121 122 123 124 125 126 0 ; 120 - 127
988 0 0 0 0 0 0 0 0 ; 128 - 135
989 0 0 0 0 0 0 0 0 ; 136 - 143
990 0 48 49 50 51 52 53 54 ; 144 - 151
991 55 56 57 228 246 252 196 214 ; 152 - 159
992 220 223 187 171 235 239 255 203 ; 160 - 167
993 207 225 233 237 243 250 253 193 ; 168 - 175
994 201 205 211 218 221 224 232 236 ; 176 - 183
995 242 249 192 200 204 210 217 226 ; 184 - 191
996 234 238 244 251 194 202 206 212 ; 192 - 199
997 219 229 197 248 216 227 241 245 ; 200 - 207
998 195 209 213 230 198 231 199 254 ; 208 - 215
999 240 222 208 163 63 63 161 191 ; 216 - 223
1000 0 0 0 0 0 0 0 0 ; 224 - 231
1001 0 0 0 0 0 0 0 0 ; 232 - 239
1002 0 0 0 0 0 0 0 0 ; 240 - 247
1003 0 0 0 0 0 0 0 0 ; 248 - 255
1006 (defun malyon-initialize-unicode-table ()
1007 "Initializes the zscii-to-unicode conversion table."
1008 (setq malyon-unicode-table
1009 (copy-sequence malyon-default-unicode-table))
1010 (let* ((ext (malyon-read-word 54))
1011 (len (if (zerop ext) 0 (malyon-read-word ext)))
1012 (table (if (< len 3) 0 (malyon-read-word (+ ext 6)))))
1013 (if (or (< malyon-story-version 5) (zerop table))
1017 (aset malyon-unicode-table (+ 155 i) (malyon-char-to-int ??))
1019 (setq len (malyon-read-byte table))
1022 (aset malyon-unicode-table (+ 155 i)
1023 (malyon-read-word (+ table 1 i)))
1024 (setq i (+ 1 i)))))))
1026 (defsubst malyon-zscii-to-unicode (char)
1027 "Converts a zscii character to unicode."
1028 (if (or (< char 0) (> char 255))
1030 (let ((uni (aref malyon-unicode-table char)))
1033 (malyon-unibyte-char-to-multibyte (malyon-int-to-char uni))))))
1035 (defsubst malyon-unicode-to-zscii (char)
1036 "Converts a unicode character to zscii."
1037 (setq char (malyon-multibyte-char-to-unibyte char))
1038 (setq char (if (malyon-characterp char) (malyon-char-to-int char) char))
1041 (let ((i 1) (found 0))
1042 (while (and (< i 255) (zerop found))
1043 (if (= char (aref malyon-unicode-table i))
1046 (malyon-int-to-char found))))
1050 (defvar malyon-output-streams nil
1051 "Valid output streams for the interpreter.")
1053 (defvar malyon-output-streams-tables nil
1054 "A list of active tables for stream 3.")
1056 (defun malyon-initialize-output-streams ()
1057 "Initializes the output streams."
1058 (setq malyon-output-streams '())
1059 (setq malyon-output-streams-tables '())
1060 (malyon-add-output-stream 1 0))
1062 (defun malyon-output-stream-function (stream)
1063 "Returns the output function representing the given stream."
1064 (cond ((= 1 stream) (if (zerop malyon-current-window)
1065 'malyon-putchar-transcript
1066 'malyon-putchar-status))
1067 ((= 2 stream) 'malyon-putchar-printer)))
1069 (defun malyon-add-output-stream (stream table)
1070 "Add a new output stream."
1073 (setq malyon-output-streams-tables
1074 (cons table malyon-output-streams-tables))
1075 (malyon-store-word table 0))
1076 (let ((function (malyon-output-stream-function stream)))
1077 (setq malyon-output-streams
1078 (if (member function malyon-output-streams)
1079 malyon-output-streams
1080 (cons function malyon-output-streams))))))
1082 (defun malyon-remove-output-stream (stream)
1083 "Remove an output stream."
1085 (setq malyon-output-streams-tables (cdr malyon-output-streams-tables))
1086 (setq malyon-output-streams
1087 (malyon-remove (malyon-output-stream-function stream)
1088 malyon-output-streams))))
1090 (defun malyon-update-output-streams ()
1091 "Update output streams when the output window has changed."
1092 (let ((one (or (member 'malyon-putchar-transcript malyon-output-streams)
1093 (member 'malyon-putchar-status malyon-output-streams))))
1094 (setq malyon-output-streams
1095 (malyon-remove 'malyon-putchar-transcript
1096 (malyon-remove 'malyon-putchar-status
1097 malyon-output-streams)))
1099 (malyon-add-output-stream 1 0))))
1101 (defsubst malyon-output-character (char)
1102 "Output a single character on all active streams."
1103 (setq char (malyon-zscii-to-unicode char))
1104 (if malyon-output-streams-tables
1105 (malyon-putchar-table char (car malyon-output-streams-tables))
1106 (malyon-mapc (lambda (s) (funcall s char)) malyon-output-streams)))
1110 (defsubst malyon-abbrev (abbrev x)
1111 "Print an abbreviation."
1113 (* 2 (malyon-read-word (+ malyon-abbreviations
1114 (* 2 (+ x (* 32 (1- abbrev)))))))))
1116 (defun malyon-newline ()
1118 (if (eq malyon-status-buffer (current-buffer))
1119 (goto-char malyon-status-buffer-point)
1120 (goto-char (point-max)))
1121 (malyon-output-character ?\r)
1122 (if (eq malyon-status-buffer (current-buffer))
1123 (setq malyon-status-buffer-point (point))
1124 (goto-char malyon-last-cursor-position-after-input))
1125 (malyon-redisplay-frame (selected-frame) nil))
1127 (defun malyon-print (object)
1129 (let ((text (if (malyon-characterp object) (char-to-string object) object))
1131 (if (eq malyon-transcript-buffer (current-buffer))
1132 (goto-char (point-max))
1133 (goto-char malyon-status-buffer-point))
1134 (setq start (point))
1135 (malyon-print-characters (malyon-string-to-list text))
1136 (put-text-property start (point) 'face malyon-current-face)
1137 (if (eq malyon-status-buffer (current-buffer))
1138 (setq malyon-status-buffer-point (point))
1139 (goto-char malyon-last-cursor-position-after-input))))
1141 (defun malyon-print-characters (text)
1142 "Print a list of characters."
1143 (malyon-mapc 'malyon-output-character text))
1145 (defsubst malyon-print-state-new (char shift abbr zscii zcode)
1146 "Generate a new print state."
1147 (list char shift abbr zscii zcode))
1149 (defsubst malyon-print-state-initial ()
1150 "Returns an initial state for the ztext decoder."
1151 (malyon-print-state-new nil -6 0 0 0))
1153 (defsubst malyon-print-state-next (x ignore shift abbr zscii z)
1154 "Print state transition function."
1156 (malyon-print-state-new (+ z x) -6 0 0 0))
1158 (malyon-print-state-new nil -6 0 2 (* 32 x)))
1160 (malyon-abbrev abbr x)
1161 (malyon-print-state-initial))
1163 (malyon-print-state-new ? -6 0 0 0))
1165 (malyon-print-state-new nil -6 x 0 0))
1167 (malyon-print-state-new nil 20 0 0 0))
1169 (malyon-print-state-new nil 46 0 0 0))
1170 ((and (= shift 46) (= x 6))
1171 (malyon-print-state-new nil -6 0 1 0))
1172 ((and (= shift 46) (= x 7))
1173 (malyon-print-state-new ?\r -6 0 0 0))
1175 (malyon-print-state-new
1176 (aref malyon-alphabet (+ shift x)) -6 0 0 0))))
1178 (defun malyon-print-text (address)
1179 "Print text at address and return the address of the following byte."
1181 (if (eq malyon-transcript-buffer (current-buffer))
1182 (goto-char (point-max))
1183 (goto-char malyon-status-buffer-point))
1184 (setq start (point))
1185 (setq address (malyon-print-ztext address))
1186 (put-text-property start (point) 'face malyon-current-face)
1187 (if (eq malyon-status-buffer (current-buffer))
1188 (setq malyon-status-buffer-point (point))
1189 (goto-char malyon-last-cursor-position-after-input))
1190 (malyon-redisplay-frame (selected-frame) nil)
1193 (defun malyon-print-ztext (address)
1194 "Print the ztext stored at the given address."
1195 (let ((high 0) (low) (a) (b) (c) (state (malyon-print-state-initial)))
1196 (while (zerop (logand 128 high))
1197 (setq high (malyon-read-byte address))
1198 (setq low (malyon-read-byte (+ 1 address)))
1199 (setq a (logand 31 (lsh high -2)))
1200 (setq b (logand 31 (logior (lsh high 3) (lsh low -5))))
1201 (setq c (logand 31 low))
1202 (setq state (apply 'malyon-print-state-next a state))
1203 (if (car state) (malyon-output-character (car state)))
1204 (setq state (apply 'malyon-print-state-next b state))
1205 (if (car state) (malyon-output-character (car state)))
1206 (setq state (apply 'malyon-print-state-next c state))
1207 (if (car state) (malyon-output-character (car state)))
1208 (setq address (+ 2 address)))
1211 (defun malyon-putchar-transcript (char)
1212 "Print a single character in the transcript window."
1213 (if (char-equal char ?\n)
1216 (setq malyon-print-separator (null (member char malyon-whitespace))))
1217 (if (and malyon-transcript-buffer-buffered
1218 (> (current-column) (current-fill-column)))
1222 (if (< 0 (current-column))
1226 (defun malyon-putchar-status (char)
1227 "Print a single character in the status window."
1228 (if malyon-status-buffer-delayed-split
1230 (malyon-split-buffer-windows malyon-status-buffer-delayed-split)
1232 (if (char-equal char ?\n)
1236 (if (= (point) (point-max))
1238 (if (> (current-column) (current-fill-column))
1243 (defun malyon-putchar-table (char table)
1244 "Print a single character into a table."
1245 (setq char (malyon-unicode-to-zscii char))
1246 (malyon-store-byte (+ 2 table (malyon-read-word table)) char)
1247 (malyon-store-word table (+ 1 (malyon-read-word table))))
1249 (defun malyon-putchar-printer (char)
1250 "Print a single character onto a printer."); not yet implemented
1254 (defvar malyon-more-continue-keymap nil
1255 "The keymap with which to continue after More has finished.")
1257 (defun malyon-more (keymap)
1259 (if (eq malyon-status-buffer (current-buffer))
1260 (use-local-map keymap)
1261 (if (< malyon-story-version 5) (malyon-opcode-show-status))
1262 (if (< (count-lines malyon-last-cursor-position-after-input (point-max))
1263 (malyon-window-displayed-height))
1265 (malyon-adjust-transcript)
1266 (use-local-map keymap))
1267 (goto-char malyon-last-cursor-position-after-input)
1270 (setq malyon-more-continue-keymap keymap)
1271 (use-local-map malyon-keymap-more)
1272 (message "[More]"))))
1274 (defun malyon-more-status-buffer ()
1275 "Enter More mode for the status buffer."
1276 (setq malyon-more-continue-keymap (current-local-map))
1277 (use-local-map malyon-keymap-more-status)
1279 (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-input))
1283 (defvar malyon-history nil
1284 "The input history.")
1286 (defun malyon-history-previous ()
1287 "Move one entry up in the input history."
1288 (let ((prev (aref malyon-history 0))
1289 (curr (aref malyon-history 1))
1290 (next (aref malyon-history 2)))
1293 (aset malyon-history 2 (if curr (cons curr next) next))
1294 (aset malyon-history 0 (cdr prev))
1295 (aset malyon-history 1 (car prev)))))
1297 (defun malyon-history-next ()
1298 "Move one entry down in the input history."
1299 (let ((prev (aref malyon-history 0))
1300 (curr (aref malyon-history 1))
1301 (next (aref malyon-history 2)))
1304 (aset malyon-history 0 (if curr (cons curr prev) prev))
1305 (aset malyon-history 2 (cdr next))
1306 (aset malyon-history 1 (car next)))))
1308 (defun malyon-history-clear ()
1309 "Clear the input history."
1310 (setq malyon-history (vector '() nil '())))
1312 (defun malyon-history-insert (entry)
1313 "Insert an entry into the input history."
1314 (let* ((prev (aref malyon-history 0))
1315 (curr (aref malyon-history 1))
1316 (next (aref malyon-history 2))
1317 (l (malyon-remove entry
1318 (append (nreverse prev)
1319 (if curr (cons curr next) next))))
1320 (cut (- (length l) 19)))
1324 (aset malyon-history 0
1325 (malyon-remove nil (malyon-remove "" (cons entry (nreverse l)))))
1326 (aset malyon-history 1 nil)
1327 (aset malyon-history 2 '())))
1329 ;; dictionary lookup
1331 (defun malyon-dictionary-word (chars)
1332 "Convert a list of characters into a dictionary word."
1333 (list (car (car chars))
1335 (malyon-encode-dictionary-word (append (malyon-mapcan 'cdr chars)
1336 '(5 5 5 5 5 5 5 5)))))
1338 (defsubst malyon-join-characters (stop list)
1339 "Joins three ztext characters into two bytes."
1340 (let ((a (car list))
1341 (b (malyon-cadr list))
1342 (c (malyon-caddr list))
1343 (x (if (zerop stop) 0 128)))
1344 (list (logior x (logand 255 (logior (lsh a 2) (lsh b -3))))
1345 (logand 255 (logior (lsh b 5) c)))))
1347 (defun malyon-encode-dictionary-word (l)
1348 "Converts a list of ztext characters into a dictionary word."
1350 (second (malyon-cdddr first))
1351 (third (malyon-cdddr second)))
1353 (if (< malyon-story-version 5)
1354 (append (malyon-join-characters 0 first)
1355 (malyon-join-characters 1 second))
1356 (append (malyon-join-characters 0 first)
1357 (malyon-join-characters 0 second)
1358 (malyon-join-characters 1 third))))))
1360 (defun malyon-lookup (dict code)
1361 "Look for the given code in the dictionary and return its address."
1362 (cond ((not code) 0)
1363 ((not dict) (malyon-binary-search code))
1364 ((= dict malyon-dictionary) (malyon-binary-search code))
1365 (t (malyon-linear-search dict code))))
1367 (defsubst malyon-compare-words (word address)
1368 "Compares the given word to the word stored at address."
1372 (y (malyon-read-byte j)))
1373 (while (not (or (/= x y) (= i malyon-dictionary-word-length)))
1377 y (malyon-read-byte j)))
1382 (defun malyon-binary-search (code)
1383 "Binary search through the main dictionary."
1385 (upper (- malyon-dictionary-num-entries 1))
1386 (median (/ (+ lower upper) 2))
1387 (entry (+ malyon-dictionary-entries
1388 (* malyon-dictionary-entry-length median)))
1389 (looking (malyon-compare-words code entry)))
1390 (while (not (or (> lower upper) (zerop looking)))
1391 (setq lower (if (< 0 looking) (+ median 1) lower)
1392 upper (if (> 0 looking) (- median 1) upper)
1393 median (/ (+ lower upper) 2)
1394 entry (+ malyon-dictionary-entries
1395 (* malyon-dictionary-entry-length median))
1396 looking (malyon-compare-words code entry)))
1397 (if (zerop looking) entry 0)))
1399 (defun malyon-linear-search (dictionary code)
1400 "Linear search through the given dictionary."
1401 (let* ((length (malyon-read-byte (+ dictionary 1
1402 (malyon-read-byte dictionary))))
1403 (number (malyon-read-word (+ dictionary 2
1404 (malyon-read-byte dictionary))))
1405 (entries (+ dictionary 4 (malyon-read-byte dictionary)))
1407 (entry (+ entries (* length i)))
1408 (looking (malyon-compare-words code entry)))
1409 (while (not (or (>= i number) (zerop looking)))
1411 entry (+ entries (* length i))
1412 looking (malyon-compare-words code entry)))
1413 (if (zerop looking) entry 0)))
1415 ;; encoding text and lexical analysis
1417 (defun malyon-split-list (sep list &optional x)
1418 "Split a list into sublists as indicated by the separators."
1420 (list (nreverse x)))
1421 ((eq sep (car list))
1422 (cons (nreverse x) (malyon-split-list sep (cdr list) '())))
1424 (malyon-split-list sep (cdr list) (cons (car list) x)))))
1426 (defun malyon-characters-to-words (list)
1427 "Turn the list of characters into a list of words."
1428 (mapcar 'malyon-dictionary-word
1429 (delete '() (malyon-split-list 'malyon-word-separator list))))
1431 (defsubst malyon-char-in-string (c s)
1432 "Returns the index of c in s if found, or length of s."
1434 (while (not (or (= i (length s)) (= c (aref s i))))
1438 (defsubst malyon-encode-into-ztext (c)
1439 "Convert a character into ztext."
1440 (let* ((index (malyon-char-in-string c malyon-alphabet))
1441 (shift (floor index 26))
1442 (char (+ 6 (mod index 26))))
1443 (cond ((> shift 2) (list 5 6 (logand 31 (lsh c -5)) (logand 31 c)))
1444 ((= shift 2) (list 5 char))
1445 ((= shift 1) (list 4 char))
1448 (defun malyon-encode-single-character (terminating-characters char)
1449 "Encode a character into ztext."
1450 (let ((pos (car char))
1452 (cond ((member c malyon-whitespace)
1453 (list 'malyon-word-separator))
1454 ((member c terminating-characters)
1455 (list 'malyon-word-separator
1456 (cons pos (malyon-encode-into-ztext c))
1457 'malyon-word-separator))
1458 (t (list (cons pos (malyon-encode-into-ztext c)))))))
1460 (defun malyon-encode-character-list (dict list)
1461 "Encode the list of characters into ztext."
1464 (while (< i (malyon-read-byte dict))
1465 (setq l (cons (malyon-read-byte (+ dict 1 i)) l)
1467 (malyon-mapcan (lambda (x) (malyon-encode-single-character l x)) list)))
1469 (defun malyon-text-length (address)
1470 "Return the length of the input text."
1471 (if (>= malyon-story-version 5)
1472 (malyon-read-byte (+ 1 address))
1474 (while (not (zerop (malyon-read-byte (+ i 1 address))))
1478 (defun malyon-text-to-character-list (address)
1479 "Convert the input text into a list of characters."
1480 (let ((i (malyon-text-length address))
1484 (cons (if (< malyon-story-version 5) i (+ 1 i))
1486 (+ i address (if (< malyon-story-version 5) 0 1))))
1491 (defun malyon-text-to-words (address dictionary)
1492 "Turn ztext into a list of dictionary words."
1493 (malyon-characters-to-words
1494 (malyon-encode-character-list (if dictionary dictionary malyon-dictionary)
1495 (malyon-text-to-character-list address))))
1497 ;; window management
1499 (defvar malyon-status-buffer-grew-this-turn nil
1500 "A flag signalling if the status buffer grew this turn.")
1502 (defun malyon-adjust-transcript ()
1503 "Adjust the position of the transcript text."
1505 (setq malyon-status-buffer-grew-this-turn nil)
1506 (set-buffer malyon-transcript-buffer)
1507 (goto-char (point-max))
1508 (recenter (- (malyon-window-displayed-height) 2))))
1510 (defun malyon-prepare-status-buffer (status)
1511 "Fill the status buffer with empty lines."
1513 (set-buffer malyon-status-buffer)
1514 (let ((lines (count-lines (point-min) (point-max)))
1518 (goto-char (point-max))
1519 (setq status (- status lines -1))
1521 (insert (make-string (+ 3 malyon-max-column) ? ))
1523 (setq status (- status 1)))
1524 (goto-char (point-min))
1525 (forward-line (+ 1 new))
1526 (kill-region (point) (point-max))
1527 (insert (make-string (+ 3 malyon-max-column) ? ))
1530 (defun malyon-restore-window-configuration ()
1531 "Restore the saved window configuration."
1532 (let ((buffer (window-buffer (selected-window))))
1533 (if malyon-window-configuration
1534 (set-window-configuration malyon-window-configuration))
1535 (cond ((eq malyon-status-buffer buffer) (other-window 1))
1536 ((eq malyon-transcript-buffer buffer) (goto-char (point-max))))))
1538 (defun malyon-set-window-configuration (status)
1539 "Set up the new window configuration."
1540 (cond ((< status malyon-status-buffer-lines)
1541 (setq malyon-status-buffer-delayed-split status)
1542 (if malyon-status-buffer-grew-this-turn
1543 (malyon-more-status-buffer)))
1544 ((> status malyon-status-buffer-lines)
1545 (malyon-split-buffer-windows status)
1546 (setq malyon-status-buffer-grew-this-turn t))
1547 ((not malyon-window-configuration)
1548 (malyon-split-buffer-windows status))))
1550 (defun malyon-split-buffer-windows (status)
1551 "Split the buffer windows.
1552 The status buffer gets 'status' lines while the transcript buffer
1553 gets the remaining lines."
1554 (delete-other-windows (get-buffer-window (current-buffer)))
1555 (setq malyon-status-buffer-lines status)
1556 (setq malyon-status-buffer-delayed-split nil)
1559 (split-window (get-buffer-window (current-buffer)) (+ status 3))
1560 (switch-to-buffer malyon-status-buffer)
1561 (malyon-prepare-status-buffer status)
1562 (malyon-opcode-set-cursor 1 1)
1564 (switch-to-buffer malyon-transcript-buffer)
1565 (setq malyon-window-configuration (current-window-configuration)))
1567 ;; getting and setting the machine state
1569 (defun malyon-current-game-state ()
1570 "Return the current state of the interpreter."
1571 (vector malyon-instruction-pointer
1572 malyon-stack-pointer
1573 malyon-frame-pointer
1574 (copy-sequence malyon-stack)
1575 (copy-sequence malyon-story-file)
1576 malyon-game-state-quetzal))
1578 (defun malyon-set-game-state (state)
1579 "Installs the given state as the new state of the interpreter."
1580 (setq malyon-instruction-pointer (aref state 0))
1581 (setq malyon-stack-pointer (aref state 1))
1582 (setq malyon-frame-pointer (aref state 2))
1583 (setq malyon-stack (copy-sequence (aref state 3)))
1584 (setq malyon-story-file (copy-sequence (aref state 4)))
1585 (setq malyon-game-state-quetzal (aref state 5))
1587 (malyon-erase-buffer malyon-status-buffer)
1588 (malyon-split-buffer-windows 0)
1589 (setq malyon-last-cursor-position-after-input
1590 (malyon-point-max malyon-transcript-buffer))))
1594 (defsubst malyon-write-byte-to-file (byte)
1595 "Write a byte to a file."
1596 (insert-char (logand 255 byte) 1))
1598 (defsubst malyon-write-word-to-file (word)
1599 "Write a word to the last opened file."
1600 (insert-char (logand 255 (lsh word -8)) 1)
1601 (insert-char (logand 255 word) 1))
1603 (defsubst malyon-write-dword-to-file (dword)
1604 "Write a dword to the last opened file."
1605 (insert-char (logand 255 (lsh dword -24)) 1)
1606 (insert-char (logand 255 (lsh dword -16)) 1)
1607 (insert-char (logand 255 (lsh dword -8)) 1)
1608 (insert-char (logand 255 dword) 1))
1610 (defsubst malyon-write-chunk-id-to-file (id)
1611 "Write a quetzal chunk id to the last opened file."
1614 (defsubst malyon-read-byte-from-file ()
1615 "Read the next byte from a file."
1616 (if (= (point) (point-max))
1619 (malyon-char-to-int (malyon-char-before))))
1621 (defsubst malyon-read-word-from-file ()
1622 "Read the next word from the last opened file."
1623 (logior (lsh (malyon-read-byte-from-file) 8) (malyon-read-byte-from-file)))
1625 (defsubst malyon-read-dword-from-file ()
1626 "Read the next dword from the last opened file."
1627 (logior (lsh (malyon-read-byte-from-file) 24)
1628 (lsh (malyon-read-byte-from-file) 16)
1629 (lsh (malyon-read-byte-from-file) 8)
1630 (malyon-read-byte-from-file)))
1632 (defsubst malyon-read-chunk-id-from-file ()
1633 "Read a quetzal chunk id from the last opened file."
1634 (string (malyon-int-to-char (malyon-read-byte-from-file))
1635 (malyon-int-to-char (malyon-read-byte-from-file))
1636 (malyon-int-to-char (malyon-read-byte-from-file))
1637 (malyon-int-to-char (malyon-read-byte-from-file))))
1639 (defun malyon-get-file-name (address)
1640 "Retrieves the file name stored at address."
1641 (let ((name (make-string (malyon-read-byte address) ? ))
1643 (while (< i (length name))
1644 (aset name i (malyon-read-byte (+ address 1 i)))
1648 ;; saving data to disk
1650 (defun malyon-save-file (file &optional table length)
1651 "Save the current game state or a memory section to disk."
1652 (interactive "FSave file: ")
1655 (set-buffer (create-file-buffer file))
1656 (malyon-disable-multibyte)
1657 (malyon-erase-buffer)
1658 (cond (table (malyon-save-table table length))
1659 (malyon-game-state-quetzal
1660 (malyon-save-quetzal-state (malyon-current-game-state)))
1662 (malyon-save-game-state (malyon-current-game-state))))
1663 (let ((coding-system-for-write 'binary))
1669 (defun malyon-save-table (table length)
1670 "Save the given section of memory to the file."
1674 (malyon-write-byte-to-file (malyon-read-byte j))
1678 (defun malyon-save-game-state (state)
1679 "Saves the game state to disk."
1680 (let ((ip (aref state 0))
1683 (stack (aref state 3))
1684 (mem (aref state 4))
1685 (dyn (malyon-read-word 14))
1687 (malyon-write-word-to-file (length malyon-story-file-name))
1688 (while (< i (length malyon-story-file-name))
1689 (malyon-write-byte-to-file (aref malyon-story-file-name i))
1691 (malyon-write-dword-to-file ip)
1692 (malyon-write-word-to-file sp)
1693 (malyon-write-word-to-file fp)
1694 (malyon-write-word-to-file dyn)
1697 (malyon-write-dword-to-file (aref stack i))
1701 (malyon-write-byte-to-file (aref mem i))
1704 (defun malyon-save-quetzal-state (state)
1705 "Saves the game state to disk in quetzal format."
1706 (goto-char (point-min))
1707 (malyon-save-quetzal-ifhd state)
1708 (malyon-save-quetzal-cmem state)
1709 (malyon-save-quetzal-stks state)
1710 (goto-char (point-min))
1711 (malyon-write-chunk-id-to-file "IFZS")
1712 (goto-char (point-min))
1713 (malyon-write-dword-to-file (- (point-max) (point-min)))
1714 (goto-char (point-min))
1715 (malyon-write-chunk-id-to-file "FORM"))
1717 (defun malyon-save-quetzal-ifhd (state)
1718 "Saves the IFhd chunk of the quetzal format."
1719 (malyon-write-chunk-id-to-file "IFhd")
1720 (malyon-write-dword-to-file 13)
1721 (malyon-write-word-to-file (malyon-read-word 2))
1722 (malyon-write-word-to-file (malyon-read-word 18))
1723 (malyon-write-word-to-file (malyon-read-word 20))
1724 (malyon-write-word-to-file (malyon-read-word 22))
1725 (malyon-write-word-to-file (malyon-read-word 28))
1726 (malyon-write-byte-to-file (lsh (aref state 0) -16))
1727 (malyon-write-byte-to-file (lsh (aref state 0) -8))
1728 (malyon-write-byte-to-file (aref state 0))
1729 (malyon-write-byte-to-file 0))
1731 (defun malyon-save-quetzal-cmem (state)
1732 "Saves the CMem chunk of the quetzal format."
1733 (let ((beginning (point-max))
1734 (original (aref malyon-game-state-restart 4))
1735 (current (aref state 4))
1736 (size (malyon-read-word 14))
1740 (goto-char (point-max))
1742 (setq byte (logxor (aref current i) (aref original i)))
1744 (setq count (+ 1 count))
1746 (malyon-write-byte-to-file 0)
1747 (setq count (- count 1))
1748 (malyon-write-byte-to-file (min 255 count))
1749 (setq count (- count (min 255 count))))
1750 (malyon-write-byte-to-file byte))
1752 (setq size (- (point-max) beginning))
1753 (if (zerop (mod size 2)) '() (malyon-write-byte-to-file 0))
1754 (goto-char beginning)
1755 (malyon-write-chunk-id-to-file "CMem")
1756 (malyon-write-dword-to-file size)))
1758 (defun malyon-save-quetzal-stks (state)
1759 "Saves the Stks chunk of the quetzal format."
1760 (let ((beginning (point-max))
1762 (goto-char (point-max))
1763 (malyon-save-quetzal-stack-frame (- (aref state 2) 4)
1766 (setq size (- (point-max) beginning))
1767 (if (zerop (mod size 2)) '() (malyon-write-byte-to-file 0))
1768 (goto-char beginning)
1769 (malyon-write-chunk-id-to-file "Stks")
1770 (malyon-write-dword-to-file size)))
1772 (defun malyon-save-quetzal-stack-frame (fp sp stack)
1773 "Saves the stack frames for the Stks chunk."
1774 (let* ((frame (malyon-get-stack-frame fp sp stack))
1775 (frame-id (aref frame 0))
1776 (previous-fp (aref frame 1))
1777 (previous-sp (aref frame 2))
1778 (return-addr (aref frame 3))
1779 (result-addr (aref frame 4))
1780 (local-vars (aref frame 5))
1781 (num-args (aref frame 6))
1782 (eval-stack (aref frame 7)))
1784 (malyon-save-quetzal-stack-frame previous-fp previous-sp stack))
1785 (malyon-write-byte-to-file (lsh return-addr -16))
1786 (malyon-write-byte-to-file (lsh return-addr -8))
1787 (malyon-write-byte-to-file return-addr)
1788 (if (zerop frame-id)
1789 (malyon-write-byte-to-file 0)
1790 (malyon-write-byte-to-file (logior (if result-addr 0 16)
1791 (length local-vars))))
1792 (malyon-write-byte-to-file (if result-addr result-addr 0))
1793 (malyon-write-byte-to-file (- (lsh 1 num-args) 1))
1794 (malyon-write-word-to-file (length eval-stack))
1795 (while (not (null local-vars))
1796 (malyon-write-word-to-file (car local-vars))
1797 (setq local-vars (cdr local-vars)))
1798 (while (not (null eval-stack))
1799 (malyon-write-word-to-file (car eval-stack))
1800 (setq eval-stack (cdr eval-stack)))))
1802 ;; restoring data from disk
1804 (defvar malyon-restore-data-error nil
1805 "An error message if restoring data from a file failed.")
1807 (defvar malyon-restore-quetzal-stack nil
1808 "A temporary stack for restoring quetzal game states.")
1810 (defvar malyon-restore-quetzal-stack-pointer nil
1811 "A temporary stack pointer for restoring quetzal game states.")
1813 (defvar malyon-restore-quetzal-frame-pointer nil
1814 "A temporary frame-pointer for restoring quetzal game states.")
1816 (defun malyon-restore-file (file &optional table length)
1817 "Restore a game state or a memory section from disk."
1818 (interactive "fLoad file: ")
1819 (if (not (and (file-exists-p file) (file-readable-p file)))
1823 (setq malyon-restore-data-error nil)
1824 (set-buffer (create-file-buffer file))
1825 (malyon-disable-multibyte)
1826 (malyon-erase-buffer)
1827 (let ((coding-system-for-read 'binary))
1828 (insert-file-contents file))
1829 (goto-char (point-min))
1831 (malyon-restore-table table length)
1832 (let* ((first (malyon-read-chunk-id-from-file))
1833 (second (malyon-read-dword-from-file))
1834 (third (malyon-read-chunk-id-from-file)))
1835 (if (and (string= "FORM" first) (string= "IFZS" third))
1836 (malyon-restore-quetzal-state (+ 8 second))
1837 (goto-char (point-min))
1838 (malyon-restore-game-state))))
1840 (if (null malyon-restore-data-error)
1842 (message malyon-restore-data-error)
1846 (defun malyon-restore-table (table length)
1847 "Restore the given section of memory from a file."
1851 (malyon-store-byte j (malyon-read-byte-from-file))
1855 (defun malyon-restore-game-state ()
1856 "Restore a saved game state from disk."
1864 (stack (copy-sequence malyon-stack))
1865 (mem (copy-sequence malyon-story-file))
1867 (setq len (malyon-read-word-from-file))
1868 (setq name (make-string len ? ))
1870 (aset name i (malyon-read-byte-from-file))
1872 (setq ip (malyon-read-dword-from-file))
1873 (setq sp (malyon-read-word-from-file))
1874 (setq fp (malyon-read-word-from-file))
1875 (setq dyn (malyon-read-word-from-file))
1878 (aset stack i (malyon-read-dword-from-file))
1882 (aset mem i (malyon-read-byte-from-file))
1884 (setq name (file-name-nondirectory name))
1885 (setq story (file-name-nondirectory malyon-story-file-name))
1886 (if (or (string-match name story) (string-match story name))
1887 (malyon-set-game-state (vector ip sp fp stack mem nil))
1888 (setq malyon-restore-data-error "Invalid save file."))))
1890 (defun malyon-restore-quetzal-state (size)
1891 "Restore a saved quetzal game state from disk."
1892 (let ((chunk-id nil)
1898 (while (< (point) size)
1899 (setq chunk-id (malyon-read-chunk-id-from-file))
1900 (setq chunk-len (malyon-read-dword-from-file))
1901 (setq beginning (point))
1902 (cond ((string= chunk-id "IFhd")
1903 (setq ip (malyon-restore-quetzal-ifhd chunk-len)))
1904 ((string= chunk-id "CMem")
1905 (setq memory (malyon-restore-quetzal-cmem chunk-len)))
1906 ((string= chunk-id "UMem")
1907 (setq memory (malyon-restore-quetzal-umem chunk-len)))
1908 ((string= chunk-id "Stks")
1909 (setq stack (malyon-restore-quetzal-stks chunk-len))))
1910 (if (zerop (mod chunk-len 2)) '() (setq chunk-len (+ 1 chunk-len)))
1911 (goto-char (+ beginning chunk-len)))
1912 (cond ((and ip memory stack)
1913 (malyon-set-game-state (vector ip
1919 ((null malyon-restore-data-error)
1920 (setq malyon-restore-data-error "invalid quetzal file.")))))
1922 (defun malyon-restore-quetzal-ifhd (size)
1923 "Restore an IFhd chunk from disk. Return the instruction pointer."
1924 (if (and (= (malyon-read-word-from-file) (malyon-read-word 2))
1925 (= (malyon-read-word-from-file) (malyon-read-word 18))
1926 (= (malyon-read-word-from-file) (malyon-read-word 20))
1927 (= (malyon-read-word-from-file) (malyon-read-word 22))
1928 (= (malyon-read-word-from-file) (malyon-read-word 28)))
1929 (logior (lsh (malyon-read-byte-from-file) 16)
1930 (lsh (malyon-read-byte-from-file) 8)
1931 (malyon-read-byte-from-file))
1932 (setq malyon-restore-data-error "quetzal file doesn't belong to game.")
1935 (defun malyon-restore-quetzal-cmem (size)
1936 "Restore a CMem chunk from disk. Return the entire memory layout."
1937 (let ((memory (copy-sequence (aref malyon-game-state-restart 4)))
1938 (max-size (+ (point) size))
1941 (while (< (point) max-size)
1942 (setq byte (malyon-read-byte-from-file))
1944 (setq i (+ 1 i (malyon-read-byte-from-file)))
1945 (aset memory i (logxor byte (aref memory i)))
1949 (defun malyon-restore-quetzal-umem (size)
1950 "Restore a UMem chunk from disk. Return the entire memory layout."
1951 (let ((memory (copy-sequence (aref malyon-game-state-restart 4)))
1954 (aset memory i (malyon-read-byte-from-file))
1958 (defun malyon-restore-quetzal-stks (size)
1959 "Restore a Stks chunk from disk. Return a vector containing the
1960 stack pointer, the frame pointer, and the stack itself."
1961 (let ((i 0) (frame-id 0))
1962 (setq malyon-restore-quetzal-stack
1963 (copy-sequence (aref malyon-game-state-restart 3)))
1964 (setq malyon-restore-quetzal-stack-pointer -1)
1965 (setq malyon-restore-quetzal-frame-pointer 2)
1967 (let* ((beginning (point))
1968 (return3 (malyon-read-byte-from-file))
1969 (return2 (malyon-read-byte-from-file))
1970 (return1 (malyon-read-byte-from-file))
1971 (return-addr (logior (lsh return3 16) (lsh return2 8) return1))
1972 (result-locals (malyon-read-byte-from-file))
1973 (has-result (zerop (logand 16 result-locals)))
1974 (num-locals (logand 15 result-locals))
1975 (result-addr (malyon-read-byte-from-file))
1976 (arg-flags (+ 1 (malyon-read-byte-from-file)))
1978 (eval-size (malyon-read-word-from-file))
1981 (while (> num-locals 0)
1982 (setq local-vars (cons (malyon-read-word-from-file) local-vars))
1983 (setq num-locals (- num-locals 1)))
1984 (while (> eval-size 0)
1985 (setq eval-stack (cons (malyon-read-word-from-file) eval-stack))
1986 (setq eval-size (- eval-size 1)))
1987 (while (> arg-flags 1)
1988 (setq arg-flags (lsh arg-flags -1))
1989 (setq num-args (+ num-args 1)))
1990 (malyon-push-stack-frame frame-id
1992 (if (zerop frame-id)
1994 (if has-result result-addr nil))
1995 (reverse local-vars)
1997 (reverse eval-stack))
1998 (setq frame-id (+ 1 frame-id))
1999 (setq i (+ i (- (point) beginning)))))
2000 (vector malyon-restore-quetzal-stack-pointer
2001 malyon-restore-quetzal-frame-pointer
2002 malyon-restore-quetzal-stack)))
2004 ;; object table management
2006 (defsubst malyon-object-address (object)
2007 "Compute the address at which the object is stored."
2008 (+ malyon-object-table
2009 (* 2 malyon-object-properties)
2010 (* malyon-object-table-entry-size (- object 1))))
2012 (defsubst malyon-object-read-parent (address)
2013 "Return the parent."
2014 (if (< malyon-story-version 5)
2015 (malyon-read-byte (+ 4 address))
2016 (malyon-read-word (+ 6 address))))
2018 (defsubst malyon-object-read-sibling (address)
2019 "Return the next sibling."
2020 (if (< malyon-story-version 5)
2021 (malyon-read-byte (+ 5 address))
2022 (malyon-read-word (+ 8 address))))
2024 (defsubst malyon-object-read-child (address)
2025 "Return the first child."
2026 (if (< malyon-story-version 5)
2027 (malyon-read-byte (+ 6 address))
2028 (malyon-read-word (+ 10 address))))
2030 (defsubst malyon-object-store-parent (address value)
2032 (if (< malyon-story-version 5)
2033 (malyon-store-byte (+ 4 address) value)
2034 (malyon-store-word (+ 6 address) value)))
2036 (defsubst malyon-object-store-sibling (address value)
2037 "Set the next sibling."
2038 (if (< malyon-story-version 5)
2039 (malyon-store-byte (+ 5 address) value)
2040 (malyon-store-word (+ 8 address) value)))
2042 (defsubst malyon-object-store-child (address value)
2043 "Set the first child."
2044 (if (< malyon-story-version 5)
2045 (malyon-store-byte (+ 6 address) value)
2046 (malyon-store-word (+ 10 address) value)))
2048 (defun malyon-find-property (object property)
2049 "Return the address of the object's property, or 0 if it doesn't exist."
2050 (let ((next (malyon-first-property object))
2052 (setq number (logand (malyon-read-byte next) malyon-object-properties))
2053 (while (> number property)
2054 (setq next (malyon-next-property next))
2055 (setq number (logand (malyon-read-byte next) malyon-object-properties)))
2056 (if (= number property) next 0)))
2058 (defun malyon-first-property (object)
2059 "Get the address of the object's first property."
2060 (let ((header (malyon-read-word (+ malyon-object-property-offset
2061 (malyon-object-address object)))))
2062 (+ header 1 (* 2 (malyon-read-byte header)))))
2064 (defun malyon-next-property (property)
2065 "Get the address of the following property."
2066 (let ((size (malyon-read-byte property))
2067 (addr (+ property 1)))
2068 (+ 1 addr (cond ((< malyon-story-version 5) (lsh size -5))
2069 ((zerop (logand 128 size)) (lsh size -6))
2071 (let ((second (logand 63 (malyon-read-byte addr))))
2072 (if (= 0 second) 64 second)))))))
2074 (defun malyon-remove-object (object)
2075 "Remove the object from the children list of its parent."
2076 (let* ((address (malyon-object-address object))
2077 (parent (malyon-object-read-parent address))
2078 (sibling (malyon-object-read-sibling address)))
2079 (malyon-object-store-parent address 0)
2080 (malyon-object-store-sibling address 0)
2082 (let ((parent-addr (malyon-object-address parent)))
2083 (let ((children (malyon-object-read-child parent-addr)))
2084 (if (or (= children 0) (= children object))
2085 (malyon-object-store-child parent-addr sibling)
2086 (let ((this (malyon-object-address children)))
2087 (let ((next (malyon-object-read-sibling this)))
2088 (while (/= next object)
2089 (setq this (malyon-object-address next))
2090 (setq next (malyon-object-read-sibling this)))
2091 (malyon-object-store-sibling this sibling)))))))))
2093 ;; function calls and code branches
2095 (defun malyon-call-routine (routine arguments &optional result)
2096 "Call a routine with the given arguments and return its result."
2098 (if result (malyon-store-variable result 0) 0)
2099 (malyon-push-stack (if result 0 1))
2100 (malyon-push-stack (if result result 0))
2101 (malyon-push-stack malyon-instruction-pointer)
2103 (logior (lsh (- malyon-stack-pointer malyon-frame-pointer) 8)
2104 (length arguments)))
2105 (setq malyon-instruction-pointer (* malyon-packed-multiplier routine))
2106 (let ((args (malyon-read-code-byte)) (value nil))
2107 (if malyon-game-state-quetzal
2108 (let ((id (lsh (aref malyon-stack malyon-frame-pointer) -8)))
2109 (malyon-push-stack (logior (lsh (+ 1 id) 8) args))))
2110 (setq malyon-frame-pointer malyon-stack-pointer)
2112 (setq value (if (< malyon-story-version 5) (malyon-read-code-word) 0))
2113 (malyon-push-stack (if (null arguments) value (car arguments)))
2114 (setq arguments (cdr arguments))
2115 (setq args (- args 1))))))
2117 (defun malyon-jump-if (condition)
2118 "Jump depending on the condition and the following jump data."
2119 (let ((byte (malyon-read-code-byte))
2122 (setq iftrue (/= 0 (logand byte 128)))
2123 (setq offset (logand byte 63))
2124 (if (= 0 (logand byte 64))
2126 (setq offset (logior (lsh offset 8) (malyon-read-code-byte)))
2127 (if (>= offset 8192) (setq offset (- offset 16384)))))
2128 (if (or (and iftrue condition) (and (not iftrue) (not condition)))
2130 (cond ((= offset 0) (malyon-opcode-rfalse))
2131 ((= offset 1) (malyon-opcode-rtrue))
2133 malyon-instruction-pointer
2134 (+ malyon-instruction-pointer offset -2))))))))
2136 (defun malyon-return (value)
2137 "Return from a routine."
2138 (setq malyon-stack-pointer malyon-frame-pointer)
2139 (if malyon-game-state-quetzal (malyon-pop-stack))
2140 (setq malyon-frame-pointer
2141 (- malyon-stack-pointer 1 (lsh (malyon-pop-stack) -8)))
2142 (setq malyon-instruction-pointer (malyon-pop-stack))
2143 (let ((result (malyon-pop-stack))
2144 (store (malyon-pop-stack)))
2146 (malyon-return-store result value)
2147 (malyon-return-ignore result value))))
2149 (defun malyon-return-ignore (where value)
2150 "Return from a routine ignoring the result.")
2152 (defun malyon-return-store (where value)
2153 "Return from a routine storing the result."
2154 (malyon-store-variable where value))
2156 (defun malyon-push-initial-frame ()
2157 "Push the initial stack frame required in quetzal mode."
2158 (if malyon-game-state-quetzal
2160 (malyon-push-stack 1)
2161 (malyon-push-stack 0)
2162 (malyon-push-stack 0)
2163 (malyon-push-stack 0)
2164 (malyon-push-stack 0))))
2166 (defun malyon-get-stack-frame (fp sp stack)
2167 "Return a decoded stack frame in quetzal mode.
2168 The result is a vector containing the frame id, the fp of the
2169 previous frame, the sp of the previous frame, the return address,
2170 the result variable if any, a list of local variables, the number
2171 of arguments, and a list of the evaluation stack elements."
2172 (let* ((has-result (zerop (aref stack fp)))
2173 (result-addr (if has-result (aref stack (+ 1 fp)) nil))
2174 (return-addr (aref stack (+ 2 fp)))
2175 (offset (lsh (aref stack (+ 3 fp)) -8))
2176 (num-args (logand 255 (aref stack (+ 3 fp))))
2177 (frame-id (lsh (aref stack (+ 4 fp)) -8))
2178 (num-locals (logand 255 (aref stack (+ 4 fp))))
2179 (start-locals (+ 5 fp))
2180 (start-eval (+ 5 fp num-locals))
2183 (if (not (zerop num-locals))
2185 (malyon-vector-to-list stack start-locals start-eval)))
2186 (if (> sp start-eval)
2188 (malyon-vector-to-list stack start-eval (+ 1 sp))))
2198 (defsubst malyon-restore-quetzal-push-stack (value)
2199 "Push a value onto the restore quetzal stack."
2200 (setq malyon-restore-quetzal-stack-pointer
2201 (+ malyon-restore-quetzal-stack-pointer 1))
2202 (aset malyon-restore-quetzal-stack
2203 malyon-restore-quetzal-stack-pointer
2206 (defun malyon-push-stack-frame
2207 (frame-id return-addr result local-vars num-args eval-stack)
2208 "Pushes a new stack frame in quetzal mode."
2209 (malyon-restore-quetzal-push-stack (if result 0 1))
2210 (malyon-restore-quetzal-push-stack (if result result 0))
2211 (malyon-restore-quetzal-push-stack return-addr)
2212 (malyon-restore-quetzal-push-stack
2213 (logior (lsh (- malyon-restore-quetzal-stack-pointer
2214 malyon-restore-quetzal-frame-pointer) 8)
2216 (malyon-restore-quetzal-push-stack
2217 (logior (lsh frame-id 8) (length local-vars)))
2218 (setq malyon-restore-quetzal-frame-pointer
2219 malyon-restore-quetzal-stack-pointer)
2220 (while (not (null local-vars))
2221 (malyon-restore-quetzal-push-stack (car local-vars))
2222 (setq local-vars (cdr local-vars)))
2223 (while (not (null eval-stack))
2224 (malyon-restore-quetzal-push-stack (car eval-stack))
2225 (setq eval-stack (cdr eval-stack))))
2229 (defvar malyon-aread-text nil
2230 "Text buffer for user input.")
2232 (defvar malyon-aread-parse nil
2233 "Parse buffer for user input.")
2235 (defvar malyon-aread-beginning-of-line nil
2236 "The beginning of the input line.")
2240 (defun malyon-interpreter ()
2241 "Run the z code interpreter on the given story file."
2242 ; (condition-case nil
2244 (malyon-restore-window-configuration)
2245 (if malyon-story-file
2246 (catch 'malyon-end-of-interpreter-loop
2247 (setq malyon-last-cursor-position-after-input
2248 (malyon-point-max malyon-transcript-buffer))
2251 ; (malyon-fatal-error "unspecified internal runtime error."))))
2253 (defsubst malyon-fetch-variable-operands (specifier)
2254 "Fetch a variable number of operands based on the specifier argument."
2255 (let ((var (logand specifier 49152))
2257 (setq specifier (logand 65535 specifier))
2258 (while (/= 0 specifier)
2259 (cond ((= var 0) (setq op (cons (malyon-read-code-word) op)))
2260 ((= var 16384) (setq op (cons (malyon-read-code-byte) op)))
2261 ((= var 32768) (setq op (cons (malyon-read-variable
2262 (malyon-read-code-byte)) op)))
2263 (t (setq specifier 0)))
2264 (setq specifier (logand 65535 (lsh specifier 2)))
2265 (setq var (logand specifier 49152)))
2268 (defsubst malyon-fetch-extended (opcode)
2269 "Fetch operands for an extended instruction."
2270 (malyon-fetch-variable-operands
2271 (logior (lsh (malyon-read-code-byte) 8) 255)))
2273 (defsubst malyon-fetch-variable (opcode)
2274 "Fetch operands for a variable instruction."
2275 (malyon-fetch-variable-operands
2276 (if (or (= opcode 236) (= opcode 250))
2277 (malyon-read-code-word)
2278 (logior (lsh (malyon-read-code-byte) 8) 255))))
2280 (defsubst malyon-fetch-short (opcode)
2281 "Fetch operands for a short instruction."
2282 (let ((op (logand opcode 48)))
2283 (cond ((= op 0) (list (malyon-read-code-word)))
2284 ((= op 16) (list (malyon-read-code-byte)))
2285 ((= op 32) (list (malyon-read-variable (malyon-read-code-byte)))))))
2287 (defsubst malyon-fetch-long (instr)
2288 "Fetch operands for a long instruction."
2289 (let ((byte1 (malyon-read-code-byte))
2290 (byte2 (malyon-read-code-byte)))
2291 (list (if (= (logand instr 64) 0) byte1 (malyon-read-variable byte1))
2292 (if (= (logand instr 32) 0) byte2 (malyon-read-variable byte2)))))
2294 (defun malyon-execute ()
2295 "Execute z code instructions.
2296 Load the next instruction opcode and its operands and execute it.
2297 Repeat ad infinitum."
2298 (let ((opcode) (operands)); (pc))
2300 ; (setq pc malyon-instruction-pointer)
2301 (setq opcode (malyon-read-code-byte))
2302 (setq operands (cond ((= opcode 190)
2303 (setq opcode (+ 256 (malyon-read-code-byte)))
2304 (malyon-fetch-extended opcode))
2306 (malyon-fetch-variable opcode))
2308 (malyon-fetch-short opcode))
2310 (malyon-fetch-long opcode))))
2311 ; (malyon-trace-opcode pc opcode operands)
2312 (apply (aref malyon-opcodes opcode) operands))))
2316 (defsubst malyon-number (n)
2317 "Convert an unsigned number into a signed one."
2318 (if (< n 32768) n (- n 65536)))
2320 (defun malyon-opcode-add (a b)
2322 (malyon-store-variable (malyon-read-code-byte)
2323 (+ (malyon-number a) (malyon-number b))))
2325 (defun malyon-opcode-and (a b)
2327 (malyon-store-variable (malyon-read-code-byte) (logand a b)))
2329 (defun malyon-opcode-aread (text parse &optional time routine)
2331 (setq malyon-aread-text text)
2332 (setq malyon-aread-parse parse)
2333 (goto-char (point-max))
2334 (setq malyon-aread-beginning-of-line (point))
2335 ; Some games violate these assumptions for the "Quit" question.
2336 ; (if (> 3 (malyon-read-byte text))
2337 ; (malyon-fatal-error "text buffer less than 3 bytes."))
2338 ; (if (and (not (zerop parse)) (> 2 (malyon-read-byte parse)))
2339 ; (malyon-fatal-error "parse buffer less than 2 bytes."))
2340 (malyon-more malyon-keymap-read)
2341 (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-input))
2343 (defun malyon-opcode-art-shift (value places)
2345 (malyon-store-variable (malyon-read-code-byte) (ash value places)))
2347 (defun malyon-opcode-buffer-mode (mode)
2348 "Toggles buffering of text in the transcript window."
2349 (setq malyon-transcript-buffer-buffered (/= 0 mode)))
2351 (defun malyon-opcode-calln (routine &rest arguments)
2352 "Call a routine and ignore the result."
2353 (malyon-call-routine routine arguments))
2355 (defun malyon-opcode-calls (routine &rest arguments)
2356 "Call a routine and store the result."
2357 (malyon-call-routine routine arguments (malyon-read-code-byte)))
2359 (defun malyon-opcode-catch ()
2360 "Return the current stack frame."
2361 (malyon-store-variable
2362 (malyon-read-code-byte)
2363 (if malyon-game-state-quetzal
2364 (lsh (aref malyon-stack malyon-frame-pointer) -8)
2365 malyon-frame-pointer)))
2367 (defun malyon-opcode-check-arg-count (count)
2368 "Tests the number of arguments passed to routine."
2370 (<= count (logand 255 (aref malyon-stack
2371 (if malyon-game-state-quetzal
2372 (- malyon-frame-pointer 1)
2373 malyon-frame-pointer))))))
2375 (defun malyon-opcode-check-unicode (char)
2376 "Check whether the given character is valid for input/output."
2377 (malyon-store-variable (malyon-read-code-byte) 0))
2379 (defun malyon-opcode-clear-attr (object attribute)
2380 "Clear the given attribute in the given object."
2381 (let ((attributes (malyon-object-address object))
2382 (byte (lsh attribute -3)))
2383 (malyon-store-byte (+ attributes byte)
2384 (logand (malyon-read-byte (+ attributes byte))
2385 (logxor (lsh 128 (- (logand attribute 7)))
2388 (defun malyon-opcode-copy-table (first second size)
2389 "Copies first table onto second one."
2390 (let* ((length (abs (malyon-number size)))
2391 (zero (zerop second))
2392 (forward (or (< (malyon-number size) 0) (> first second)))
2394 (a (if forward first (+ first length -1)))
2395 (b (if forward (if zero first second) (+ second length -1))))
2397 (malyon-store-byte b (if zero 0 (malyon-read-byte a)))
2399 a (if forward (+ a 1) (- a 1))
2400 b (if forward (+ b 1) (- b 1))))))
2402 (defun malyon-opcode-dec (var)
2403 "Decrement variable."
2404 (malyon-store-variable var
2405 (- (malyon-number (malyon-read-variable var)) 1)))
2407 (defun malyon-opcode-dec-chk (variable threshold)
2408 "Decrement variable and jump if it's less than the given value."
2409 (let ((value (malyon-number (malyon-read-variable variable))))
2410 (malyon-store-variable variable (- value 1))
2411 (malyon-jump-if (< (- value 1) (malyon-number threshold)))))
2413 (defun malyon-opcode-div (a b)
2415 (if (zerop b) (malyon-fatal-error "division by 0."))
2416 (malyon-store-variable (malyon-read-code-byte)
2417 (/ (malyon-number a) (malyon-number b))))
2419 (defun malyon-opcode-encode-text (text length from encoded)
2420 "Encode the zscii text starting at from with the given length.
2421 The result is stored at encoded."
2427 (setq l (cons (malyon-read-byte (+ text from i -1)) l)
2429 (setq word (malyon-encode-dictionary-word
2430 (append (malyon-mapcan 'malyon-encode-into-ztext l)
2431 '(5 5 5 5 5 5 5 5))))
2433 (malyon-store-byte j (car l))
2438 (defun malyon-opcode-erase-line (value)
2439 "Erases the rest of the line."
2441 (if (eq malyon-transcript-buffer (current-buffer))
2444 (let ((i (current-column)))
2445 (while (<= i malyon-max-column)
2448 (setq i (+ 1 i))))))))
2450 (defun malyon-opcode-erase-window (window)
2451 "Erase the contents of the given window."
2453 (let ((w (malyon-number window)))
2454 (if (or (= w 0) (= w -1) (= w -2))
2455 (malyon-erase-buffer malyon-transcript-buffer))
2456 (if (or (= w 1) (= w -1) (= w -2))
2457 (malyon-erase-buffer malyon-status-buffer))
2459 (malyon-split-buffer-windows 0)))
2460 (setq malyon-last-cursor-position-after-input
2461 (malyon-point-max malyon-transcript-buffer))))
2463 (defun malyon-opcode-get-child (object)
2464 "Get the first child of the given object and jump."
2465 (let ((child (malyon-object-read-child (malyon-object-address object))))
2466 (malyon-store-variable (malyon-read-code-byte) child)
2467 (malyon-jump-if (/= 0 child))))
2469 (defun malyon-opcode-get-cursor (array)
2470 "Retrieves the current cursor position."
2472 (set-buffer malyon-status-buffer)
2473 (malyon-store-word array (- (count-lines (point-min) (point)) 1))
2474 (malyon-store-word (+ 2 array) (+ 1 (current-column)))))
2476 (defun malyon-opcode-get-next-prop (object property)
2477 "Retrieve the first or next property id of object."
2478 (let ((next (malyon-first-property object))
2480 (if (zerop property)
2482 (setq number (logand (malyon-read-byte next)
2483 malyon-object-properties))
2484 (setq next (malyon-next-property next))
2485 (while (> number property)
2486 (setq number (logand (malyon-read-byte next)
2487 malyon-object-properties))
2488 (setq next (malyon-next-property next)))
2489 (if (/= number property)
2490 (malyon-fatal-error "property does not exist.")))
2491 (setq number (logand (malyon-read-byte next) malyon-object-properties))
2492 (malyon-store-variable (malyon-read-code-byte) number)))
2494 (defun malyon-opcode-get-parent (object)
2495 "Get the parent of the given object."
2496 (malyon-store-variable (malyon-read-code-byte)
2497 (malyon-object-read-parent
2498 (malyon-object-address object))))
2500 (defun malyon-opcode-get-prop (object property)
2501 "Get the value of the object's property."
2502 (let* ((address (malyon-find-property object property))
2503 (size (malyon-read-byte address)))
2504 (malyon-store-variable
2505 (malyon-read-code-byte)
2506 (cond ((zerop address)
2507 (malyon-read-word (+ malyon-object-table (* 2 (- property 1)))))
2508 ((and (< malyon-story-version 5) (zerop (lsh size -5)))
2509 (malyon-read-byte (+ address 1)))
2510 ((and (>= malyon-story-version 5) (zerop (logand 192 size)))
2511 (malyon-read-byte (+ address 1)))
2513 (malyon-read-word (+ address 1)))))))
2515 (defun malyon-opcode-get-prop-addr (object property)
2516 "Get the address of the object's property."
2517 (let* ((address (malyon-find-property object property))
2518 (size (malyon-read-byte address))
2519 (offset (if (< malyon-story-version 5)
2521 (if (zerop (logand 128 size)) 1 2))))
2522 (malyon-store-variable (malyon-read-code-byte)
2523 (if (zerop address) 0 (+ address offset)))))
2525 (defun malyon-opcode-get-prop-len (property)
2526 "Get the length of the object's property."
2527 (let ((size (malyon-read-byte (- property 1))))
2528 (malyon-store-variable
2529 (malyon-read-code-byte)
2530 (cond ((< malyon-story-version 5) (+ 1 (lsh size -5)))
2531 ((zerop (logand 128 size)) (+ 1 (lsh size -6)))
2532 ((zerop (logand 63 size)) 64)
2533 (t (logand 63 size))))))
2535 (defun malyon-opcode-get-sibling (object)
2536 "Get the next object in the tree and jump."
2537 (let ((sibling (malyon-object-read-sibling (malyon-object-address object))))
2538 (malyon-store-variable (malyon-read-code-byte) sibling)
2539 (malyon-jump-if (/= 0 sibling))))
2541 (defun malyon-opcode-illegal (&rest ignore)
2542 "Print an error message and exit the interpreter."
2543 (malyon-fatal-error "illegal opcode."))
2545 (defun malyon-opcode-inc (var)
2546 "Increment variable."
2547 (malyon-store-variable var
2548 (+ (malyon-number (malyon-read-variable var)) 1)))
2550 (defun malyon-opcode-inc-chk (variable threshold)
2551 "Increment variable and jump if it's greater than the given value."
2552 (let ((value (malyon-number (malyon-read-variable variable))))
2553 (malyon-store-variable variable (+ value 1))
2554 (malyon-jump-if (> (+ value 1) (malyon-number threshold)))))
2556 (defun malyon-opcode-input-stream (number)
2557 "Select the given input stream. Only the keyboard is supported."
2558 (if (zerop (malyon-number number))
2560 (message "Only the keyboard is supported as an input stream.")))
2562 (defun malyon-opcode-insert-obj (object destination)
2563 "Insert an object into the children list of another."
2564 (let ((child (malyon-object-address object))
2565 (parent (malyon-object-address destination)))
2566 (malyon-remove-object object)
2567 (malyon-object-store-parent child destination)
2568 (malyon-object-store-sibling child (malyon-object-read-child parent))
2569 (malyon-object-store-child parent object)))
2571 (defun malyon-opcode-je (a &rest rest)
2572 "Jump if first operand equals any of the following."
2573 (malyon-jump-if (member (malyon-number a) (mapcar 'malyon-number rest))))
2575 (defun malyon-opcode-jg (a b)
2576 "Jump if first operand > second operand."
2577 (malyon-jump-if (> (malyon-number a) (malyon-number b))))
2579 (defun malyon-opcode-jin (child parent)
2580 "Jump if second object is parent of the first one."
2582 (= parent (malyon-object-read-parent (malyon-object-address child)))))
2584 (defun malyon-opcode-jl (a b)
2585 "Jump if first operand < second operand."
2586 (malyon-jump-if (< (malyon-number a) (malyon-number b))))
2588 (defun malyon-opcode-jump (offset)
2589 "Jump unconditionally."
2590 (setq malyon-instruction-pointer (+ malyon-instruction-pointer
2591 (malyon-number offset) -2)))
2593 (defun malyon-opcode-jz (a)
2594 "Jump if operand = 0."
2595 (malyon-jump-if (zerop a)))
2597 (defun malyon-opcode-load (variable)
2599 (malyon-store-variable (malyon-read-code-byte)
2600 (malyon-read-variable variable)))
2602 (defun malyon-opcode-loadb (array index)
2603 "Load an array element into a variable."
2604 (malyon-store-variable (malyon-read-code-byte)
2605 (malyon-read-byte (+ array index))))
2607 (defun malyon-opcode-loadw (array index)
2608 "Load an array element into a variable."
2609 (malyon-store-variable (malyon-read-code-byte)
2610 (malyon-read-word (+ array (* 2 index)))))
2612 (defun malyon-opcode-log-shift (value places)
2614 (malyon-store-variable (malyon-read-code-byte) (lsh value places)))
2616 (defun malyon-opcode-mod (a b)
2618 (malyon-store-variable (malyon-read-code-byte)
2619 (mod (malyon-number a) (malyon-number b))))
2621 (defun malyon-opcode-mul (a b)
2623 (malyon-store-variable (malyon-read-code-byte)
2624 (* (malyon-number a) (malyon-number b))))
2626 (defun malyon-opcode-new-line ()
2630 (defun malyon-opcode-nop (&rest ignore)
2633 (defun malyon-opcode-not (a)
2635 (malyon-store-variable (malyon-read-code-byte) (logand 65535 (lognot a))))
2637 (defun malyon-opcode-or (a b)
2639 (malyon-store-variable (malyon-read-code-byte) (logior a b)))
2641 (defun malyon-opcode-output-stream (stream &optional table)
2642 "Select an output stream."
2643 (let ((stream (malyon-number stream)))
2644 (cond ((< 0 stream) (malyon-add-output-stream stream table))
2645 ((> 0 stream) (malyon-remove-output-stream (- stream))))))
2647 (defun malyon-opcode-piracy ()
2648 "Piracy check, effectively an unconditional jump."
2651 (defun malyon-opcode-print ()
2653 (setq malyon-instruction-pointer
2654 (malyon-print-text malyon-instruction-pointer)))
2656 (defun malyon-opcode-print-addr (address)
2658 (malyon-print-text address))
2660 (defun malyon-opcode-print-char (c)
2661 "Print a character."
2662 (malyon-print (char-to-string c)))
2664 (defun malyon-opcode-print-num (n)
2666 (malyon-print (number-to-string (malyon-number n))))
2668 (defun malyon-opcode-print-obj (obj)
2669 "Print the short name of the object."
2671 (+ 1 (malyon-read-word (+ malyon-object-property-offset
2672 (malyon-object-address obj))))))
2674 (defun malyon-opcode-print-paddr (address)
2676 (malyon-print-text (* malyon-packed-multiplier address)))
2678 (defun malyon-opcode-print-ret ()
2679 "Print a string, print a newline, return true/1."
2680 (setq malyon-instruction-pointer
2681 (malyon-print-text malyon-instruction-pointer))
2685 (defun malyon-opcode-print-table (text width &optional height skip)
2686 "Print the given table."
2687 (if (not height) (setq height 1))
2688 (if (not skip) (setq skip 0))
2689 (let ((column (current-column))
2697 (malyon-print-characters (make-string column ? )))
2700 (malyon-output-character (malyon-read-byte address))
2701 (setq address (+ 1 address))
2703 (setq address (+ skip address))
2706 (defun malyon-opcode-print-unicode (char)
2707 "Prints a unicode character.")
2709 (defun malyon-opcode-pull (variable)
2710 "Pull value off stack."
2711 (malyon-store-variable variable (malyon-pop-stack)))
2713 (defun malyon-opcode-push (value)
2714 "Push value onto stack."
2715 (malyon-push-stack value))
2717 (defun malyon-opcode-put-prop (object property value)
2718 "Set the object's property to the given value."
2719 (let* ((address (malyon-find-property object property))
2720 (size (malyon-read-byte address)))
2721 (cond ((= address 0)
2722 (malyon-fatal-error "property does not exist."))
2723 ((and (< malyon-story-version 5) (zerop (lsh size -5)))
2724 (malyon-store-byte (+ 1 address) (logand 255 value)))
2725 ((and (>= malyon-story-version 5) (zerop (logand size 192)))
2726 (malyon-store-byte (+ 1 address) (logand 255 value)))
2728 (malyon-store-word (+ 1 address) value)))))
2730 (defun malyon-opcode-quit ()
2731 "End the game immediately."
2732 (malyon-adjust-transcript)
2734 (throw 'malyon-end-of-interpreter-loop 'malyon-opcode-quit))
2736 (defun malyon-opcode-random (limit)
2737 "Generate a random number or set the seed value."
2738 (malyon-store-variable (malyon-read-code-byte)
2739 (if (>= 0 (malyon-number limit))
2741 (+ 1 (random (malyon-number limit))))))
2743 (defun malyon-opcode-read-char (&optional device &rest ignore)
2745 (if (and device (/= 1 device))
2746 (malyon-fatal-error "illegal device specified in read_char."))
2747 (if (eq malyon-transcript-buffer (current-buffer))
2748 (goto-char (point-max)))
2749 (message "[Press a key.]")
2750 (malyon-more malyon-keymap-readchar)
2751 (throw 'malyon-end-of-interpreter-loop 'malyon-waiting-for-character))
2753 (defun malyon-opcode-remove-obj (object)
2754 "Remove an object from its parent's children list."
2755 (malyon-remove-object object))
2757 (defun malyon-opcode-restart ()
2759 (malyon-set-game-state malyon-game-state-restart))
2761 (defun malyon-opcode-restore (&optional table bytes name)
2762 "Restore a saved game state or a section of memory from a file."
2763 (let ((result (if name
2764 (malyon-restore-file
2765 (malyon-get-file-name name) table bytes)
2766 (call-interactively 'malyon-restore-file))))
2767 (if (< malyon-story-version 5)
2768 (malyon-jump-if (not (zerop result)))
2769 (malyon-store-variable (malyon-read-code-byte) result))))
2771 (defun malyon-opcode-restore-undo ()
2772 "Restore game state for undo."
2773 (if malyon-game-state-undo
2774 (malyon-set-game-state malyon-game-state-undo))
2775 (malyon-store-variable (malyon-read-code-byte) 2))
2777 (defun malyon-opcode-ret (value)
2779 (malyon-return value))
2781 (defun malyon-opcode-ret-popped ()
2782 "Return top of stack."
2783 (malyon-return (malyon-pop-stack)))
2785 (defun malyon-opcode-rfalse ()
2789 (defun malyon-opcode-rtrue ()
2793 (defun malyon-opcode-save (&optional table bytes name)
2794 "Save the current game state or a section of memory to a file."
2795 (let ((result (if name
2796 (malyon-save-file (malyon-get-file-name name) table bytes)
2797 (call-interactively 'malyon-save-file))))
2798 (if (< malyon-story-version 5)
2799 (malyon-jump-if (not (zerop result)))
2800 (malyon-store-variable (malyon-read-code-byte) result))))
2802 (defun malyon-opcode-save-undo ()
2803 "Save game state for undo."
2804 (setq malyon-game-state-undo (malyon-current-game-state))
2805 (malyon-store-byte (malyon-read-code-byte) 1))
2807 (defun malyon-opcode-scan-table (x table len &optional form)
2808 "Scan the given table for the first occurrence of x."
2809 (if (not form) (setq form 130))
2810 (let ((inc (logand 127 form))
2811 (byte (zerop (logand 128 form)))
2815 (while (and (zerop found) (< index len))
2818 (if (= x (malyon-read-byte addr)) addr 0)
2819 (if (= x (malyon-read-word addr)) addr 0)))
2820 (setq addr (+ addr inc))
2821 (setq index (+ index 1)))
2822 (malyon-store-variable (malyon-read-code-byte) found)
2823 (malyon-jump-if (not (zerop found)))))
2825 (defun malyon-opcode-set-attr (object attribute)
2826 "Set the given attribute in the given object."
2827 (let ((attributes (malyon-object-address object))
2828 (byte (lsh attribute -3)))
2829 (malyon-store-byte (+ attributes byte)
2830 (logior (malyon-read-byte (+ attributes byte))
2831 (lsh 128 (- (logand attribute 7)))))))
2833 (defun malyon-opcode-set-color (foreground background)
2834 "Sets the fore- and background colors ie. does nothing.")
2836 (defun malyon-opcode-set-cursor (&optional line column)
2838 (if (eq malyon-transcript-buffer (current-buffer))
2839 (goto-char (point-max))
2840 (if malyon-status-buffer-delayed-split
2842 (malyon-split-buffer-windows malyon-status-buffer-delayed-split)
2844 (if line '() (setq line (count-lines (point-min) (point))))
2845 (if column '() (setq column (current-column)))
2846 (if (> line malyon-status-buffer-lines)
2848 (malyon-split-buffer-windows line)
2850 (goto-char (point-min))
2851 (if (and (<= 1 line) (<= line malyon-status-buffer-lines))
2853 (beginning-of-line))
2854 (if (and (<= 1 column) (<= column malyon-max-column))
2855 (forward-char (- column 1))
2856 (beginning-of-line))
2857 (setq malyon-status-buffer-point (point))))
2859 (defun malyon-opcode-set-font (font)
2860 "Sets the font if available or 0 otherwise."
2861 (malyon-store-variable (malyon-read-code-byte) 0))
2863 (defun malyon-opcode-set-text-style (style)
2864 "Set the text style/face."
2865 (let ((face (assq style malyon-faces)))
2866 (setq malyon-current-face (if face (cdr face) 'malyon-face-plain))))
2868 (defun malyon-opcode-set-window (window)
2869 "Set the current window."
2870 (malyon-restore-window-configuration)
2871 (setq malyon-current-window window)
2872 (malyon-update-output-streams)
2874 (if (not (eq malyon-transcript-buffer (current-buffer)))
2876 (if (not (eq malyon-status-buffer (current-buffer)))
2878 (malyon-opcode-set-cursor 1 1)))
2880 (defun malyon-opcode-show-status ()
2881 "Display the status line."
2883 (malyon-opcode-split-window 1)
2884 (malyon-restore-window-configuration)
2885 (malyon-opcode-set-window 1)
2886 (malyon-prepare-status-buffer 1)
2887 (malyon-opcode-set-cursor 1 1)
2888 (malyon-opcode-print-obj (malyon-read-global-variable 0))
2889 (if (<= (current-column) (- (current-fill-column) 10))
2890 (let* ((x (malyon-read-global-variable 1))
2891 (y (malyon-read-global-variable 2))
2892 (hours (if (> x 12) (- x 12) x))
2893 (ampm (if (> x 12) "PM" "AM"))
2894 (score (format "%4d/%4d" x y))
2895 (time (format "%02d:%02d%s" hours y ampm)))
2896 (malyon-opcode-set-cursor 1 (- (current-fill-column) 10))
2897 (malyon-print (if malyon-score-game score time))))
2898 (malyon-opcode-set-window 0)
2899 (malyon-adjust-transcript)))
2901 (defun malyon-opcode-split-window (size)
2902 "Split upper and lower window."
2903 (malyon-set-window-configuration size))
2905 (defun malyon-opcode-store (variable value)
2906 "Store a value in a variable."
2907 (malyon-store-variable variable value))
2909 (defun malyon-opcode-storeb (array index value)
2910 "Store a value in an array at the given index."
2911 (malyon-store-byte (+ array index) value))
2913 (defun malyon-opcode-storew (array index value)
2914 "Store a value in an array at the given index."
2915 (malyon-store-word (+ array (* 2 index)) value))
2917 (defun malyon-opcode-sub (a b)
2919 (malyon-store-variable (malyon-read-code-byte)
2920 (- (malyon-number a) (malyon-number b))))
2922 (defun malyon-opcode-test (bitmap flags)
2923 "Test if all of the flags are set in the bitmap."
2924 (malyon-jump-if (= flags (logand bitmap flags))))
2926 (defun malyon-opcode-test-attr (object attribute)
2927 "Jump depending on the given attribute in the given object."
2929 (/= 0 (logand (malyon-read-byte (+ (malyon-object-address object)
2930 (lsh attribute -3)))
2931 (lsh 128 (- (logand attribute 7)))))))
2933 (defun malyon-opcode-throw (value frame)
2934 "Return from the given stack frame."
2935 (if malyon-game-state-quetzal
2936 (let ((id (lsh (aref malyon-stack malyon-frame-pointer) -8)))
2937 (while (/= frame id)
2938 (setq malyon-stack-pointer malyon-frame-pointer)
2940 (setq malyon-frame-pointer
2941 (- malyon-stack-pointer 1 (lsh (malyon-pop-stack) -8)))
2944 (setq id (lsh (aref malyon-stack malyon-frame-pointer) -8))))
2945 (setq malyon-frame-pointer frame))
2946 (malyon-return value))
2948 (defun malyon-opcode-tokenise (text parse &optional dict flag)
2949 "Perform lexical analysis on the text buffer."
2950 (let* ((words (malyon-text-to-words text dict))
2953 (len (malyon-cadr word))
2954 (code (malyon-caddr word))
2955 (entry (malyon-lookup dict code))
2957 (while (not (or (null words) (= i (malyon-read-byte parse))))
2958 (if (and (zerop entry) flag (/= 0 flag))
2960 (malyon-store-word (+ 2 parse (* 4 i)) entry)
2961 (malyon-store-byte (+ 4 parse (* 4 i)) len)
2962 (malyon-store-byte (+ 5 parse (* 4 i)) start))
2963 (setq words (cdr words)
2966 len (malyon-cadr word)
2967 code (malyon-caddr word)
2968 entry (malyon-lookup dict code)
2970 (malyon-store-byte (+ 1 parse) i)))
2972 (defun malyon-opcode-verify ()
2973 "Verify the correctness of the story file."
2974 (let ((length (+ 1 (* malyon-packed-multiplier (malyon-read-word 26))))
2978 (setq sum (mod (+ sum (malyon-read-byte i)) 65536)
2980 (malyon-jump-if (= (malyon-read-word 28) sum))))
2984 (defun malyon-end-input ()
2985 "Store the input line in a text buffer and perform lexical analysis."
2989 (malyon-adjust-transcript)
2990 (switch-to-buffer malyon-transcript-buffer)
2991 (goto-char (point-max))
2992 (let* ((input (downcase
2993 (buffer-substring-no-properties
2994 (if (< malyon-aread-beginning-of-line (point))
2995 malyon-aread-beginning-of-line
2998 (vec (malyon-string-to-vector input))
2999 (text (apply 'vector (mapcar 'malyon-unicode-to-zscii vec)))
3000 (len (min (malyon-read-byte malyon-aread-text) (length text)))
3002 (malyon-history-insert input)
3003 (if (>= malyon-story-version 5)
3004 (malyon-store-byte (+ malyon-aread-text 1) len))
3007 (+ malyon-aread-text (if (< malyon-story-version 5) 1 2) i)
3008 (malyon-char-to-int (aref text i)))
3010 (if (< malyon-story-version 5)
3011 (malyon-store-byte (+ malyon-aread-text 1 len) 0)))
3012 (if (/= 0 malyon-aread-parse)
3013 (malyon-opcode-tokenise malyon-aread-text malyon-aread-parse))
3015 (if (>= malyon-story-version 5)
3016 (malyon-store-variable (malyon-read-code-byte) 10))
3017 (malyon-interpreter))
3019 (malyon-fatal-error "unspecified internal runtime error."))))
3021 (defun malyon-more-char ()
3022 "Page down in More mode."
3027 (if (>= (count-lines (point) (point-max))
3028 (malyon-window-displayed-height))
3030 (goto-char (point-max))
3031 (malyon-adjust-transcript)
3032 (use-local-map malyon-more-continue-keymap)))
3034 (defun malyon-more-char-status ()
3035 "Wait for a key then continue."
3039 (malyon-adjust-transcript)
3040 (use-local-map malyon-more-continue-keymap)
3041 (malyon-interpreter))
3043 (malyon-fatal-error "unspecified internal runtime error."))))
3045 (defun malyon-wait-char ()
3046 "Store the input character in a variable and resume execution."
3050 (malyon-store-variable
3051 (malyon-read-code-byte)
3052 (malyon-char-to-int (malyon-unicode-to-zscii last-command-char)))
3053 (use-local-map malyon-keymap-read)
3054 (malyon-interpreter))
3056 (malyon-fatal-error "unspecified internal runtime error."))))
3058 (defun malyon-history-previous-char (arg)
3059 "Display the previous item in the input history."
3061 (let ((input (malyon-history-previous)))
3062 (cond ((> malyon-aread-beginning-of-line (point))
3063 (funcall malyon-history-saved-up arg))
3066 (set-buffer malyon-transcript-buffer)
3067 (delete-region malyon-aread-beginning-of-line (point-max)))
3068 (goto-char (point-max))
3070 (malyon-adjust-transcript)))))
3072 (defun malyon-history-next-char (arg)
3073 "Display the next item in the input history."
3075 (let ((input (malyon-history-next)))
3076 (cond ((> malyon-aread-beginning-of-line (point))
3077 (funcall malyon-history-saved-down arg))
3080 (set-buffer malyon-transcript-buffer)
3081 (delete-region malyon-aread-beginning-of-line (point-max)))
3082 (goto-char (point-max))
3084 (malyon-adjust-transcript)))))
3086 (defun malyon-beginning-of-line (arg)
3087 "Go to the beginning of the line."
3089 (if (> malyon-aread-beginning-of-line (point))
3091 (goto-char malyon-aread-beginning-of-line)))
3093 (defun malyon-kill-region (arg)
3096 (if (<= malyon-aread-beginning-of-line (point))
3097 (kill-region (point) (mark))
3098 (message "Editing is restricted to the input prompt.")))
3100 (defun malyon-kill-line (arg)
3101 "Kill rest of the current line."
3103 (if (<= malyon-aread-beginning-of-line (point))
3105 (message "Editing is restricted to the input prompt.")))
3107 (defun malyon-kill-word (arg)
3108 "Kill the current word."
3110 (if (<= malyon-aread-beginning-of-line (point))
3112 (message "Editing is restricted to the input prompt.")))
3114 (defun malyon-yank (arg)
3117 (if (<= malyon-aread-beginning-of-line (point))
3119 (message "Editing is restricted to the input prompt.")))
3121 (defun malyon-yank-pop (arg)
3124 (if (<= malyon-aread-beginning-of-line (point))
3126 (message "Editing is restricted to the input prompt.")))
3128 (defun malyon-delete-char (arg)
3129 "Delete a character."
3131 (if (<= malyon-aread-beginning-of-line (point))
3133 (message "Editing is restricted to the input prompt.")))
3135 (defun malyon-backward-delete-char (arg)
3136 "Delete a character backwards."
3138 (if (< malyon-aread-beginning-of-line (point))
3139 (backward-delete-char-untabify 1)
3140 (message "Editing is restricted to the input prompt.")))
3142 (defun malyon-self-insert-command (arg)
3143 "Insert a character."
3145 (if (> malyon-aread-beginning-of-line (point))
3146 (goto-char (point-max)))
3147 (self-insert-command 1))
3151 (defun malyon-trace-file ()
3152 "Turn tracing on for a particular file."
3155 (concat "Malyon Trace " malyon-story-file-name))))
3159 (malyon-erase-buffer)
3160 (insert (concat "Tracing " malyon-story-file-name "..."))
3163 (defun malyon-trace-newline ()
3164 "Output tracing newline."
3165 (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
3169 (goto-char (point-max))
3172 (defun malyon-trace-opcode (pc opcode operands)
3173 "Output a z code instruction."
3174 (malyon-trace-string
3175 (format "%8d %-3d %-25s %s\n"
3178 (symbol-name (aref malyon-opcodes opcode))
3179 (apply 'concat (malyon-mapcan
3183 (if (malyon-characterp x)
3184 (malyon-char-to-int x)
3188 (defun malyon-trace-string (s)
3189 "Output tracing string."
3190 (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
3194 (goto-char (point-max))
3197 (defun malyon-trace-object (o)
3198 "Output tracing object."
3199 (let ((trace (get-buffer (concat "Malyon Trace " malyon-story-file-name))))
3203 (goto-char (point-max))
3206 ;;; announce malyon-mode
3208 (provide 'malyon-mode)
3211 ;;; malyon-mode.el ends here