/* 6809dasm.c - a 6809 opcode disassembler */
/* Version 1.4 1-MAR-95 */
/* Copyright © 1995 Sean Riddle */

/* thanks to Franklin Bowen for bug fixes, ideas */

/* Freely distributable on any medium given all copyrights are retained */
/* by the author and no charge greater than $7.00 is made for obtaining */
/* this software */

/* Please send all bug reports, update ideas and data files to: */
/* sriddle@ionet.net */

/* latest version at: */
/* <a href="http://www.ionet.net/~sriddle">Please don't hurl on my URL!</a> */

/* the data files must be kept compatible across systems! */

/* usage: 6809disasm <file> [<data file>] */
/* output to stdout, so use redirection */

/* The optional data file contains 7 types of lines: */
/* o Remarks - these are lines beginning with a semi-colon (;) */
/*   they are completely ignored. */
/* o 1 ORG line - gives the origin of the code; this is the starting */
/*   address to be used for the disassembly. */
/* o COMMENT lines - used to add comments to the end of lines of the */
/*   disassembly. */
/* o COMMENTLINE lines - provide full-line comments to be included before */
/*   a given address in the disassembly. */
/* o DATA lines - mark sections as data.  These sections will not be */
/*   disassembled, but dumped as hex data instead. */
/* o ASCII lines - mark sections as text.  These sections will not be */
/*   disassembled, but printed as text instead. */
/* o WTEXT lines - interprets section as text encoded as in Joust, */
/*   Bubbles, Sinistar (0x0=0,...,0xa=space,0xb=A,...,0x24=Z,...,0x32=:*/
/* See sample data files (*.dat) for examples. */

/* current limitations: */
/* o number of LABEL, DATA/ASCII, COMMENT and COMMENTLINE lines determined */
/*   at compile-time - see MAXLABEL, MAXDATA, MAXCOMMENT and MAXCOMMLINE */
/* o all DATA/ASCII lines in data file must be sorted in ascending */
/*   address order */
/* o ditto for COMMENT and COMMENTLINE lines */
/* o if a DATA/ASCII area is preceded by what 6809dasm thinks is code */
/*   that continues into the DATA/ASCII area, the data will not be marked */
/*   as such, and an error will be printed.  If this is the case, mark the */
/*   line before the data as data also. */

/* to do: */
/* o sort comment, ascii, data lines */
/* o look at JMP and JSR addresses- set to code unless overridden */
/*   in data file */
/* o perhaps a 'scan' that creates a first-guess .dat file? */
/*   generate dummy labels, mark code, find ASCII, etc. */

/* compiled on Amiga SAS/C 6.51 and Sun 10 Unix cc */

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

/* a few of my datatypes (from Amiga) */
#define TRUE (1==1)
#define FALSE (!TRUE)
typedef short BOOL;                                    /* boolean quantity */
typedef unsigned char UBYTE;                    /* unsigned 8-bit quantity */

/* array sizes for labels, DATA/ASCII definitions and COMMENT/COMMENTLINE */
#define MAXLABEL 999
#define MAXDATA 999                           /* both DATA and ASCII lines */
#define MAXCOMMENT 999
#define MAXCOMMLINE 999

/* output listing spacing */
#define TABOPNAME 19
#define TABOPERAND 25
#define TABCOMM 40

#define OPNAMETAB (TABOPNAME-1)
#define OPERANDTAB (TABOPERAND-1)
#define COMMTAB (TABCOMM-1)

typedef struct {                                       /* opcode structure */
   UBYTE opcode;                                     /* 8-bit opcode value */
   UBYTE numoperands;
   char name[6];                                            /* opcode name */
   UBYTE mode;                                          /* addressing mode */
   UBYTE numcycles;                         /* number of cycles - not used */
} opcodeinfo;
   
/* 6809 ADDRESSING MODES */
#define INH 0        
#define DIR 1
#define IND 2
#define REL 3
#define EXT 4
#define IMM 5
#define LREL 6
#define PG2 7                                    /* PAGE SWITCHES - Page 2 */
#define PG3 8                                                    /* Page 3 */
   
/* number of opcodes in each page */
#define NUMPG1OPS 223
#define NUMPG2OPS 38
#define NUMPG3OPS 9

int numops[3]={
   NUMPG1OPS,NUMPG2OPS,NUMPG3OPS,
};

char modenames[9][14]={
   "inherent",
   "direct",
   "indexed",
   "relative",
   "extended",
   "immediate",
   "long relative",
   "page 2",
   "page 3",
};

opcodeinfo pg1opcodes[NUMPG1OPS]={                           /* page 1 ops */
   0,1,"NEG",DIR,6,
   3,1,"COM",DIR,6,
   4,1,"LSR",DIR,6,
   6,1,"ROR",DIR,6,
   7,1,"ASR",DIR,6,
   8,1,"ASL",DIR,6,
   9,1,"ROL",DIR,6,
   10,1,"DEC",DIR,6,   
   12,1,"INC",DIR,6,
   13,1,"TST",DIR,6,
   14,1,"JMP",DIR,3,
   15,1,"CLR",DIR,6,

   16,1,"page2",PG2,0,
   17,1,"page3",PG3,0,
   18,0,"NOP",INH,2,
   19,0,"SYNC",INH,4,
   22,2,"LBRA",LREL,5,
   23,2,"LBSR",LREL,9,
   25,0,"DAA",INH,2,
   26,1,"ORCC",IMM,3,
   28,1,"ANDCC",IMM,3,
   29,0,"SEX",INH,2,
   30,1,"EXG",IMM,8,
   31,1,"TFR",IMM,6,
   
   32,1,"BRA",REL,3,
   33,1,"BRN",REL,3,
   34,1,"BHI",REL,3,
   35,1,"BLS",REL,3,
   36,1,"BCC",REL,3,
   37,1,"BCS",REL,3,
   38,1,"BNE",REL,3,
   39,1,"BEQ",REL,3,
   40,1,"BVC",REL,3,
   41,1,"BVS",REL,3,
   42,1,"BPL",REL,3,
   43,1,"BMI",REL,3,
   44,1,"BGE",REL,3,
   45,1,"BLT",REL,3,
   46,1,"BGT",REL,3,
   47,1,"BLE",REL,3,
   
   48,1,"LEAX",IND,2,
   49,1,"LEAY",IND,2,
   50,1,"LEAS",IND,2,
   51,1,"LEAU",IND,2,
   52,1,"PSHS",INH,5,
   53,1,"PULS",INH,5,
   54,1,"PSHU",INH,5,
   55,1,"PULU",INH,5,
   57,0,"RTS",INH,5,
   58,0,"ABX",INH,3,
   59,0,"RTI",INH,6,
   60,1,"CWAI",IMM,20,
   61,0,"MUL",INH,11,
   63,0,"SWI",INH,19,
   
   64,0,"NEGA",INH,2,
   67,0,"COMA",INH,2,
   68,0,"LSRA",INH,2,
   70,0,"RORA",INH,2,
   71,0,"ASRA",INH,2,
   72,0,"ASLA",INH,2,
   73,0,"ROLA",INH,2,
   74,0,"DECA",INH,2,
   76,0,"INCA",INH,2,
   77,0,"TSTA",INH,2,
   79,0,"CLRA",INH,2,
   
   80,0,"NEGB",INH,2,
   83,0,"COMB",INH,2,
   84,0,"LSRB",INH,2,
   86,0,"RORB",INH,2,
   87,0,"ASRB",INH,2,
   88,0,"ASLB",INH,2,
   89,0,"ROLB",INH,2,
   90,0,"DECB",INH,2,
   92,0,"INCB",INH,2,
   93,0,"TSTB",INH,2,
   95,0,"CLRB",INH,2,
   
   96,1,"NEG",IND,6,
   99,1,"COM",IND,6,
   100,1,"LSR",IND,6,
   102,1,"ROR",IND,6,
   103,1,"ASR",IND,6,
   104,1,"ASL",IND,6,
   105,1,"ROL",IND,6,
   106,1,"DEC",IND,6,
   108,1,"INC",IND,6,
   109,1,"TST",IND,6,
   110,1,"JMP",IND,3,
   111,1,"CLR",IND,6,
   
   112,2,"NEG",EXT,7,
   115,2,"COM",EXT,7,
   116,2,"LSR",EXT,7,
   118,2,"ROR",EXT,7,
   119,2,"ASR",EXT,7,
   120,2,"ASL",EXT,7,
   121,2,"ROL",EXT,7,
   122,2,"DEC",EXT,7,
   124,2,"INC",EXT,7,
   125,2,"TST",EXT,7,
   126,2,"JMP",EXT,4,
   127,2,"CLR",EXT,7,
   
   128,1,"SUBA",IMM,2,
   129,1,"CMPA",IMM,2,
   130,1,"SBCA",IMM,2,
   131,2,"SUBD",IMM,4,
   132,1,"ANDA",IMM,2,
   133,1,"BITA",IMM,2,
   134,1,"LDA",IMM,2,
   136,1,"EORA",IMM,2,
   137,1,"ADCA",IMM,2,
   138,1,"ORA",IMM,2,
   139,1,"ADDA",IMM,2,
   140,2,"CMPX",IMM,4,
   141,1,"BSR",REL,7,
   142,2,"LDX",IMM,3,
   
   144,1,"SUBA",DIR,4,
   145,1,"CMPA",DIR,4,
   146,1,"SBCA",DIR,4,
   147,1,"SUBD",DIR,6,
   148,1,"ANDA",DIR,4,
   149,1,"BITA",DIR,4,
   150,1,"LDA",DIR,4,
   151,1,"STA",DIR,4,
   152,1,"EORA",DIR,4,
   153,1,"ADCA",DIR,4,
   154,1,"ORA",DIR,4,
   155,1,"ADDA",DIR,4,
   156,1,"CPX",DIR,6,
   157,1,"JSR",DIR,7,
   158,1,"LDX",DIR,5,
   159,1,"STX",DIR,5,
   
   160,1,"SUBA",IND,4,
   161,1,"CMPA",IND,4,
   162,1,"SBCA",IND,4,
   163,1,"SUBD",IND,6,
   164,1,"ANDA",IND,4,
   165,1,"BITA",IND,4,
   166,1,"LDA",IND,4,
   167,1,"STA",IND,4,
   168,1,"EORA",IND,4,
   169,1,"ADCA",IND,4,
   170,1,"ORA",IND,4,
   171,1,"ADDA",IND,4,
   172,1,"CPX",IND,6,
   173,1,"JSR",IND,7,
   174,1,"LDX",IND,5,
   175,1,"STX",IND,5,
   
   176,2,"SUBA",EXT,5,
   177,2,"CMPA",EXT,5,
   178,2,"SBCA",EXT,5,
   179,2,"SUBD",EXT,7,
   180,2,"ANDA",EXT,5,
   181,2,"BITA",EXT,5,
   182,2,"LDA",EXT,5,
   183,2,"STA",EXT,5,
   184,2,"EORA",EXT,5,
   185,2,"ADCA",EXT,5,
   186,2,"ORA",EXT,5,
   187,2,"ADDA",EXT,5,
   188,2,"CPX",EXT,7,
   189,2,"JSR",EXT,8,
   190,2,"LDX",EXT,6,
   191,2,"STX",EXT,6,
   
   192,1,"SUBB",IMM,2,
   193,1,"CMPB",IMM,2,
   194,1,"SBCB",IMM,2,
   195,2,"ADDD",IMM,4,
   196,1,"ANDB",IMM,2,
   197,1,"BITB",IMM,2,
   198,1,"LDB",IMM,2,
   200,1,"EORB",IMM,2,
   201,1,"ADCB",IMM,2,
   202,1,"ORB",IMM,2,
   203,1,"ADDB",IMM,2,
   204,2,"LDD",IMM,3,
   206,2,"LDU",IMM,3,
   
   208,1,"SUBB",DIR,4,
   209,1,"CMPB",DIR,4,
   210,1,"SBCB",DIR,4,
   211,1,"ADDD",DIR,6,
   212,1,"ANDB",DIR,4,
   213,1,"BITB",DIR,4,
   214,1,"LDB",DIR,4,
   215,1,"STB",DIR,4,
   216,1,"EORB",DIR,4,
   217,1,"ADCB",DIR,4,
   218,1,"ORB",DIR,4,
   219,1,"ADDB",DIR,4,
   220,1,"LDD",DIR,5,
   221,1,"STD",DIR,5,
   222,1,"LDU",DIR,5,
   223,1,"STU",DIR,5,
   
   224,1,"SUBB",IND,4,
   225,1,"CMPB",IND,4,
   226,1,"SBCB",IND,4,
   227,1,"ADDD",IND,6,
   228,1,"ANDB",IND,4,
   229,1,"BITB",IND,4,
   230,1,"LDB",IND,4,
   231,1,"STB",IND,4,
   232,1,"EORB",IND,4,
   233,1,"ADCB",IND,4,
   234,1,"ORB",IND,4,
   235,1,"ADDB",IND,4,
   236,1,"LDD",IND,5,
   237,1,"STD",IND,5,
   238,1,"LDU",IND,5,
   239,1,"STU",IND,5,
   
   240,2,"SUBB",EXT,5,
   241,2,"CMPB",EXT,5,
   242,2,"SBCB",EXT,5,
   243,2,"ADDD",EXT,7,
   244,2,"ANDB",EXT,5,
   245,2,"BITB",EXT,5,
   246,2,"LDB",EXT,5,
   247,2,"STB",EXT,5,
   248,2,"EORB",EXT,5,
   249,2,"ADCB",EXT,5,
   250,2,"ORB",EXT,5,
   251,2,"ADDB",EXT,5,
   252,2,"LDD",EXT,6,
   253,2,"STD",EXT,6,
   254,2,"LDU",EXT,6,
   255,2,"STU",EXT,6,
};

opcodeinfo pg2opcodes[NUMPG2OPS]={                       /* page 2 ops 10xx*/
   33,3,"LBRN",LREL,5,
   34,3,"LBHI",LREL,5,
   35,3,"LBLS",LREL,5,
   36,3,"LBCC",LREL,5,
   37,3,"LBCS",LREL,5,
   38,3,"LBNE",LREL,5,
   39,3,"LBEQ",LREL,5,
   40,3,"LBVC",LREL,5,
   41,3,"LBVS",LREL,5,
   42,3,"LBPL",LREL,5,
   43,3,"LBMI",LREL,5,
   44,3,"LBGE",LREL,5,
   45,3,"LBLT",LREL,5,
   46,3,"LBGT",LREL,5,
   47,3,"LBLE",LREL,5,
   63,2,"SWI2",INH,20,
   131,3,"CMPD",IMM,5,
   140,3,"CMPY",IMM,5,
   142,3,"LDY",IMM,4,
   147,2,"CMPD",DIR,7,
   156,2,"CMPY",DIR,7,
   158,2,"LDY",DIR,6,
   159,2,"STY",DIR,6,
   163,2,"CMPD",IND,7,
   172,2,"CMPY",IND,7,
   174,2,"LDY",IND,6,
   175,2,"STY",IND,6,
   179,3,"CMPD",EXT,8,
   188,3,"CMPY",EXT,8,
   190,3,"LDY",EXT,7,
   191,3,"STY",EXT,7,
   206,3,"LDS",IMM,4,
   222,2,"LDS",DIR,6,
   223,2,"STS",DIR,6,
   238,2,"LDS",IND,6,
   239,2,"STS",IND,6,
   254,3,"LDS",EXT,7,
   255,3,"STS",EXT,7,
};

opcodeinfo pg3opcodes[NUMPG3OPS]={                      /* page 3 ops 11xx */
   63,1,"SWI3",INH,20,
   131,3,"CMPU",IMM,5,
   140,3,"CMPS",IMM,5,
   147,2,"CMPU",DIR,7,
   156,2,"CMPS",DIR,7,
   163,2,"CMPU",IND,7,
   172,2,"CMPS",IND,7,
   179,3,"CMPU",EXT,8,
   188,3,"CMPS",EXT,8,
};
   
opcodeinfo *pgpointers[3]={
   pg1opcodes,pg2opcodes,pg3opcodes,
};

int count;                             /* current program counter for disasm */

/* getbyte() - get a byte from a file, and increment the byte counter */
int getbyte(FILE *fp) {
   int c;

   count++;
   c=getc(fp);
   return(c);
}

#ifdef NOCONST                                /* TFB had to undefine const */
   #define const
#endif
const char *regs[5]={"X","Y","U","S","PC"};
const char *teregs[16]={"D","X","Y","U","S","PC","inv","inv","A","B","CC",
      "DP","inv","inv","inv","inv"};
      
BOOL PC=FALSE;                      /* to see if a PUL instr is pulling PC */

#define LABELSIZE 40
/* label structure */
struct lastruct {
   unsigned short lab;                                    /* label address */
   char label[LABELSIZE];                                    /* label text */
} *labarray=NULL;
int numlab=0;                                  /* number of labels defined */

#ifndef AMIGA
/* hmmm, these aren't ANSI */
/* stricmp() - compare two strings, case insensitive */
int stricmp(const char *s1, const char *s2) {
   for(;toupper(*s1)==toupper(*s2);++s1,++s2)
      if(*s1=='\0')
         return(0);
   return((toupper(*(unsigned char *)s1)<toupper(*(unsigned char *)s2))?-1:1);
}

/* strnicmp() - compare two strings, case insensitive, length limited */
int strnicmp(const char *s1, const char *s2, size_t n) {
   for(;0<n;++s1,++s2,--n)
      if(toupper(*s1)!=toupper(*s2))
         return((toupper(*(unsigned char *)s1)<
               toupper(*(unsigned char *)s2))?-1:1);
      else if(*s1=='\0')
         return(0);
   return(0);
}
#endif

char labtemp[30];                   /* global return for checklabs() - tsk */

/* checklabs() - check the defined labels from data file */
/* substitute label for address if found */
char *checklabs(int address,BOOL lab2,BOOL printdollar) {
   int i;
   
   address&=0xffff;
   labtemp[0]='\0';
   for(i=0;i<numlab;i++)
      if(address==labarray[i].lab) {
         sprintf(labtemp,"%s",labarray[i].label);
         if(lab2)
            strcat(labtemp,":\n");
         i=numlab;
      }
   if(!strlen(labtemp)&&!lab2) {
      if(printdollar)
         sprintf(labtemp,"$%04hX",address);
      else
         sprintf(labtemp,"%04hX",address);
   }
   return(labtemp);
}

/* printoperands() - print operands for the given opcode */
void printoperands(int opcode,UBYTE numoperands,UBYTE *operandarray,
      UBYTE mode,FILE *fp,char *opname,char *str) {
   int i,rel,pb,offset=0,reg,pb2;
   BOOL comma;
   char out2[80];
   int sp;
   BOOL printdollar;                  /* print a leading $? before address */

   printdollar=FALSE;
   PC=FALSE;
   if((opcode!=0x1f)&&(opcode!=0x1e)) {
      switch(mode) {                              /* print before operands */
         case IMM:
            strcat(str,"#");
         case DIR:
         case EXT:
            printdollar=TRUE;
            break;
         default:
            break;
      }
   }
   switch(mode) {
      case REL:                                          /* 8-bit relative */
         rel=operandarray[0];
         sprintf(out2,checklabs((short)(count+((rel<128) ? rel : rel-256)),
               FALSE,TRUE));
         strcat(str,out2);
         break;
      case LREL:                                   /* 16-bit long relative */
         rel=(operandarray[0]<<8)+operandarray[1];
         sprintf(out2,checklabs(count+((rel<32768) ? rel : rel-65536),
               FALSE,TRUE));
         strcat(str,out2);
         break;
      case IND:                                  /* indirect- many flavors */
         pb=operandarray[0];
         reg=(pb>>5)&0x3;
         pb2=pb&0x8f;
         if((pb2==0x88)||(pb2==0x8c)) {                    /* 8-bit offset */
            offset=getbyte(fp);
            sprintf(out2,"%02hX ",offset);
            strcat(str,out2);
            if(offset>127)                            /* convert to signed */
               offset=offset-256;
            if(pb==0x8c)
               reg=4;
            for(sp=strlen(str);sp<OPNAMETAB;sp++)
               strcat(str," ");
            sprintf(out2,"%s",opname);
            strcat(str,out2);
            for(sp=strlen(str);sp<OPERANDTAB;sp++)
               strcat(str," ");
            if((pb&0x90)==0x90)
               strcat(str,"[");
            if(pb==0x8c)
               sprintf(out2,"%s,%s",checklabs(offset,FALSE,TRUE),regs[reg]);
            else if(offset>=0)
               sprintf(out2,"$%02X,%s",offset,regs[reg]);
            else
               sprintf(out2,"-$%02X,%s",-offset,regs[reg]);
            strcat(str,out2);
            if(pb==0x8c) {
               sprintf(out2," ; ($%04X)",offset+count);
               strcat(str,out2);
            }
         } else if((pb2==0x89)||(pb2==0x8d)||(pb2==0x8f)) { /* 16-bit */
            offset=(getbyte(fp)<<8);
            sprintf(out2,"%02X ",offset>>8);
            strcat(str,out2);
            offset+=getbyte(fp);
            sprintf(out2,"%02X ",offset&0xff);
            strcat(str,out2);
            if((pb!=0x8f)&&(offset>32767))
               offset=offset-65536;
            offset&=0xffff;
            if(pb==0x8d)
               reg=4;
            for(sp=strlen(str);sp<OPNAMETAB;sp++)
               strcat(str," ");
            sprintf(out2,"%s",opname);
            strcat(str,out2);
            for(sp=strlen(str);sp<OPERANDTAB;sp++)
               strcat(str," ");
            if((pb&0x90)==0x90)
               strcat(str,"[");
            if(pb==0x8d)
               sprintf(out2,"%s,%s",checklabs(offset,FALSE,TRUE),regs[reg]);
            else if(offset>=0)
               sprintf(out2,"$%04X,%s",offset,regs[reg]);
            else
               sprintf(out2,"-$%04X,%s",offset,regs[reg]);
            strcat(str,out2);
            if(pb==0x8d) {
               sprintf(out2," ; ($%04X)",offset+count);
               strcat(str,out2);
            }
         } else if(pb&0x80) {
            for(sp=strlen(str);sp<OPNAMETAB;sp++)
               strcat(str," ");
            sprintf(out2,"%s",opname);
            strcat(str,out2);
            for(sp=strlen(str);sp<OPERANDTAB;sp++)
               strcat(str," ");
            if((pb&0x90)==0x90)
               strcat(str,"[");
            if((pb&0x8f)==0x80) {
               sprintf(out2,",%s+",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x81) {
               sprintf(out2,",%s++",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x82) {
               sprintf(out2,",-%s",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x83) {
               sprintf(out2,",--%s",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x84) {
               sprintf(out2,",%s",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x85) {
               sprintf(out2,"B,%s",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x86) {
               sprintf(out2,"A,%s",regs[reg]);
               strcat(str,out2);
            } else if((pb&0x8f)==0x8b) {
               sprintf(out2,"D,%s",regs[reg]);
               strcat(str,out2);
            }
         } else {                                          /* 5-bit offset */
            offset=pb&0x1f;
            if(offset>15)
               offset=offset-32;
            for(sp=strlen(str);sp<OPNAMETAB;sp++)
               strcat(str," ");
            sprintf(out2,"%s",opname);
            strcat(str,out2);
            for(sp=strlen(str);sp<OPERANDTAB;sp++)
               strcat(str," ");
            sprintf(out2,"%s,%s",checklabs(offset,FALSE,TRUE),regs[reg]);
            strcat(str,out2);
         }
         if((pb&0x90)==0x90)
            strcat(str,"]");
         break;
      default:
         if((opcode==0x1f)||(opcode==0x1e)) {                   /* TFR/EXG */
            sprintf(out2,"%s,%s",teregs[(operandarray[0]>>4)&0xf],
                  teregs[operandarray[0]&0xf]);
            strcat(str,out2);
         } else if((opcode==0x34)||(opcode==0x36)) {              /* PUSH */
            comma=FALSE;
            if(operandarray[0]&0x80) {
               strcat(str,"PC");
               comma=TRUE;
               PC=TRUE;
            }
            if(operandarray[0]&0x40) {
               if(comma)
                  strcat(str,",");
               if((opcode==0x34)||(opcode==0x35))
                  strcat(str,"U");
               else
                  strcat(str,"S");
               comma=TRUE;
            }
            if(operandarray[0]&0x20) {
               if(comma)
                  strcat(str,",");
               strcat(str,"Y");
               comma=TRUE;
            }
            if(operandarray[0]&0x10) {
               if(comma)
                  strcat(str,",");
               strcat(str,"X");
               comma=TRUE;
            }
            if(operandarray[0]&0x8) {
               if(comma)
                  strcat(str,",");
               strcat(str,"DP");
               comma=TRUE;
            }
            if(operandarray[0]&0x4) {
               if(comma)
                  strcat(str,",");
               strcat(str,"B");
               comma=TRUE;
            }
            if(operandarray[0]&0x2) {
               if(comma)
                  strcat(str,",");
               strcat(str,"A");
               comma=TRUE;
            }
            if(operandarray[0]&0x1) {
               if(comma)
                  strcat(str,",");
               strcat(str,"CC");
            }
         } else if((opcode==0x35)||(opcode==0x37)) {              /* PULL */
            comma=FALSE;
            if(operandarray[0]&0x1) {
               strcat(str,"CC");
               comma=TRUE;
            }
            if(operandarray[0]&0x2) {
               if(comma)
                  strcat(str,",");
               strcat(str,"A");
               comma=TRUE;
            }
            if(operandarray[0]&0x4) {
               if(comma)
                  strcat(str,",");
               strcat(str,"B");
               comma=TRUE;
            }
            if(operandarray[0]&0x8) {
               if(comma)
                  strcat(str,",");
               strcat(str,"DP");
               comma=TRUE;
            }
            if(operandarray[0]&0x10) {
               if(comma)
                  strcat(str,",");
               strcat(str,"X");
               comma=TRUE;
            }
            if(operandarray[0]&0x20) {
               if(comma)
                  strcat(str,",");
               strcat(str,"Y");
               comma=TRUE;
            }
            if(operandarray[0]&0x40) {
               if(comma)
                  strcat(str,",");
               if((opcode==0x34)||(opcode==0x35))
                  strcat(str,"U");
               else
                  strcat(str,"S");
               comma=TRUE;
            }
            if(operandarray[0]&0x80) {
               if(comma)
                  strcat(str,",");
               strcat(str,"PC");
					strcat(str," ;(PUL? PC=RTS)");
               PC=TRUE;
            }
         } else {
            if(numoperands==2) {
               strcat(str,checklabs((operandarray[0]<<8)+operandarray[1],
                     FALSE,TRUE));
            } else {
               if(printdollar)
                  strcat(str,"$");
               for(i=0;i<numoperands;i++) {
                  sprintf(out2,"%02X",operandarray[i]);
                  strcat(str,out2);
               }
            }
         }
         break;
   }
}

#define DATA 0                                  /* type of data to display */
#define ASCII 1
#define WTEXT 2

/* DATA/ASCII structure definition */
struct dastruct {
   unsigned short start;                /* beginning address of DATA/ASCII */
   unsigned short end;                                   /* ending address */
   unsigned short per_line;                   /* values to print on a line */
   short type;                                           /* DATA or ASCII ? */
} *dataarray=NULL;

#define COMMENTSIZE 80
/* COMMENT/COMMENTLINE structure definition */
struct castruct {
   unsigned short comline;                         /* comment line address */
   char comment[COMMENTSIZE];                              /* comment text */
} *commarray=NULL,*commlinearray=NULL;

char out[512],out2[80];                             /* temp string buffers */

void readdatafile(char *filename,
      int *org,int *numlab,int *numdata,int *numcomm,int *numcommline) {
   char line2[256];
   char *line;
   FILE *fp;
   int dataline,data,data2,per_line,i,k;
   
   if(fp=fopen(filename,"r")) {                          /* read data file */
      while(fgets(line2,255,fp)) {
         while(strlen(line2)&&!isprint(line2[strlen(line2)-1])) /* strip cr */
            line2[strlen(line2)-1]='\0';
         for(i=0;i<strlen(line2);i++)
            if((line2[i]==';')||isalpha(line2[i])) {
               line= &(line2[i]);
               i=strlen(line2);
            }
         if(!strnicmp(line,"ORG ",4)) {
            if(*org==-1)
               sscanf(&line[4],"%X",org);
            else
               printf("More than one ORG line\n");
         } else if(!strnicmp(line,"LABEL ",6)) {
            if(*numlab<MAXLABEL) {
               sscanf(&line[6],"%X",&dataline);
               labarray[*numlab].lab=dataline;
               k=6;
               while(line[k]==' ')
                  k++;
               while(line[k]!=' ')
                  k++;
               while(line[k]==' ')
                  k++;
               strncpy(labarray[*numlab].label,&line[k],LABELSIZE);
               labarray[*numlab].label[LABELSIZE-1]=0;  /* just in case */
               for(i=0;i<*numlab;i++)
                  if(!strcmp(labarray[i].label,labarray[*numlab].label)) {
                     printf("duplicate label: %s\n",labarray[i].label);
                     break;
                  }
               if(i>=*numlab)   
                  (*numlab)++;
            } else
               printf("Too many labels\n");
         } else if(!strnicmp(line,"DATA ",5)) {
            if(*numdata<MAXDATA) {
               data2=0;
               sscanf(&line[5],"%X-%X",&data,&data2);
               k=5;
               while(line[k]==' ')
                  k++;
               while(line[k]!=' ')
                  k++;
               while(line[k]==' ')
                  k++;
               if (k < i)
                  sscanf(&(line[k]),"%d",&per_line);
               else
                  per_line = 0;
               if(data2<data)
                  data2=data;
               dataarray[*numdata].type=DATA;
               dataarray[*numdata].start=data;
               dataarray[*numdata].end=data2;
               dataarray[*numdata].per_line=per_line;
               if(((*numdata)&&(dataarray[*numdata-1].start<
                     dataarray[*numdata].start))||!*numdata)
                  (*numdata)++;
               else
                  printf("`%s'\nDATA out of order\n\n",line);
            } else
               printf("Too many DATA/ASCII lines\n");
         } else if(!strnicmp(line,"ASCII ",6)) {
            if(*numdata<MAXDATA) {
               data2=0;
               per_line=0;
               sscanf(&line[6],"%X-%X %d",&data,&data2,&per_line);
               if(data2<data)
                  data2=data;
               dataarray[*numdata].type=ASCII;
               dataarray[*numdata].start=data;
               dataarray[*numdata].end=data2;
               dataarray[*numdata].per_line=per_line;
               if(((*numdata)&&(dataarray[*numdata-1].start<
                     dataarray[*numdata].start))||!*numdata)
                  (*numdata)++;
               else if(*numdata)
                  printf("`%s'\nASCII out of order\n\n",line);
            } else
               printf("Too many DATA/ASCII lines\n");
         } else if(!strnicmp(line,"WTEXT ",6)) {
            if(*numdata<MAXDATA) {
               data2=0;
               per_line=0;
               sscanf(&line[6],"%X-%X %d",&data,&data2,&per_line);
               if(data2<data)
                  data2=data;
               dataarray[*numdata].type=WTEXT;
               dataarray[*numdata].start=data;
               dataarray[*numdata].end=data2;
               dataarray[*numdata].per_line=per_line;
               if(((*numdata)&&(dataarray[*numdata-1].start<
                     dataarray[*numdata].start))||!*numdata)
                  (*numdata)++;
               else if(*numdata)
                  printf("`%s'\nWTEXT out of order\n\n",line);
            } else
               printf("Too many DATA/ASCII lines\n");
         } else if(!strnicmp(line,"COMMENT ",8)) {
            if(*numcomm<MAXCOMMENT) {
               sscanf(&line[8],"%X",&dataline);
               commarray[*numcomm].comline=dataline;
                              
               k=8;
               while(line[k]==' ')
                  k++;
               while(line[k]!=' ')
                  k++;
               while(line[k]==' ')
                  k++;
               strncpy(commarray[*numcomm].comment,&line[k],COMMENTSIZE);
               commarray[*numcomm].comment[COMMENTSIZE-1]=0; /* in case */

               if(((*numcomm)&&(commarray[(*numcomm)-1].comline<=
                     commarray[*numcomm].comline))||!*numcomm)
                  (*numcomm)++;
               else
                  printf("`%s'\nCOMMENT out of order\n\n",line);
            } else
               printf("Too many COMMENT lines\n");
         } else if(!strnicmp(line,"COMMENTLINE ",12)) {
            if(*numcommline<MAXCOMMLINE) {
               sscanf(&line[12],"%X",&dataline);
               commlinearray[*numcommline].comline=dataline;
               k=12;
               while(line[k]==' ')
                  k++;
               while(line[k]!=' ')
                  k++;
               while(line[k]==' ')
                  k++;
               strncpy(commlinearray[*numcommline].comment,
                     &line[k],COMMENTSIZE);
               commlinearray[*numcommline].comment[COMMENTSIZE-1]=0;
               
               if(((*numcommline)&&(commlinearray[(*numcommline)-1].comline<=
                     commlinearray[*numcommline].comline))||!*numcommline)
                  (*numcommline)++;
               else
                  printf("`%s'\nCOMMENTLINE out of order\n\n",line);
            } else
               printf("Too many COMMENTLINE lines\n");
         } else if(line[0]==';')                    /* remark in data file */
            ;
         else if(strlen(line))
            printf("`%s'\nError in file `%s'\n\n",line,filename);
      }
      fclose(fp);
   } else
      fprintf(stderr,"Can't open file `%s'\n",filename);
}

void docomment(int line,int *curcomm,int numcomm,int numoperands) {
   int  sp;
   BOOL first_comment = TRUE;

   if(!numcomm)
      return;
   if(*curcomm<numcomm) {                 /* see if we've passed a comment */
      while(((line-1)>commarray[*curcomm].comline+numoperands)&&
            (*curcomm<numcomm)) {
         printf("Error: missed a comment at %X, line=%X\n",
               commarray[*curcomm].comline,line);
         (*curcomm)++;
      }

      while((((line-1)+numoperands)>=commarray[*curcomm].comline)||
            ((line==commarray[*curcomm].comline)&&(numoperands==1))||
            (((line-1)==commarray[*curcomm].comline)&&(numoperands==0))) {
         if(*curcomm>=numcomm)
				break;
			if(first_comment!=TRUE) {
				printf("%s\n",out);
				out[0]='\0';
			}
         for(sp=strlen(out);sp<COMMTAB;sp++)
            strcat(out," ");
         strcat(out,";");
         strcat(out,commarray[*curcomm].comment);
         (*curcomm)++;
			first_comment=FALSE;
      }
   }
}

void docommline(int line,int *curcommline,int numcommline) {
   BOOL didone=FALSE;
   
   if(!numcommline)
      return;
   if(*curcommline<numcommline) {     /* see if we've passed a comment line*/
      while(((line-1)>commlinearray[*curcommline].comline)&&
            (*curcommline<numcommline)) {
         printf("Error: missed a comment line at %X, line=%X\n",
               commlinearray[*curcommline].comline,line);
         (*curcommline)++;
      }

      while(((line-1)==commlinearray[*curcommline].comline)) {
         if(!didone)
            printf("\n");
         printf("*** %s\n",commlinearray[*curcommline].comment);
         (*curcommline)++;
         didone=TRUE;
      }

   }
}

BOOL newl=FALSE;
char wchars[15]={
   "<=-?!()',./&\":"
};

char wtext(int data) {
   if(data>127) {
      data-=128;
      newl=TRUE;
   } else
      newl=FALSE;
   if(data<10)
      data+=48;
   else if(data==10)
      data=32;
   else if(data<37)
      data+=54;
   else if(data<51)
      data=(int)wchars[data-37];
   else
      data=39;
   return((char)data);
}

BOOL diddata=FALSE;

int dumpdata(int opcode,int *curdata,int numdata,FILE *fp,
      int *curcomm,int numcomm,int *curcommline,int numcommline) {
   int pnum,tnum;
   int numoperands;
   int k;
   int numchars=0;
   
   numoperands=2+dataarray[*curdata].end-count;
   pnum=dataarray[*curdata].per_line;       /* print up to pnum bytes data */
   if(dataarray[*curdata].type==DATA) {
      sprintf(out2,"%02X ",(UBYTE)opcode);
      strcat(out,out2);
      if((pnum<1)||(pnum>24))
         pnum=16;                       /* no more than 24 bytes hex data */
   } else {
      if(dataarray[*curdata].type==ASCII)
         out2[0]=opcode;
      else
         out2[0]=wtext(opcode);
      out2[1]='\0';
      strcat(out,out2);
      if((pnum<1)||(pnum>70))
         pnum=32;                      /* no more than 70 bytes ASCII data */
   }
   newl=FALSE;
   tnum=(pnum>numoperands)?numoperands-2:pnum-1;
   for(k=0;k<numoperands-1;k++) {                        /* print the data */
      if((++numchars>=pnum)||newl) {
         if((tnum)||(pnum==1)) {
            docomment(count-tnum,curcomm,numcomm,tnum);
            docommline(count-tnum,curcommline,numcommline);
         }
         printf("%s\n",out);
         sprintf(out,"%04X: ",count);
         numchars=0;
         newl=FALSE;
      }
      if(dataarray[*curdata].type==DATA)
         sprintf(out2,"%02X ",getbyte(fp));                         /* hex */
      else {
         if(dataarray[*curdata].type==ASCII)
            out2[0]=getbyte(fp);                                  /* ASCII */
         else
            out2[0]=wtext(getbyte(fp));                            /* text */
         out2[1]='\0';
      }
      strcat(out,out2);
   }
   if(*curdata<numdata)
      (*curdata)++;
   diddata=TRUE;
   return(numoperands-2);
}

void freearrays(void) {
   if(labarray)
      free(labarray);
   if(dataarray)
      free(dataarray);
   if(commarray)
      free(commarray);
   if(commlinearray)
      free(commlinearray);
}

BOOL mallocarrays(void) {
   BOOL gotit=FALSE;
   
   if(labarray=(struct lastruct *)malloc(sizeof(struct lastruct)*MAXLABEL))
      if(dataarray=(struct dastruct *)
            malloc(sizeof(struct dastruct)*MAXDATA))
         if(commarray=(struct castruct *)
               malloc(sizeof(struct castruct)*MAXCOMMENT))
            if(commlinearray=(struct castruct *)
                  malloc(sizeof(struct castruct)*MAXCOMMLINE))
               gotit=TRUE;
   if(!gotit)
      freearrays();
   return(gotit);
}

void main(int argc,char *argv[]) {
   int i,j,k;
   int opcode,page;
   UBYTE operand[4];
   FILE *fp;
   int org=~0;
   opcodeinfo *op;
   int numoperands;
   int sp;
   int numcomm=0,numdata=0,numcommline=0;
   int curcomm=0,curdata=0,curcommline=0;

   if(argc>1) {
      if(!stricmp(argv[1],"list")) {              /* show all instructions */
         for(i=0;i<numops[0];i++)
            printf("opcode %02X, operands %d, name %6s, mode %s, cycles %d\n",
                  pg1opcodes[i].opcode,pg1opcodes[i].numoperands,
                  pg1opcodes[i].name,
                  modenames[pg1opcodes[i].mode],pg1opcodes[i].numcycles);
         for(i=0;i<numops[1];i++)
            printf("opcode 10 %02X, operands %d, name %6s, mode %s, cycles %d\n",
                  pg2opcodes[i].opcode,pg2opcodes[i].numoperands,
                  pg2opcodes[i].name,
                  modenames[pg2opcodes[i].mode],pg2opcodes[i].numcycles);
         for(i=0;i<numops[2];i++)
            printf("opcode 11 %02X, operands %d, name %6s, mode %s, cycles %d\n",
                  pg3opcodes[i].opcode,pg3opcodes[i].numoperands,
                  pg3opcodes[i].name,
                  modenames[pg3opcodes[i].mode],pg3opcodes[i].numcycles);
      } else {
         if(!mallocarrays()) {
            printf("Can't get memory for arrays\n");
            exit(20);
         }
         if(argc>2)
            readdatafile(argv[2],&org,&numlab,&numdata,&numcomm,&numcommline);
         if(org>-1)                              /* int PC to ORG val or 0 */
            count=org;
         else
            count=0;

         for(k=0;k<numlab;k++)                       /* print labels first */
            printf("%s EQU $%04X\n",labarray[k].label,labarray[k].lab);
         if(numlab)
            printf("\n");
         printf("ORG $%04X\n",count);
                     
         if(fp=fopen(argv[1],"rb")) {                  /* open binary file */
            while((opcode=getbyte(fp))!=EOF) {
               op=NULL;
               docommline(count,&curcommline,numcommline);

               printf(checklabs(count-1,TRUE,FALSE)); /* if add lab, print */
               sprintf(out,"%04X: ",count-1);                  /* print PC */
               
               if(numdata) {
                  while(((count-1)>dataarray[curdata].end)&&
                        (curdata<numdata)) {
                     printf("Error: missed a data line, start=%X, end=%X\n",
                           dataarray[curdata].start,dataarray[curdata].end);
                     curdata++;
                  }
               }
               if(numdata&&
                     ((count-1)>=dataarray[curdata].start)&&  /* data? */
                     ((count-1)<=dataarray[curdata].end)) {
                  numoperands=dumpdata(opcode,&curdata,numdata,fp,
                        &curcomm,numcomm,&curcommline,numcommline);
                  i=numops[0]+1;             /* skip decoding as an opcode */
               } else {                    /* not data - search for opcode */
                  sprintf(out2,"%02X ",(UBYTE)opcode);
                  strcat(out,out2);
                  for(i=0;(i<numops[0])&&(pg1opcodes[i].opcode!=opcode);i++)
                     ;
               }
               if(i<numops[0]) {                           /* opcode found */
                  if(pg1opcodes[i].mode>=PG2) {             /* page switch */
                     opcode=getbyte(fp);
                     sprintf(out2,"%02X ",(UBYTE)opcode);
                     strcat(out,out2);
                     page=pg1opcodes[i].mode-PG2+1;          /* get page # */
                     for(k=0;(k<numops[page])&&(opcode!=
                           pgpointers[page][k].opcode);k++)
                        ;
                     if(k!=numops[page]) {                 /* opcode found */
                        op=(opcodeinfo *) &(pgpointers[page][k]);
                        numoperands=pgpointers[page][k].numoperands;
                        for(j=0;j<numoperands-1;j++) {
                           sprintf(out2,"%02X ",(operand[j]=getbyte(fp)));
                           strcat(out,out2);
                        }
                           
                        if(pgpointers[page][k].mode!=IND) {
                           for(sp=strlen(out);sp<OPNAMETAB;sp++)
                              strcat(out," ");
                           sprintf(out2,"%s",pgpointers[page][k].name);
                           strcat(out,out2);
                           for(sp=strlen(out);sp<OPERANDTAB;sp++)
                              strcat(out," ");
                        }
                        printoperands(opcode,numoperands-1,
                              operand,pgpointers[page][k].mode,fp,
                              pgpointers[page][k].name,out);
                     } else {               /* not found in alternate page */
                        for(sp=strlen(out);sp<OPNAMETAB;sp++)
                           strcat(out," ");
                        strcat(out,"Illegal Opcode");
                     }
                  } else {                                /* page 1 opcode */
                     op=(opcodeinfo *) &(pg1opcodes[i]);
                     numoperands=pg1opcodes[i].numoperands;
                     for(j=0;j<numoperands;j++) {
                        sprintf(out2,"%02X ",(operand[j]=getbyte(fp)));
                        strcat(out,out2);
                     }
                     if(pg1opcodes[i].mode!=IND) {
                        for(sp=strlen(out);sp<OPNAMETAB;sp++)
                           strcat(out," ");
                        sprintf(out2,"%s",pg1opcodes[i].name);
                        strcat(out,out2);
                        for(sp=strlen(out);sp<OPERANDTAB;sp++)
                           strcat(out," ");
                     }
                     printoperands(opcode,numoperands,operand,
                           pg1opcodes[i].mode,fp,pg1opcodes[i].name,out);
                  }
               } else if(i==numops[0]) {            /* not found in page 1 */
                  for(sp=strlen(out);sp<OPNAMETAB;sp++)  
                     strcat(out," ");
                  strcat(out,"Illegal Opcode");
               }
               docomment(count-1-numoperands,&curcomm,numcomm,numoperands+1);
               if(op) {
                  if((!stricmp(op->name,"BRA"))||   /* extra space - branch */
                        (!stricmp(op->name,"LBRA"))||
                        (!stricmp(op->name,"RTS"))||
                        (!stricmp(op->name,"JMP"))||
                        (!stricmp(op->name,"RTI"))||
                        (!strnicmp(op->name,"PUL",3)&&PC)||  /* PUL? PC=RTS */
                        (!stricmp(op->name,"WAI"))) {
                     if(strlen(out)&&(out[strlen(out)-1]!='\n'))
                        strcat(out,"\n");
                  }
               }
               if(diddata) {
                  if(strlen(out)&&(out[strlen(out)-1]!='\n'))
                     strcat(out,"\n");
               }
               printf("%s\n",out);
               PC=FALSE;
               diddata=FALSE;
            }
            fclose(fp);
         } else
            fprintf(stderr,"Can't open file `%s'\n",argv[1]);
      }
   } else
      printf("Usage: `%s <file> [<datafile>]'\n",argv[0]);
   freearrays();
}

