// Generic tree-walking disassembler.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "dis_6809.h"           // extern int dis(CODE_ADDR addr, FLAGS *F, int *length, CODE_ADDR *branch, char *disasm);
                                // External module disassembles a *single opcode*.
                                // Can replace with similar code for z80, 6502 etc.
                                // Currently mostly machine-independent, but I may move the Vectrex support here too.
                                // There are other 6809 systems that could be supported too.

#include "cycle.h"

// TO DO: running without --vectrex option should still find all the code
// even if it doesn't understand the header.  Need to work out why this is
// not the case.

int DEC_DEBUG = FALSE; // TRUE to examine address tagging.

static int Vectrex = FALSE; // Do we implement vectrex-specific support?
                            // (use --vectrex if it's a Vectrex rom -
                            //  it'll be a significantly more readable decode)

// Segments are the start and end of contiguous blocks loaded into the address space.
// This is to support future use of non-contiguous code, such as gaps in S-records,
// and/or multiple roms.  For now only first segment is ever used.
int next_free_segment;
CODE_ADDR segstart[MAX_SEGMENTS];
CODE_ADDR seglen[MAX_SEGMENTS];
CODE_ADDR entrypt = 0xFFC2;

// These could be made sparse at the expense of complexity
// But we have the RAM available nowadays and this is simple.
uint8_t mem[0x10000];
//uint8_t DP[0x10000];
FLAGS F[0x10000];
//FLAGS TempF[0x10000];
CODE_ADDR branch[0x10000];
uint8_t opcode_length[0x10000];
char *disassembly[0x10000];
char *comment[0x10000];
char *Label[0x10000];
static CODE_ADDR bad_code[0x10000]; // so when I say 'leads to bad code', I'ld like to know where that code is...
static char *explanation[0x10000]; // debugging

#define NOWHERE 0x10000 // An address that cannot be part of our code.
                        // May need to rethink if I add support for banked memory.

static char *decode_F (FLAGS F) { // Print flags in debugging statements
  FLAGS i;
  static char decode[256];
  char *s = decode;

  const char *what[] = {
    "present", "invalid_memory", "leads_to_bad_code", "visited", "entry_point",
    "unconditional_jump", "explicit_jump", "opcode", "operand", "data",
    "char", "decimal", "hex", "word_hi", "word_lo", "addr_hi", "addr_lo",
    "start_of_table", "label", "procedure_entry", "jump_target", "return_to",
    "readonly", "opcode?", "operand?", "enqueued", "jump_target?", "definite",
    "diffy", "fcc_string", "V_string", "packet", "duffy"
  };

  if (!DEC_DEBUG) return "";

  s = decode+sprintf (decode, " [%10llX", F);
  for (i = 0; i < (sizeof what) / (sizeof what[0]); i++) {
    if (F&(1LL << i)) s += sprintf (s, " %s", what[i]);
  }
  for (i = (sizeof what) / (sizeof what[0]); i < 32; i++) {
    if (F&(1LL << i)) s += sprintf (s, " ERROR%0lld", i);
  }
  s += sprintf (s, " ]");

  return decode;
}

static char *decode_FF (FLAGS F) { // unconditional version of above.
  char *s;
  int save = DEC_DEBUG;
  DEC_DEBUG = TRUE; s = decode_F(F); DEC_DEBUG = save; // hacky
  return s;
}

// =========================== 6809 vectrex support ==============================
static int could_be_Vectrex_string(CODE_ADDR addr) {
  int saved_addr = addr;
  if (!Vectrex) return FALSE;
  
  while ((32 <= mem[addr]) && (mem[addr] <= 32+64)) {
    if ((F[addr]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
    if (F[addr]&(F_data|F_invalid_memory)) return FALSE;
    addr += 1;
  }
  if ((addr - saved_addr) < 3) return FALSE;
  if ((addr - saved_addr) > 64) return FALSE;
  if ((F[addr]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
  if (F[addr]&(F_data|F_invalid_memory)) return FALSE;
  if (mem[addr] != 0x80) return FALSE;
  return TRUE;
}

static CODE_ADDR print_Vectrex_string(CODE_ADDR addr, int print) { // FALSE means just mark.
  CODE_ADDR startaddr = addr;

  if (print) fprintf (stdout, "%04X: %02X            L%04X    FCC   \"",
           addr,
           mem[addr],
           addr
          );
  F[addr] &= ~(F_hex|F_char); F[addr] |= F_data|F_V_string;
  for (;;) {
    if (print) {
      fprintf(stdout, "%c", mem[addr]);
      if (mem[addr] == '"') fprintf(stdout, "\"");
    }
    addr += 1;
    F[addr] &= ~F_hex; F[addr] |= F_data|F_char;
    if (mem[addr] == 0x80) break;
  }
  F[addr] &= ~F_char; F[addr] |= F_hex;
  if (print) {
    fprintf (stdout, "\"%s%s\n",
           comment[addr],
           decode_F(F[startaddr])
          );
    fprintf (stdout, "%04X: %02X                     FCB   $%02x%s%s\n",
           addr,
           mem[addr],
           mem[addr],
           comment[addr],
           decode_F(F[addr])
          );
  }
  addr += 1;
  return addr;
}

static int could_be_diffy_or_duffy(CODE_ADDR addr) {
  int count, i;
  // these are const arrays of x,y pairs - NOT packet format that uses 3 bytes.  There are
  // no moves within such a list, just contiguous lines. This is the most basic type of sprite
  // and the least useful.

  // Diffy:
  // byte 0 - Vector count – 1 [optional]
  // byte 1 - Scale factor [optional]
  // bytes 2 / 3 - 'Y:X' for coordinate 1
  // bytes n / n+1 - 'Y:X' for coordinate 'n'

  // Duffy:
  // A 'Duffy' style list is identical to a 'Diffy' style list.
  // The only difference appears to be in the way
  // that it is processed. When processing one of these, the
  // drawing functions will move to the first
  // point in the list. It will then draw a line to the
  // next relative coordinate, until no more points
  // remain
  if (!Vectrex) return FALSE;
  
  if ((F[addr]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
  if (F[addr]&(F_data|F_invalid_memory)) return FALSE;
  count = mem[addr++]+1; // no initial move
  // if (count == 1) return FALSE; // is it likely anyone would create a list for a single line?
  for (i = addr; i < addr+count*2; i += 2) {
    if ((F[i]&F_OPCODE) && (F[i]&F_Definite)) return FALSE;
    if (F[i]&(F_data|F_invalid_memory)) return FALSE;
  }
  return TRUE;
}

static int print_d_list(CODE_ADDR addr, int print, char *type) {
  int count, i;
  if (print) fprintf (stdout, "%04X: %02X            L%04X    FCB   $%02x%s%s ; %s\n",
           addr,
           mem[addr],
           addr,
           mem[addr],
           comment[addr],
           decode_F(F[addr]),
           type
          );
  count = mem[addr]+1;
  addr += 1;
  i = addr+count*2;
  while (addr < i) {
    if (print) fprintf (stdout, "%04X: %02X %02X                  FCB   $%02x, $%02x%s%s\n",
             addr,
             mem[addr],
             mem[addr+1],
             mem[addr],
             mem[addr+1],
             comment[addr],
             decode_F(F[addr])
            );
    addr += 2;
  }
  if (print) fprintf (stdout, "\n");
  if (print) F[addr] |= F_Label;
  return addr;
}

static int print_duffy(CODE_ADDR addr, int print) { return print_d_list(addr, print, "duffy"); }
static int print_diffy(CODE_ADDR addr, int print) { return print_d_list(addr, print, "diffy"); }
static int print_diffy_or_duffy(CODE_ADDR addr, int print) { return print_d_list(addr, print, "diffy or duffy"); }

static int could_be_packet(CODE_ADDR addr) {

  // TO DO: Look at choplifter.asm - there are some obvious packet lists that are not detected
  // - need to do a more extensive check of data areas after disassembly is complete.
  // There is also a list where some bytes in the middle looked a little like code which
  // would probably inhibit a final guess at being a packet list.
  
  // 'Packet' Description
  // A 'Packet' style list is an uncounted list of (mode:Y:X) triplets.
  // This type of packet is useful if you need to mix move and draw requests
  // within the same list. The end of the list is indicated by the presence of
  // a list terminator ($01).
  // Depending upon the function processing the list, the first byte may be expected to contain the
  // scale factor to be used when processing tlist, or this value may need to be stored into RAM.
  // A sample 'Packet' list might look like the following:
  //   byte 0 - Scale factor (optional)
  //   bytes 1 / 2 / 3 - 'mode:Y:X' for coordinate 1
  //   bytes n / n+1 / n+2 - 'mode:Y:X' for coordinate 'n'
  //   $01 - packet terminator

  // where 'mode' can assume one of the following values:
  // $00 - Move to the specified point
  // $FF (-1) - Draw a line to the specified point

  // packet-format lists contain enough redundancy that they can be spotted
  // quite reliably, unfortunately diffy/duffy lists do not and are likely
  // to cause false positives when trying to classify data

  // So rather than just guessing that data may be graphics lists, we
  // now look at the actual BIOS calls to draw the lists and work back
  // to the address of the list from that.  This required that the
  // 6809 module cached register contents so that the parameters to
  // calls such as Draw_VLp could be examined.
  
  if (!Vectrex) return FALSE;
  
  if (mem[addr] != 0x00 && mem[addr] != 0xFF) return FALSE;
  for (;;) {
    if ((F[addr]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
    if (F[addr]&(F_data|F_invalid_memory)) return FALSE;
    // 00 move
    // ff draw
    // 01 end of list
    // Note: two moves in a row would *probably* signify that this isn't
    // really a list.
    if (mem[addr] != 0x00 && mem[addr] != 0x01 && mem[addr] != 0xFF) return FALSE;
    if ((F[addr+1]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
    if (F[addr+1]&(F_data|F_invalid_memory)) return FALSE;
    if ((F[addr+2]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
    if (F[addr+2]&(F_data|F_invalid_memory)) return FALSE;
    addr += 3;
    if (mem[addr] == 0x01) break;
  }
  if ((F[addr]&F_OPCODE) && (F[addr]&F_Definite)) return FALSE;
  if (F[addr]&(F_data|F_invalid_memory)) return FALSE;
  return TRUE;
}

static int print_packet(CODE_ADDR addr, int print) {
  if (print) fprintf (stdout, "%04X: %02X %02X %02X      L%04X    FCB   $%02x, $%02x, $%02x%s%s ; packet format\n",
                      addr, mem[addr], mem[addr+1], mem[addr+2], addr, mem[addr], mem[addr+1], mem[addr+2], comment[addr], decode_F(F[addr]));
  for (;;) {
    // 00 move, ff draw, 01 end of list
    addr += 3;
    if (mem[addr] == 0x01) break;
    if (print) fprintf (stdout, "%04X: %02X %02X %02X               FCB   $%02x, $%02x, $%02x%s%s\n",
                        addr, mem[addr], mem[addr+1], mem[addr+2], mem[addr], mem[addr+1], mem[addr+2], comment[addr], decode_F(F[addr]));
  }
  if (print) {
    fprintf (stdout, "%04X: %02X                     FCB   $%02x          %s%s\n",
             addr, mem[addr], mem[addr], comment[addr], decode_F(F[addr]));
    fprintf (stdout, "\n");
    F[addr+1] |= F_Label;
  }
  return addr+1;
}

// ===============================================================================


#define MAX_Q 0x1000           // WAY overkill.
static CODE_ADDR q[MAX_Q];
static int next_free_q = 0;

// Treewalking is a breadth-first search.  This adds the next level to a queue.
static void enqueue (CODE_ADDR addr) {
  if (addr == NOWHERE) return;
  if (F[addr]&F_enqueued) return;
  F[addr] |= F_enqueued; // We'll label it elsewhere... |F_Label;
  q[next_free_q++] = addr;
  if (next_free_q == MAX_Q) {
    fprintf(stderr, "dec: impossibly large queue of addresses to explore. Try increasing MAX_Q in dec.c. 0x10000 would definitely be safe.\n");
    exit(EXIT_FAILURE);
  }
}

// Remove next item off queue.
static int dequeue (CODE_ADDR *addrp) {
  if (next_free_q) {
    next_free_q -= 1;
    *addrp = q[next_free_q];
    F[*addrp] &= ~F_enqueued;
    return TRUE;
  }
  return FALSE;
}

// If TreeWalk returns early, there are still items in the queue. Wipe them.
static inline void WipeEnqueued(void) {
  CODE_ADDR addr;
  while (dequeue(&addr)) /* ... */;
}

// A lot of searches have to mark visited nodes in order to kill recursion.
// They have to be wiped after treewalking.
static inline void clear_visited (void) {
  CODE_ADDR addr;
  for (addr = 0; addr <= 0xFFFF; addr++) F[addr] &= ~F_Visited;
}

// Update flags after a successful treewalk.  If it was visited, it was good.
// (we don't need to mark bad addresses as visited, since a bad address causes
//  an immediate FALSE return from the treewalk).
static void replace(FLAGS old, FLAGS new) {
  CODE_ADDR i;
  for (i = 0; i <= 0xFFFF; i++) {
    if (F[i]&F_Visited) {
      F[i] &= ~F_Visited;
      if ((new&F_OPCODE) == F_OPCODE) F[i] &= ~F_may_be_valid_OPCODE; // upgrade from potential to actual
      F[i] |= new;
      if ((new&F_OPCODE) == F_OPCODE) {
        // add the F_OPERAND tags as well!
        int opaddr, length;
        length =  opcode_length[i];
        for (opaddr = i+1; opaddr < i+length; opaddr++) {
          F[opaddr] &= ~F_may_be_valid_OPERAND;
          F[opaddr] |= F_OPERAND;
        }
      }
    }
  }
}

static int TreeWalk (CODE_ADDR addr) {
  CODE_ADDR start_addr;

  // The 'explanation' string is only for debugging.  Helps find cases where data was
  // not recognised as opcodes and it isn't obvious why. Uses --explain 0xNNNN param.
  
  enqueue(addr); if (!dequeue(&addr)) return TRUE; // TOCOTOX.
  // OK... I'll explain.  enqueue does some top-level filtering that could be done
  // here, but it's a lot more efficient if it is done at the enqueue stage.

 restart:

  if (addr == NOWHERE) {
    fprintf(stderr, "Should never happen.  Line %d\n", __LINE__);
    exit(EXIT_FAILURE);
  }

  start_addr = addr;
  for (;;) {                    // walk linearly first, but enqueue anything we see on the way...
    
    if (F[addr]&F_Visited) break; /* recursion killer. */ else F[addr] |= F_Visited;
    explanation[addr] = "Treewalked OK\n";
    
    if (Vectrex && ((0xE000 <= addr) && (addr <= 0xFFFF))) {
      // we'll assume BIOS area calls are all good. For now.  Can be more specific later.
      bad_code[addr] = NOWHERE; // just to be sure. But I think it already was.
      explanation[addr] = "BIOS entrypoint. Assumed good\n";
      break; // and return TRUE
    }

    if (!((segstart[0] <= addr) && (addr < (segstart[0]+seglen[0])))) {
      // E000:FFFF already handled
      F[start_addr] |= F_leads_to_bad_code;
      F[addr] |= F_leads_to_bad_code;
      if (bad_code[addr] == NOWHERE) {
        bad_code[start_addr] = addr;
        explanation[start_addr] = "An instruction in a sequence during a linear walk from here must have been past the end of a segment?\n";
        explanation[addr] = "This instruction found during a linear walk may be past the end of a segment?\n";
        if (addr == start_addr) explanation[start_addr] = "This entry point to a tree walk is outside of our segment\n";
      }
      return FALSE; // not in our code area
    }
    
    if (F[addr]&F_invalid_memory) {
      F[start_addr] |= F_leads_to_bad_code;
      F[addr] |= F_leads_to_bad_code;
      if (bad_code[addr] == NOWHERE) {
        bad_code[start_addr] = addr;
        explanation[start_addr] = "An instruction in a sequence during a linear walk from here must have been past the end of valid memory\n";
        explanation[addr] = "This instruction found during a linear walk may be past the end of valid memory?\n";
        if (addr == start_addr) explanation[start_addr] = "This entry point to a tree walk is in an invalid memory area\n";
      }
      fprintf(stdout, "; well... I thought that was a redundant test.  Guess it wasn't.  Line %d  addr=%04X\n", __LINE__, addr);
      return FALSE;
    }

    if ((F[addr]&(F_may_be_valid_OPCODE|F_OPCODE)) == 0) {
      F[start_addr] |= F_leads_to_bad_code;
      F[addr] |= F_leads_to_bad_code;
      if (bad_code[addr] == NOWHERE) {
        bad_code[start_addr] = addr;
        explanation[start_addr] = "An instruction in a sequence during a linear walk from here was not a valid opcode\n";
        explanation[addr] = "This instruction found during a linear walk is not a valid opcode\n";
        if (addr == start_addr) explanation[start_addr] = "This entry point to a tree walk is not a valid opcode\n";
      }
      return FALSE;
    }

    if (branch[addr] != NOWHERE) enqueue (branch[addr]);

    if (F[addr]&F_unconditional_jump) break; /* JMP, RTI, etc. */
    addr = addr+opcode_length[addr];  // drop through to next instruction
  }
  // exits loop if all good so far, now we handle remaining address in the queue (branches)
  
  if (dequeue(&addr)) goto restart;

  return TRUE;
}

static int valid_sequence (CODE_ADDR addr, int want_branches, int modify) { // not a tree walk, but very close.
  CODE_ADDR saved_addr = addr;

  for (;;) {
    if (addr >= segstart[0]+seglen[0]) {  // should only happen if code runs past the end of this segment
      F[saved_addr] |= F_leads_to_bad_code; return FALSE;
    }
    if ((F[addr]&(F_OPCODE|F_may_be_valid_OPCODE)) == 0) {
      F[addr] |= F_leads_to_bad_code; return FALSE;
    }
    if (F[addr]&F_leads_to_bad_code) return FALSE;
    if (F[addr]&F_invalid_memory) return FALSE;
    if ((want_branches) && (branch[addr] != NOWHERE) && (F[branch[addr]]&F_leads_to_bad_code)) return FALSE;

    if (F[addr]&F_OPCODE) return TRUE;
    
    if (modify && (F[addr]&F_may_be_valid_OPCODE)) {
      F[addr] &= ~F_may_be_valid_OPCODE;
      F[addr] |= F_OPCODE;
    }
    
    if (F[addr]&F_unconditional_jump) return TRUE; // end of straight-line sequence

    addr = addr+opcode_length[addr];  // drop through to next sequential instruction

    // heuristic test for 00 00 00 00 - not valid code:
    // 1FF6: 00 00                  NEG   <$00
    // 1FF8: 00 00                  NEG   <$00
    if ((addr-saved_addr >= 4) && (mem[addr-1] == 0x00) && (mem[addr-2] == 0x00) && (mem[addr-3] == 0x00) && (mem[addr-4] == 0x00)) return FALSE;
    // Should add more heuristic tests here to catch data areas that look plausibly like code...
    // But be wary of finding code in the middle of what is very likely a packet list.
  }
}


static inline int simple_valid_sequence (CODE_ADDR addr, int modify) {
  return valid_sequence(addr, FALSE, modify);
}

static inline int valid_sequence_with_tested_branches (CODE_ADDR addr, int modify) {
  return valid_sequence(addr, TRUE, modify);
}

static void initialise (void) {
  CODE_ADDR addr;
  int segment;

  for (segment = 0; segment < MAX_SEGMENTS; segment++) {
    segstart[segment] = 0;
    seglen[segment] = 0;
  }

  for (addr = 0; addr <= 0xFFFF; addr++) {
    mem[addr] = 0xFF;
    //DP[addr] = 0;
    F[addr] = 0LL;
    //TempF[addr] = 0;
    branch[addr] = NOWHERE;
    bad_code[addr] = NOWHERE;
    explanation[addr] = "Never treewalked and not an instruction\n";
    disassembly[addr] = "";
    Label[addr] = "";
    
    if (DEC_DEBUG) {
      comment[addr] = " ; ";
    } else {
      comment[addr] = "";
    }

    opcode_length[addr] = 1;
  }
}

void load_binary (FILE *in) {
  CODE_ADDR addr = 0;

  for (;;) {
    int c = fgetc(in);
    if (c == EOF) break;
    F[addr] |= F_present|F_READONLY;
    mem[addr] = c&255;
    addr += 1;
  }
  fclose (in);
  segstart[next_free_segment] = 0;
  seglen[next_free_segment] = addr;
  next_free_segment = 1;
}

static void vectrex_tables(void) {
  CODE_ADDR addr;

  // the more memory addresses we can eliminate, the better the treewalk

  if (!Vectrex) return;
  // I'll remove the redundant entries later, and move this and other Vectrex items to a separate module.
  Label[0xC800] = "Vec_Snd_Shadow";
  Label[0xC80F] = "Vec_Btn_State";
  Label[0xC810] = "Vec_Prev_Btns";
  Label[0xC811] = "Vec_Buttons";
  Label[0xC812] = "Vec_Button_1_1";
  Label[0xC813] = "Vec_Button_1_2";
  Label[0xC814] = "Vec_Button_1_3";
  Label[0xC815] = "Vec_Button_1_4";
  Label[0xC816] = "Vec_Button_2_1";
  Label[0xC817] = "Vec_Button_2_2";
  Label[0xC818] = "Vec_Button_2_3";
  Label[0xC819] = "Vec_Button_2_4";
  Label[0xC81A] = "Vec_Joy_Resltn";
  Label[0xC81B] = "Vec_Joy_1_X";
  Label[0xC81C] = "Vec_Joy_1_Y";
  Label[0xC81D] = "Vec_Joy_2_X";
  Label[0xC81E] = "Vec_Joy_2_Y";
  Label[0xC81F] = "Vec_Joy_Mux_1_X";
  Label[0xC820] = "Vec_Joy_Mux_1_Y";
  Label[0xC821] = "Vec_Joy_Mux_2_X";
  Label[0xC822] = "Vec_Joy_Mux_2_Y";
  Label[0xC823] = "Vec_Misc_Count";
  Label[0xC824] = "Vec_0Ref_Enable";
  Label[0xC825] = "Vec_Loop_Count";
  Label[0xC827] = "Vec_Brightness";
  Label[0xC828] = "Vec_Dot_Dwell";
  Label[0xC829] = "Vec_Pattern";
  Label[0xC82A] = "Vec_Text_HeightW";
  Label[0xC82B] = "Vec_Text_Width";
  Label[0xC82C] = "Vec_Str_Ptr";
  Label[0xC82E] = "Vec_Counter_1";
  Label[0xC82F] = "Vec_Counter_2";
  Label[0xC830] = "Vec_Counter_3";
  Label[0xC831] = "Vec_Counter_4";
  Label[0xC832] = "Vec_Counter_5";
  Label[0xC833] = "Vec_Counter_6";
  Label[0xC834] = "Vec_RiseRun_Tmp";
  Label[0xC836] = "Vec_Angle";
  Label[0xC837] = "Vec_Run_Index";
  Label[0xC839] = "Vec_Rise_Index_CopyRStartup";
  Label[0xC83B] = "Vec_RiseRun_Len_or_HighScoreColdStartFlag";
  Label[0xC83C] = "Vec_temp";
  Label[0xC83D] = "Vec_Rfrsh_lo";
  Label[0xC83E] = "Vec_Rfrsh_hi";
  Label[0xC83F] = "Vec_Music_Work";
  Label[0xC842] = "Vec_Music_Wk_A";
  Label[0xC843] = "Vec_reg9";
  Label[0xC844] = "Vec_reg8";
  Label[0xC845] = "Vec_Music_Wk_7";
  Label[0xC846] = "Vec_Music_Wk_6";
  Label[0xC847] = "Vec_Music_Wk_5";
  Label[0xC848] = "Vec_reg4";
  Label[0xC849] = "Vec_reg3";
  Label[0xC84A] = "Vec_reg2";
  Label[0xC84B] = "Vec_Music_Wk_1";
  Label[0xC84C] = "Vec_reg0";
  Label[0xC84D] = "Vec_Freq_Table";
  Label[0xC84F] = "Vec_Max_Players_or_ADSR_Table";
  Label[0xC850] = "Vec_Max_Games";
  Label[0xC851] = "Vec_Twang_Table";
  Label[0xC853] = "Vec_Expl_ChanA";
  Label[0xC853] = "Vec_Music_Ptr";
  Label[0xC854] = "Vec_Expl_Chans";
  Label[0xC855] = "Vec_Music_Chan";
  Label[0xC856] = "Vec_Music_Flag";
  Label[0xC857] = "Vec_Duration";
  Label[0xC858] = "Vec_Expl_1";
  Label[0xC858] = "Vec_Music_Twang";
  Label[0xC859] = "Vec_Expl_2";
  Label[0xC85A] = "Vec_Expl_3";
  Label[0xC85B] = "Vec_Expl_4";
  Label[0xC85C] = "Vec_Expl_Chan";
  Label[0xC85D] = "Vec_Expl_ChanB";
  Label[0xC85E] = "Vec_ADSR_Timers_or_ScratchScore0";
  Label[0xC861] = "Vec_Music_Freq";
  Label[0xC867] = "Vec_Expl_Flag";
  Label[0xC877] = "Vec_Expl_Timer";
  Label[0xC879] = "Vec_Num_Players";
  Label[0xC87A] = "Vec_Num_Game";
  Label[0xC87B] = "Vec_Seed_Ptr";
  Label[0xC87D] = "Vec_Random_Seed";
  Label[0xCBEA] = "Vec_Default_Stk";
  Label[0xCBEB] = "Vec_High_Score";
  Label[0xCBF2] = "Vec_SWI2_3_Vector";
  Label[0xCBF5] = "Vec_FIRQ_Vector";
  Label[0xCBF8] = "Vec_IRQ_NMI_SWI_Vector";
  Label[0xCBFE] = "Vec_Cold_Flag";
  Label[0xD000] = "VIA_port_b";
  Label[0xD001] = "VIA_port_a";
  Label[0xD002] = "VIA_DDR_b";
  Label[0xD003] = "VIA_DDR_a";
  Label[0xD004] = "VIA_t1_cnt_lo";
  Label[0xD005] = "VIA_t1_cnt_hi";
  Label[0xD006] = "VIA_t1_lch_lo";
  Label[0xD007] = "VIA_t1_lch_hi";
  Label[0xD008] = "VIA_t2_lo";
  Label[0xD009] = "VIA_t2_hi";
  Label[0xD00A] = "VIA_shift_reg";
  Label[0xD00B] = "VIA_aux_cntl";
  Label[0xD00C] = "VIA_cntl";
  Label[0xD00D] = "VIA_int_flags";
  Label[0xD00E] = "VIA_int_enable";
  Label[0xD00F] = "VIA_port_a_nohs";

  Label[0xE7B5] = "Displ8_xy";
  Label[0xE7D2] = "Displ16_xy";
  Label[0xE98A] = "Ranpos";
  Label[0xEA3E] = "Rnd_Cone";
  Label[0xEA5D] = "Dot_y";
  Label[0xEA6D] = "Dot_py";
  Label[0xEA7F] = "Draw_Pack";
  Label[0xEA8D] = "Draw_Pack_py";
  Label[0xEAA8] = "Print_Msg";
  Label[0xEAB4] = "Draw_Score";
  Label[0xEACF] = "Draw_Scores";
  Label[0xEAF0] = "Wait_Bound";
  
  Label[0xF000] = "Cold_Start";
  Label[0xF000] = "POWER";
  Label[0xF06C] = "Warm_Start";
  Label[0xF14C] = "Init_VIA";
  Label[0xF164] = "Init_OS_RAM";
  Label[0xF18B] = "Init_OS";
  Label[0xF192] = "Wait_Recal";
  Label[0xF1A2] = "Set_Refresh";
  Label[0xF1AA] = "DP_to_D0";
  Label[0xF1AF] = "DP_to_C8";
  Label[0xF1B4] = "Read_Btns_Mask";
  Label[0xF1BA] = "Read_Btns";
  Label[0xF1F5] = "Joy_Analog";
  Label[0xF1F8] = "Joy_Digital";
  Label[0xF256] = "Sound_Byte";
  Label[0xF259] = "Sound_Byte_x";
  Label[0xF25B] = "Sound_Byte_raw";
  Label[0xF272] = "Clear_Sound";
  Label[0xF27D] = "Sound_Bytes";
  Label[0xF284] = "Sound_Bytes_x";
  Label[0xF289] = "Do_Sound";
  Label[0xF28C] = "Do_Sound_x";
  Label[0xF29D] = "Intensity_1F";
  Label[0xF2A1] = "Intensity_3F";
  Label[0xF2A5] = "Intensity_5F";
  Label[0xF2A9] = "Intensity_7F";
  Label[0xF2AB] = "Intensity_a";
  Label[0xF2BE] = "Dot_ix_b";
  Label[0xF2C1] = "Dot_ix";
  Label[0xF2C3] = "Dot_d";
  Label[0xF2C5] = "Dot_here";
  Label[0xF2D5] = "Dot_List";
  Label[0xF2DE] = "Dot_List_Reset";
  Label[0xF2E6] = "Recalibrate";
  Label[0xF2F2] = "Moveto_x_7F";
  Label[0xF2FC] = "Moveto_d_7F";
  Label[0xF308] = "Moveto_ix_FF";
  Label[0xF30C] = "Moveto_ix_7F";
  Label[0xF30E] = "Moveto_ix_a";
  Label[0xF310] = "Moveto_ix";
  Label[0xF312] = "Moveto_d";
  Label[0xF34A] = "Reset0Ref_D0";
  Label[0xF34F] = "Check0Ref";
  Label[0xF354] = "Reset0Ref";
  Label[0xF35B] = "Reset_Pen";
  Label[0xF36B] = "Reset0Int";
  Label[0xF373] = "Print_Str_hwyx";
  Label[0xF378] = "Print_Str_yx";
  Label[0xF37A] = "Print_Str_d";
  Label[0xF385] = "Print_List_hw";
  Label[0xF38A] = "Print_List";
  Label[0xF38C] = "Print_List_chk";
  Label[0xF391] = "Print_Ships_x";
  Label[0xF393] = "Print_Ships";
  Label[0xF3AD] = "Mov_Draw_VLc_a";
  Label[0xF3B1] = "Mov_Draw_VL_b";
  Label[0xF3B5] = "Mov_Draw_VLcs";
  Label[0xF3B7] = "Mov_Draw_VL_ab";
  Label[0xF3B9] = "Mov_Draw_VL_a";
  Label[0xF3BC] = "Mov_Draw_VL";
  Label[0xF3BE] = "Mov_Draw_VL_d";
  Label[0xF3CE] = "Draw_VLc";
  Label[0xF3D2] = "Draw_VL_b";
  Label[0xF3D6] = "Draw_VLcs";
  Label[0xF3D8] = "Draw_VL_ab";
  Label[0xF3DA] = "Draw_VL_a";
  Label[0xF3DD] = "Draw_VL";
  Label[0xF3DF] = "Draw_Line_d";
  Label[0xF404] = "Draw_VLp_FF";
  Label[0xF408] = "Draw_VLp_7F";
  Label[0xF40C] = "Draw_VLp_scale";
  Label[0xF40E] = "Draw_VLp_b";
  Label[0xF410] = "Draw_VLp";
  Label[0xF433] = "DASHE";
  Label[0xF434] = "Draw_Pat_VL_a";
  Label[0xF437] = "Draw_Pat_VL";
  Label[0xF439] = "Draw_Pat_VL_d";
  Label[0xF46E] = "Draw_VL_mode";
  Label[0xF495] = "Print_Str";
  Label[0xF498] = "RASTER";
  Label[0xF511] = "Random_3";
  Label[0xF517] = "Random";
  Label[0xF533] = "Init_Music_Buf";
  Label[0xF53F] = "Clear_x_b";
  Label[0xF542] = "Clear_C8_RAM";
  Label[0xF545] = "Clear_x_256";
  Label[0xF548] = "Clear_x_d";
  Label[0xF550] = "Clear_x_b_80";
  Label[0xF552] = "Clear_x_b_a";
  Label[0xF55A] = "Dec_3_Counters";
  Label[0xF55E] = "Dec_6_Counters";
  Label[0xF563] = "Dec_Counters";
  Label[0xF56D] = "Delay_3";
  Label[0xF571] = "Delay_2";
  Label[0xF575] = "Delay_1";
  Label[0xF579] = "Delay_0";
  Label[0xF57A] = "Delay_b";
  Label[0xF57D] = "Delay_RTS";
  Label[0xF57E] = "Bitmask_a";
  Label[0xF584] = "Abs_a_b";
  Label[0xF58B] = "Abs_b";
  Label[0xF593] = "Rise_Run_Angle";
  Label[0xF5D9] = "Get_Rise_Idx";
  Label[0xF5DB] = "Get_Run_Idx";
  Label[0xF5EF] = "Get_Rise_Run";
  Label[0xF5FF] = "Rise_Run_X";
  Label[0xF601] = "Rise_Run_Y";
  Label[0xF603] = "Rise_Run_Len";
  Label[0xF610] = "Rot_VL_ab";
  Label[0xF613] = "DISROT";
  Label[0xF616] = "Rot_VL";
  Label[0xF61F] = "Rot_VL_Mode_a";
  Label[0xF622] = "POTATE";
  Label[0xF62B] = "Rot_VL_Mode_dft";
  Label[0xF637] = "Rot_VL_dft";
  Label[0xF65B] = "Xform_Run_a";
  Label[0xF65D] = "Xform_Run";
  Label[0xF661] = "Xform_Rise_a";
  Label[0xF663] = "Xform_Rise";
  Label[0xF67F] = "Move_Mem_a_1";
  Label[0xF683] = "Move_Mem_a";
  Label[0xF687] = "Init_Music_chk";
  Label[0xF68D] = "Init_Music";
  Label[0xF690] = "SOPLAY";
  Label[0xF692] = "Init_Music_x";
  Label[0xF742] = "XPLAY";
  Label[0xF7A9] = "Select_Game";
  Label[0xF84F] = "Clear_Score";
  Label[0xF85E] = "Add_Score_a";
  Label[0xF87C] = "Add_Score_d";
  Label[0xF880] = "SADD2";
  Label[0xF8B7] = "Strip_Zeros";
  Label[0xF8C7] = "Compare_Score";
  Label[0xF8D8] = "New_High_Score";
  Label[0xF8E5] = "Obj_Will_Hit_u";
  Label[0xF8F3] = "Obj_Will_Hit";
  Label[0xF8FF] = "Obj_Hit";
  Label[0xF92E] = "Explosion_Snd";
  Label[0xF9CA] = "LOUDIN";
  Label[0xF9F4] = "Char_Table";
  Label[0xFBD4] = "Char_Table_End";
  Label[0xFD0D] = "music1";
  Label[0xFD1D] = "music2";
  Label[0xFD81] = "music3";
  Label[0xFDD3] = "music4";
  Label[0xFE38] = "music5";
  Label[0xFE76] = "music6";
  Label[0xFEC6] = "music7";
  Label[0xFEF8] = "music8";
  Label[0xFF26] = "music9";
  Label[0xFF44] = "musica";
  Label[0xFF62] = "musicb";
  Label[0xFF7A] = "musicc";
  Label[0xFF8F] = "musicd";
  Label[0xFF9F] = "Draw_Grid_VL";

  // better names - let's override the previous ones.
  Label[0xF000] = "Reset";
  Label[0xF14C] = "Init_VIA";
  Label[0xF164] = "Init_OS_RAM";
  Label[0xF18B] = "Init_OS";
  Label[0xF192] = "Wait_Recal";
  Label[0xF1AA] = "DP_to_D0";
  Label[0xF1AF] = "DP_to_C8";
  Label[0xF1B4] = "Read_Btns_Mask";
  Label[0xF1BA] = "Read_Btns";
  Label[0xF1F5] = "Joy_Analog";
  Label[0xF1F8] = "Joy_Digital";
  Label[0xF256] = "Sound_Byte";
  Label[0xF259] = "Sound_Byte_x";
  Label[0xF272] = "Clear_Sound";
  Label[0xF27D] = "Sound_Bytes";
  Label[0xF284] = "Sound_Bytes_x";
  Label[0xF289] = "Do_Sound";
  Label[0xF28C] = "Do_Sound_x";
  Label[0xF29D] = "Intensity_1F";
  Label[0xF2A1] = "Intensity_3F";
  Label[0xF2A5] = "Intensity_5F";
  Label[0xF2A9] = "Intensity_7F";
  Label[0xF2AB] = "Intensity_a";
  Label[0xF2BE] = "Dot_ix_b";
  Label[0xF2C1] = "Dot_ix";
  Label[0xF2C3] = "Dot_d";
  Label[0xF2C5] = "Dot_here";
  Label[0xF2D5] = "Dot_List";
  Label[0xF2DE] = "Dot_List_Reset";
  Label[0xF2E6] = "Recalibrate";
  Label[0xF2F2] = "Moveto_x_7F";
  Label[0xF2FC] = "Moveto_d_7F";
  Label[0xF308] = "Moveto_ix_FF";
  Label[0xF30C] = "Moveto_ix_7F";
  Label[0xF30E] = "Moveto_ix_b";
  Label[0xF310] = "Moveto_ix";
  Label[0xF312] = "Moveto_d";
  Label[0xF34A] = "Reset0Ref_D0";
  Label[0xF34F] = "Check0Ref";
  Label[0xF354] = "Reset0Ref";
  Label[0xF35B] = "Reset_Pen";
  Label[0xF36B] = "Reset0Int";
  Label[0xF373] = "Print_Str_hwyx";
  Label[0xF378] = "Print_Str_yx";
  Label[0xF37A] = "Print_Str_d";
  Label[0xF385] = "Print_List_hw";
  Label[0xF38A] = "Print_List";
  Label[0xF38C] = "Print_List_chk";
  Label[0xF391] = "Print_Ships_x";
  Label[0xF393] = "Print_Ships";
  Label[0xF3AD] = "Mov_Draw_VLc_a";
  Label[0xF3B1] = "Mov_Draw_VL_b";
  Label[0xF3B5] = "Mov_Draw_VLcs";
  Label[0xF3B7] = "Mov_Draw_VL_ab";
  Label[0xF3B9] = "Mov_Draw_VL_a";
  Label[0xF3BC] = "Mov_Draw_VL";
  Label[0xF3BE] = "Mov_Draw_VL_d";
  Label[0xF3CE] = "Draw_VLc";
  Label[0xF3D2] = "Draw_VL_b";
  Label[0xF3D6] = "Draw_VLcs";
  Label[0xF3D8] = "Draw_VL_ab";
  Label[0xF3DA] = "Draw_VL_a";
  Label[0xF3DD] = "Draw_VL";
  Label[0xF3DF] = "Draw_Line_d";
  Label[0xF404] = "Draw_VLp_FF";
  Label[0xF408] = "Draw_VLp_7F";
  Label[0xF40C] = "Draw_VLp_scale";
  Label[0xF40E] = "Draw_VLp_b";
  Label[0xF410] = "Draw_VLp";
  Label[0xF433] = "Draw_Pat_VL_aa";
  Label[0xF434] = "Draw_Pat_VL_a";
  Label[0xF437] = "Draw_Pat_VL";
  Label[0xF439] = "Draw_Pat_VL_d";
  Label[0xF46E] = "Draw_VL_mode";
  Label[0xF495] = "Print_Str";
  Label[0xF498] = "Print_MRast";
  Label[0xF511] = "Random_3";
  Label[0xF517] = "Random";
  Label[0xF533] = "Init_Music_Buf";
  Label[0xF53F] = "Clear_x_b";
  Label[0xF542] = "Clear_C8_RAM";
  Label[0xF545] = "Clear_x_256";
  Label[0xF548] = "Clear_x_d";
  Label[0xF550] = "Clear_x_b_80";
  Label[0xF552] = "Clear_x_b_a";
  Label[0xF55A] = "Dec_3_Counters";
  Label[0xF55E] = "Dec_6_Counters";
  Label[0xF563] = "Dec_Counters";
  Label[0xF56D] = "Delay_3";
  Label[0xF571] = "Delay_2";
  Label[0xF575] = "Delay_1";
  Label[0xF579] = "Delay_0";
  Label[0xF57A] = "Delay_b";
  Label[0xF57D] = "Delay_RTS";
  Label[0xF57E] = "Bitmask_a";
  Label[0xF584] = "Abs_a_b";
  Label[0xF58B] = "Abs_b";
  Label[0xF593] = "Rise_Run_Angle";
  Label[0xF5D9] = "Get_Rise_Idx";
  Label[0xF5DB] = "Xform_Sin";
  Label[0xF5EF] = "Get_Rise_Run";
  Label[0xF5FF] = "Rise_Run_X";
  Label[0xF601] = "Rise_Run_Y";
  Label[0xF603] = "Rise_Run_Len";
  Label[0xF610] = "Rot_VL_ab";
  Label[0xF613] = "Rot_VL_Diff";
  Label[0xF616] = "Rot_VL";
  Label[0xF61F] = "Rot_VL_Mode";
  Label[0xF622] = "Rot_VL_Pack";
  Label[0xF62B] = "Rot_VL_M_dft";
  Label[0xF65B] = "Xform_Run_a";
  Label[0xF65D] = "Xform_Run";
  Label[0xF661] = "Xform_Rise_a";
  Label[0xF663] = "Xform_Rise";
  Label[0xF67F] = "Move_Mem_a_1";
  Label[0xF683] = "Move_Mem_a";
  Label[0xF687] = "Init_Music_chk";
  Label[0xF68D] = "Init_Music";
  Label[0xF690] = "Init_Music_a";
  Label[0xF692] = "Init_Music_x";
  Label[0xF742] = "Stop_Sound";
  Label[0xF7A9] = "Select_Game";
  Label[0xF835] = "Display_Option";
  Label[0xF84F] = "Clear_Score";
  Label[0xF85E] = "Add_Score_a";
  Label[0xF87C] = "Add_Score_d";
  Label[0xF8B7] = "Strip_Zeros";
  Label[0xF8C7] = "Compare_Score";
  Label[0xF8D8] = "New_High_Score";
  Label[0xF8E5] = "Obj_Will_Hit_u";
  Label[0xF8F3] = "Obj_Will_Hit";
  Label[0xF8FF] = "Obj_Hit";
  Label[0xF92E] = "Explosion_Snd";
  Label[0xF9CA] = "Set_Amp";
  
  for (addr = segstart[0]+seglen[0]; addr < 0x8000; addr++) {   // unused rom space in our test rom
    F[addr] |= F_invalid_memory;
  }
  for (addr = 0x8000; addr < 0xC800; addr++) {  // 8000-C000 *could* be used, but not by this rom.
    F[addr] |= F_invalid_memory;
  }
  for (addr = 0xD800; addr < 0xE000; addr++) {  // invalid device space
    F[addr] |= F_invalid_memory;
  }
  for (addr = 0xE000; addr <= 0xFFFF; addr++) { // invalid device space
    F[addr] |= F_READONLY;
  }
}

static void gather_hints (void) {
  int c;
  CODE_ADDR addr = 0;

  if (!Vectrex) return;

  // Parse a Vectrex ROM header. We assume it's good - not a lot of error checking.
  
  // Pre-scan already done, so we need to overwrite the F array for header fields.

  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND|F_char); F[addr] |= F_leads_to_bad_code|F_V_string|F_data|F_Label;
  for (;;) {                    // getstring80 (fp) Copyright
    c = mem[addr++];
    if (c == 0x80) break;
    F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_char;
  }
  F[addr-1] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND|F_char); F[addr-1] |= F_leads_to_bad_code|F_data|F_hex;
  
  // mark_music_table((mem[addr]<<8) | mem[addr+1]); // need to find documentation on music table format
  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_addr_hi|F_Label;
  c = mem[addr++];  // music
  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_addr_lo;
  c = mem[addr++];

  F[(mem[addr-2]<<8) | mem[addr-1]] |= F_Label | F_data | F_hex;
  
  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_hex|F_Label;
  c = mem[addr++];  // h

second_string:
  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_hex;
  c = mem[addr++];  // w

  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_hex|F_Label;
  c = mem[addr++];  // y

  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_hex;
  c = mem[addr++];  // x

  F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND|F_char); F[addr] |= F_leads_to_bad_code|F_V_string|F_data|F_Label;
  c = mem[addr++];
  for (;;) {                    // getstring80 (fp) Copyright
    if (addr >= segstart[0]+seglen[0]) break;
    c = mem[addr++];
    F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_may_be_valid_OPERAND); F[addr] |= F_leads_to_bad_code|F_data|F_char;
    if (c == 0x80) break;
  }
  F[addr-1] &= ~F_char; F[addr-1] |= F_hex; // 0x80

  c = mem[addr++];
  if (c != 0x00) {
    F[addr-1] &= ~(F_char); F[addr-1] |= F_leads_to_bad_code|F_data|F_hex|F_Label; // h
    // not very well documented that there can be multiple strings (and y,x coords)
    goto second_string;
  }
  F[addr-1] &= ~(F_char); F[addr-1] |= F_leads_to_bad_code|F_data|F_hex;      // \0

  F[entrypt = addr] |= F_Label;
}

static int ishex(char c, int *h) {
  if (('0' <= c) && (c <= '9')) {
    *h = c-'0'; return TRUE;
  }
  if (('A' <= c) && (c <= 'F')) {
    *h = c-'A'+10; return TRUE;
  }
  if (('a' <= c) && (c <= 'f')) {
    *h = c-'a'+10; return TRUE;
  }
  return FALSE;
}

/*
  Rather than only do treewalks starting at fixed entry points, and marking the
  walked code so that a subsequent call can return when it hits a decoded area,
  we will linearly scan down the entire loaded file area and pre-mark every
  byte with the status of that address as passed to a disassembly procedure.
  That way the tree walking does not need to repeatedly call the disassembly code,
  but rather can immediately test the previously looked-up result.
*/

static void prescan_rom_area (void) {  // cache the disassembly hints.
  CODE_ADDR addr, opaddr, this_branch;
  int length = 0;
  char text[MAX_DISASM];

  for (addr = 0; addr < segstart[0]+seglen[0]; addr++) {
    this_branch = NOWHERE;      // illegal destination
    length = 1;
    if (dis(addr, F, &length, &this_branch, text)) {   // length includes opcode. Treat extra bytes of multi-byte opcode as if they were operands.
      explanation[addr] = "May be an instruction, but never Treewalked.\n";
      opcode_length[addr] = length;
      F[addr] |= F_may_be_valid_OPCODE;
      for (opaddr = addr+1; opaddr < addr+length; opaddr++) {
        F[opaddr] |= F_may_be_valid_OPERAND;
      }
      if (this_branch != NOWHERE) {
        branch[addr] = this_branch;
        F[this_branch] |= F_may_be_jump_target;
      }
      disassembly[addr] = strdup (text);
    } else {
      F[addr] &= ~F_OPCODE;
    }
  }
}

extern int A_, B_, X_, Y_, S_, U_; // 6809-specific

static void classify_data_first_pass(void) {
  int segment;
  CODE_ADDR /*startaddr,*/ addr, segend;
  static char label_field[64];
  static char constants[MAX_DISASM];
  
  if (!Vectrex) return;

  A_ = B_ = S_ = U_ = X_ = Y_ = NOWHERE; // Init
  for (segment = 0; segment < next_free_segment; segment++) {
    addr = segstart[segment];
    segend = segstart[segment]+seglen[segment];
    while (addr < segend) {
      //startaddr = addr;
      strcpy (label_field, "        ");
      if (F[addr]&(F_JumpTarget|F_Label)) {
        snprintf (label_field, 9, "L%04X   ", addr);
        A_ = B_ = S_ = U_ = X_ = Y_ = NOWHERE; // easiest to forget everything on a jump destination.
                                               // We don't need no steenkin' Phi-nodes...
      }
      
      if (F[addr]&F_OPCODE) { // BAD: if ((F[addr]&F_OPCODE) && ((F[addr]&F_leads_to_bad_code)==0)) {
        //int i;
        char *s;
        int length;
        CODE_ADDR this_branch = NOWHERE;
        char text[MAX_DISASM];

        if ((A_ & B_ & S_ & U_ & X_ & Y_ & NOWHERE) == 0) { // something is cached from preceding instructions
          if (((strstr(disassembly[addr], " JSR ") != NULL) || // strictly also BSR and LBSR but this is safe for BIOS calls
               (strstr(disassembly[addr], " JMP ") != NULL))
            && (branch[addr] >= 0xE000)) { // JMP to BIOS address is really JSR+RTS
            s = constants+sprintf(constants, "  ; ");
            if ((A_ & NOWHERE) == 0) s = s+sprintf(s, "A = $%02x  ", A_);
            if ((B_ & NOWHERE) == 0) s = s+sprintf(s, "B = $%02x  ", B_);
            if ((X_ & NOWHERE) == 0) s = s+sprintf(s, "X = $%04x  ", X_);
            if ((Y_ & NOWHERE) == 0) s = s+sprintf(s, "Y = $%04x  ", Y_);
            if ((S_ & NOWHERE) == 0) s = s+sprintf(s, "S = $%04x  ", S_);
            if ((U_ & NOWHERE) == 0) s = s+sprintf(s, "U = $%04x  ", U_);
            comment[addr] = strdup(constants);

            // Can we intuit anything about constant data from the upcoming JSR call?
            if ((s = strchr(disassembly[addr], '$')) != NULL) {
              int h1,h2,h3,h4;
              // hopefully later code has not yet turned the target address into a string...
              if (ishex(s[1],&h1) && ishex(s[2],&h2) && ishex(s[3],&h3) && ishex(s[4],&h4)) {
                CODE_ADDR targetaddr = (h1<<12) | (h2<<8) | (h3<<4) | h4;
                // Can we determine a diffy or duffy parameter from the registers
                // and the subroutine address? (Well, not in C when there is a wrapper, but for older code, maybe)
                switch (targetaddr) {
                //  Diffy  $F610 DANROT   DROT   -   Rotate 'Diffy' style list A=Angle B=no of Vectors X=Source list U=Dest
                //  Diffy  $F433 DASHE    DSHDF1 $D0 Draw dashed lines from 'DIFFY' list
                //  Diffy  $F434 DASHEL   DSHDF  $D0 Draw dashed lines from 'DIFFY' list A=Vectors-1 X=List
                //  Diffy  $F437 DASHY    DASHDF $D0 Draw dashed lines from 'DIFFY' list X
                //  Diffy  $F3CE DIFFAX   -      $D0 Draw from 'DIFFY' style list X=List
                //  Diffy  $F3D8 DIFFXT   DIFFY  $D0 Draw from 'DIFFY' style list A=Vectors-1 B=Scale X=List
                //  Diffy  $F3DD DIFFY    -      $D0 Draw from 'DIFFY' style list X=List
                //  Diffy  $F3D6 DIFLST   -      $D0 Draw from 'DIFFY' style list     <---- need to check if list is at X...?
                //  Diffy  $F616 DIFROT   ADROT  -   Rotate 'DIFFY' style list X=Source U=Dest ANGLE
                //  Diffy  $F3D2 DIFTIM   -      $D0 Draw from 'DIFFY' style list B=scale X=List
                //  Diffy  $F3DA DIFTLSL  DIFFY  $D0 Draw from 'DIFFY' style list A=Vectors-1, X=List
                //  Diffy  $F613 DISROT   BDROT  -   'DIFFY' style rotate B=vector count X=source list U=Dest
                //  Dot    $F2D5 DIFDOT   -      $D0 Draw dots according to 'DIFFY' format X=List pointer
                //  Dot    $F2BE DOTTIM   -      $D0 Draw one dot from 'DIFFY' style list B=on time X=List
                //  Dot    $F2C1 DOTX     -      $D0 Draw one dot from 'DIFFY' style list X
                case 0xF610:
                case 0xF433:
                case 0xF434:
                case 0xF437:
                case 0xF3CE:
                case 0xF3D8:
                case 0xF3DD:
                case 0xF3D6:
                case 0xF616:
                case 0xF3D2:
                case 0xF3DA:
                case 0xF613:
                case 0xF2D5:
                case 0xF2BE:
                case 0xF2C1:
                  if (X_ < 0xC800) {
                    //fprintf(stdout, "; There appears to be a DIFFY list at %04X\n", X_);
                    comment[X_] = " ; maybe a DIFFY list!";
                    if (could_be_diffy_or_duffy(X_)) {
                      F[X_] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                      F[X_] |= F_leads_to_bad_code|F_data|F_diffy;
                    }
                  }
                  break;

                //  Duffy  $F3AD DUFFAX   -      $D0 Draw from 'DUFFY' style list X=list
                //  Duffy  $F3BC DUFFY    -      $D0 Draw from 'DUFFY' style list X=list          -\   ...
                //  Duffy  $F3BC DUFLTLST DUFFY  $D0 Draw from 'DUFFY' style list B=length X=List --\  One of these two wrong address? (F3BC)
                //  Duffy  $F3B5 DUFLST   -      $D0 Draw from 'DUFFY' style list X=list
                //  Duffy  $F3B1 DUFTIM   -      $D0 Draw from 'DUFFY' style list B=length X=List
                //  Diffy  $F3B9 DUFLST          $D0 A=Vectors-1 X=List

                case 0xF3AD:
                case 0xF3BC:
                case 0xF3B5:
                case 0xF3B1:
                case 0xF3B9:
                  if (X_ < 0xC800) {
                    //fprintf(stdout, "; There appears to be a DUFFY list at %04X\n", X_);
                    comment[X_] = " ; maybe a DUFFY list!";
                    if (could_be_diffy_or_duffy(X_)) {
                      F[X_] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                      F[X_] |= F_leads_to_bad_code|F_data|F_duffy;
                    }
                  }
                  break;

                //  Dot    $F2DE DOTPAK   DOTPCK $D0 Draw dots according to 'PACKET' format X=list
                //  Packet $F46E DASHY3   DASHPK $D0 Draw dashed lines from 'PACKET' list X
                //  Packet $F408 PAC1X    PACK1X $D0 Draw according to 'Packet' style list X scale 1x
                //  Packet $F404 PAC2X    PACK2X $D0 Draw according to 'Packet' style list X scale 2x
                //  Packet $F40E PACBT    PACK   $D0 Draw from 'PACKET' list X B=Scale
                //  Packet $F410 PACKET   -      $D0 Draw according to 'Packet' style list X
                //  Packet $F40C PACXXL   PACK   $D0 Draw from 'PACKET' style list X=list
                //  Packet $F61F POTATA   PROT   -   'PACKET' style rotate A=Angle X=Source List U=Dest List
                //  Packet $F622 POTATEA  PROT   -   'PACKET' style rotate

                case 0xF2DE:
                case 0xF46E:
                case 0xF408:
                case 0xF404:
                case 0xF40E:
                case 0xF410:
                case 0xF40C:
                case 0xF61F:
                case 0xF622:
                  if (X_ < 0xC800) {
                    //fprintf(stdout, "; There appears to be a PACKET list at %04X\n", X_);
                    comment[X_] = " ; maybe a PACKET list!";
                    if (could_be_packet(X_)) {
                      F[X_] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                      F[X_] |= F_leads_to_bad_code|F_data|F_packet;
                    }
                  }
                  break;

                case 0xF378: // Print_Str_yx
                  if (U_ < 0xC800) {
                    CODE_ADDR addr = U_;
                    // this overrides any previous decode at that address of possible code.
                    //fprintf(stdout, "; There appear to be y,x coords followed by a 0x80-terminated string at %04X.\n", U_);
                    comment[U_] = "; maybe y,x coords followed by a 0x80-terminated string";
                    F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                    F[addr] |= F_Label|F_leads_to_bad_code|F_data|F_hex;
                    addr += 1;
                    F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                    F[addr] |= F_leads_to_bad_code|F_data|F_hex;
                    addr += 1;
                    F[addr] |= F_V_string;
                    for (;;) {
                      F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                      F[addr] |= F_leads_to_bad_code|F_data|F_char;
                      if (mem[addr] == 0x80) break;
                      addr += 1;
                    }
                    F[addr] &= ~F_char;
                    F[addr] |= F_hex;
                  }
                  break;

                case 0xF37A: // Print_Str_d
                  if (U_ < 0xC800) {
                    // this overrides any previous decode at that address of possible code.
                    //fprintf(stdout, "; There appears to be a 0x80-terminated string at %04X.\n", U_);
                    CODE_ADDR addr = U_;
                    // this overrides any previous decode at that address of possible code.
                    //fprintf(stdout, "; There appear to be y,x coords followed by a 0x80-terminated string at %04X.\n", U_);
                    comment[U_] = "; maybe a 0x80-terminated string";
                    F[addr] |= F_V_string;
                    for (;;) {
                      F[addr] &= ~(F_OPCODE|F_may_be_valid_OPCODE|F_OPERAND);
                      F[addr] |= F_leads_to_bad_code|F_data|F_char;
                      if (mem[addr] == 0x80) break;
                      addr += 1;
                    }
                    F[addr] &= ~F_char;
                    F[addr] |= F_hex;
                  }
                  break;
                  
                default: /* no help */;
                }
              }
            }
          }
        }

        constants[0] = '\0';
        if (dis(addr, F, &length, &this_branch, text) && ((A_ & B_ & S_ & U_ & X_ & Y_ & NOWHERE) == 0)) { // recalculate cache
        }

        // Would prefer to feed table of labels back to dis() and re-disassemble the instruction to pick up the label.
        if ((s = strchr(text, '$')) != NULL) {
          int h1,h2,h3,h4; // known BIOS address
          if (ishex(s[1],&h1) && ishex(s[2],&h2) && ishex(s[3],&h3) && ishex(s[4],&h4)) {
            CODE_ADDR targetaddr = (h1<<12) | (h2<<8) | (h3<<4) | h4;
            if ( /*(targetaddr >= 0x12) && */ (Label[targetaddr] != NULL) && (*Label[targetaddr] != '\0')) {
              // I think this is the only use of malloc in the code. There are also calls to 'strdup'.
              // I did consider using a string pool to avoid the need for use of any dynamic memory.
              disassembly[addr] = malloc(strlen(text)-5+strlen(Label[targetaddr])+1);
              strcpy(disassembly[addr], text);
              s = strchr(disassembly[addr], '$');
              strcpy(s, Label[targetaddr]);
              s = strchr(text, '$');
              strcat(disassembly[addr], s+5);
            } else {
              disassembly[addr] = strdup (text);
              if (F[addr]&F_Label) {
                s = strchr(disassembly[addr], '$');
                if (((targetaddr < 0x12) || (s[-1] != '#')) && (strstr(disassembly[addr], "LEA") == NULL)) s[0] = 'L';//   else s[0]='1';
              }
            }
          } else {
            disassembly[addr] = strdup (text);
          }
        } else {
          disassembly[addr] = strdup (text);
        }

        // Would prefer to feed table of labels back to dis() and re-disassemble the instruction to pick up the label.
        if ((s = strchr(disassembly[addr], '$')) != NULL) {
          int h1,h2,h3,h4; // quick hack until we implement a control file containing label names
          if (ishex(s[1],&h1) && ishex(s[2],&h2) && ishex(s[3],&h3) && ishex(s[4],&h4)) {
            CODE_ADDR targetaddr = (h1<<12) | (h2<<8) | (h3<<4) | h4;
            if ( /*(targetaddr < 0x12) &&*/ (F[targetaddr]&F_Label) && (s[-1] != '#') && (strstr(disassembly[addr], "LEA") == NULL)) s[0] = 'L';
          }
        }

        if (F[addr]&F_unconditional_jump) F[addr+opcode_length[addr]] |= F_Label;
        addr += opcode_length[addr];

      } else if (F[addr]&F_data) {
        if (F[addr]&F_diffy) {
          addr = print_diffy(addr, FALSE); // mark
        } else if (F[addr]&F_duffy) {
          addr = print_duffy(addr, FALSE); // mark
        } else if (F[addr]&F_packet) {
          addr = print_packet(addr, FALSE); // mark
        } else if (F[addr]&F_V_string) {
          addr = print_Vectrex_string(addr, FALSE); // mark
        } else if ((F[addr]&F_addr_hi) && (F[addr+1]&F_addr_lo)) {
          addr += 2;
        } else if ((F[addr]&F_word_hi) && (F[addr+1]&F_word_lo)) {
          addr += 2;
        } else if (F[addr]&F_char) {
          addr += 1;
        } else if (F[addr]&F_hex) {
          addr += 1;
        } else {
          // data but we don't know which style
          // could count up total bytes used by successive lists and only mark if above small threshold
          if ((mem[addr] == 0xFF) && (could_be_packet(addr))) { // maybe also get length and check minimum...
            while (could_be_packet(addr)) {
              F[addr] |= F_Label;
              addr = print_packet(addr, FALSE); // mark
            }
          } else {
            addr += 1;
          }
        }
      } else {
        // it's either an opcode or its data. Anything else is a coding error.
        if (could_be_Vectrex_string(addr)) {
          while (could_be_Vectrex_string(addr)) {
            F[addr] |= F_Label;
            addr = print_Vectrex_string(addr,FALSE);
          }
        } else if (could_be_packet(addr)) {
          while (could_be_packet(addr)) {
            F[addr] |= F_Label;
            addr = print_packet(addr, FALSE);
          }
        } else {
          addr += 1;
        }
      }
    }
  }
}

static void simple_dump_rom(void) {
  int segment;
  CODE_ADDR startaddr, addr, segend;
  static char label_field[64];

  // Makes no new formatting decisions, doesn't set any F flags.
  
  for (segment = 0; segment < next_free_segment; segment++) {
    addr = segstart[segment];
    segend = segstart[segment]+seglen[segment];
    while (addr < segend) {

      int ccnt = cycle_count(mem, addr, 1);
      
      startaddr = addr;
      strcpy (label_field, "        ");
      if (F[addr]&(F_JumpTarget|F_Label)) {
        if (Label[addr] != NULL && *Label[addr] != '\0') {
          if (strlen(Label[addr]) < 9) {
          sprintf (label_field, "%-8s", Label[addr]);
          } else {
            sprintf (label_field, "%s\n                            ", Label[addr]);
          }
        } else {
          sprintf (label_field, "L%04X   ", addr);
        }
      }
      
      if (F[addr]&F_OPCODE) {
        int i;

        fprintf (stdout, "%04X:", addr);
        for (i = 0; i < 5; i++) {
          if (i >= opcode_length[addr]) {
            fprintf (stdout, "   ");
          } else {
            fprintf (stdout, " %02X", mem[addr+i]);
          }
        }

        fprintf (stdout, "%s%s%s%s ; (%d)\n", label_field, disassembly[addr],
                 comment[addr], decode_F (F[startaddr]), ccnt); /* maybe have an option to omit cycle count? */

        if (F[addr]&F_unconditional_jump) {
          putchar ('\n'); // just to make start of a block easier to read
        }

        addr += opcode_length[addr];

      } else if (F[addr]&F_data) {
        if ((F[addr]&F_diffy) && (F[addr]&F_duffy)) {
          // A litle extra checking for debugging.
          if (F[addr]&(F_packet|F_V_string|F_addr_hi|F_addr_lo|F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/9 %s\n", decode_FF(F[addr]));
          addr = print_diffy_or_duffy(addr, TRUE);
        } else if (F[addr]&F_diffy) {
          if (F[addr]&(F_packet|F_V_string|F_addr_hi|F_addr_lo|F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/8 %s\n", decode_FF(F[addr]));
          addr = print_diffy(addr, TRUE);

        } else if (F[addr]&F_duffy) {
          if (F[addr]&(F_packet|F_V_string|F_addr_hi|F_addr_lo|F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/7 %s\n", decode_FF(F[addr]));
          addr = print_duffy(addr, TRUE);

        } else if (F[addr]&F_packet) {
          if (F[addr]&(F_V_string|F_addr_hi|F_addr_lo|F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/6 %s\n", decode_FF(F[addr]));
          addr = print_packet(addr, TRUE);

        } else if (F[addr]&F_V_string) {
          if (F[addr]&(F_addr_hi|F_addr_lo|F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/4 %s\n", decode_FF(F[addr]));
          addr = print_Vectrex_string(addr, TRUE);

        } else if ((F[addr]&F_addr_hi) && (F[addr+1]&F_addr_lo)) {
          if (F[addr]&(F_word_hi|F_word_lo|F_char|F_hex)) fprintf(stdout, "; OVERLOADED/3 %s\n", decode_FF(F[addr]));
          fprintf (stdout, "%04X: %02X %02X         %s FDB   $%04x%s%s\n",
                   addr, mem[addr], mem[addr+1], label_field,
                   ((mem[addr]) << 8)|(mem[addr+1]),
                   comment[addr], decode_F (F[addr]));
          addr += 2;

        } else if ((F[addr]&F_word_hi) && (F[addr+1]&F_word_lo)) {
          if (F[addr]&(F_char|F_hex)) fprintf(stdout, "; OVERLOADED/2 %s\n", decode_FF(F[addr]));
          fprintf (stdout, "%04X: %02X %02X         %s FDB   $%04x%s%s\n",
                   addr, mem[addr], mem[addr+1], label_field,
                   ((mem[addr]) << 8)|(mem[addr+1]),
                   comment[addr], decode_F (F[addr]));
          addr += 2;

        } else if (F[addr]&F_char) { // reinstate loop for sequential chars
          if (F[addr]&F_hex) fprintf(stdout, "; OVERLOADED/1 %s\n", decode_FF(F[addr]));
          fprintf (stdout, "%04X: %02X            %s FCC   '%c'%s%s\n",
                   addr, mem[addr], label_field, mem[addr], comment[addr], decode_F (F[addr]));
          addr += 1;

        } else if (F[addr]&F_hex) {
          fprintf (stdout, "%04X: %02X            %s FCB   $%02x%s%s\n",
                   addr, mem[addr], label_field, mem[addr], comment[addr], decode_F (F[addr]));
          addr += 1;

        } else {
          // data but we don't know which style
          if ((' ' <= (mem[addr])) && ((mem[addr]) < 127)) {
            fprintf (stdout, "%04X: %02X            %s FCB   $%02x ; '%c' %s%s  // unclassified data\n",
                     addr, mem[addr], label_field, mem[addr],  mem[addr], comment[addr], decode_FF (F[addr]));
          } else {
            fprintf (stdout, "%04X: %02X            %s FCB   $%02x%s%s  // unclassified data\n",
                     addr, mem[addr], label_field, mem[addr], comment[addr], decode_FF (F[addr]));
          }
          addr += 1;
        }
      } else {
        // it's either an opcode or its data. Anything else is a coding error.
        // data but we don't know which style
        // Again, this should be in 'classify' rather than dump_rom
        if ((' ' <= (mem[addr])) && ((mem[addr]) < 127)) {
          fprintf (stdout, "%04X: %02X            %s FCB   $%02x ; '%c' %s%s  ; %s\n",
                   addr, mem[addr], label_field, mem[addr],  mem[addr], comment[addr], decode_F (F[addr]), disassembly[addr]);
        } else {
          fprintf (stdout, "%04X: %02X            %s FCB   $%02x%s%s         ; %s\n",
                   addr, mem[addr], label_field, mem[addr], comment[addr], decode_F (F[addr]), disassembly[addr]);
        }
        addr += 1;
      }
    }
  }
}

static void explain(int addr) {
  if (F[addr]&F_Visited) return;
  F[addr] |= F_Visited;
  if (bad_code[addr] == NOWHERE) {
    fprintf(stderr, "%04X was bad because:\n   %s   %s\n", addr, explanation[addr], disassembly[addr]);
  } else {
    fprintf(stderr, "%04X was bad because %04X was bad, and:\n   %s   %s\n", addr, bad_code[addr], explanation[addr], disassembly[addr]);
    explain(bad_code[addr]);
  }
}

static int htoi(char *s) {
  int rc, x;
  rc = sscanf(s, "%x", &x);
  if (rc != 1) {
    fprintf(stderr, "dec: bad hex --explain '%s'\n", s);
    exit(EXIT_FAILURE);
  }
  return x;
}

static void look_for_gcc6809_labels(void);

int main(int argc, char **argv) {
  CODE_ADDR addr;
  FILE *bin;
  int explanation_address = NOWHERE;

  // Could write this better to allow arbitrary order of params.
  if ((argc > 2) && (strcmp(argv[1], "-d")==0)) {
    argc -= 1; argv += 1;
    DEC_DEBUG = TRUE;
  }

  if ((argc > 2) && (strcmp(argv[1], "--vectrex")==0)) {
    argc -= 1; argv += 1;
    Vectrex = TRUE;
  }

  if ((argc > 3) && (strcmp(argv[1], "--explain")==0)) { // hidden debug option
    explanation_address = htoi(argv[2]);
    argc -= 2; argv += 2;
  }

  // add { {--org} file.bin }* for minestorm, bios segments
  // add {--code xxxx} {--data xxxx} to force code/data flags
  // add {--label xxxx=name}

  // give some thought to how we might implement --bank n
  
  if (argc != 2) {
    fprintf(stderr, "syntax: dec [-d] [--vectrex] file.bin\n");
    exit(EXIT_FAILURE);
  }
  
  bin = fopen(argv[1], "rb");
  if (!bin) {
    fprintf(stderr, "dec: %s - %s\n", argv[1], strerror(errno));
    exit(EXIT_FAILURE);
  }
  
  initialise();
  load_binary(bin);

  vectrex_tables();
  look_for_gcc6809_labels();
  
  prescan_rom_area();           // Mark every byte that *could* be a valid instruction.
                                // and if it looks like a branch of some sort, mark its destination
  gather_hints();

  clear_visited();
  //if ((mem[0xFFF2] | mem[0xFFF3]) != 0) if (TreeWalk((mem[0xFFF2]<<8) | mem[0xFFF3])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFF4] | mem[0xFFF5]) != 0) if (TreeWalk((mem[0xFFF4]<<8) | mem[0xFFF5])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFF6] | mem[0xFFF7]) != 0) if (TreeWalk((mem[0xFFF6]<<8) | mem[0xFFF7])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFF8] | mem[0xFFF9]) != 0) if (TreeWalk((mem[0xFFF8]<<8) | mem[0xFFF9])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFFA] | mem[0xFFFB]) != 0) if (TreeWalk((mem[0xFFFA]<<8) | mem[0xFFFB])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFFC] | mem[0xFFFD]) != 0) if (TreeWalk((mem[0xFFFC]<<8) | mem[0xFFFD])) replace(F_Visited, F_OPCODE|F_Definite);
  //if ((mem[0xFFFE] | mem[0xFFFF]) != 0) if (TreeWalk((mem[0xFFFE]<<8) | mem[0xFFFF])) replace(F_Visited, F_OPCODE|F_Definite);
  if (TreeWalk(entrypt)) {        // Mark every definite instruction sequence
    replace(F_Visited, F_OPCODE|F_Definite);
  } else clear_visited();
  WipeEnqueued();

  // bunch of different heuristics below.  You can ifdef one out and run the regression script to see
  // what it's primary effects are.
  
  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_jump_target) {  // Go over all possible jmp/jsr/bra destinations, and
                                         // if they don't look plausible, mark this potential entry point as bad
      if (!simple_valid_sequence (addr, FALSE)) {
        F[addr] &= ~(F_may_be_jump_target|F_OPCODE|F_may_be_valid_OPCODE);
        F[addr] |= F_leads_to_bad_code;  // even if it is a valid opcode, it doesn't lead to valid code.
      }
    }
  }

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_jump_target) {  // Go over all possible jmp/jsr/bra destinations from the code
                                         // that immediately follows this potential entrypoint, and
                                         // if any of those branch destination is marked as bad,
                                         // mark this potential entry point as bad.
      if (!valid_sequence_with_tested_branches (addr, FALSE)) {
        // This OUGHT to be equivalent to a full tree walk, but be careful
        // that any tested branches have been marked with success/failure
        // Unless we do a transitive closure, I think some bad paths can be missed.
        F[addr] &= ~F_may_be_jump_target; // BAD: F[addr] &= ~(F_may_be_jump_target|F_OPCODE|F_may_be_valid_OPCODE);
        F[addr] |= F_leads_to_bad_code;
      }
    }
  }

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_valid_OPCODE) { // Now we do the same sort of eliminations, but rather than
                                         // just look at the first instruction in a sequence that may
                                         // be a branch destination, we look at *all* potential instructions
                                         // and the code that linearlky follows them.  Again, not marking
                                         // all the code, just that first instruction in the bad sequence
      if (!simple_valid_sequence (addr, FALSE)) {
        F[addr] &= ~F_may_be_jump_target;
        F[addr] |= F_leads_to_bad_code;
      }
    }
  }

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_valid_OPCODE) {  // And now we do the same but this time if there is a branch
                                          // instruction in the following sequence, we'll reject the
                                          // entry point if any of those branches points to a bad instruction
      if (!valid_sequence_with_tested_branches (addr, FALSE)) {
        F[addr] &= ~F_may_be_jump_target;
        F[addr] |= F_leads_to_bad_code;
      }
    }
  }

  // Does another block of code follow a block of code?
  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if ((F[addr]&F_unconditional_jump) && (F[addr+opcode_length[addr]]&F_may_be_valid_OPCODE)) {
                                          // Now we look at the first byte after a sequence of good
                                          // instructions.  If it looks like we can do a complete
                                          // treewalk from here successfully, we do so and mark
                                          // the entire tree as good.  This finds whole blocks of
                                          // code that were not found in the initial tree-walk,
                                          // probably because they were reached via jump tables
                                          // and other indirections, rather than direct jumps.
      addr += opcode_length[addr];
      if ((F[addr]&F_may_be_valid_OPCODE) && valid_sequence_with_tested_branches (addr, FALSE)) {
        F[addr] |= F_JumpTarget|F_Label;
        clear_visited ();
        if (TreeWalk (addr)) { // true if all branches are good!
          replace(F_Visited, F_OPCODE);
        } else clear_visited();
        WipeEnqueued();
      }
    }
  }
  
  // mop up... may be some dubious short code sequences here in the middle of data blocks
  // - they're usually easy to spot
  // I may add some 'stupid code' detection such as finding obviously redundant loads or stores
  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_OPCODE) {               // We now go over every remaining potential instruction
                                          // in the rom, and mark as bad any that lead to non code.
      if (!valid_sequence_with_tested_branches (addr, FALSE)) {
        F[addr] &= ~F_may_be_jump_target;
        F[addr] |= F_leads_to_bad_code;  // even if it is a valid opcode, it doesn't lead to valid code.
      }
      if (branch[addr] != NOWHERE) F[branch[addr]] |= F_Label;
    }
  }

                                           // There may be a few short sequences of what appear
                                           // to be instructions in the middle of data blocks that
                                           // are self-consistent and could be valid code, even
                                           // though they are actually data.  There will be no
                                           // other code that points to them - they will be marked
                                           // as tentative not definite.  These are likely to be
                                           // very short sequences ending in a branch and preceded
                                           // by a very small number of instructions, possibly
                                           // forming a sequence that makes little sense (eg containing
                                           // redundant loads or stores).  These are candidates
                                           // for being knocked back to data status.  The code to
                                           // do so has not been written yet and has to be somewhat
                                           // conservative when it is.

  classify_data_first_pass();              // Don't lean too heavily on guesswork yet

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_V_string) {               // We now go over every remaining potential instruction
      for (;;) {
        addr += 1;
        if (mem[addr] == 0x80) break;
      }
      addr += 1;
      // find a known V_string, then look for more immediately following it.
      if (could_be_Vectrex_string(addr)) {
        while (could_be_Vectrex_string(addr)) {
          fprintf(stderr, "Adding a VString at %04x\n", addr);
          fprintf(stdout, "; Adding a VString at %04x\n", addr);
          addr = print_Vectrex_string(addr, FALSE); // mark it
        }
      }
    }
  }

  // check for undetected code after we've added some data tags
  // This is the expensive pass, however it does fill in just about all
  // possible omitted segments.  And cause some data to be marked as code.

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_valid_OPCODE) {               // We now go over every remaining potential instruction
      clear_visited ();
      if (TreeWalk (addr)) { // true if all branches are good!
        CODE_ADDR i, count = 0;
        for (i = 0; i <= 0xFFFF; i++) { // a little faster if we restrict ourselves to segstart/end but would it be safe?
          if (F[i]&F_Visited) count += 1;
        }
        // If a treewalk completes successfully *and* we discover 20+ new opcodes,
        // convert the tentative walk from data to code (but don't mark as F_Definitive)
        if (count) replace(F_Visited, F_OPCODE); else clear_visited();
      } else clear_visited();
      WipeEnqueued();
    }
  }

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_valid_OPCODE) {               // We now go over every remaining potential instruction
                                          // in the rom, and mark as bad any that lead to non code.
      if (valid_sequence_with_tested_branches (addr, FALSE)) {
        // mark it as code
        valid_sequence_with_tested_branches (addr, TRUE);
      }
    }
  }

  for (addr = segstart[0]; addr < segstart[0]+seglen[0]; addr++) {
    if (F[addr]&F_may_be_valid_OPCODE) {
      if (simple_valid_sequence (addr, FALSE)) {
        // mark it as code
        simple_valid_sequence (addr, TRUE);
      }
    }
  }

  // At this point we should identify the C library entry points and replace the numeric
  // labels with the C labels.

  simple_dump_rom();                       // And finally we output the decoded rom.

  clear_visited (); if (explanation_address != NOWHERE) explain(explanation_address);
  
  exit (EXIT_SUCCESS);
  return EXIT_FAILURE;
}

int CodeMatch(int Addr, char *Code) {
  int c0, c1, c2;
  do {
    c0 = Code[0]; c1 = Code[1]; c2 = Code[2]; Code += 3;
    if ('A' <= c0 && c0 <= 'F') c0 = c0 - 'A' + 10; else c0 = c0 - '0';
    if ('A' <= c1 && c1 <= 'F') c1 = c1 - 'A' + 10; else c1 = c1 - '0';
    if (mem[Addr] != (c0<<4|c1)) return FALSE;
    Addr += 1;
  } while (c2 == ' ');
  return TRUE;
}

static int Addr = 0;

static void TryLabel(char *Code, char *Lab) {
  for (; Addr < 0x0500; Addr++) {
    int StartAddr = Addr;
    if (CodeMatch(Addr, Code)) {
      Label[StartAddr] = Lab; // search from here on next procedure
      return;
    }
  }
  Addr = 0; // search from 0 if this procedure was not found
}

static void look_for_gcc6809_labels(void) {
  // --gcc6809 option (to be added) should understand these entrypoints (which may move around):


// 0164: A6 62         L0164    LDA   2,S ; (5)
// 0166: 7E F2 C3               JMP   Dot_d ; (4)

  TryLabel("A6 62 7E F2 C3", "_Dot_d");

//0169: 1F 10         L0169    TFR   X,D ; (6)
//016B: 7E F2 C3               JMP   Dot_d ; (4)

  TryLabel("1F 10 7E F2 C3", "_Dot_dd");
    
//016E: 34 40         L016E    PSHS  U ; (7)
//0170: 1F 13                  TFR   X,U ; (6)
//0172: BD F3 73               JSR   Print_Str_hwyx ; (8)
//0175: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F3 73 35 C0", "_Print_Str_hwyx");
    
//0177: 34 40         L0177    PSHS  U ; (7)
//0179: 1F 13                  TFR   X,U ; (6)
//017B: BD F3 78               JSR   Print_Str_yx ; (8)
//017E: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F3 78 35 C0", "_Print_Str_yx");
    
//0180: 34 40         L0180    PSHS  U ; (7)
//0182: A6 64                  LDA   4,S ; (5)
//0184: 1F 13                  TFR   X,U ; (6)
//0186: BD F3 7A               JSR   Print_Str_d ; (8)
//0189: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 A6 64 1F 13 BD F3 7A 35 C0", "_Print_Str_d");
    
//018B: 34 40         L018B    PSHS  U ; (7)
//018D: 1F 10                  TFR   X,D ; (6)
//018F: EE 64                  LDU   4,S ; (6)
//0191: BD F3 7A               JSR   Print_Str_d ; (8)
//0194: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 10 EE 64 BD F3 7A 35 C0", "_Print_Str_dd");
    
//0196: 34 40         L0196    PSHS  U ; (7)
//0198: 1F 13                  TFR   X,U ; (6)
//019A: BD F3 85               JSR   Print_List_hw ; (8)
//019D: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F3 85 35 C0", "_Print_List_hw");

//019F: 34 40         L019F    PSHS  U ; (7)
//01A1: 1F 13                  TFR   X,U ; (6)
//01A3: BD F3 8A               JSR   Print_List ; (8)
//01A6: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F3 8A 35 C0", "_Print_List");

//01A8: 34 40         L01A8    PSHS  U ; (7)
//01AA: 1F 13                  TFR   X,U ; (6)
//01AC: BD F3 8C               JSR   Print_List_chk ; (8)
//01AF: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F3 8C 35 C0", "_Print_List_chk");
    
//01B1: A6 62         L01B1    LDA   2,S ; (5)
//01B3: 7E F3 91               JMP   Print_Ships_x ; (4)

  TryLabel("A6 62 7E F3 91", "_Print_Ships_x");
    
//01B6: A6 62         L01B6    LDA   2,S ; (5)
//01B8: 7E F3 93               JMP   Print_Ships ; (4)

  TryLabel("A6 62 7E F3 93", "_Print_Ships");

//01BB: 34 40         L01BB    PSHS  U ; (7)
//01BD: 1F 13                  TFR   X,U ; (6)
//01BF: BD F4 95               JSR   Print_Str ; (8)
//01C2: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F4 95 35 C0", "_Print_Str");
    
//01C4: 34 40         L01C4    PSHS  U ; (7)
//01C6: BD F4 98               JSR   Print_MRast ; (8)
//01C9: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 BD F4 98 35 C0", "_Print_MRast");
    
//01CB: 1F 98         L01CB    TFR   B,A ; (6)
//01CD: 7E F4 33               JMP   Draw_Pat_VL_aa ; (4)

  TryLabel("1F 98 7E F4 33", "_Draw_Pat_VL_aa");
    
//01D0: 1F 98         L01D0    TFR   B,A ; (6)
//01D2: 7E F4 34               JMP   Draw_Pat_VL_a ; (4)

  TryLabel("1F 98 7E F4 34", "_Draw_Pat_VL_a");
    
//01D5: A6 62         L01D5    LDA   2,S ; (5)
//01D7: 7E F3 DF               JMP   Draw_Line_d ; (4)

  TryLabel("A6 62 7E F3 DF", "_Draw_Line_d");
    
//01DA: A6 62         L01DA    LDA   2,S ; (5)
//01DC: 7E F3 D8               JMP   Draw_VL_ab ; (4)

  TryLabel("A6 62 7E F3 D8", "_Draw_VL_ab");
    
//01DF: 1F 98         L01DF    TFR   B,A ; (6)
//01E1: 7E F3 DA               JMP   Draw_VL_a ; (4)

  TryLabel("1F 98 7E F3 DA", "_Draw_VL_a");
    
//01E4: A6 62         L01E4    LDA   2,S ; (5)
//01E6: 7E F3 B7               JMP   Mov_Draw_VL_ab ; (4)

  TryLabel("A6 62 7E F3 B7", "_Mov_Draw_VL_ab");
    
//01E9: 1F 98         L01E9    TFR   B,A ; (6)
//01EB: 7E F3 B9               JMP   Mov_Draw_VL_a ; (4)

  TryLabel("1F 98 7E F3 B9", "_Mov_Draw_VL_a");
    
//01EE: A6 62         L01EE    LDA   2,S ; (5)
//01F0: 7E F3 BE               JMP   Mov_Draw_VL_d ; (4)

  TryLabel("A6 62 7E F3 BE", "_Mov_Draw_VL_d");
    
//01F3: 34 40         L01F3    PSHS  U ; (7)
//01F5: 1F 98                  TFR   B,A ; (6)
//01F7: EE 64                  LDU   4,S ; (6)
//01F9: BD F6 1F               JSR   Rot_VL_Mode ; (8)
//01FC: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 98 EE 64 BD F6 1F 35 C0", "_Rot_VL_Mode");
    
//01FE: 34 40         L01FE    PSHS  U ; (7)
//0200: EE 64                  LDU   4,S ; (6)
//0202: BD F6 22               JSR   Rot_VL_Pack ; (8)
//0205: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F6 22 35 C0", "_Rot_VL_Pack");
    
//0207: 34 40         L0207    PSHS  U ; (7)
//0209: EE 64                  LDU   4,S ; (6)
//020B: BD F6 2B               JSR   Rot_VL_M_dft ; (8)
//020E: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F6 2B 35 C0", "_Rot_VL_M_dft");
    
//0210: BD F5 11      L0210    JSR   Random_3 ; (8)
//0213: 1F 89                  TFR   A,B ; (6)
//0215: 39                     RTS   ; (5)

  TryLabel("BD F5 11 1F 89 39", "_Random_3");
    
//0216: BD F5 17      L0216    JSR   Random ; (8)
//0219: 1F 89                  TFR   A,B ; (6)
//021B: 39                     RTS   ; (5)

  TryLabel("BD F5 17 1F 89 39", "_Random");
    
//021C: 1F 98         L021C    TFR   B,A ; (6)
//021E: BD F5 7E               JSR   Bitmask_a ; (8)
//0221: 1F 89                  TFR   A,B ; (6)
//0223: 39                     RTS   ; (5)

  TryLabel("1F 98 BD F5 7E 1F 89 39", "_Bitmask_a");
    
//0224: A6 62         L0224    LDA   2,S ; (5)
//0226: BD F5 84               JSR   Abs_a_b ; (8)
//0229: 1F 01                  TFR   D,X ; (6)
//022B: 39                     RTS   ; (5)

  TryLabel("A6 62 BD F5 84 1F 01 39", "_Abs_a_b");

//022C: 1F 98         L022C    TFR   B,A ; (6)
//022E: BD F5 DB               JSR   Xform_Sin ; (8)
//0231: 1F 89                  TFR   A,B ; (6)
//0233: 39                     RTS   ; (5)

  TryLabel("1F 98 BD F5 DB 1F 89 39", "_Xform_Sin");
    
//0234: BD F5 EF      L0234    JSR   Get_Rise_Run ; (8)
//0237: 1F 01                  TFR   D,X ; (6)
//0239: 39                     RTS   ; (5)

  TryLabel("BD F5 EF 1F 01 39", "_Get_Rise_Run");
    
//023A: 1F 98         L023A    TFR   B,A ; (6)
//023C: BD F6 5B               JSR   Xform_Run_a ; (8)
//023F: 1F 01                  TFR   D,X ; (6)
//0241: 39                     RTS   ; (5)

  TryLabel("1F 98 BD F6 5B 1F 01 39", "_Xform_Run_a");
    
//0242: BD F6 5D      L0242    JSR   Xform_Run ; (8)
//0245: 1F 89                  TFR   A,B ; (6)
//0247: 39                     RTS   ; (5)

  TryLabel("BD F6 5D 1F 89 39", "_Xform_Run");
    
//0248: 1F 98         L0248    TFR   B,A ; (6)
//024A: BD F6 61               JSR   Xform_Rise_a ; (8)
//024D: 1F 89                  TFR   A,B ; (6)
//024F: 39                     RTS   ; (5)

  TryLabel("1F 98 BD F6 61 1F 89 39", "_Xform_Rise_a");
    
//0250: BD F6 63      L0250    JSR   Xform_Rise ; (8)
//0253: 1F 89                  TFR   A,B ; (6)
//0255: 39                     RTS   ; (5)

  TryLabel("BD F6 63 1F 89 39", "_Xform_Rise");
    
//0256: EC 62         L0256    LDD   2,S ; (6)
//0258: 7E F5 48               JMP   Clear_x_d ; (4)

  TryLabel("EC 62 7E F5 48", "_Clear_x_d");
    
//025B: 34 40         L025B    PSHS  U ; (7)
//025D: 1F 98                  TFR   B,A ; (6)
//025F: EE 64                  LDU   4,S ; (6)
//0261: BD F6 7F               JSR   Move_Mem_a_1 ; (8)
//0264: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 98 EE 64 BD F6 7F 35 C0", "_Move_Mem_a_1");

//0266: 34 40         L0266    PSHS  U ; (7)
//0268: 1F 98                  TFR   B,A ; (6)
//026A: EE 64                  LDU   4,S ; (6)
//026C: BD F6 7F               JSR   Move_Mem_a_1 ; (8)  <--- error
//026F: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 98 EE 64 BD F6 83 35 C0", "_Move_Mem_a"); // wrong address, should be F683
    
//0271: A6 62         L0271    LDA   2,S ; (5)
//0273: 7E F5 52               JMP   Clear_x_b_a ; (4)

  TryLabel("A6 62 7E F5 52", "_Clear_x_b_a");
    
//0276: 1F 98         L0276    TFR   B,A ; (6)
//0278: 7E F1 B4               JMP   Read_Btns_Mask ; (4)

  TryLabel("1F 98 7E F1 B4", "_Read_Btns_Mask");

//027B: 34 60         L027B    PSHS  U,Y ; (9)
//027D: A6 66                  LDA   6,S ; (5)
//027F: BD F7 A9               JSR   Select_Game ; (8)
//0282: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 A6 66 BD F7 A9 35 E0", "_Select_Game");
    
//0284: 34 60         L0284    PSHS  U,Y ; (9)
//0286: 1F 98                  TFR   B,A ; (6)
//0288: 1F 12                  TFR   X,Y ; (6)
//028A: BD F8 35               JSR   Display_Option ; (8)
//028D: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 1F 98 1F 12 BD F8 35 35 E0", "_Display_Option");
    
//028F: 34 40         L028F    PSHS  U ; (7)
//0291: 1F 98                  TFR   B,A ; (6)
//0293: BD F8 5E               JSR   Add_Score_a ; (8)
//0296: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 98 BD F8 5E 35 C0", "_Add_Score_a");
    
//0298: EC 62         L0298    LDD   2,S ; (6)
//029A: 7E F8 7C               JMP   Add_Score_d ; (4)

  TryLabel("EC 62 7E F8 7C", "_Add_Score_d");
    
//029D: 34 40         L029D    PSHS  U ; (7)
//029F: EE 64                  LDU   4,S ; (6)
//02A1: BD F8 C7               JSR   Compare_Score ; (8)
//02A4: 1F 89                  TFR   A,B ; (6)
//02A6: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F8 C7 1F 89 35 C0", "_Compare_Score");
    
//02A8: 34 40         L02A8    PSHS  U ; (7)
//02AA: EE 64                  LDU   4,S ; (6)
//02AC: BD F8 D8               JSR   New_High_Score ; (8)
//02AF: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F8 D8 35 C0", "_New_High_Score");
    
//02B1: A6 62         L02B1    LDA   2,S ; (5)
//02B3: 7E F2 56               JMP   Sound_Byte ; (4)

  TryLabel("A6 62 7E F2 56", "_Sound_Byte");
    
//02B6: A6 62         L02B6    LDA   2,S ; (5)
//02B8: 7E F2 59               JMP   Sound_Byte_x ; (4)

  TryLabel("A6 62 7E F2 59", "_Sound_Byte_x");
    
//02BB: 34 40         L02BB    PSHS  U ; (7)
//02BD: 1F 13                  TFR   X,U ; (6)
//02BF: BD F2 7D               JSR   Sound_Bytes ; (8)
//02C2: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F2 7D 35 C0", "_Sound_Bytes");
    
//02C4: 34 40         L02C4    PSHS  U ; (7)
//02C6: EE 64                  LDU   4,S ; (6)
//02C8: BD F2 84               JSR   Sound_Bytes_x ; (8)
//02CB: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F2 84 35 C0", "_Sound_Bytes_x");
    
//02CD: 34 40         L02CD    PSHS  U ; (7)
//02CF: BD F2 89               JSR   Do_Sound ; (8)
//02D2: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 BD F2 89 35 C0", "_Do_Sound");
    
//02D4: 34 60         L02D4    PSHS  U,Y ; (9)
//02D6: 1F 13                  TFR   X,U ; (6)
//02D8: BD F6 87               JSR   Init_Music_chk ; (8)
//02DB: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 1F 13 BD F6 87 35 E0", "_Init_Music_chk");
    
//02DD: 34 40         L02DD    PSHS  U ; (7)
//02DF: 1F 13                  TFR   X,U ; (6)
//02E1: BD F6 8D               JSR   Init_Music ; (8)
//02E4: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F6 8D 35 C0", "_Init_Music");
    
//02E6: 34 40         L02E6    PSHS  U ; (7)
//02E8: EE 64                  LDU   4,S ; (6)
//02EA: BD F6 90               JSR   Init_Music_a ; (8)
//02ED: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F6 90 35 C0", "_Init_Music_a");
    
//02EF: 34 60         L02EF    PSHS  U,Y ; (9)
//02F1: 1F 13                  TFR   X,U ; (6)
//02F3: BD F6 92               JSR   Init_Music_x ; (8)
//02F6: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 1F 13 BD F6 92 35 E0", "_Init_Music_x");
    
//02F8: 34 40         L02F8    PSHS  U ; (7)
//02FA: 1F 13                  TFR   X,U ; (6)
//02FC: BD F9 2E               JSR   Explosion_Snd ; (8)
//02FF: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 1F 13 BD F9 2E 35 C0", "_Explosion_Snd");
    
//0301: A6 62         L0301    LDA   2,S ; (5)
//0303: 7E F2 FC               JMP   Moveto_d_7F ; (4)

  TryLabel("A6 62 7E F2 FC", "_Moveto_d_7F");
    
//0306: 1F 10         L0306    TFR   X,D ; (6)
//0308: 7E F2 FC               JMP   Moveto_d_7F ; (4)

  TryLabel("1F 10 7E F2 FC", "_Moveto_dd_7F");
    
//030B: A6 62         L030B    LDA   2,S ; (5)
//030D: 7E F3 12               JMP   Moveto_d ; (4)

  TryLabel("A6 62 7E F3 12", "_Moveto_d");
    
//0310: 1F 10         L0310    TFR   X,D ; (6)
//0312: 7E F3 12               JMP   Moveto_d ; (4)

  TryLabel("1F 10 7E F3 12", "_Moveto_dd");
    
//0315: 1F 98         L0315    TFR   B,A ; (6)
//0317: 7E F2 AB               JMP   Intensity_a ; (4)

  TryLabel("1F 98 7E F2 AB", "_Intensity_a");
    
//031A: 34 60         L031A    PSHS  U,Y ; (9)
//031C: A6 66                  LDA   6,S ; (5)
//031E: 10 AE 67               LDY   7,S ; (7)
//0321: EE 69                  LDU   9,S ; (6)
//0323: BD F8 E5               JSR   Obj_Will_Hit_u ; (8)
//0326: C6 00                  LDB   #$00 ; (2)
//0328: C9 00                  ADCB  #$00 ; (2)
//032A: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 A6 66 10 AE 67 EE 69 BD F8 E5 C6 00 C9 00 35 E0", "_Obj_Will_Hit_u");
    
//032C: 34 60         L032C    PSHS  U,Y ; (9)
//032E: A6 66                  LDA   6,S ; (5)
//0330: 10 AE 67               LDY   7,S ; (7)
//0333: EE 69                  LDU   9,S ; (6)
//0335: BD F8 F3               JSR   Obj_Will_Hit ; (8)
//0338: C6 00                  LDB   #$00 ; (2)
//033A: C9 00                  ADCB  #$00 ; (2)
//033C: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 A6 66 10 AE 67 EE 69 BD F8 F3 C6 00 C9 00 35 E0", "_Obj_Will_Hit");
    
//033E: 34 20         L033E    PSHS  Y ; (7)
//0340: A6 64                  LDA   4,S ; (5)
//0342: 10 AE 65               LDY   5,S ; (7)
//0345: BD F8 FF               JSR   Obj_Hit ; (8)
//0348: C6 00                  LDB   #$00 ; (2)
//034A: C9 00                  ADCB  #$00 ; (2)
//034C: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 A6 64 10 AE 65 BD F8 FF C6 00 C9 00 35 A0", "_Obj_Hit");
    
//034E: A6 62         L034E    LDA   2,S ; (5)
//0350: BD F5 FF               JSR   Rise_Run_X ; (8)
//0353: 1F 01                  TFR   D,X ; (6)
//0355: 39                     RTS   ; (5)

  TryLabel("A6 62 BD F5 FF 1F 01 39", "_Rise_Run_X");
    
//0356: A6 62         L0356    LDA   2,S ; (5)
//0358: BD F6 01               JSR   Rise_Run_Y ; (8)
//035B: 1F 01                  TFR   D,X ; (6)
//035D: 39                     RTS   ; (5)

  TryLabel("A6 62 BD F6 01 1F 01 39", "_Rise_Run_Y");
    
//035E: 1F 98         L035E    TFR   B,A ; (6)
//0360: BD F6 03               JSR   Rise_Run_Len ; (8)
//0363: 1F 01                  TFR   D,X ; (6)
//0365: 39                     RTS   ; (5)

  TryLabel("1F 98 BD F6 03 1F 01 39", "_Rise_Run_Len");
    
//0366: 34 40         L0366    PSHS  U ; (7)
//0368: A6 64                  LDA   4,S ; (5)
//036A: EE 65                  LDU   5,S ; (6)
//036C: BD F6 10               JSR   Rot_VL_ab ; (8)
//036F: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 A6 64 EE 65 BD F6 10 35 C0", "_Rot_VL_ab");
    
//0371: 34 40         L0371    PSHS  U ; (7)
//0373: EE 64                  LDU   4,S ; (6)
//0375: BD F6 13               JSR   Rot_VL_Diff ; (8)
//0378: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F6 13 35 C0", "_Rot_VL_Diff");
    
//037A: 34 40         L037A    PSHS  U ; (7)
//037C: EE 64                  LDU   4,S ; (6)
//037E: BD F6 16               JSR   Rot_VL ; (8)
//0381: 35 C0                  PULS  PC,U ; (9)

  TryLabel("34 40 EE 64 BD F6 16 35 C0", "_Rot_VL");
    
//0383: 34 20         L0383    PSHS  Y ; (7)
//0385: 1F 12                  TFR   X,Y ; (6)
//0387: BD EA 5D               JSR   Dot_y ; (8)
//038A: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 1F 12 BD EA 5D 35 A0", "_Dot_y");
    
//038C: 34 20         L038C    PSHS  Y ; (7)
//038E: 1F 12                  TFR   X,Y ; (6)
//0390: BD EA 6D               JSR   Dot_py ; (8)
//0393: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 1F 12 BD EA 6D 35 A0", "_Dot_py");
    
//0395: 34 20         L0395    PSHS  Y ; (7)
//0397: 10 AE 64               LDY   4,S ; (7)
//039A: BD EA 7F               JSR   Draw_Pack ; (8)
//039D: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 10 AE 64 BD EA 7F 35 A0", "_Draw_Pack");
    
//039F: 34 20         L039F    PSHS  Y ; (7)
//03A1: 10 AE 64               LDY   4,S ; (7)
//03A4: BD EA 8D               JSR   Draw_Pack_py ; (8)
//03A7: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 10 AE 64 BD EA 8D 35 A0", "_Draw_Pack_py");
    
//03A9: 34 60         L03A9    PSHS  U,Y ; (9)
//03AB: 1F 12                  TFR   X,Y ; (6)
//03AD: EE 66                  LDU   6,S ; (6)
//03AF: BD EA A8               JSR   Print_Msg ; (8)
//03B2: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 1F 12 EE 66 BD EA A8 35 E0", "_Print_Msg");
    
//03B4: 34 20         L03B4    PSHS  Y ; (7)
//03B6: A6 64                  LDA   4,S ; (5)
//03B8: BD E7 B5               JSR   Displ8_xy ; (8)
//03BB: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 A6 64 BD E7 B5 35 A0", "_Displ8_xy");
    
//03BD: 34 20         L03BD    PSHS  Y ; (7)
//03BF: A6 64                  LDA   4,S ; (5)
//03C1: BD E7 D2               JSR   Displ16_xy ; (8)
//03C4: 35 A0                  PULS  PC,Y ; (9)

  TryLabel("34 20 A6 64 BD E7 D2 35 A0", "_Displ16_xy");
    
//03C6: BD EA 5D      L03C6    JSR   Dot_y ; (8)
//03C9: 1F 01                  TFR   D,X ; (6)
//03CB: 39                     RTS   ; (5)

  TryLabel("BD EA 5D 1F 01 39", "_Ranpos");
    
//03CC: 34 60         L03CC    PSHS  U,Y ; (9)
//03CE: BD EA CF               JSR   Draw_Scores ; (8)
//03D1: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 BD EA CF 35 E0", "_Draw_Scores");
    
//03D3: 34 60         L03D3    PSHS  U,Y ; (9)
//03D5: BD EA B4               JSR   Draw_Score ; (8)
//03D8: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 BD EA B4 35 E0", "_Draw_Score");
    
//03DA: 34 60         L03DA    PSHS  U,Y ; (9)
//03DC: BD EA F0               JSR   Wait_Bound ; (8)
//03DF: 35 E0                  PULS  PC,U,Y ; (11)

  TryLabel("34 60 BD EA F0 35 E0", "_Wait_Bound");

}

