TXD and ZTools

I’ve been tinkering with a Z-machine disassembler on my own and used TXD some. During this process i’ve found some bugs and some opcodes that isn’t supported in TXD. Are TXD still maintained? At gitlab there is a repository (David Griffith / ztools · GitLab). Is this the “official” one and can I report issues there?

The thing I’ve found so far:

  1. The EXT opcodes print_unicode and check_unicode aren’t defined (version 5-8).
  2. The EXT opcode set_true_colour is not defined for either version 6 or version 5, 7-8 (different definitions).
  3. The EXT opcode buffer_screen is not defined for version 6.
  4. The 2OP opcode throw is only definied for version 6, It should also be valid for version 5, 7-8.
  5. The 0OP opcode restart should be defined as a RETURN-type (is currently defined as a PLAIN-type). This one am I not entirely sure about but one dialog routine ended with restart and will disassemble correctly when restart is retyped.

Point 5 above is a bit more complicated.

To get my disassembly to work I treat quit and restart as RETURN-types if not the next opcode is a valid opcode, otherwise they are considered as PLAIN-types.

Example from Trinity (Release 12 / Serial Number 860926):

Source code:
<ROUTINE FINISH ("AUX" X)
	 <CRLF>
	 <V-SCORE>
	 <TELL CR
"Do you want to restart the story, restore a saved position, or quit?" CR CR>
	 <REPEAT ()
		 <TELL "[Type RESTART, RESTORE or QUIT.] >">
	 	 <READ ,P-INBUF ,P-LEXV>
	 	 <SET X <GET ,P-LEXV 1>>
	 	 <COND (<EQUAL? .X ,W?RESTART>
			<SET X <RESTART>>
			<FAILED "RESTART">)
	       	       (<EQUAL? .X ,W?RESTORE>
			<SET X <RESTORE>>
			<FAILED "RESTORE">)
	       	       (<EQUAL? .X ,W?Q ,W?QUIT>
			<CRLF>
			<QUIT>)>>>

TDX disassemble this as:
Routine 28cfc, 1 local (0000)

28cff:  bb                      new_line        
28d00:  88 51 88 00             call_1s         14620 -> sp
28d04:  bb                      new_line        
28d05:  b2 ...                  print           "Do you want to restart the story, restore a saved position, or quit?"
28d32:  bb                      new_line        
28d33:  bb                      new_line        
28d34:  b2 ...                  print           "[Type RESTART, RESTORE or QUIT.] >"
28d5f:  e4 af b3 67             read            ga3 g57
28d63:  4f 67 01 01             loadw           g57 #01 -> local0
28d67:  c1 8f 01 e0 31 50       je              local0 "restart" ~28d7b
28d6d:  b7                      restart         
28d6e:  0d 01 01                store           local0 #01
28d71:  d9 0f 51 80 f6 09 00    call_2s         14600 s083 -> sp
28d78:  8c ff bb                jump            28d34
28d7b:  c1 8f 01 e0 3a 4e       je              local0 "restore" ~28d8d
28d81:  b6 01                   restore         -> local0
28d83:  d9 0f 51 80 f6 0c 00    call_2s         14600 s084 -> sp
28d8a:  8c ff a9                jump            28d34
28d8d:  c1 83 01 de 30 de 81 3f a0 
                               je              local0 "q" "quit" ~28d34
28d96:  bb                      new_line        
28d97:  ba                      quit            

orphan code fragment:

28d98:  8c ff 9b                jump            28d34

'quit' here is not the end of the routine, it's the jump (a part of the REPEAT-statement.

Example from the Dialog version of Craverly Heights:

Source code:
%% RESTART

(understand command [restart])
(perform [restart])
	Restart the game from the beginning? \(
	(if) (library links enabled) (then) (link) y (else) y (endif)
	(no space) / (no space)
	(if) (library links enabled) (then) (link) n (else) n (endif)
	\)
	(if) (yesno) (then)
		(restart)
		Failed to restart.
	(endif)
	(stop)

(parse game over [restart])
	(restart)

TXD won't disassemble these, but if I apply the changes above to my still unfinished program, I get:
0A93C 00                       Routine: 0A93C, 0 locals
0A93D F9 13 0B 7B 10 5B BC     call_vn
0A944 F9 26 0B 42 38 45 35     call_vn
0A94B D9 2F 0C 25 45 24        call_2s
0A951 C1 83 24 21 0F 3E 71 F4  je
0A959 C1 8F 24 21 15 ED        je
0A95F C1 8F 24 21 16 E4        je
0A965 C1 8F 24 21 97 C3        je
0A96B B1                       rfalse
0A96C DA 1F 0B 6B 08           call_2n
0A971 E1 9B 13 03 14           storew
0A976 DA 0F 0B F7 5B C7        call_2n
0A97C CD 4F 12 5B C2           store
0A981 2D 15 14                 store
0A984 8B 0E F4                 ret
0A987 8B 18 76                 ret
0A98A B7                       restart
0A98B DA 1F 0B 6B 06           call_2n
0A990 CD 4F 12 5B C0           store
0A995 2D 15 14                 store
0A998 8B 23 9D                 ret

Padding:
0A99B 00 

0CF4C 00                       Routine: 0CF4C, 0 locals
0CF4D 4F 14 00 14              loadw
0CF51 B7                       restart

Padding:
0CF52 00 00 

In the first routine restart has a valid opcode following and is treated as a PLAIN-type 
and in the second routine restart is followed by an invalid opcode and treated as a RETURN-type.

The problem here is trying to figure out where the routine ends, right? I haven’t looked at the txd code, but it seems like this is the same problem for QUIT and RESTART as it is for RETURN. Any of them can occur in the middle of a function, if there’s branching.

Exactly! Usually txd moves the goalposts for the end of a routine when it encounters a branching instructions. But in the case of restart is defined as plain (can’t be at the end of a function), quit is definied as return (can be the end of a function).

Seems like restart should be defined as RETURN, then.

(I took a quick look at the code.)

1 Like