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 |