2 * Copyright (c) 2010-2011 Chris Spiegel
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
34 #include <sys/types.h>
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)
41 static int dumphex = 0;
42 static int dumpstring = 0;
43 static int print_width = 8;
44 static char newlinechar = '\n';
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;
52 static int ignore_errors = 0;
54 static uint8_t atable[26 * 3] =
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',
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',
65 0x0, 0xd, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.',
66 ',', '!', '?', '_', '#', '\'','"', '/', '\\','-', ':', '(', ')',
73 static uint8_t BYTE(uint32_t addr)
75 if(addr >= memory_size) errx("byte %lx is out of bounds", (unsigned long)addr);
80 static uint16_t WORD(uint32_t addr)
82 if(addr + 1 >= memory_size) errx("word %lx is out of bounds", (unsigned long)addr);
84 return memory[addr] << 8 | memory[addr + 1];
90 #define F_BRANCH 0x002
91 #define F_STRING 0x004
92 #define F_PSTRING 0x008
97 #define F_INDIRECT 0x100
98 #define F_RETURN 0x200
102 enum count { ZERO, ONE, TWO, VAR, EXT } count;
111 unsigned long addr, nextaddr;
113 const struct opcode *opcode;
119 unsigned long branchto;
125 struct instruction *next;
128 static struct instruction *instructions;
131 static void add_instruction(struct instruction inst)
133 struct instruction *new;
135 new = malloc(sizeof *new);
136 if(new == NULL) err("malloc");
139 new->next = instructions;
145 static void append(struct instruction **list, struct instruction **tail, struct instruction *node)
151 *list = *tail = node;
155 (*tail)->next = node;
160 static struct instruction *merge(struct instruction *l, struct instruction *r)
162 struct instruction *ret = NULL, *tail = NULL, *tmp;
164 while(l != NULL && r != NULL)
166 if(l->addr < r->addr)
170 append(&ret, &tail, tmp);
176 append(&ret, &tail, tmp);
180 if(l == NULL) tail->next = r;
186 static struct instruction *merge_sort(struct instruction *list, size_t n)
188 struct instruction *left = NULL, *lt = NULL, *right = NULL, *rt = NULL;
190 if(n <= 1) return list;
192 for(size_t i = 0; i < n; i++)
194 struct instruction *tmp;
199 if(i < n / 2) append(&left, <, tmp);
200 else append(&right, &rt, tmp);
203 left = merge_sort(left, n / 2);
204 right = merge_sort(right, n - (n / 2));
206 return merge(left, right);
209 static void sort_instructions(void)
211 instructions = merge_sort(instructions, ninst);
214 static void free_instructions(void)
216 struct instruction *tmp;
218 while(instructions != NULL)
221 instructions = instructions->next;
227 #define MAX_STORY_SIZE 524288
229 static unsigned long routines[MAX_STORY_SIZE];
230 static unsigned char seen_address[MAX_STORY_SIZE];
232 static struct opcode opcodes[5][256];
234 static void OP(enum count count, const char *name, int min, int max, int number, int flags)
236 int v = zversion > 6 ? 5 : zversion;
238 if(count > 4 || number > 255) errx("internal error: opcode %d %d is out of range", (int)count, number);
240 if(v >= min && v <= max) opcodes[count][number] = (struct opcode){ .count = count, .number = number, .name = name, .flags = flags };
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__)
249 static void setup_opcodes(void)
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);
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);
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);
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);
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);
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);
398 static unsigned long unpack(uint32_t addr, int string)
402 case 1: case 2: case 3:
407 return (addr * 4UL) + (string ? S_O : R_O);
412 errx("bad version %d", zversion);
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.
423 static long variable(int var)
428 static void decode_var(uint8_t types, uint32_t *pc)
430 for(int i = 6; i >= 0; i -= 2)
432 int type = (types >> i) & 0x03;
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));
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.
448 static uint32_t handle_routine(uint32_t addr)
453 if(addr == 0) return 0;
455 addr = unpack(addr, 0);
457 nlocals = BYTE(new++);
458 if(zversion <= 4) new += (nlocals * 2);
460 routines[new] = addr;
465 static size_t znchars, zindex;
468 static void add_zchar(uint8_t c)
470 if(zindex == znchars)
472 zchars = realloc(zchars, znchars += 256);
473 if(zchars == NULL) err("realloc");
476 if(c == 13) c = newlinechar;
478 zchars[zindex++] = c;
481 static int print_zcode(uint32_t addr, int in_abbr, void (*outc)(uint8_t))
483 int abbrev = 0, shift = 0, special = 0;
484 int c, lastc = 0; /* Initialize lastc to shut gcc up */
486 uint32_t counter = addr;
487 int current_alphabet = 0;
493 for(int i = 10; i >= 0; i -= 5)
499 if(special == 2) lastc = c;
500 else outc((lastc << 5) | c);
509 new_addr = WORD(abbr_table + 64 * (abbrev - 1) + 2 * c);
511 /* new_addr is a word address, so multiply by 2 */
512 print_zcode(new_addr * 2, 1, outc);
532 if(zversion > 2 || (zversion == 2 && c == 1))
534 if(in_abbr) errx("abbreviation being used recursively");
546 current_alphabet = (current_alphabet + 1) % 3;
557 current_alphabet = (current_alphabet + 2) % 3;
566 if(zversion <= 2) shift = (current_alphabet + shift) % 3;
575 if(zversion <= 2 && c != 6) shift = (current_alphabet + shift) % 3;
576 outc(atable[(26 * shift) + (c - 6)]);
583 } while((w & 0x8000) == 0);
585 return counter - addr;
588 /* Turn an argument into a string representation. Values zero and above
589 * are constants, whereas those below zero are variables.
591 static const char *decode(long var, int store)
595 if(var < -256 || var > 65535) errx("invalid variable %ld", var);
599 snprintf(ret, sizeof ret, "#%02lx", (unsigned long)var);
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));
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.
621 static void interpret(uint32_t pc, int indent, int verbose)
623 #define iprintf(...) do { if(verbose) printf(__VA_ARGS__); } while(0)
624 if(pc >= memory_size && verbose)
626 iprintf("%*s%04lx: outside of memory\n", indent * 2, "", (unsigned long)pc);
627 if(ignore_errors) return;
628 errx("cannot continiue");
634 unsigned long orig_pc;
635 struct instruction inst = { 0 };
640 if(seen_address[pc]) return;
642 seen_address[pc] = 1;
652 if(opcode & 0x40) args[0] = variable(BYTE(pc++));
653 else args[0] = BYTE(pc++);
655 if(opcode & 0x20) args[1] = variable(BYTE(pc++));
656 else args[1] = BYTE(pc++);
659 number = opcode & 0x1f;
663 else if(opcode < 0xb0)
671 else if(opcode < 0xa0)
673 args[0] = BYTE(pc++);
677 args[0] = variable(BYTE(pc++));
681 number = opcode & 0x0f;
685 else if(opcode < 0xc0)
690 number = opcode & 0x0f;
694 else if(opcode < 0xe0)
698 decode_var(BYTE(pc++), &pc);
701 number = opcode & 0x1f;
704 /* Double variable VAR */
705 else if(opcode == 0xec || opcode == 0xfa)
707 uint8_t types1, types2;
713 decode_var(types1, &pc);
714 decode_var(types2, &pc);
717 number = opcode & 0x1f;
725 decode_var(BYTE(pc++), &pc);
728 number = opcode & 0x1f;
732 if(count == ZERO && number == 0x0e)
739 decode_var(BYTE(pc++), &pc);
742 iprintf("%*s%04lx: ", indent * 2, "", orig_pc);
744 inst.opcode = &opcodes[count][number];
745 if(inst.opcode->name == NULL)
747 iprintf("unknown opcode %d %d\n", count, number);
749 if(ignore_errors) return;
751 if(verbose) errx("cannot continiue");
752 else errx("unknown opcode %d %d @%lx", count, number, orig_pc);
755 iprintf("%s ", inst.opcode->name);
757 for(int i = 0; i < nargs; i++) iprintf("%s ", decode(args[i], 0));
759 if(inst.opcode->flags & F_STORE)
761 inst.storeto = variable(BYTE(pc++));
762 iprintf("-> %s", decode(inst.storeto, 1));
765 if(inst.opcode->flags & F_BRANCH)
772 offset = branch & 0x3f;
774 if((branch & 0x40) == 0)
776 offset = (offset << 8) | BYTE(pc++);
778 /* Get the sign right. */
779 if(offset & 0x2000) offset |= 0xc000;
782 inst.invbranch = !(branch & 0x80);
784 /* Branch to new offset from pc. */
787 newpc = pc + (int16_t)offset - 2;
788 inst.branchto = newpc;
792 inst.branchto = offset;
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");
801 if(inst.opcode->flags & F_STRING)
803 pc += print_zcode(pc, 0, add_zchar);
806 if((inst.opcode->flags & F_PSTRING) && args[0] > 0)
808 print_zcode(unpack(args[0], 1), 0, add_zchar);
814 inst.string = malloc(zindex + 1);
815 if(inst.string == NULL) err("malloc");
816 memcpy(inst.string, zchars, zindex);
817 inst.string[zindex] = 0;
820 if(inst.opcode->flags & F_CALL1)
822 if(args[0] > 0) newpc = handle_routine(args[0]);
825 if(inst.opcode->flags & F_CALL2)
827 if(nargs == 3 && args[2] > 0) newpc = handle_routine(args[2]);
830 if(inst.opcode->flags & F_CALL3)
832 if(nargs == 4 && args[3] > 0) newpc = handle_routine(args[3]);
835 if((inst.opcode->flags & F_JUMP) && args[0] >= 0)
837 newpc = pc + (int16_t)args[0] - 2;
840 if(inst.opcode->flags & F_INDIRECT)
842 args[0] = variable(args[0]);
849 for(int i = 0; i < nargs; i++) inst.args[i] = args[i];
851 add_instruction(inst);
855 if(newpc != 0) interpret(newpc, indent + 1, verbose);
857 if(inst.opcode->flags & F_RETURN) return;
861 static unsigned long roundup(unsigned long v, unsigned long multiple)
863 return ((v + multiple - 1) / multiple) * multiple;
866 static void print_code(void)
868 uint32_t lastaddr = 0;
872 for(struct instruction *inst = instructions; inst != NULL; inst = inst->next)
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...
878 if(lastaddr != inst->addr) putchar('\n');
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.
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]);
888 lastaddr = inst->nextaddr;
890 printf("%6lx: ", inst->addr);
894 /* Strings can be very long, so don’t dump unless requested. */
895 if(!dumpstring && inst->opcode->flags & F_STRING)
897 printf("%02x ...%*s", (unsigned)memory[inst->addr], (print_width * 3) - 6, "");
901 for(unsigned long i = 0; i < roundup(inst->nextaddr - inst->addr, print_width); i++)
903 if(i > 0 && i % print_width == 0) printf("\n%8s", "");
905 if(inst->addr + i < inst->nextaddr) printf("%02x ", (unsigned)memory[inst->addr + i]);
913 printf("%-16s ", inst->opcode->name);
915 if(inst->string != NULL) printf("%s ", inst->string);
917 if((inst->opcode->flags & F_CALL1) && inst->args[0] >= 0)
919 printf("#%lx ", unpack(inst->args[0], 0));
920 for(int i = 1; i < inst->nargs; i++) printf("%s ", decode(inst->args[i], 0));
922 else if((inst->opcode->flags & F_JUMP) && inst->args[0] >= 0)
924 printf("#%lx ", inst->addr + (int16_t)inst->args[0] + 1);
928 for(int i = 0; i < inst->nargs; i++) printf("%s ", decode(inst->args[i], 0));
931 if(inst->opcode->flags & F_STORE) printf("-> %s ", decode(inst->storeto, 1));
933 if(inst->opcode->flags & F_BRANCH)
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");
943 int main(int argc, char **argv)
949 static const size_t MAXADDR = 1024;
951 uint32_t addr[MAXADDR];
954 while((c = getopt(argc, argv, "a:A:bdDinvw:")) != -1)
959 if(naddr == MAXADDR) errx("too many addresses: max %zu", MAXADDR);
960 addr[naddr++] = strtoul(optarg, NULL, 16);
966 if(strcmp(optarg, "-") == 0)
972 fp = fopen(optarg, "r");
973 if(fp == NULL) err("%s", optarg);
976 while(fgets(line, sizeof line, fp) != NULL)
978 if(naddr == MAXADDR) errx("too many addresses: max %zu", MAXADDR);
979 addr[naddr++] = strtoul(line, NULL, 16);
982 if(fp != stdin) fclose(fp);
986 setvbuf(stdout, NULL, _IONBF, 0);
1004 print_width = strtol(optarg, NULL, 10);
1005 if(print_width <= 0) print_width = 8;
1012 if(argc - optind != 1) errx("bad call");
1014 fp = fopen(argv[optind], "rb");
1015 if(fp == NULL) err("%s", argv[optind]);
1017 if(fstat(fileno(fp), &st) == -1) err("fstat");
1019 if(st.st_size > MAX_STORY_SIZE) errx("file too large");
1021 memory = malloc(memory_size = st.st_size);
1022 if(memory == NULL) err("malloc");
1024 if(fread(memory, memory_size, 1, fp) != 1) errx("short read");
1030 abbr_table = WORD(0x18);
1032 if(zversion == 0 || zversion > 8) errx("unknown z-machine version %d", zversion);
1034 if(zversion == 6 || zversion == 7)
1036 R_O = WORD(0x28) * 8UL;
1037 S_O = WORD(0x2a) * 8UL;
1042 memcpy(&atable[26 * 2], " 0123456789.,!?_#'\"/\\<-:()", 26);
1044 else if(zversion >= 5 && WORD(0x34) != 0)
1046 if(WORD(0x34) + 26 * 3 >= memory_size) errx("corrupted story: alphabet table out of range");
1048 memcpy(atable, &memory[WORD(0x34)], 26 * 3);
1053 if(zversion == 6) start = handle_routine(start);
1055 main_routine = start;
1061 addr[naddr++] = start;
1065 for(size_t i = 0; i < naddr; i++)
1073 uint32_t orig = addr[i];
1075 if(zversion <= 4) addr[i] += (2 * BYTE(addr[i]));
1078 routines[addr[i]] = orig;
1083 for(size_t i = 0; i < naddr; i++)
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');
1097 free_instructions();