[comp.sys.cbm] C-Power Reverse Assembler

mark@unisec.usi.com (Mark Rinfret) (05/18/87)

Here's a utility for C-Power which will (in most cases) convert a C-Power
object file, including the runtime objects, into a source file which may
be edited and later reassembled using the C-ASSM assembler which I posted
earlier.  This program, named "ra" (reverse assembler), provides you with
a symbol definition file (ra.symbols) to which you can add custom symbol
definitions.  Absolute object addresses matching these symbol definitions 
will be replaced with the appropriate symbol name, making the resulting 
assembly language output quite useful and readable.  Also included is 
documentation describing the C-Power relocatable object format.  

If you plan to merge assembly language modules with your C-Power programs,
you'll probably find this tool to be quite useful in providing you with
insights as to how parameter passing/referencing is accomplished.  Just
write up a short C test program, compile it, then run it through ra.

As always, enjoy yourselves.  I created this utility in anticipation of
C-ASSM, so that I'd have a debugging aid when testing my object generation
routines.  No restrictions of any kind are placed on the source or executable -
redistribute it or hack it up as necessary.
--------------------------- c u t  h e r e ---------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	cobject.doc
#	openfile.c
#	ra.fo
#	ra.h
#	ra.symbols
#	ra1.c
#	ra2.c
#	rad1.c
# This archive created: Mon May 18 16:09:12 1987
export PATH; PATH=/bin:$PATH
if test -f 'cobject.doc'
then
	echo shar: will not over-write existing file "'cobject.doc'"
else
cat << \SHAR_EOF > 'cobject.doc'
---------------------------------

Object file format:

Each object file has five parts:  the code, relocation information,
external identifier definitions, external reference list, and data
block list, each of which will be described below.  Each part starts
with a count of the number of entries contained.

All counts, addresses, and offsets are two bytes, low order
byte first.  Identifiers are strings of non-zero bytes and arbitrary
length terminated with a zero byte.


1. Code

The first two bytes of an object file indicate the number of bytes
of code to be linked.  Following the count is the code itself, which
is compiled or assembled starting at address zero.  Instructions
whose operands are not known (external references) must have
garbage operands as place holders.


2. Relocation

Each relocation entry is an offset to an instruction whose operand
(absolute addressing only) is to be relocated.  For example, if the
the first instruction of the file is a jsr to a location within the file
there should be a relocation entry of two zero bytes.


3. External identifier definitions

The first item in each entry is the identifier to be made globally
known.  Following this is a one byte flag which has a value of one
if the value of the identifier is to be relocated, zero otherwise.
Last is the value of the identifier (usually an address).


4. External references

First in each entry is the referenced external identifier (e.g. printf
in many C programs).  Next is a two byte code indicating what is to
be done with value of the identifier once it is known. The value of the
two low order bits (i.e. the two low order bits in the first byte)
choose among the following actions:

         value

           0      --   use both bytes of the value of the identifier
           1      --   use only the high order byte
           2      --   use only the low order byte

1 and 2 are useful for loading the registers with the value of an
identifier, and 2 is useful for zero page addressing.  The high
order 14 bits of the code is added to the value of the identifier
after an arithmetic shift right of two bits.

Last in each entry is an offset to the instruction making the reference.


5. Data blocks

This is used to allocate blocks of memory.  An entry here may be the
result of a declaration of a static array in C such as

static int abc[50];

or an assembler directive reserving memory such as

abc  .reserve  100

Data blocks are placed immediately after the code once the entire
program has been linked.

For each reference to a data block there must be an entry in the
external reference list.

Each entry consists of an identifier and the number of bytes to be
reserved.

-----------------------------------

This format is inadequate in some ways. For example, to take the addresses
of non-global identifiers, or to reference non-global data blocks, one must
stick random bytes in front of the identifiers (I use two), put the new
identifiers in the external definition list, and put the references
(with the new identifiers) in the external references list. I admit this is
very kludgy but it does work. Someday I may get around to fixing it.

-----------------------------------

C parameter passing mechanisms


Machine language function entrance procedure:

1.  Decrement the address contained in the zero page locations 26 and
    27 decimal by one byte.
2.  Get the byte pointed to by this address.
3.  Use this byte as an index into the tape buffer ($033c) to access
    arguments.

Returning values:

Store return values the same place you get the arguments from
(i.e. tape buffer + index).

Example:

main()
{
     printf ("%d\n", add (1, 2));
}

The machine language for add may look something like this:

        .global  add

        args   = $033c
        argptr = 26

add     lda  argptr
        bne  skip
        dec  argptr+1
skip    dec  argptr
        ldy  #0
        lda  (argptr),y
        tax
        clc
        lda  args,x
        adc  args+2,x
        sta  args,x
        lda  args+1,x
        adc  args+3,x
        sta  args+1,x
        rts

% /* end of file */
SHAR_EOF
fi # end of overwriting check
if test -f 'openfile.c'
then
	echo shar: will not over-write existing file "'openfile.c'"
else
cat << \SHAR_EOF > 'openfile.c'
/* home-brew file extensions         */
/* author:   Mark R. Rinfret         */
/* date:     04/19/84                */
/* filename: cbmfile.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));
}
SHAR_EOF
fi # end of overwriting check
if test -f 'ra.fo'
then
	echo shar: will not over-write existing file "'ra.fo'"
else
cat << \SHAR_EOF > '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 '*=*+' type 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 LOCDBG (local debug).  If this flag is defined,
a main routine is compiled which will print out the instruction translation
tables.  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!

SHAR_EOF
fi # end of overwriting check
if test -f 'ra.h'
then
	echo shar: will not over-write existing file "'ra.h'"
else
cat << \SHAR_EOF > 'ra.h'
/*
 * Reverse Assembler
 * Constant and Structure Definitions
 *
 */
#include <stdio.h>
#include <strings.h>

#undef  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;      /* MUST be a word for xref's */
  unsigned global;    /* could be byte, suspect alignment problems */
  unsigned offset;
};

SHAR_EOF
fi # end of overwriting check
if test -f 'ra.symbols'
then
	echo shar: will not over-write existing file "'ra.symbols'"
else
cat << \SHAR_EOF > 'ra.symbols'
c$parmsize 0x00fb
c$local    0x002b
c$parms    0x033c
acptr      0xffa5
chkin      0xffc6
chkout     0xffc9
chrin      0xffcf
chrout     0xffd2
ciout      0xffa8
cint       0xff81
clall      0xffe7
close      0xffc3
clrchn     0xffcc
getin      0xffe4
iobase     0xfff3
ioinit     0xff84
listen     0xffb1
load       0xffd5
membot     0xff9c
memtop     0xff99
open       0xffc0
plot       0xfff0
ramtas     0xff87
rdtim      0xffde
readst     0xffb7
restor     0xff8a
save       0xffd8
scnkey     0xff9f
screen     0xffed
second     0xff93
setlfs     0xffba
setmsg     0xff90
setnam     0xffbd
settim     0xffdb
settmo     0xffa2
stop       0xffe1
talk       0xffb4
tksa       0xff96
udtim      0xffea
unlsn      0xffae
untlk      0xffab
vector     0xff8d
SHAR_EOF
fi # end of overwriting check
if test -f 'ra1.c'
then
	echo shar: will not over-write existing file "'ra1.c'"
else
cat << \SHAR_EOF > 'ra1.c'
/* Object File Reverse Assembler
 * Author:   Mark R. Rinfret
 * Date:     05/13/85
 * Filename: ra1.c
 * History:
 *
 *   11/02/85 - Output .def, .ref
 *
 *   04/13/86 - Fixed xdef list linking problem, added 'standard' symbol
 *              definitions.
 *
 *   04/15/86 - Inhibit multiple .ref's to same symbol
 */
#include "ra.h"

/* external data */
extern struct label *xdefhd,*xdeftl;  /* hd,tl of xdef label list */
extern struct label *xrefhd,*xreftl; /* hd and tl 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 */
  defstd();          /* define 'standard' symbols */
  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 */      
  }
}

/* Define 'standard' labels
   This routine inputs the contents of the file 'ra.symbols' and adds
   the definitions found therein to the list of global symbols.  Please
   note that these symbols are not externalized with .def statements as
   the external symbols from the object file are.  The symbol definitions
   are output to the disassembly listing as they are defined.
*/

defstd()
{
  FILE symfile;
  char *malloc();
  register unsigned i;
  struct label *newlbl;
  char *p;
  unsigned symleng,offset;
  char c,line[81],sym[MAXID+1];

  if ((symfile=fopen("ra.symbols","r"))==NULL || ferror()) {
    printf("Can't load symbols!\n");
    return;
  }

  p = "c$$start"; /* special first symbol */
  newlbl = (struct label *) malloc(sizeof(struct label));
  newlbl->next = NULL;
  newlbl->name = malloc(strlen(p)+1);
  strcpy(newlbl->name,p);
  newlbl->offset = bias;
  newlbl->flag = 1;
  newlbl->global = 1;

  addsym(newlbl);

  while (fgets(line,81,symfile)) {
    if (*line != ';') {
      p = line;  /* copy string pointer */
      symleng = 0;
      while ((c = *p++) != ' ') {
        if (symleng < MAXID) sym[symleng++] = c;
      }
      sym[symleng] = 0; /* null terminator */
      if (symleng) {
        newlbl =  (struct label *) malloc(sizeof(struct label));
        newlbl->name = malloc(++symleng);
        strcpy(newlbl->name,sym);
        newlbl->offset = getaddr(p);
        newlbl->flag = NULL;
        newlbl->global = NULL;
        newlbl->next = NULL;
        addsym(newlbl);  /* add local symbol def */
        fprintf(lst,"%-12s = $%04x\n",sym,newlbl->offset);
      }
      else printf("Bad symbol definition:\n%s",line);
    }
  }
  fclose(symfile);
}

/* Input an address value as either decimal (default) or hexadecimal if 
   preceded by '0x'.  This routine was added because it appears that scanf
   does not properly handle hex values. (?)
*/

int getaddr(s)
char *s;
{
  char c;
  char base = 10;
  unsigned value;

  while ((c=*s++)==' ' || c == '0');
  if ((c == '\n') || (c == 0)) return -1;
  if (c == 'x' || c == 'X') base = 16;
  while (isalnum((c=*s++))) {
    value *= base;
    if (c>='0' && c<='9') value += (c-'0');
    else {
      if (base==10) return -1;
      if ((c>='a' && c<='f')) value += (10 + c-'a');
      else if (c>='A' && c<='F') value += (10 + c-'A');
      else return -1;
    }
  }
  return value;      
}
  
/* 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);

/* relocate symbol to start address */

    if (newlbl->flag) ofst += bias;
    newlbl->offset = ofst;
    newlbl->global = 1;
    newlbl->next = NULL;
    addsym(newlbl);  /* add symbol */
  }
}

/* Add symbol to global symbol list
** Called with:
**   Symbol definition
*/
addsym(lbl)
struct label *lbl;
{
  struct label *cur,*prev;

  if (xdefhd == NULL) { /* first entry */
    xdefhd = lbl;
    xdeftl = lbl;
  }
  else {
    if (lbl->offset < xdeftl->offset) { /* Out of order? */
      cur = xdefhd;
      prev = xdefhd; /* insert into list */
      while (cur) {
        if (lbl->offset < cur-> offset) {
          if (cur==xdefhd) { /* new first element? */
            lbl->next = xdefhd;
            xdefhd = lbl;
            break;
          }
          else {
           prev->next = lbl;
           lbl->next = cur;
           break;
          }
        }
        else {
          prev = cur;
          cur = cur->next;
        }
      }
    }
    else { /* add to end of list */
      xdeftl->next = lbl;
      xdeftl = lbl;
    }
  }
}

/*
 * Input external references
 *
 */

getxref()
{
  struct label *malloc();

  unsigned count,printit;
  struct label *newxref,*testref;
  char id[MAXID];

  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;
    newxref->global = 1;

    printit = 1;       /* assume .ref is unique */

    if (xrefhd==NULL) {/* first entry */
      xrefhd = newxref;
      xreftl = newxref;
    }
    else {
      testref = xrefhd;
      while (testref) {
        if (!strcmp(testref->name,newxref->name)) {
          printit = 0;
          break;
        }
        testref = testref->next;
      }
      xreftl->next = newxref;
      xreftl = newxref;
    } 
    if (printit)
      fprintf(lst," .ref %s\n",newxref->name);
  }
}
/*
 * 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++;
  }
}

/* Open a file, allowing device number specification. */

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

{
  char *xname;
  char c;
  unsigned dvc;
  unsigned stat;

  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);
  stat = fopen(xname,how);
  device(8);
  return stat;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'ra2.c'
then
	echo shar: will not over-write existing file "'ra2.c'"
else
cat << \SHAR_EOF > 'ra2.c'
/* 
 * Reverse Assembler
 * Translation Routines
 * Filename: ra2.c
 * Modified:
 *   11/02/85 - Removed dashes between external labels
 *   04/14/86 - Added 'symbol+offset' syntax output.
 */
#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 (1 or 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: 
#undef OLDWAY

            if (a>=128) { /* sign extend */
              addr = (0xff00 | a);
              fprintf(lst,"*%d",addr+2);
            }
            else {
              fprintf(lst,"*+%d",a+2);
            }
#ifdef OLDWAY
            addr=addr+loc+2;
            pav(addr,m,s);
#endif           
            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;
  if (!count) ckdef(); 
  else
  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 (xdefptr->global) {
     if (!dc) {
       fputc('\n',lst); /* skip line on first label */
     }
     dc++;
     if ((xdefptr->flag==0) || (xdefptr->offset < loc)) { /* absolute? */
       fprintf(lst,"%-12s = $%04x\n",xdefptr->name,xdefptr->offset);
     }
     else
       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;
{
#define MAXDIFF 127  /* max displacement for symbol expression */

  struct label *ptr,*closest;
  register unsigned diff,min;

  ptr = xdefhd; /* get xdef list pointer */
  min = MAXDIFF + 1;
  while (ptr) {  /* nothing fancy - just a linear search */
    diff = a - ptr->offset;
    if ((a >= ptr->offset) && (diff <= MAXDIFF)) {
      if (diff == 0) {
        min = 0;
        closest = ptr;
        break;
      }
      else if (diff < min) {
        min = diff;
        closest = ptr;
      }
    }
    ptr = ptr->next;
  }
  if (min <= MAXDIFF) {
    fprintf(lst,"%s",closest->name);
    if (min) fprintf(lst,"+%d",min);
    return(closest->offset);
  }
  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.   *
     * Another possibility is the jmp c$start at the beginning of each   *
     * code segment.                                                     *
     * If these patterns are not detected, then switch to data mode and  *
     * output .byte directives.                                          *
     *********************************************************************/

    if ((c1==0x85) && (c2==0xfb))
      return(1);       /* set instruction mode */
    else if (xrefptr && (loc == xrefptr->offset))
      return 1;
    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 .dseg %d\n",id,size);
    }
  }
}

SHAR_EOF
fi # end of overwriting check
if test -f 'rad1.c'
then
	echo shar: will not over-write existing file "'rad1.c'"
else
cat << \SHAR_EOF > '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
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
| Mark R. Rinfret, SofTech, Inc.		mark@unisec.usi.com |
| Guest of UniSecure Systems, Inc., Newport, RI                     |
| UUCP:  {gatech|mirror|cbosgd|uiucdcs|ihnp4}!rayssd!unisec!mark    |
| work: (401)-849-4174	home: (401)-846-7639                        |