Importing zdevtools release 20110529 from https://zdevtools.codeplex.com/ minus the...
[zdevtools.git] / zd / zd.c
1 /*-
2  * Copyright (c) 2010-2011 Chris Spiegel
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include <limits.h>
32 #include <errno.h>
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37
38 #define err(...)        do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, ": %s\n", strerror(errno)); exit(1); } while(0)
39 #define errx(...)       do { fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); exit(1); } while(0)
40
41 static int dumphex = 0;
42 static int dumpstring = 0;
43 static int print_width = 8;
44 static char newlinechar = '\n';
45
46 static uint8_t *memory;
47 static size_t memory_size;
48 static uint16_t abbr_table;
49 static uint32_t R_O, S_O;
50 static uint32_t main_routine;
51
52 static int ignore_errors = 0;
53
54 static uint8_t atable[26 * 3] =
55 {
56   /* A0 */
57   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
58   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
59
60   /* A1 */
61   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
62   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
63
64   /* A2 */
65   0x0, 0xd, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
66   ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')',
67 };
68
69
70 static int nargs;
71 static long args[8];
72
73 static uint8_t BYTE(uint32_t addr)
74 {
75   if(addr >= memory_size) errx("byte %lx is out of bounds", (unsigned long)addr);
76
77   return memory[addr];
78 }
79
80 static uint16_t WORD(uint32_t addr)
81 {
82   if(addr + 1 >= memory_size) errx("word %lx is out of bounds", (unsigned long)addr);
83
84   return memory[addr] << 8 | memory[addr + 1];
85 }
86
87 static int zversion;
88
89 #define F_STORE         0x001
90 #define F_BRANCH        0x002
91 #define F_STRING        0x004
92 #define F_PSTRING       0x008
93 #define F_CALL1         0x010
94 #define F_CALL2         0x020
95 #define F_CALL3         0x040
96 #define F_JUMP          0x080
97 #define F_INDIRECT      0x100
98 #define F_RETURN        0x200
99
100 struct opcode
101 {
102   enum count { ZERO, ONE, TWO, VAR, EXT } count;
103   int number;
104
105   const char *name;
106   int flags;
107 };
108
109 struct instruction
110 {
111   unsigned long addr, nextaddr;
112
113   const struct opcode *opcode;
114
115   int nargs;
116   long args[8];
117
118   int invbranch;
119   unsigned long branchto;
120
121   long storeto;
122
123   char *string;
124
125   struct instruction *next;
126 };
127
128 static struct instruction *instructions;
129 static size_t ninst;
130
131 static void add_instruction(struct instruction inst)
132 {
133   struct instruction *new;
134
135   new = malloc(sizeof *new);
136   if(new == NULL) err("malloc");
137
138   *new = inst;
139   new->next = instructions;
140   instructions = new;
141
142   ninst++;
143 }
144
145 static void append(struct instruction **list, struct instruction **tail, struct instruction *node)
146 {
147   node->next = NULL;
148
149   if(*list == NULL)
150   {
151     *list = *tail = node;
152   }
153   else
154   {
155     (*tail)->next = node;
156     *tail = node;
157   }
158 }
159
160 static struct instruction *merge(struct instruction *l, struct instruction *r)
161 {
162   struct instruction *ret = NULL, *tail = NULL, *tmp;
163
164   while(l != NULL && r != NULL)
165   {
166     if(l->addr < r->addr)
167     {
168       tmp = l;
169       l = l->next;
170       append(&ret, &tail, tmp);
171     }
172     else
173     {
174       tmp = r;
175       r = r->next;
176       append(&ret, &tail, tmp);
177     }
178   }
179
180   if(l == NULL) tail->next = r;
181   else          tail->next = l;
182
183   return ret;
184 }
185
186 static struct instruction *merge_sort(struct instruction *list, size_t n)
187 {
188   struct instruction *left = NULL, *lt = NULL, *right = NULL, *rt = NULL;
189
190   if(n <= 1) return list;
191
192   for(size_t i = 0; i < n; i++)
193   {
194     struct instruction *tmp;
195
196     tmp = list;
197     list = list->next;
198
199     if(i < n / 2) append(&left, &lt, tmp);
200     else          append(&right, &rt, tmp);
201   }
202
203   left = merge_sort(left, n / 2);
204   right = merge_sort(right, n - (n / 2));
205
206   return merge(left, right);
207 }
208
209 static void sort_instructions(void)
210 {
211   instructions = merge_sort(instructions, ninst);
212 }
213
214 static void free_instructions(void)
215 {
216   struct instruction *tmp;
217
218   while(instructions != NULL)
219   {
220     tmp = instructions;
221     instructions = instructions->next;
222     free(tmp->string);
223     free(tmp);
224   }
225 }
226
227 #define MAX_STORY_SIZE  524288
228
229 static unsigned long routines[MAX_STORY_SIZE];
230 static unsigned char seen_address[MAX_STORY_SIZE];
231
232 static struct opcode opcodes[5][256];
233
234 static void OP(enum count count, const char *name, int min, int max, int number, int flags)
235 {
236   int v = zversion > 6 ? 5 : zversion;
237
238   if(count > 4 || number > 255) errx("internal error: opcode %d %d is out of range", (int)count, number);
239
240   if(v >= min && v <= max) opcodes[count][number] = (struct opcode){ .count = count, .number = number, .name = name, .flags = flags };
241 }
242
243 #define ZEROOP(...)     OP(ZERO, __VA_ARGS__)
244 #define ONEOP(...)      OP(ONE, __VA_ARGS__)
245 #define TWOOP(...)      OP(TWO, __VA_ARGS__)
246 #define VAROP(...)      OP(VAR, __VA_ARGS__)
247 #define EXTOP(...)      OP(EXT, __VA_ARGS__)
248
249 static void setup_opcodes(void)
250 {
251   ZEROOP("rtrue",          1, 6, 0x00, F_RETURN);
252   ZEROOP("rfalse",         1, 6, 0x01, F_RETURN);
253   ZEROOP("print",          1, 6, 0x02, F_STRING);
254   ZEROOP("print_ret",      1, 6, 0x03, F_RETURN | F_STRING);
255   ZEROOP("nop",            1, 6, 0x04, 0);
256   ZEROOP("save",           1, 3, 0x05, F_BRANCH);
257   ZEROOP("save",           4, 4, 0x05, F_STORE);
258   ZEROOP("restore",        1, 3, 0x06, F_BRANCH);
259   ZEROOP("restore",        4, 4, 0x06, F_STORE);
260   ZEROOP("restart",        1, 6, 0x07, F_RETURN);
261   ZEROOP("ret_popped",     1, 6, 0x08, F_RETURN);
262   ZEROOP("pop",            1, 4, 0x09, 0);
263   ZEROOP("catch",          5, 6, 0x09, F_STORE);
264   ZEROOP("quit",           1, 6, 0x0a, F_RETURN);
265   ZEROOP("new_line",       1, 6, 0x0b, 0);
266   ZEROOP("show_status",    3, 3, 0x0c, 0);
267   ZEROOP("nop",            4, 6, 0x0c, 0);
268   ZEROOP("verify",         3, 6, 0x0d, F_BRANCH);
269   ZEROOP("piracy",         5, 6, 0x0f, F_BRANCH);
270
271   ONEOP("jz",              1, 6, 0x00, F_BRANCH);
272   ONEOP("get_sibling",     1, 6, 0x01, F_STORE | F_BRANCH);
273   ONEOP("get_child",       1, 6, 0x02, F_STORE | F_BRANCH);
274   ONEOP("get_parent",      1, 6, 0x03, F_STORE);
275   ONEOP("get_prop_len",    1, 6, 0x04, F_STORE);
276   ONEOP("inc",             1, 6, 0x05, F_INDIRECT);
277   ONEOP("dec",             1, 6, 0x06, F_INDIRECT);
278   ONEOP("print_addr",      1, 6, 0x07, 0);
279   ONEOP("call_1s",         4, 6, 0x08, F_STORE | F_CALL1);
280   ONEOP("remove_obj",      1, 6, 0x09, 0);
281   ONEOP("print_obj",       1, 6, 0x0a, 0);
282   ONEOP("ret",             1, 6, 0x0b, F_RETURN);
283   ONEOP("jump",            1, 6, 0x0c, F_RETURN | F_JUMP);
284   ONEOP("print_paddr",     1, 6, 0x0d, F_PSTRING);
285   ONEOP("load",            1, 6, 0x0e, F_STORE | F_INDIRECT);
286   ONEOP("not",             1, 4, 0x0f, F_STORE);
287   ONEOP("call_1n",         5, 6, 0x0f, F_CALL1);
288
289   TWOOP("je",              1, 6, 0x01, F_BRANCH);
290   TWOOP("jl",              1, 6, 0x02, F_BRANCH);
291   TWOOP("jg",              1, 6, 0x03, F_BRANCH);
292   TWOOP("dec_chk",         1, 6, 0x04, F_BRANCH | F_INDIRECT);
293   TWOOP("inc_chk",         1, 6, 0x05, F_BRANCH | F_INDIRECT);
294   TWOOP("jin",             1, 6, 0x06, F_BRANCH);
295   TWOOP("test",            1, 6, 0x07, F_BRANCH);
296   TWOOP("or",              1, 6, 0x08, F_STORE);
297   TWOOP("and",             1, 6, 0x09, F_STORE);
298   TWOOP("test_attr",       1, 6, 0x0a, F_BRANCH);
299   TWOOP("set_attr",        1, 6, 0x0b, 0);
300   TWOOP("clear_attr",      1, 6, 0x0c, 0);
301   TWOOP("store",           1, 6, 0x0d, F_INDIRECT);
302   TWOOP("insert_obj",      1, 6, 0x0e, 0);
303   TWOOP("loadw",           1, 6, 0x0f, F_STORE);
304   TWOOP("loadb",           1, 6, 0x10, F_STORE);
305   TWOOP("get_prop",        1, 6, 0x11, F_STORE);
306   TWOOP("get_prop_addr",   1, 6, 0x12, F_STORE);
307   TWOOP("get_next_prop",   1, 6, 0x13, F_STORE);
308   TWOOP("add",             1, 6, 0x14, F_STORE);
309   TWOOP("sub",             1, 6, 0x15, F_STORE);
310   TWOOP("mul",             1, 6, 0x16, F_STORE);
311   TWOOP("div",             1, 6, 0x17, F_STORE);
312   TWOOP("mod",             1, 6, 0x18, F_STORE);
313   TWOOP("call_2s",         4, 6, 0x19, F_STORE | F_CALL1);
314   TWOOP("call_2n",         5, 6, 0x1a, F_CALL1);
315   TWOOP("set_colour",      5, 6, 0x1b, 0);
316   TWOOP("throw",           5, 6, 0x1c, 0);
317
318   VAROP("call_vs",         1, 6, 0x00, F_STORE | F_CALL1);
319   VAROP("storew",          1, 6, 0x01, 0);
320   VAROP("storeb",          1, 6, 0x02, 0);
321   VAROP("put_prop",        1, 6, 0x03, 0);
322   VAROP("read",            1, 3, 0x04, 0);
323   VAROP("read",            4, 4, 0x04, F_CALL3);
324   VAROP("read",            5, 6, 0x04, F_STORE | F_CALL3);
325   VAROP("print_char",      1, 6, 0x05, 0);
326   VAROP("print_num",       1, 6, 0x06, 0);
327   VAROP("random",          1, 6, 0x07, F_STORE);
328   VAROP("push",            1, 6, 0x08, 0);
329   VAROP("pull",            1, 5, 0x09, F_INDIRECT);
330   VAROP("pull",            6, 6, 0x09, F_STORE);
331   VAROP("split_window",    3, 6, 0x0a, 0);
332   VAROP("set_window",      3, 6, 0x0b, 0);
333   VAROP("call_vs2",        4, 6, 0x0c, F_STORE | F_CALL1);
334   VAROP("erase_window",    4, 6, 0x0d, 0);
335   VAROP("erase_line",      4, 6, 0x0e, 0);
336   VAROP("set_cursor",      4, 6, 0x0f, 0);
337   VAROP("get_cursor",      4, 6, 0x10, 0);
338   VAROP("set_text_style",  4, 6, 0x11, 0);
339   VAROP("buffer_mode",     4, 6, 0x12, 0);
340   VAROP("output_stream",   3, 6, 0x13, 0);
341   VAROP("input_stream",    3, 6, 0x14, 0);
342   VAROP("sound_effect",    3, 4, 0x15, 0);
343   VAROP("sound_effect",    5, 6, 0x15, F_CALL3);
344   VAROP("read_char",       4, 6, 0x16, F_STORE | F_CALL2);
345   VAROP("scan_table",      4, 6, 0x17, F_STORE | F_BRANCH);
346   VAROP("not",             5, 6, 0x18, F_STORE);
347   VAROP("call_vn",         5, 6, 0x19, F_CALL1);
348   VAROP("call_vn2",        5, 6, 0x1a, F_CALL1);
349   VAROP("tokenise",        5, 6, 0x1b, 0);
350   VAROP("encode_text",     5, 6, 0x1c, 0);
351   VAROP("copy_table",      5, 6, 0x1d, 0);
352   VAROP("print_table",     5, 6, 0x1e, 0);
353   VAROP("check_arg_count", 5, 6, 0x1f, F_BRANCH);
354
355   EXTOP("save",            5, 6, 0x00, F_STORE);
356   EXTOP("restore",         5, 6, 0x01, F_STORE);
357   EXTOP("log_shift",       5, 6, 0x02, F_STORE);
358   EXTOP("art_shift",       5, 6, 0x03, F_STORE);
359   EXTOP("set_font",        5, 6, 0x04, F_STORE);
360   EXTOP("draw_picture",    6, 6, 0x05, 0);
361   EXTOP("picture_data",    6, 6, 0x06, F_BRANCH);
362   EXTOP("erase_picture",   6, 6, 0x07, 0);
363   EXTOP("set_margins",     6, 6, 0x08, 0);
364   EXTOP("save_undo",       5, 6, 0x09, F_STORE);
365   EXTOP("restore_undo",    5, 6, 0x0a, F_STORE);
366   EXTOP("print_unicode",   5, 6, 0x0b, 0);
367   EXTOP("check_unicode",   5, 6, 0x0c, F_STORE);
368   EXTOP("set_true_colour", 5, 6, 0x0d, 0);
369   EXTOP("move_window",     6, 6, 0x10, 0);
370   EXTOP("window_size",     6, 6, 0x11, 0);
371   EXTOP("window_style",    6, 6, 0x12, 0);
372   EXTOP("get_wind_prop",   6, 6, 0x13, F_STORE);
373   EXTOP("scroll_window",   6, 6, 0x14, 0);
374   EXTOP("pop_stack",       6, 6, 0x15, 0);
375   EXTOP("read_mouse",      6, 6, 0x16, 0);
376   EXTOP("mouse_window",    6, 6, 0x17, 0);
377   EXTOP("push_stack",      6, 6, 0x18, F_BRANCH);
378   EXTOP("put_wind_prop",   6, 6, 0x19, 0);
379   EXTOP("print_form",      6, 6, 0x1a, 0);
380   EXTOP("make_menu",       6, 6, 0x1b, F_BRANCH);
381   EXTOP("picture_table",   6, 6, 0x1c, 0);
382   EXTOP("buffer_screen",   6, 6, 0x1d, 0);
383
384   /* Zoom extensions. */
385   EXTOP("start_timer",     5, 6, 0x80, 0);
386   EXTOP("stop_timer",      5, 6, 0x81, 0);
387   EXTOP("read_timer",      5, 6, 0x82, F_STORE);
388   EXTOP("print_timer",     5, 6, 0x83, 0);
389 }
390
391 #undef ZEROOP
392 #undef ONEOP
393 #undef TWOOP
394 #undef VAROP
395 #undef EXTOP
396 #undef OP
397
398 static unsigned long unpack(uint32_t addr, int string)
399 {
400   switch(zversion)
401   {
402     case 1: case 2: case 3:
403       return addr * 2UL;
404     case 4: case 5:
405       return addr * 4UL;
406     case 6: case 7:
407       return (addr * 4UL) + (string ? S_O : R_O);
408     case 8:
409       return addr * 8UL;
410   }
411
412   errx("bad version %d", zversion);
413 }
414
415 /* Both constants and variables are stored in a long.  They are
416  * differentiated by making sure that variables are always negative,
417  * while the unsigned representations of constants are used, meaning
418  * they will be positive (or zero).  Variables are encoded as the
419  * negative value of the variable, minus one, so the stack pointer,
420  * which is variable number 0, is stored as -1, the first local
421  * (variable 1) is stored as -2, and so on.
422  */
423 static long variable(int var)
424 {
425   return -var - 1;
426 }
427
428 static void decode_var(uint8_t types, uint32_t *pc)
429 {
430   for(int i = 6; i >= 0; i -= 2)
431   {
432     int type = (types >> i) & 0x03;
433
434     if     (type == 0) args[nargs++] = WORD((*pc)++);
435     else if(type == 1) args[nargs++] = BYTE(*pc);
436     else if(type == 2) args[nargs++] = variable(BYTE(*pc));
437     else return;
438
439     (*pc)++;
440   }
441 }
442
443 /* Given the (packed) address of a routine, this function returns the
444  * address of the first instruction in the routine.  In addition, it
445  * stores the address of the routine in a table so it can be identified
446  * during the printout of the disassembly.
447  */
448 static uint32_t handle_routine(uint32_t addr)
449 {
450   uint8_t nlocals;
451   uint32_t new;
452
453   if(addr == 0) return 0;
454
455   addr = unpack(addr, 0);
456   new = addr;
457   nlocals = BYTE(new++);
458   if(zversion <= 4) new += (nlocals * 2);
459
460   routines[new] = addr;
461
462   return new;
463 }
464
465 static size_t znchars, zindex;
466 static char *zchars;
467
468 static void add_zchar(uint8_t c)
469 {
470   if(zindex == znchars)
471   {
472     zchars = realloc(zchars, znchars += 256);
473     if(zchars == NULL) err("realloc");
474   }
475
476   if(c == 13) c = newlinechar;
477
478   zchars[zindex++] = c;
479 }
480
481 static int print_zcode(uint32_t addr, int in_abbr, void (*outc)(uint8_t))
482 {
483   int abbrev = 0, shift = 0, special = 0;
484   int c, lastc = 0; /* Initialize lastc to shut gcc up */
485   uint16_t w;
486   uint32_t counter = addr;
487   int current_alphabet = 0;
488
489   do
490   {
491     w = WORD(counter);
492
493     for(int i = 10; i >= 0; i -= 5)
494     {
495       c = (w >> i) & 0x1f;
496
497       if(special)
498       {
499         if(special == 2) lastc = c;
500         else             outc((lastc << 5) | c);
501
502         special--;
503       }
504
505       else if(abbrev)
506       {
507         uint32_t new_addr;
508
509         new_addr = WORD(abbr_table + 64 * (abbrev - 1) + 2 * c);
510
511         /* new_addr is a word address, so multiply by 2 */
512         print_zcode(new_addr * 2, 1, outc);
513
514         abbrev = 0;
515       }
516
517       else switch(c)
518       {
519         case 0:
520           outc(' ');
521           shift = 0;
522           break;
523         case 1:
524           if(zversion == 1)
525           {
526             outc('\n');
527             shift = 0;
528             break;
529           }
530           /* fallthrough */
531         case 2: case 3:
532           if(zversion > 2 || (zversion == 2 && c == 1))
533           {
534             if(in_abbr) errx("abbreviation being used recursively");
535             abbrev = c;
536             shift = 0;
537           }
538           else
539           {
540             shift = c - 1;
541           }
542           break;
543         case 4:
544           if(zversion <= 2)
545           {
546             current_alphabet = (current_alphabet + 1) % 3;
547             shift = 0;
548           }
549           else
550           {
551             shift = 1;
552           }
553           break;
554         case 5:
555           if(zversion <= 2)
556           {
557             current_alphabet = (current_alphabet + 2) % 3;
558             shift = 0;
559           }
560           else
561           {
562             shift = 2;
563           }
564           break;
565         case 6:
566           if(zversion <= 2) shift = (current_alphabet + shift) % 3;
567           if(shift == 2)
568           {
569             shift = 0;
570             special = 2;
571             break;
572           }
573           /* fallthrough */
574         default:
575           if(zversion <= 2 && c != 6) shift = (current_alphabet + shift) % 3;
576           outc(atable[(26 * shift) + (c - 6)]);
577           shift = 0;
578           break;
579       }
580     }
581
582     counter += 2;
583   } while((w & 0x8000) == 0);
584
585   return counter - addr;
586 }
587
588 /* Turn an argument into a string representation.  Values zero and above
589  * are constants, whereas those below zero are variables.
590  */
591 static const char *decode(long var, int store)
592 {
593   static char ret[12];
594
595   if(var < -256 || var > 65535) errx("invalid variable %ld", var);
596
597   if(var >= 0)
598   {
599     snprintf(ret, sizeof ret, "#%02lx", (unsigned long)var);
600   }
601   else
602   {
603     var = -(var + 1);
604     if(var == 0) strcpy(ret, store ? "-(SP)" : "(SP)+");
605     else if(var <= 0x0f) snprintf(ret, sizeof ret, "L%02ld", var - 1);
606     else snprintf(ret, sizeof ret, "G%02lx", (unsigned long)(var - 0x10));
607   }
608
609   return ret;
610 }
611
612 /* Begin interpreting at address “pc”.  If a call or branch instruction
613  * is encountered, recursively call this function with the new address.
614  * Because there is no “end of code” marker, interpretation has to stop
615  * whenever we cannot be sure there is more code, which means opcodes
616  * such as @rtrue, @quit, and @jump will terminate the current
617  * interpretation loop, either returning to the previous loop or, if
618  * this is the first call, returning to the initial caller.  Opcodes
619  * that trigger this behavior have F_RETURN set.
620  */
621 static void interpret(uint32_t pc, int indent, int verbose)
622 {
623 #define iprintf(...)    do { if(verbose) printf(__VA_ARGS__); } while(0)
624   if(pc >= memory_size && verbose)
625   {
626     iprintf("%*s%04lx: outside of memory\n", indent * 2, "", (unsigned long)pc);
627     if(ignore_errors) return;
628     errx("cannot continiue");
629   }
630
631   while(1)
632   {
633     uint8_t opcode;
634     unsigned long orig_pc;
635     struct instruction inst = { 0 };
636     int count, number;
637
638     uint32_t newpc = 0;
639
640     if(seen_address[pc]) return;
641
642     seen_address[pc] = 1;
643
644     orig_pc = pc;
645
646     opcode = BYTE(pc++);
647
648     /* long 2OP */
649     if(opcode < 0x80)
650     {
651       nargs = 2;
652       if(opcode & 0x40) args[0] = variable(BYTE(pc++));
653       else              args[0] = BYTE(pc++);
654
655       if(opcode & 0x20) args[1] = variable(BYTE(pc++));
656       else              args[1] = BYTE(pc++);
657
658       count = TWO;
659       number = opcode & 0x1f;
660     }
661
662     /* short 1OP */
663     else if(opcode < 0xb0)
664     {
665       nargs = 1;
666       if(opcode < 0x90)
667       {
668         args[0] = WORD(pc);
669         pc += 2;
670       }
671       else if(opcode < 0xa0)
672       {
673         args[0] = BYTE(pc++);
674       }
675       else
676       {
677         args[0] = variable(BYTE(pc++));
678       }
679
680       count = ONE;
681       number = opcode & 0x0f;
682     }
683
684     /* 0OP */
685     else if(opcode < 0xc0)
686     {
687       nargs = 0;
688
689       count = ZERO;
690       number = opcode & 0x0f;
691     }
692
693     /* variable 2OP */
694     else if(opcode < 0xe0)
695     {
696       nargs = 0;
697
698       decode_var(BYTE(pc++), &pc);
699
700       count = TWO;
701       number = opcode & 0x1f;
702     }
703
704     /* Double variable VAR */
705     else if(opcode == 0xec || opcode == 0xfa)
706     {
707       uint8_t types1, types2;
708
709       nargs = 0;
710
711       types1 = BYTE(pc++);
712       types2 = BYTE(pc++);
713       decode_var(types1, &pc);
714       decode_var(types2, &pc);
715
716       count = VAR;
717       number = opcode & 0x1f;
718     }
719
720     /* variable VAR */
721     else
722     {
723       nargs = 0;
724
725       decode_var(BYTE(pc++), &pc);
726
727       count = VAR;
728       number = opcode & 0x1f;
729     }
730
731     /* extended */
732     if(count == ZERO && number == 0x0e)
733     {
734       nargs = 0;
735
736       count = EXT;
737       number = BYTE(pc++);
738
739       decode_var(BYTE(pc++), &pc);
740     }
741
742     iprintf("%*s%04lx: ", indent * 2, "", orig_pc);
743
744     inst.opcode = &opcodes[count][number];
745     if(inst.opcode->name == NULL)
746     {
747       iprintf("unknown opcode %d %d\n", count, number);
748
749       if(ignore_errors) return;
750
751       if(verbose) errx("cannot continiue");
752       else        errx("unknown opcode %d %d @%lx", count, number, orig_pc);
753     }
754
755     iprintf("%s ", inst.opcode->name);
756
757     for(int i = 0; i < nargs; i++) iprintf("%s ", decode(args[i], 0));
758
759     if(inst.opcode->flags & F_STORE)
760     {
761       inst.storeto = variable(BYTE(pc++));
762       iprintf("-> %s", decode(inst.storeto, 1));
763     }
764
765     if(inst.opcode->flags & F_BRANCH)
766     {
767       uint8_t branch;
768       uint16_t offset;
769
770       branch = BYTE(pc++);
771
772       offset = branch & 0x3f;
773
774       if((branch & 0x40) == 0)
775       {
776         offset = (offset << 8) | BYTE(pc++);
777
778         /* Get the sign right. */
779         if(offset & 0x2000) offset |= 0xc000;
780       }
781
782       inst.invbranch = !(branch & 0x80);
783
784       /* Branch to new offset from pc. */
785       if(offset > 1)
786       {
787         newpc = pc + (int16_t)offset - 2;
788         inst.branchto = newpc;
789       }
790       else
791       {
792         inst.branchto = offset;
793       }
794
795       if(inst.branchto > 1) iprintf("[%s] %lx", inst.invbranch ? "FALSE" : "TRUE", inst.branchto);
796       else                  iprintf("[%s] %s",  inst.invbranch ? "FALSE" : "TRUE", inst.branchto == 1 ? "RTRUE" : "RFALSE");
797     }
798
799     zindex = 0;
800
801     if(inst.opcode->flags & F_STRING)
802     {
803       pc += print_zcode(pc, 0, add_zchar);
804     }
805
806     if((inst.opcode->flags & F_PSTRING) && args[0] > 0)
807     {
808       print_zcode(unpack(args[0], 1), 0, add_zchar);
809       nargs = 0;
810     }
811
812     if(zindex != 0)
813     {
814       inst.string = malloc(zindex + 1);
815       if(inst.string == NULL) err("malloc");
816       memcpy(inst.string, zchars, zindex);
817       inst.string[zindex] = 0;
818     }
819
820     if(inst.opcode->flags & F_CALL1)
821     {
822       if(args[0] > 0) newpc = handle_routine(args[0]);
823     }
824
825     if(inst.opcode->flags & F_CALL2)
826     {
827       if(nargs == 3 && args[2] > 0) newpc = handle_routine(args[2]);
828     }
829
830     if(inst.opcode->flags & F_CALL3)
831     {
832       if(nargs == 4 && args[3] > 0) newpc = handle_routine(args[3]);
833     }
834
835     if((inst.opcode->flags & F_JUMP) && args[0] >= 0)
836     {
837       newpc = pc + (int16_t)args[0] - 2;
838     }
839
840     if(inst.opcode->flags & F_INDIRECT)
841     {
842       args[0] = variable(args[0]);
843     }
844
845     inst.addr = orig_pc;
846     inst.nextaddr = pc;
847
848     inst.nargs = nargs;
849     for(int i = 0; i < nargs; i++) inst.args[i] = args[i];
850
851     add_instruction(inst);
852
853     iprintf("\n");
854
855     if(newpc != 0) interpret(newpc, indent + 1, verbose);
856
857     if(inst.opcode->flags & F_RETURN) return;
858   }
859 }
860
861 static unsigned long roundup(unsigned long v, unsigned long multiple)
862 {
863   return ((v + multiple - 1) / multiple) * multiple;
864 }
865
866 static void print_code(void)
867 {
868   uint32_t lastaddr = 0;
869
870   sort_instructions();
871
872   for(struct instruction *inst = instructions; inst != NULL; inst = inst->next)
873   {
874     /* If the last instruction does not directly abut the current
875      * instruction, there is a gap (probably caused by a jump of some
876      * sort); represent that with a newline...
877      */
878     if(lastaddr != inst->addr) putchar('\n');
879
880     /* ... except that it’s possible for the start of the main routine
881      * (which really isn’t a routine since it has no header) to be
882      * adjacent to a routine placed right before it.  In this case,
883      * there should be a newline, so add it.
884      */
885     if(inst->addr == main_routine) printf("%sMAIN ROUTINE %lx\n", lastaddr == inst->addr ? "\n" : "", inst->addr - 1);
886     else if(routines[inst->addr]) printf("ROUTINE %lx\n", routines[inst->addr]);
887
888     lastaddr = inst->nextaddr;
889
890     printf("%6lx: ", inst->addr);
891
892     if(dumphex)
893     {
894       /* Strings can be very long, so don’t dump unless requested. */
895       if(!dumpstring && inst->opcode->flags & F_STRING)
896       {
897         printf("%02x ...%*s", (unsigned)memory[inst->addr], (print_width * 3) - 6, "");
898       }
899       else
900       {
901         for(unsigned long i = 0; i < roundup(inst->nextaddr - inst->addr, print_width); i++)
902         {
903           if(i > 0 && i % print_width == 0) printf("\n%8s", "");
904
905           if(inst->addr + i < inst->nextaddr) printf("%02x ", (unsigned)memory[inst->addr + i]);
906           else                                printf("   ");
907         }
908       }
909
910       putchar(' ');
911     }
912
913     printf("%-16s ", inst->opcode->name);
914
915     if(inst->string != NULL) printf("%s ", inst->string);
916
917     if((inst->opcode->flags & F_CALL1) && inst->args[0] >= 0)
918     {
919       printf("#%lx ", unpack(inst->args[0], 0));
920       for(int i = 1; i < inst->nargs; i++) printf("%s ", decode(inst->args[i], 0));
921     }
922     else if((inst->opcode->flags & F_JUMP) && inst->args[0] >= 0)
923     {
924       printf("#%lx ", inst->addr + (int16_t)inst->args[0] + 1);
925     }
926     else
927     {
928       for(int i = 0; i < inst->nargs; i++) printf("%s ", decode(inst->args[i], 0));
929     }
930
931     if(inst->opcode->flags & F_STORE) printf("-> %s ", decode(inst->storeto, 1));
932
933     if(inst->opcode->flags & F_BRANCH)
934     {
935       if(inst->branchto > 1) printf("[%s] %lx", inst->invbranch ? "FALSE" : "TRUE", inst->branchto);
936       else                   printf("[%s] %s",  inst->invbranch ? "FALSE" : "TRUE", inst->branchto == 1 ? "RTRUE" : "RFALSE");
937     }
938
939     putchar('\n');
940   }
941 }
942
943 int main(int argc, char **argv)
944 {
945   int c;
946   int vflag = 0;
947   FILE *fp;
948   uint32_t start;
949   static const size_t MAXADDR = 1024;
950   size_t naddr = 0;
951   uint32_t addr[MAXADDR];
952   struct stat st;
953
954   while((c = getopt(argc, argv, "a:A:bdDinvw:")) != -1)
955   {
956     switch(c)
957     {
958       case 'a':
959         if(naddr == MAXADDR) errx("too many addresses: max %zu", MAXADDR);
960         addr[naddr++] = strtoul(optarg, NULL, 16);
961         break;
962       case 'A':
963         {
964           char line[16];
965
966           if(strcmp(optarg, "-") == 0)
967           {
968             fp = stdin;
969           }
970           else
971           {
972             fp = fopen(optarg, "r");
973             if(fp == NULL) err("%s", optarg);
974           }
975
976           while(fgets(line, sizeof line, fp) != NULL)
977           {
978             if(naddr == MAXADDR) errx("too many addresses: max %zu", MAXADDR);
979             addr[naddr++] = strtoul(line, NULL, 16);
980           }
981
982           if(fp != stdin) fclose(fp);
983         }
984         break;
985       case 'b':
986         setvbuf(stdout, NULL, _IONBF, 0);
987         break;
988       case 'd':
989         dumphex = 1;
990         break;
991       case 'D':
992         dumpstring = 1;
993         break;
994       case 'i':
995         ignore_errors = 1;
996         break;
997       case 'n':
998         newlinechar = '^';
999         break;
1000       case 'v':
1001         vflag = 1;
1002         break;
1003       case 'w':
1004         print_width = strtol(optarg, NULL, 10);
1005         if(print_width <= 0) print_width = 8;
1006         break;
1007       default:
1008         exit(1);
1009     }
1010   }
1011
1012   if(argc - optind != 1) errx("bad call");
1013
1014   fp = fopen(argv[optind], "rb");
1015   if(fp == NULL) err("%s", argv[optind]);
1016
1017   if(fstat(fileno(fp), &st) == -1) err("fstat");
1018
1019   if(st.st_size > MAX_STORY_SIZE) errx("file too large");
1020
1021   memory = malloc(memory_size = st.st_size);
1022   if(memory == NULL) err("malloc");
1023
1024   if(fread(memory, memory_size, 1, fp) != 1) errx("short read");
1025
1026   fclose(fp);
1027
1028   zversion   = BYTE(0);
1029   start      = WORD(0x06);
1030   abbr_table = WORD(0x18);
1031
1032   if(zversion == 0 || zversion > 8) errx("unknown z-machine version %d", zversion);
1033
1034   if(zversion == 6 || zversion == 7)
1035   {
1036     R_O = WORD(0x28) * 8UL;
1037     S_O = WORD(0x2a) * 8UL;
1038   }
1039
1040   if(zversion == 1)
1041   {
1042     memcpy(&atable[26 * 2], " 0123456789.,!?_#'\"/\\<-:()", 26);
1043   }
1044   else if(zversion >= 5 && WORD(0x34) != 0)
1045   {
1046     if(WORD(0x34) + 26 * 3 >= memory_size) errx("corrupted story: alphabet table out of range");
1047
1048     memcpy(atable, &memory[WORD(0x34)], 26 * 3);
1049     atable[52] = 0x00;
1050     atable[53] = 0x0d;
1051   }
1052
1053   if(zversion == 6) start = handle_routine(start);
1054
1055   main_routine = start;
1056
1057   setup_opcodes();
1058
1059   if(naddr == 0)
1060   {
1061     addr[naddr++] = start;
1062   }
1063   else
1064   {
1065     for(size_t i = 0; i < naddr; i++)
1066     {
1067       if(addr[i] == 0)
1068       {
1069         addr[i] = start;
1070       }
1071       else
1072       {
1073         uint32_t orig = addr[i];
1074
1075         if(zversion <= 4) addr[i] += (2 * BYTE(addr[i]));
1076
1077         addr[i]++;
1078         routines[addr[i]] = orig;
1079       }
1080     }
1081   }
1082
1083   for(size_t i = 0; i < naddr; i++)
1084   {
1085     if(vflag) printf("Beginning disassembly at %lx\n"
1086                      "-----------------------------\n",
1087                      (unsigned long)addr[i]);
1088     interpret(addr[i], 0, vflag);
1089     if(vflag) putchar('\n');
1090   }
1091
1092   print_code();
1093
1094   free(memory);
1095   free(zchars);
1096
1097   free_instructions();
1098
1099   return 0;
1100 }