[net.sources] C Power Reverse Assembler

mrr@rayssd.UUCP (05/28/85)

#!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
#    This is a shell archive.
#    Run the following text with /bin/sh to extract.

cat - << \Funky!Stuff! > ra/README
	The source that follows is a reverse assembler for C Power (tm)
C compiler object files.  It represents my first attempt at programming
in C and is probably full of inconsistencies and other code which might
be noxious to seasoned C programmers.  I would be greatful for comments
and suggestions, especially from other owners of C power, with regard to
enhancing or otherwise improving this program.  One minor point in its
favor is that it works.  I downloaded this without benefit of Xmodem or
Kermit so there may be a garble or two, though I've screened the source
here very carefully.  Don't try to compile it on Unix - FILE is defined
as int in C Power.  Hope this is of use to someone.

Mark Rinfret
Funky!Stuff!
cat - << \Funky!Stuff! > ra/openfile.c
/* home-brew file extensions         */
/* author:   Mark R. Rinfret         */
/* date:     04/19/84                */
/* filename: openfile.c              */

#include <stdio.h>
#include <strings.h>

openfile(name,how)
char *name,how;

{
  char *xname;
  char c;
  unsigned dvc;

  xname = name; /* copy pointer */
  dvc = 8;  /* default is device 8 */
  if ((c=*xname) == '#') {
    xname++;
    if (((c=*xname) == '8') || ((c=*xname) == '9')) {
      xname++;
      dvc = (8 + (c - '8'));
      if (*xname==':') xname++;
      else {
        printf("missing ':' in device specification\n%s\n",xname);
        return NULL;
        }
      }
    else {
      printf("illegal device number:\n%s\n",xname);
      return NULL;
      }
    }

  device(dvc);
  return (fopen(xname,how));
}
Funky!Stuff!
cat - << \Funky!Stuff! > ra/ra.fo
.in 10
.rm 70
.ls 2
.sp 5
.ce 3
A Reverse Assembler
for C Power (tm) 
Object Files

.sp 20
.nf
Mark R. Rinfret
348 Indian Ave.
Portsmouth, RI  02871
(401)-846-7639


C Power is a trademark of Pro-Line Software Ltd.
.fi
.bp
.fo //-#-//
.he /ra/-#-/Reverse Assembler/
Introduction
     I wrote the reverse assembler 
to help debug an assembler which I hope to implement in the near future.  The
assembler will output C Power compatible object files and will be useful
for implementing machine-dependent or time/space critical code not easily
handled by the compiler.

     Original intent aside, the reverse assembler is
also useful for gaining insight into the quality of machine language
generated for various C language constructs and also for gleaning interface
requirements as well as characteristics of the runtime library routines.


Operation
     The reverse assembler is invoked with a command of the following form:
.br
     ra [options] objectfile
.br

Currently, no special checks are made for the ".o" or ".obj" suffix indicating
object file format so it is possible to invoke RA with a source file (which
should yield very wierd results).  RA employs a special routine, named openfile,
to parse filenames.  This allows users with dual 1541's to specify which device
the file is on, rather than having to move the file to the work disk or play
games with the 'sys' command.  If the default device is to be used, the filename
is specified as always.  To indicate a specific drive, precede the filename with
the number sign, device number (8,9) and a colon.  Example:
.br
.ti+5
#9:ra1.obj
.br
The legal options are as follows:
.br
.in +5
-a address 
.br
The disassembly process normally sets the address origin to $1800
(6144 decimal) which is the load address for programs linked to 
execute under the shell.  This was done to allow the program to
more reasonably differentiate between zero-page and arg buffer
references when replacing numeric operands with symbols.  The address
value is expected to be a hex number.  No range checking is applied.
.sp2
-p
.br
RA aspires to be intelligent in its choice of formatting mode
(current IQ is about 60).  If a run gives poor results, you can
override the internal decision-making (RA won't be offended) and
RA will prompt you for the formatting mode (instruction or .byte)
to be used whenever a 'transition point' is detected.  If this 
still doesn't work, rewrite RA to do what you want.  
.sp 2
-l listfile
.br
If you want to capture the disassembly in a file, it is recommended
that you use the -l option rather than simply redirecting output.
Standard output is used for certain diagnostic messages as well as
prompts and inputs related to the -p option.  If you want hardcopy
and are not using the -p option, go ahead and use >> to redirect
output to the printer.  If you are using -p and want hardcopy, use
use the -l option and pr (or print) the resulting list file.  RA
performs no pagination, so the print method may be desirable for that
reason, also.
.br
.in-5
.sp2
Implementation Details
     RA initializes itself by collecting the first four parts of the
object file (code, relocation info, external definitions, external
references) and applying the relocation address ($1800 or -a value)
to the appropriate code words and all external definitions.  RA then
enters a loop which attempts to determine on each pass whether the
code should be disassembled as 6502 instructions or as hex constants 
in .byte directives.  This check is triggered by one of the following
events:
.br
.in+5
Detection of an external definition whose offset coincides with the 
current value of the location counter.
.br
Detection of an external reference to the library routine C$106 which
is called whenever a function exits.
.br
Detection of an invalid opcode.
.br
.in-5
.sp2
     In automatic mode, the choice to use instruction mode is based on the
observation that (as far as I can tell) all functions begin with a
'sta $fb' instruction.  This does not hold true for runtime routines
(i.e. c105.obj).  Obviously, this is highly dependent on the current
implementation of the compiler and, I suppose, subject to change.  In spite
of its shortcomings, RA has so far given quite useful and readable output
from a variety of sources.  

     As each instruction is disassembled, the external definition list
is scanned to see if the operand address is the same as that of one of
the external symbols.  If so, the hex operand is replaced with the 
symbol name.  This works best if the default origin is used (no -a option).
After all code has been reverse assembled, all data block definitions
are formatted as a series of '*=*+' directives.

.br
Building RA

     RA is currently comprised of 4 source modules as well as being dependent
upon the openfile routine.  These are:
.in+5
.nf
ra.h   - Constant and structure definitions
rad1.c - Global data definitions  
ra1.c  - Initialization routines
ra2.c  - Translation routines
.fi
Once all of the sources have been compiled, they are bound by the linker
as follows:
.nf
.ls 1
$ link
> ra1.o
> ra2.o
> openfile.o 
> rad1.o
> ^
> 
output to: ra.sh
$
.fi
.ls 2
Actually, I have added openfile to my system library to allow automatic
resolution during the library search.
.br

     There is a preprocessor flag, DEBUG, in ra.h which causes extra output
when defined.  This extra output includes code size, number of relocation
entries and relocation data list, external definition count and list, and
external reference count.  In addition, for each line disassembled in
instruction format, the raw data and location counter are printed to the
left of the instruction mnemonic.  The source file rad1.c (global data)
contains a preprocessor flag, LOCDBG (local debug).  If this flag is defined,
a main routine is compiled which will print out the instruction translation
tables.  These tables are printed in a format easily cross-checked to the
tables in the Commodore 64 Programmer's Reference manual. This main routine 
must not be present when the whole program is linked.
.br

Release
     I am releasing this program to the public domain for any
non-profit use or redistribution.  I would greatly appreciate
feedback, comments, suggestions or details of any enhancements
or corrections that are applied.  One area for enhancement is
the generation of labels for all memory references not already
'covered' by an external definition.  Further work on RA by me
at this time will just keep me from getting on with my
assembler.  Enjoy!

Funky!Stuff!
cat - << \Funky!Stuff! > ra/ra.h
/*
 * Reverse Assembler
 * Constant and Structure Definitions
 *
 */
#include <stdio.h>
#include <strings.h>

#define DEBUG 
#define MAXID 20

/* addressing mode constants */
#define ACC    0
#define IMM    1
#define ZER    2
#define ZPX    3
#define ZPY    4
#define ABS    5
#define ABX    6
#define ABY    7
#define IMP    8
#define REL    9
#define INDX  10
#define INDY  11
#define IND   12
 
typedef char byte;

struct label {
  struct label *next; /* forward linked list  */
  char *name;
  unsigned flag;
  unsigned offset;
};

Funky!Stuff!
cat - << \Funky!Stuff! > ra/ra1.c
/* Object File Reverse Assembler
 * Author:   Mark R. Rinfret
 * Date:     05/13/85
 * Filename: ra1.c
 */
#include "ra.h"

/* external data */
extern struct label *xdefhd,*xdeftl;  /* head,tail of xdef label list */
extern struct label *xrefhd,*xreftl;  /* head,tail pointers for xref list */
extern FILE lst;             /* listing file */
extern FILE obj;              /* object file */
extern unsigned bias,codesz,relsz,xrefsz,datasz;
extern char *codebuf;        /* code buffer     */
extern unsigned prompt;      /* interactive mode flag */
extern unsigned *relbuf;     /* relocation data */

/* global data */
struct label *newlbl;        /* new label pointer */

/****************/
main (argc, argv)
unsigned argc;
char **argv;
{

   lst = stdout;             /* default is standard output */
   bias = 0x1800;            /* starting address for shell programs */
   prompt = 0;               /* non-interactive mode */

   while (argc>2) {
     if (!strcmp(*++argv,"-l")) { /* listing option */
       if ((lst=openfile(*++argv,"w"))==NULL || ferror()) {
         printf("can't open listing file, %s\n",*argv);
         exit();
       }
       argc -= 2;
     }
     else if (!strcmp(*argv,"-p")) { /* interactive option */
       prompt = 1;
       argc--;
     }
     else if (!strcmp(*argv,"-a")) { /* change bias */
       sscanf(*++argv,"%x",&bias);
       argc -= 2;
     }
     else {
       printf("--bad option: %s\n",*argv);
       exit();
     }
   } /* end while */

   if (argc<2) usage();

   if ((obj = openfile(*++argv, "r")) == NULL || ferror()) {
     printf ("can't open object file, %s\n", *argv);
   }
   else {
     reverse ();
     fclose (obj);
   }
}

/* 
 * Display correct program usage.
 *
 */
usage()
{
  printf("usage: ra [options] object file\n");
  printf("options:\n"); 
  printf("  -p (enable format prompting)\n");
  printf("  -l listfile (output to listfile)\n");
  printf("  -a address (align at hex address)\n");
  exit();
}

reverse()
{
  xdefhd = NULL;
  xdeftl = NULL;
  xrefhd = NULL;
  xreftl = NULL;
  getcode(); /* input code segment  */
  getrel();  /* get relocation data */
  getxdef(); /* get external id's   */
  getxref();      /* get external ref's  */
  rvrs();        /* do reverse assembly */
}

/* Input code segment */
getcode()
{
  char *malloc();

  codesz = getw(obj);
#ifdef DEBUG
  fprintf(lst,"Code Size: %5d(D) / %x(X)\n",codesz,codesz);
#endif
  codebuf = malloc(codesz);
  fread(codebuf,1,codesz,obj);
}

/* 
 * Input relocation information and perform relocation.
 *
 */

getrel()
{
  char *malloc();
  char *cp; /* temp code pointer */
  unsigned ad,cnt1,cnt2;
  unsigned *rp;
   

  relsz = getw(obj);
  relbuf = (int *) malloc(relsz*2);
  fread(relbuf,2,relsz,obj);
#ifdef DEBUG
  fprintf(lst,"%d relocation entries\n",relsz);
  cnt1 = relsz;
  cnt2 = 0;
  rp = relbuf;
  while (cnt1--) {
    fprintf(lst,"%04x  ",*rp++);
    if (++cnt2 == 8) {
      fputc('\n',lst);
      cnt2=0;
    }
  }
  fprintf(lst,"\n;--------------------\n");
#endif

/* Relocate the code according to 'bias' */
  rp = relbuf;
  cnt1 = relsz;
  while (cnt1--) {
/*
 * Compute pointer into code buffer.  It is important to note that the
 * relocation entry always assumes that an opcode is present (3 byte code
 * entry).  Thus, relocation entries which modify character string pointers
 * appear to be one less than they should be.
 *
 */
    cp = codebuf + *rp++ + 1;
    ad = *cp;                 /* get low byte of address */
    ad = ad | (*(cp+1) << 8); /* 'or' with upper byte */
    ad = ad + bias;           /* relocate */    
    *cp = ad;                 /* put back lower */
    *(cp+1) = (ad >> 8);      /* put back upper */      
  }
}

/* Input external identifiers */
getxdef()
{
  char *malloc();

  unsigned count,i,ofst;
  char id[MAXID];

  count = getw(obj); /* get count of id's */
#ifdef DEBUG
  fprintf(lst,"%d external definitions\n",count);
#endif
  while (count--) {
    if (!(newlbl = (struct label *) malloc(sizeof(struct label)))) {
      fprintf(lst,"Out of memory!");
      abort();
    }
    getid(id,obj); /* input identifier */
    newlbl->name = malloc(strlen(id)+1);
    strcpy(newlbl->name,id);
    newlbl->flag = getc(obj);
    ofst = getw(obj);
    if (newlbl->flag) ofst += bias; /* relocate symbol to start address */
    newlbl->offset = ofst;
    newlbl->next = NULL;

#ifdef DEBUG
    fprintf(lst,"%-12s  ",newlbl->name);
    fprintf(lst,"%c  ",(newlbl->flag ? 'R' : 'A'));
    fprintf(lst,"%04x\n",newlbl->offset);
#endif

    if (xdefhd==NULL) { /* first entry */
      xdefhd = newlbl;
      xdeftl = newlbl;
    }
    else { /* add to end of list */
      xdeftl->next = newlbl;
      xdeftl = newlbl;
    }
  }
}

/*
 * Input external references
 *
 */

getxref()
{
  struct label *malloc();
  unsigned count;
  char id[MAXID];
  struct label *newxref;

  count=getw(obj); /* get number of xrefs */
#ifdef DEBUG
  fprintf(lst,"%d external references.\n\n",count);
#endif
  while (count--) {
    getid(id,obj); /* input identifier */
    newxref=malloc(sizeof(struct label));
    newxref->name = malloc(strlen(id));
    strcpy(newxref->name,id);
    newxref->next = NULL;
    newxref->flag = getw(obj);
    newxref->offset = getw(obj)+bias;

    if (xrefhd==NULL) { /* first entry */
      xrefhd = newxref;
      xreftl = newxref;
    }
    else {
      xreftl->next = newxref;
      xreftl = newxref;
    } 
  }
}
/*
 * Input identifier from file, replacing 'kludge' characters
 *
 */

getid(s,f)
char *s;
FILE f;
{
  char *s1;
  char c;

  s1 = s; /* save copy of pointer */
  while (*s++ = getc(f))
    ;

  if (!isalpha(*s1)) { /* kludge label? */
    *s1++ = '.';
    if (*s1) { /* more than 1 character? */
      *s1++ = '.';
    }
  }
  while (c=*s1) {    /* check rest of label */
    if (!isprint(c)) *s1 += ('a'-1); /* make printable character */
    s1++;
  }
}
Funky!Stuff!
cat - << \Funky!Stuff! > ra/ra2.c
/* 
 * Reverse Assembler
 * Translation Routines
 * Filename: ra2.c
 *
 */
#include "ra.h"

/* external data */
extern FILE obj;
extern FILE lst;
extern struct label *xdefhd;
extern struct label *xrefhd;
extern char *codebuf;
extern unsigned codesz,bias,datasz,prompt;
extern unsigned *relbuf;

extern char *mtable[];  /* mnemonic strings */
extern char opcodes[];  /* opcode to mnemonic index translation */
extern char amodes[];   /* opcode to addressing mode translation */
extern char modesz[];   /* mode to address field size */
/* 
 * Package global data
 *
 */
char *codeptr; /* code buffer pointer */
int count;     /* code counter */
char efunc;    /* true when end of function detected (c$106) */
unsigned loc;  /* current location */
int *relptr;   /* relocation info pointer */
struct label *cursym; /* current symbol */
struct label *xdefptr,*xrefptr; /* pointer to next xdef, xref */

/*
 * Print address value as either a symbolic quantity or hex value
 * Called with:
 *   Address, addressing mode, size (0-2)
 */
pav(a,m,s)
unsigned a,m,s;
{
 if (!ckref()) {    /* xref pending?  */
   if (m != IMM) {  /* don't do symbol search for immediate operands */
     if (cksym(a)) return;
   }
   if (s==2) fprintf(lst,"$%04x",a);
   else fprintf(lst,"$%02x",a);
 }
}

/*
 * Print address field based on opcode
 * Called with:
 *   address
 *   addressing mode
 *   size (0-2)
 */
paf(a,m,s)
unsigned a,m,s;
{
  unsigned addr; /* address value */

  switch (m) /* prefix? */
  {
  case ACC: fputc('A',lst);
            break;

  case IMM: fputc('#',lst);
            pav(a,m,s);
            break;
  case ZER:
  case ABS:    
            pav(a,m,s);
            break;

  case ZPX:
  case ABX: pav(a,m,s);
            fprintf(lst,",X");
            break;

  case ZPY:
  case ABY: pav(a,m,s);
            fprintf(lst,",Y");
            break;

  case IMP: break;

  case REL: 
            if (a>=128) /* sign extend */
              addr = (0xff00 | a);
            else
              addr = a;
            addr=addr+loc+2; 
            pav(addr,m,s);
            break;

  case INDX: fputc('(',lst);
             pav(a,m,s);
             fprintf(lst,",X)");
             break;

  case INDY: fputc('(',lst);
             pav(a,m,s);
             fprintf(lst,"),Y");
             break;

  case IND: fputc('(',lst);
            pav(a,m,s);
            fputc(')',lst);
            break;

  default: fprintf(lst,"????");

  }
}

/* 
 * Main routine for this package
 *
 */
rvrs()
{
  char op;
  unsigned a1,a2,defcnt;
  unsigned mode,imode,size,i;

  codeptr = codebuf;
  relptr = relbuf;
  xdefptr = xdefhd;
  xrefptr = xrefhd;
  loc = bias;
  count = codesz;
  imode = 1; /* always start in instruction mode */
  efunc = 0;
  fprintf(lst,"  *= $%04x\n",bias);
  while (count) {
    if (ckdef() || efunc) imode=ckmode();
    efunc = 0;
    if (!opcodes[*codeptr]) { /* invalid opcode? */
      fprintf(lst,";!!!Invalid opcode - switching to byte mode.\n");
      imode=0;
    } 
    if (imode==0) {
      dobyte();
      if (count) imode = ckmode();
    }
    else {         /* instruction mode */
      op = *codeptr++;
      count--;
      mode = amodes[op];
      size = modesz[mode];
      a1=0;
      a2=0;
      if (size) {
        a1 = *codeptr++;
        count--;
        if (size==2) {
          a2 = *codeptr++;
          count--;
        }
      }
      if (count<0) {
        fprintf(lst,"--- code underflow ---\n");
        abort();
      }
#ifdef DEBUG
      fprintf(lst,"%02x %02x %02x / %04x  ",op,a1,a2,loc);
#endif
      fprintf(lst," %s ",mtable[opcodes[op]]);
      if (size==2)
        a1 = a1 | (a2 << 8);
      paf(a1,mode,size); /* print address field */
      fputc('\n',lst);
      loc=loc+size+1;
    }                    /* instruction mode */
  } /* while */
  dodata();              /* output data blocks */
  fputc('\n',lst);
}

/* 
 * Format data in .byte mode until a next label definition.
 *
 */
dobyte()
{
  unsigned bytecnt=8,first=1;

  while (count) {
    if (ckdef()) break;  
    if (bytecnt==8) {
      if (!first) fputc('\n',lst);
      first=0;
      bytecnt=0;
#ifdef DEBUG
      fprintf(lst,"           %04x  ",loc);
#endif
      fprintf(lst," .byte ");
    }
    fprintf(lst,"%c $%02x ",(bytecnt>0 ? ',' : ' '),*codeptr++);
    bytecnt++;
    count--;
    loc++;
  } /* while */
  fputc('\n',lst);
}
  
/*
 * See if current location has one or more labels defined.
 * Returns a count of labels defined for this location.
 */
ckdef()
{
 unsigned dc;

 dc = 0;
 while (xdefptr && (loc == xdefptr->offset)) {
     if (!dc) {
       fputc('\n',lst); /* skip line on first label */
       fprintf(lst,";--------------------\n");
     }
     dc++;
     fprintf(lst,"%s\n",xdefptr->name);
     cursym = xdefptr; /* save current symbol */
     xdefptr = xdefptr->next;
 }
 return(dc);
}

/* 
 * See if current location has an external reference.
 *
 */
ckref()
{
 if (xrefptr && (loc == xrefptr->offset)) {
/*
 * Here lies a kludge which assists in the determination of the formatting
 * mode to be used.  If a reference to runtime routine C$106 is being made,
 * we are about to exit a function.  What follows could be another function
 * or it could be the beginning of unlabeled preset string or array data.  
 * The main loop tests the flag 'efunc', which is set here, to see if a mode
 * check is necessary.
 */
   efunc = !strcmp(xrefptr->name,"c$106");
   if (xrefptr->flag == 1) fputc('<',lst); /* high order byte */
   else if (xrefptr->flag == 2) fputc('>',lst); /* low order byte */
   fprintf(lst,"%s",xrefptr->name);
   xrefptr = xrefptr->next;
   if (efunc) fputc('\n',lst);
   return(1);
 }
 else return(0);
}

/*
 * See if address value matches external symbol definition offset 
 * Returns symbol offset if found, zero otherwise.  This assumes
 * that zero is an invalid offset, which in fact, it is, since the
 * code at zero is always a jmp c$start.
 */
cksym(a)
unsigned a;
{
  struct label *ptr;

  ptr = xdefhd; /* get xdef list pointer */

  while (ptr) {  /* nothing fancy - just a linear search */
    if (a == ptr->offset) {
      fprintf(lst,"%s",ptr->name);
      return(ptr->offset);
    }
    ptr = ptr->next;
  }
  return(0);
}

/* 
 * Check for formatting mode change.
 * Returns 1 for instruction mode, 0 for .byte mode
 *
 */
ckmode()
{
  char c1,c2; /* next 2 byte values */
  char s[20]; /* response string */  
  c1 = *codeptr;
  c2 = *(codeptr+1);

  if (prompt) { /* user wants authority */
    printf("current symbol: %s\nfirst 2 bytes: %02x %02x\n",cursym->name,c1,c2);
    for (;;) {
      printf("Instruction or Byte mode? ");
      gets(s);
      switch(*s) {
      case 'I':
      case 'i': return(1);
      case 'B':
      case 'b': return(0);
      default: printf("Enter an I or a B, please\n");
      }
    }
  }
  else {
    /*********************************************************************
     * Here comes the big kludge, folks!  In order to decide whether the *
     * code that follows is instruction or data, we test for the pattern *
     * 'sta $fb' which seems to occur at the entry to every procedure.   *
     * If this pattern is not detected, then we switch to data mode and  *
     * output .byte directives.                                          *
     *********************************************************************/

    if ((c1==0x85) && (c2==0xfb))
      return(1); /* set instruction mode */
    else
      return(0);
  }
}

/*
 * Output Data Blocks
 *
 */

dodata()
{
  unsigned cnt,size;
  char id[MAXID];

  cnt = getw(obj); /* get number of entries */
  if (cnt) {
    fprintf(lst,"\n;Module data blocks.\n");

#ifdef DEBUG
    fprintf(lst,"%d data blocks.\n",cnt);
#endif

    while (cnt--) {
      getid(id,obj); /* get identifier */
      size = getw(obj);
      fprintf(lst,"%-10s *=*+%d\n",id,size);
    }
  }
}

Funky!Stuff!
cat - << \Funky!Stuff! > ra/rad1.c
/*
 * Reverse Assembler Global Data
 * Filename: rad1.c
 */
#include "ra.h"
#undef LOCDBG             /* turn off local debug */
#define NUMOPS 57

struct label *xdefhd,*xdeftl; /* head and tail of xdef label list */
struct label *xrefhd,*xreftl; /* head and tail of xref label list */
FILE lst;                 /* listing file */
FILE obj;                 /* object file */
unsigned bias,codesz,relsz,xrefsz,datasz;
unsigned prompt;          /* 1 => prompt for format */
char *codebuf;            /* code buffer     */
unsigned *relbuf;         /* relocation data */

char *mtable[]={ /* mnemonic table */
      "???",              /* 0     */
      "adc","and","asl",  /* 01-03 */
      "bcc","bcs","beq",  /* 04-06 */
      "bit","bmi","bne",  /* 07-09 */
      "bpl","brk","bvc",  /* 10-12 */
      "bvs","clc","cld",  /* 13-15 */
      "cli","clv","cmp",  /* 16-18 */
      "cpx","cpy","dec",  /* 19-21 */
      "dex","dey","eor",  /* 22-24 */
      "inc","inx","iny",  /* 25-27 */
      "jmp","jsr","lda",  /* 28-30 */
      "ldx","ldy","lsr",  /* 31-33 */
      "nop","ora","pha",  /* 34-36 */
      "php","pla","plp",  /* 37-39 */
      "rol","ror","rti",  /* 40-42 */
      "rts","sbc","sec",  /* 43-45 */
      "sed","sei","sta",  /* 46-48 */
      "stx","sty","tax",  /* 49-51 */
      "tay","tsx","txa",  /* 52-54 */
      "txs","tya"         /* 55-56 */
};

char opcodes[256]={/* opcode to mnemonic translation */

/* 00 */  11, 35, 00, 00, 00, 35, 03, 00,
/* 08 */  37, 35, 03, 00, 00, 35, 03, 00,
/* 10 */  10, 35, 00, 00, 00, 35, 03, 00, 
/* 18 */  14, 35, 00, 00, 00, 35, 03, 00,
/* 20 */  29, 02, 00, 00, 07, 02, 40, 00,
/* 28 */  39, 02, 40, 00, 07, 02, 40, 00,
/* 30 */  08, 02, 00, 00, 00, 02, 40, 00,
/* 38 */  45, 02, 00, 00, 00, 02, 40, 00,
/* 40 */  42, 24, 00, 00, 00, 24, 33, 00,
/* 48 */  36, 24, 33, 00, 28, 24, 33, 00,
/* 50 */  12, 24, 00, 00, 00, 24, 33, 00,
/* 58 */  16, 24, 00, 00, 00, 24, 33, 00,
/* 60 */  43, 01, 00, 00, 00, 01, 41, 00,
/* 68 */  38, 01, 41, 00, 28, 01, 41, 00,
/* 70 */  13, 01, 00, 00, 00, 01, 41, 00,
/* 78 */  47, 01, 00, 00, 00, 01, 41, 00,
/* 80 */  00, 48, 00, 00, 50, 48, 49, 00,
/* 88 */  23, 00, 54, 00, 50, 48, 49, 00,
/* 90 */  04, 48, 00, 00, 50, 48, 49, 00,
/* 98 */  56, 48, 55, 00, 00, 48, 00, 00,
/* a0 */  32, 30, 31, 00, 32, 30, 31, 00,
/* a8 */  52, 30, 51, 00, 32, 30, 31, 00,
/* b0 */  05, 30, 00, 00, 32, 30, 31, 00,
/* b8 */  17, 30, 53, 00, 32, 30, 31, 00,
/* c0 */  20, 18, 00, 00, 20, 18, 21, 00,
/* c8 */  27, 18, 22, 00, 20, 18, 21, 00,
/* d0 */  09, 18, 00, 00, 00, 18, 21, 00,
/* d8 */  15, 18, 00, 00, 00, 18, 21, 00,
/* e0 */  19, 44, 00, 00, 19, 44, 25, 00,
/* e8 */  26, 44, 34, 00, 19, 44, 25, 00,
/* f0 */  06, 44, 00, 00, 00, 44, 25, 00,
/* f8 */  46, 44, 00, 00, 00, 44, 25, 00

};

char amodes[256] = { /* opcode to addressing mode */
/* 00 */ IMP , INDX, IMP , IMP , IMP , ZER , ZER , IMP ,
/* 08 */ IMP , IMM , ACC , IMP , IMP , ABS , ABS , IMP ,
/* 10 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* 18 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP ,
/* 20 */ ABS , INDX, IMP , IMP , ZER , ZER , ZER , IMP ,
/* 28 */ IMP , IMM , ACC , IMP , ABS , ABS , ABS , IMP ,
/* 30 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* 38 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP ,
/* 40 */ IMP , INDX, IMP , IMP , IMP , ZER , ZER , IMP ,
/* 48 */ IMP , IMM , ACC , IMP , ABS , ABS , ABS , IMP ,
/* 50 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* 58 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP ,
/* 60 */ IMP , INDX, IMP , IMP , IMP , ZER , ZER , IMP ,
/* 68 */ IMP , IMM , ACC , IMP , IND , ABS , ABS , IMP ,
/* 70 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* 78 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP ,
/* 80 */ IMP , INDX, IMP , IMP , ZER , ZER , ZER , IMP ,
/* 88 */ IMP , IMP , IMP , IMP , ABS , ABS , ABS , IMP ,
/* 90 */ REL , INDY, IMP , IMP , ZPX , ZPX , ZPY , IMP ,
/* 98 */ IMP , ABY , IMP , IMP , IMP , ABX , IMP , IMP ,
/* a0 */ IMM , INDX, IMM , IMP , ZER , ZER , ZER , IMP ,
/* a8 */ IMP , IMM , IMP , ABS , ABS , ABS , ABS , IMP ,
/* b0 */ REL , INDY, IMP , IMP , ZPX , ZPX , ZPY , IMP ,
/* b8 */ IMP , ABY , IMP , IMP , ABX , ABX , ABY , IMP ,
/* c0 */ IMM , INDX, IMP , IMP , ZER , ZER , ZER , IMP ,
/* c8 */ IMP , IMM , IMP , IMP , ABS , ABS , ABS , IMP ,
/* d0 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* d8 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP ,
/* e0 */ IMM , INDX, IMP , IMP , ZER , ZER , ZER , IMP ,
/* e8 */ IMP , IMM , IMP , IMP , ABS , ABS , ABS , IMP ,
/* f0 */ REL , INDY, IMP , IMP , IMP , ZPX , ZPX , IMP ,
/* f8 */ IMP , ABY , IMP , IMP , IMP , ABX , ABX , IMP
};

char modesz[13] = {
 /* ACC */ 0,
 /* IMM */ 1,
 /* ZER */ 1,
 /* ZPX */ 1,
 /* ZPY */ 1,
 /* ABS */ 2,
 /* ABX */ 2,
 /* ABY */ 2,
 /* IMP */ 0,
 /* REL */ 1,
 /* INDX*/ 1,
 /* INDY*/ 1,
 /* IND */ 2
};
 
char *modename[] = {
 "accumulator",
 "immediate",
 "zero page",
 "zero page,x",
 "zero page,y",
 "absolute",
 "absolute,x",
 "absolute,y",
 "implied",
 "relative",
 "(indirect,x)",
 "(indirect),y",
 "(indirect)"
};

#ifdef LOCDBG
prtop(op)
unsigned op;
{

  printf(" %02x %-3s %-12s",
    op,mtable[opcodes[op]],
    modename[amodes[op]]); 

}

main() /* for local test only */
{
 unsigned i,j;

 for (i=0;i<=0xc0;i+= 0x40) {
   for (j=0;j<0x20;j++) {
     prtop(i+j);
     printf("  ");
     prtop(i+0x20+j);
     putchar('\n');
   }
   printf("\n\n");
 }
  putchar('\n');

}
#endif
Funky!Stuff!
-- 
	Mark R. RInfret, SofTech Inc.
	Raytheon Co.; Portsmouth RI; (401)-847-8000 x4938
	...!decvax!brunix!rayssd!mrr
	...!allegra!rayssd!mrr
	...!linus!rayssd!mrr