/* ******** * L2.C * New linker for BDS C ******** Written 1980 by Scott W. Layson This code is in the public domain. This is an improved linker for BDS C CRL format. Modified to v2.2.3, 11/27/1982 by David Kirkland This is version 2.2 as distributed by BDS C UG, with the following modifications: - The c debugger is supported. This adds the "-NS", "-S", and "-D" command line options. - DEFF3.CRL is scanned, if it is present on disk - New mechanism for default drive selection--the DEF_DRIVE macro added. - Minor changes to messages. - Eliminates need for SCOTT.C by change to fscanf format string in function "loadsyms" when reading .SYM file for overlay processing. - Ability to rescan DEFF*.CRL if reply with a carriage return to prompt message when functions are missing. - "-I" option, which allows interactive entry of command line arguments. If the command line ends in a "-i", then L2 treats the command line as Incomplete, and prompts the user for more arguments. This is especially useful if you have a replacement CCP which does not allow 127 character long command lines (e.g., ZCPR). This option orginally implemented, in a different way, by Gil Shapiro. Modified to v2.2.4, 03/24/1983 by David Kirkland - "-N" option added to produce .COM files which do not perform a warm boot after execution As in clink, if both "-n" and "-t" are specified, "-t" is given priority. - "-NS" option removed/"-S" option changed. Now the default is NO system library files (see CDB docs for explanation of system library files); system library functions made it impossible to set a breakpoint at the return of library functions in certain instances. Mod. v2.2.5, July 1984 by Greg Lee to read MicroSoft REL files: - Z option added like that of CLINK -- don't zero externals. - E option added to provide default beginning external address. - Program or library files can be entered with extension .REL, in which case they are assumed to be REL files with a CSEG and/or a DSEG (but no ASEG or COMMON). DSEG's are intended to correspond with external data of C-compiled modules. - Function names must be "public" and external functions "ext" in the assembler source for M80. All public names are assumed to be functions by C-compiled modules, but references from one assembled module to data in another are ok. As with L80, if external references are not forward, the appropriate modules of a library may fail to be loaded on the first pass. - One can't do arithmetic with external symbols, such as "abra equ cadabra+6" where cadabra is external. - CRL symbols can have 8 characters, but REL file symbols only 7, so 7 character long REL file symbols are considered to have an 8th wild character ('?') that will match any 8th character in a CRL symbol. - DSEG data and addresses are assumed to be offset by the address provided with the "-e" option when the main program module was compiled or else with the new "-e" option of the linker. If no such address was given, and a DSEG is encountered, this causes the linker to abort. - If any real data is found in a DSEG (after "db" or "dw"), then (1) the Z option is automatically triggered, and (2) the COM file written to disk is extended sufficiently to include the part of the external area where the the (initialization) data was deposited. - Long files requiring disk mode may require DSEG data to be loaded last, since the linker cannot write code to disk after DSEG data has been written to disk. - The M80 pseudo-op '.REQUEST' is implemented. The requested REL files are searched after other library files. Compilation instructions (assuming l2.com, v2.2.5 is available): cc l2 -e5100 cc chario m80 =rdrl m80 =chario m80 =spr l2 l2 rdrl.rel spr.rel chario.rel (SPR.MAC is just an assembly language version of the '_spr' library function -- it need not be used, but it saves 1/2k of code. Also, the C version of CHARIO could be used.) Mod. v2.2.6, November, 1984 by Greg Lee to use the library BDSCLIB.REL (instead of DEFF.CRL and DEFF2.CRL) - * option added to load WILD.CCC instead of C.CCC (cf. wildccc.doc) Revised compilation instructions (using v2.2.6 of l2): cc l2 -e4900 cc chario m80 =rdrl m80 =chario l2 l2 rdrl.rel chario.rel (The above "-e4900" assumes my revised C.CCC, which is 1/2k smaller than the standard version.) ********************************* The DEF_DRIVE macro is used to define the drive from which L2 will load C.CCC, DEFF.CRL, DEFF2.CRL, and DEFF3.CRL (if it exists). The macro takes as an argument the filename and extension, and "returns" the name with whatever drive designator is needed. The macro also encloses the name in quotes; thus, the argument when the macro is invoked must NOT be within quotes. That is, to open C.CCC on the proper drive, we use the C code if (ERROR==fopen(DEF_DRIVE(C.CCC), iobuf)) ..... */ #define RTRACE 0 /*3 show stuff in rel file */ #define DEF_DRIVE(fn) "fn" /* Make this "0/A:fn" for, say, user 0 on A */ #define SUB_FILE "$$$.SUB" /* submit file to delete on error exit... * if you use SDOS, use "a:$$$$.sub"; if you've * hacked your CCP, you may need to change the * drive designator letter */ #define RST_NUM 6 /* C debugger RST number. Should be identical to the RSTNUM symbol in CCC.ASM */ #define OVERLAYS /* comment this out for shorter version */ /* These #defines from NOBOOT.C for version 1.50 */ #define SNOBSP 0x0138 /* Location of Set NoBoot SP routine in C.CCC */ #define NOBRET 0x013B /* Location of NoBoot RETurn routine in C.CCC */ #define CLREX 0x0141 /*3 location of clear externals vector in C.CCC */ /* #define MARC /* for MARC cross-linker version (enables the "-marc" option) */ */ #include #define NUL 0 #define FLAG char #define repeat while (1) #define STDOUT 1 /* Phase control */ #define INMEM 1 /* while everything still fits */ #define DISK1 2 /* overflow; finish building table */ #define DISK2 3 /* use table to do window link */ int phase; /* function table */ struct funct { char fname[9]; FLAG flinkedp; /* in memory already? */ FLAG fdebug; /* TRUE unless this routine required only by a lib function after -s */ char *faddr; /* address of first ref link if not linked */ } *ftab; int nfuncts; /* no. of functions in table */ int maxfuncts; /* table size */ #define LINKED 1 /* (flinkedp) function really here */ #define EXTERNAL 2 /* function defined in separate symbol table */ char fdir [512]; /* CRL file function directory */ /* command line parameters etc. */ int nprogs, nlibs; char progfiles [30] [15]; /* program file names */ char libfiles [20] [15]; /* library file names */ int deflibindex; /* index of first default (DEFF*) library */ FLAG symsp, /* write symbols to .sym file? */ appstatsp, /* append stats to .sym file? */ sepstatsp; /* write stats to .lnk file? */ FLAG Zflag, /*3 disable zeroing of external area? */ Wflag; /*3 use wild expansion? */ #ifdef MARC FLAG maxmemp, /* punt MARC shell? */ marcp; /* uses CM.CCC */ #endif FLAG relflag; /*3 loading a REL file? */ FLAG anynotlinked; /*3 any unlinked symbols after scanlib? */ int minlk; /*3 during scanlib, index before first unlinked symbol */ FLAG nomorecode; /*3 cannot accept code after data written */ char mainfunct[10]; FLAG ovlp; /* make overlay? */ char symsfile [15]; /* file to load symbols from (for overlays) */ /* C debugger variables */ FLAG Dflag; FLAG SysStat; /* TRUE if "-s" option given & now active */ int SysNum; /* index into libfiles of "-s", or -1 if none */ FLAG Tflag; /* TRUE if "-t" option given */ FLAG Nflag; /* TRUE if "-n" option given */ unsigned Tval; /* arg to "-t", if present */ /* useful things to have defined */ struct inst { char opcode; char *address; }; union ptr { unsigned u; /* an int */ unsigned *w; /* a word ptr */ char *b; /* a byte ptr */ struct inst *i; /* an instruction ptr */ }; /* Link control variables */ union ptr codend; /* last used byte of code buffer + 1 */ union ptr loadpt; /*3 where to put code during REL load */ union ptr datapt; /*3 where to put data during REL load */ union ptr maxdata; /*3 max dseg address, to estimate end of of externals when main() is .REL */ union ptr exts; /* start of externals */ union ptr acodend; /* actual code-end address */ unsigned extspc; /* size of externals */ unsigned origin; /* origin of code */ unsigned buforg; /* origin of code buffer */ unsigned jtsaved; /* bytes of jump table saved */ char *lspace; /* space to link in */ char *lspcend; /* end of link area */ char *lodstart; /* beginning of current file */ char *lastdata; /*3 remember how much of ext. area to save */ /*3 external function rdrl reads REL bitstream */ struct rrec { char itemtype; char segtype; char linktype; int dataword; char lsymlen; char lsymbol[8]; } *rdrl(), *cd; /*3 REL file address type codes */ #define ASEG 0 #define CSEG 1 #define DSEG 2 #define COMMON 3 /*3 character used for 8th char.C of REL external symbols */ #define WILDC '?' /* i/o buffer */ struct iobuf { int fd; int isect; /* currently buffered sector */ int nextc; /* index of next char in buffer */ char buff [128]; } ibuf, obuf; /* BDS C i/o buffer */ char symbuf[BUFSIZ]; /* seek opcodes */ #define ABSOLUTE 0 #define RELATIVE 1 #define INPUT 0 #define TRUE (-1) #define FALSE 0 #define NULL 0 /* 8080 instructions */ #define LHLD 0x2A #define LXISP 0x31 #define LXIH 0x21 #define SPHL 0xF9 #define JMP 0xC3 #define CALL 0xCD #define RET 0xC9 #define PARMSIZE 400 char parmtext[PARMSIZE]; /* "-i" command line args go here */ int parmindex; /* first unused character in parmtext */ /* strcmp7 locals, made global for speed */ char _c1, _c2, _end1, _end2; /**************** End of Globals ****************/ main (argc, argv) int argc; char **argv; { char *argvv[40]; puts ("Mark of the Unicorn Linker ver. 2.2.6\n"); inc_proc(&argc, argv, &argvv); setup (argc, argvv); linkprog(); linklibs(); if (phase == DISK1) rescan(); else wrtcom(); if (symsp) wrtsyms(); } inc_proc(count, argv, argvv) int *count; char **argv, **argvv; { /* process the "-i" argument by building a new argv vector * in argvv. */ int i; for (i=0; i<*count; i++) argvv[i] = argv[i]; while (!strcmp(argvv[*count-1],"-I")) buildvec(count, argvv); } buildvec (count, argvv) int *count; char **argvv; { char line[MAXLINE], *p; puts("Enter continuation\n*"); gets(line); for (p=line, --*count; ;) { while (isspace(*p)) p++; if (!*p) break; argvv[(*count)++] = &parmtext[parmindex]; while (*p && !isspace(parmtext[parmindex] = toupper(*p++)) ) parmindex++; parmtext[parmindex++] = 0; } } setup (argc, argv) /* initialize function table, etc. */ int argc; char **argv; { symsp = appstatsp = sepstatsp = FALSE; /* default options */ #ifdef MARC marcp = maxmemp = FALSE; #endif ovlp = FALSE; nprogs = 0; nlibs = 0; strcpy (&mainfunct, "MAIN"); /* default top-level function */ origin = 0x100; /* default origin */ maxfuncts = 200; /* default function table size */ Zflag = Tflag = Nflag = FALSE; /* no "-z","-t" or "-n" yet */ SysNum = -1; SysStat = FALSE; Dflag = FALSE; cmdline (argc, argv); ftab = endext(); lspace = ftab + maxfuncts; lspcend = topofmem() - (1024 + 2100); if (lspace > lspcend) Fatal ("Insufficient memory to do anything at all!\n"); loadccc(); nfuncts = 0; #ifdef OVERLAYS if (ovlp) loadsyms(); #endif intern (&mainfunct); phase = INMEM; buforg = origin; jtsaved = 0; nomorecode = FALSE; /* no data yet written to disk */ maxdata.u = lastdata = 0; /* no data encountered so far */ } cmdline (argc, argv) /* process command line */ int argc; char **argv; { int i, progp; if (argc == 1) { puts ("Usage is:\n"); puts (" l2 {program files} [-l {library files} ] "); puts ("[-s {library files} ]\n"); puts ("\t[-m ] [-f ] [-org ]"); puts (" [-t ] [-e ]\n"); puts ("\t[-d] [-w | -wa | -ws] [-n][-z][-*]\n"); #ifdef OVERLAYS puts ("\t[-ovl ]"); #endif #ifdef MARC puts ("\t[-marc]"); #endif puts ("\t[-i]"); lexit (1); } progp = TRUE; for (i=1; i < argc; ++i) { if (argv[i][0] == '-') { if (!strcmp (argv[i], "-F")) { if (++i>=argc) Fatal ("-f argument missing.\n"); sscanf (argv[i], "%d", &maxfuncts); } else if (!strcmp (argv[i], "-L")) progp = FALSE; else if (!strcmp (argv[i], "-S")) { progp = FALSE; SysNum = nlibs; } else if (!strcmp (argv[i], "-M")) { if (++i>=argc) Fatal ("-m argument missing.\n"); strcpy (&mainfunct, argv[i]); } #ifdef MARC else if (!strcmp (argv[i], "-MARC")) { maxmemp = TRUE; marcp = TRUE; } #endif else if (!strcmp (argv[i], "-ORG")) { if (++i>=argc) Fatal ("-org argument missing.\n"); sscanf (argv[i], "%x", &origin); } else if (!strcmp (argv[i], "-N")) Nflag = TRUE; else if (!strcmp (argv[i], "-Z")) Zflag = TRUE; else if (!strcmp (argv[i], "-*")) Wflag = TRUE; else if (!strcmp (argv[i], "-T")) { if (++i >= argc) Fatal ("-t argument missing.\n"); Tflag = TRUE; sscanf (argv[i], "%x", &Tval); } else if (!strcmp (argv[i], "-E")) { if (++i >= argc) Fatal ("-e argument missing.\n"); sscanf (argv[i], "%x", &exts.u); } #ifdef OVERLAYS else if (!strcmp (argv[i], "-OVL")) { ovlp = TRUE; if (i + 2 >= argc) Fatal ("-ovl argument missing.\n"); strcpy (&symsfile, argv[++i]); sscanf (argv[++i], "%x", &origin); } #endif else if (!strcmp (argv[i], "-D")) Dflag = TRUE; else if (!strcmp (argv[i], "-W")) symsp = TRUE; else if (!strcmp (argv[i], "-WA")) symsp = appstatsp = TRUE; else if (!strcmp (argv[i], "-WS")) symsp = sepstatsp = TRUE; else if (!strcmp (argv[i], "-I")) printf("-I ignored, must be last on line\n"); else printf ("Unknown option: '%s'\n", argv[i]); } else { if (progp) strcpy (&progfiles[nprogs++], argv[i]); else strcpy (&libfiles[nlibs++], argv[i]); } } if (ovlp) strcpy(&mainfunct, &progfiles[0][2*(progfiles[0][1]==':')] ); if (Dflag || SysNum!=-1) Dflag = symsp = TRUE; deflibindex = nlibs; #ifdef MARC #define NDEFF 3 strcpy(&libfiles[nlibs++], marcp ? DEF_DRIVE(DEFFM) : DEF_DRIVE(DEFF)); strcpy(&libfiles[nlibs++], marcp ? DEF_DRIVE(DEFF2M): DEF_DRIVE(DEFF2)); strcpy(&libfiles[nlibs++], marcp ? DEF_DRIVE(DEFF3M): DEF_DRIVE(DEFF3)); #else #define NDEFF 4 /*3 deff's + bdsclib*/ strcpy(&libfiles[nlibs++], DEF_DRIVE(BDSCLIB.REL) ); strcpy(&libfiles[nlibs++], DEF_DRIVE(DEFF) ); strcpy(&libfiles[nlibs++], DEF_DRIVE(DEFF2) ); strcpy(&libfiles[nlibs++], DEF_DRIVE(DEFF3) ); #endif } loadccc() /* load C.CCC (runtime library) */ { union ptr temp; unsigned len; codend.b = lspace; if (!ovlp) { #ifdef MARC if (copen (&ibuf, marcp ? DEF_DRIVE(CM.CCC) : DEF_DRIVE(C.CCC) ) < 0) #else if (copen (&ibuf, Wflag ? DEF_DRIVE(WILD.CCC) : DEF_DRIVE(C.CCC) ) < 0) #endif Fatal ("Can't open %s\n", DEF_DRIVE(C.CCC) ); if (cread (&ibuf, lspace, 128) < 128) /* read a sector */ Fatal ("C.CCC: read error!\n"); temp.b = lspace + 0x17; len = *temp.w; /* how long is it? */ cread (&ibuf, lspace + 128, len - 128); /* read rest */ codend.b += len; cclose (&ibuf); } else codend.i++->opcode = JMP; } linkprog() /* link in all program files */ { int i; union ptr dirtmp; struct funct *fnct; for (i=0; iflinkedp) linkmod (fnct, lodstart + *dirtmp.w - 0x205,0); else if (phase != DISK2) { puts ("Duplicate program function '"); puts (&fnct->fname); puts ("', not linked.\n"); } dirtmp.w++; } /* intern & link it */ cclose (&ibuf); } } linklibs() /* link in library files */ { int ifile; anynotlinked = TRUE; minlk = 0; for (ifile=0; anynotlinked && ifile < nlibs; ++ifile) { if (ifile==SysNum) SysStat = TRUE; scanlib (ifile); } while (missingp()) { anynotlinked = TRUE; puts ("Enter the name of a file to be searched or CR: "); gets (&libfiles[nlibs]); if (libfiles[nlibs][0]) { SysStat = FALSE; scanlib (nlibs); } else { if (SysNum!=-1) SysStat = TRUE; for (ifile=0; anynotlinked && ifile codend.b) codend.b = lastdata; if (cwrite (&obuf, lspace, codend.b - lspace) == -1 || cflush (&obuf) < 0) Fatal ("Disk write error!\n"); cclose (&obuf); stats (STDOUT); } readprog (mainp) /* read in a program file */ FLAG mainp; { char extp; /* was -e used? */ char *extstmp; union ptr dir; unsigned len; if (cread (&ibuf, &fdir, 512) < 512) /* read directory */ Fatal ("-- read error!\n"); if (phase == INMEM && mainp) { cread (&ibuf, &extp, 1); cread (&ibuf, &extstmp, 2); cread (&ibuf, &extspc, 2); if (extp) exts.b = extstmp; else exts.b = 0; /* will be set later */ } else cseek (&ibuf, 5, RELATIVE); for (dir.b=&fdir; *dir.b != 0x80; nextd (&dir)); /* find end of dir */ ++dir.b; len = *dir.w - 0x205; readobj (len, TRUE); } readobj (len, crllod) /* read in an object (program or lib funct) */ unsigned len; FLAG crllod; /*3 read CRL? (or just check len for REL load) */ { if (phase == DISK1 || codend.b + len >= lspcend) { if (phase == INMEM) { puts("\n** Out of memory--switching to disk mode **\n"); phase = DISK1; } if (phase == DISK2) { if (cwrite (&obuf, lspace, codend.b - lspace) == -1) Fatal ("Disk write error!\n"); } buforg += codend.b - lspace; codend.b = lspace; if (codend.b + len >= lspcend) Fatal ("Module won't fit in memory at all!\n"); } lodstart = codend.b; if (crllod && cread (&ibuf, lodstart, len) < len) Fatal ("-- read error!\n"); } scanlib (ifile) int ifile; { int i; union ptr dirtmp; FLAG morerel; /*3 keep track of whether have read eof link in REL */ if (minlk >= nfuncts) { anynotlinked = FALSE; return; } makecrl (&libfiles[ifile]); if (copen (&ibuf, libfiles[ifile]) < 0) { if (ifile != deflibindex + (NDEFF-1)) printf ("Can't open %s\n", libfiles[ifile]); return; } printf ("Scanning %s\n", &libfiles[ifile]); if (!relflag && cread (&ibuf, &fdir, 512) < 512) /* read directory */ Fatal ("-- Read error!\n"); anynotlinked = FALSE; if (relflag) { morerel = readrel(FALSE); /* position at first link 0 item */ i = minlk; while (i < nfuncts && morerel) { if (ftab[i].flinkedp) /* a feeble attempt at opt'n */ { if (i == minlk) minlk++; } else { anynotlinked = TRUE; if (!strcmp7(cd->lsymbol,&ftab[i].fname) ) /* load this module and go back to first unlinked f'n*/ { morerel = readrel(2); i = minlk-1; } } i++; /* when last unlinked function has been compared, scan to next module (if there is one) and go back to first unlinked function */ if (i == nfuncts) { if (morerel) morerel = readrel(FALSE); i = minlk; } } /* make sure to read to end of REL file */ while (morerel) morerel = readrel(FALSE); } else for (i=minlk; iitemtype) { case 0: #if RTRACE printf("code %02x\n", cd->dataword); #endif if (loadit) { if (currseg == DSEG) { dataroom(1); if (phase != DISK1) *datapt.b++ = cd->dataword; } else *loadpt.b++ = cd->dataword; } break; case 1: #if RTRACE printf("addr %04x seg %d\n", cd->dataword,cd->segtype); #endif if (loadit) { temp.b = (cd->segtype==DSEG) ? (exts.b + cd->dataword) : cd->dataword + modoffset + finalloc.b; if (currseg == CSEG) *loadpt.w++ = temp.b; else if (currseg == DSEG) { dataroom(2); if (phase != DISK1) *datapt.w++ = temp.b; } } break; case 2: litem = cd->linktype; #if RTRACE printf("link %d : ", litem); if (litem >= 5 && litem <= 14) printf("addr %x seg %d ", cd->dataword,cd->segtype); if (litem <= 7) for (i = 0; i < cd->lsymlen; i++ ) putchar(0x7F & (cd->lsymbol[i])); printf("\n"); #endif if (!loadit) { if (litem == 0) return(TRUE); } else switch(litem) { case 0: /* PUBLIC SYMBOL */ modoffset = loadpt.b - lodstart; if (loadit > 2) { codend.b = loadpt.b; return(TRUE); } break; case 7: /* ENTRY POINT */ fnct = intern(cd->lsymbol); linkmod(fnct, lodstart, cd->dataword + modoffset); /* during scanlib, signal stop at next link item 0 */ if (loadit > 1) loadit++; break; case 6: /* EXTERNAL REFERENCE */ /* a symbol declared "ext" but not referenced will be link item 6, seg 0=ASEG - it should be ignored */ if (cd->segtype != ASEG) { fnct = intern(cd->lsymbol); temp.b = raddr.b = lodstart + cd->dataword + modoffset; if (!fnct->flinkedp && phase == INMEM) { /* find end of chain in REL file */ while (*raddr.w) { #if RTRACE printf("*chaining addr %x, contents %x\n", raddr.b, *raddr.w); #endif /* convert to real memory address */ *raddr.w = (*raddr.w - modoffset) + (lspace - buforg); /* and descend one link */ raddr.b = *raddr.w; } /* point end of chain to head of old chain */ *raddr.w = fnct->faddr; /* enter here in ftab as new chain head */ fnct->faddr = temp.b; } else /* put addr in each link of REL chain */ while (temp.b) { temp.b = *raddr.w; *raddr.w = fnct->faddr; #if RTRACE printf("*Changed %x from %x to %x\n", raddr.b, temp.b, *raddr.w); #endif raddr.u = (temp.u - modoffset) + (lspace - buforg); } } /* end of if (cd->segtype == CSEG) */ break; /* for case litem == 6 */ case 11: /* ORG */ if ( (currseg = cd->segtype) == CSEG) loadpt.b = lodstart + modoffset + cd->dataword; else if (currseg == DSEG) { if (!exts.b) Fatal("Need -e
for DSEG"); if (maxdata.u < cd->dataword) maxdata.u = cd->dataword; datapt.b = (exts.u + cd->dataword) + (lspace - buforg); if (datapt.u < lspace) Fatal("DSEG addr. too low"); } break; case 13: /* LENGTH OF CODE IN MODULE */ if (nomorecode) Fatal("Cannot write code because of preceding DSEG data"); if ( loadit && (phase == DISK1 || loadpt.b + cd->dataword >= lspcend) ) { codend.b = loadpt.b; readobj(cd->dataword, FALSE); loadpt.b = codend.b; modoffset = 0; } break; case 3: /* REQUEST */ if (loadit && phase != DISK2) { temp.b = &libfiles[nlibs++]; strcpy7(temp.b, cd->lsymbol); str7tont(temp.b); *(temp.b+7) = '\0'; makeext(temp.b,"REL"); #if RTRACE printf("Requested %s\n", temp.b); #endif } break; /* other cases for possible future use are - 9 - offset for external reference - 4 - arithmetic with external symbols (1st letter of lsymbol gives the operation to perform) - 2 - gives module name - 10 - gives length of data */ default: break; } /* end switch(litem) */ break; default: Fatal("Illegal code in REL file\n"); break; } /* end switch(cd->itemtype) */ } /* end while(litem != 15) */ if (loadit) codend.b = loadpt.b; return(FALSE); } /*3 check room for DSEG data */ dataroom(len) char len; { if (phase != DISK1 && datapt.b + len >= lspcend) if (phase == INMEM) phase = DISK1; else { codend.b = datapt.b; readobj(len, FALSE); datapt.b = codend.b; nomorecode = TRUE; } lastdata = datapt.b + len; Zflag = TRUE; } /*3 global return value from cread1 */ char rrch ; /*3 called by rdrl to read one byte from REL file */ cread1() { if (cread(&ibuf, &rrch, 1) != 1) Fatal("Premature end of REL file\n"); return(rrch); } linkmod (fnct, modstart, foffset) /* link in a module */ struct funct *fnct; union ptr modstart; /* loc. of module in memory */ unsigned foffset; /*3 for REL files with late entry pts*/ { union ptr temp, jump, /* jump table temp */ body, /* loc. of function in memory */ code, /* loc. of code proper in mem. */ finalloc; /* runtime loc. of function */ unsigned flen, nrelocs, jtsiz, offset; fnct->flinkedp = LINKED; if (phase != DISK2) { finalloc.b = (relflag && cd->segtype == DSEG)? exts.u + foffset : codend.b - lspace + buforg + foffset; if (phase == INMEM) chase (fnct->faddr, finalloc.b); fnct->faddr = finalloc.b; } else finalloc.b = fnct->faddr; /* REL file relocation already done */ if (relflag) return; body.b = modstart.b + strlen(modstart.b) + 3; /* loc. of fn body */ jump.i = body.i + (*modstart.b ? 1 : 0); for (temp.b = modstart.b; *temp.b; skip7(&temp)) { jump.i->address = intern (temp.b); ++jump.i; } ++temp.b; flen = *temp.w; code.b = jump.b; temp.b = body.b + flen; /* loc. of reloc parameters */ nrelocs = *temp.w++; jtsiz = code.b - body.b; if (Dflag && fnct->fdebug) { if (phase!=DISK1) { codend.i->opcode = (0307 + (8*RST_NUM)); codend.i->address = 0; finalloc.b += 3; } codend.b += 3; } offset = code.b - codend.b; if (phase != DISK1) while (nrelocs--) relocate (*temp.w++, body.b, jtsiz, finalloc.b, offset, flen); flen -= jtsiz; if (phase != DISK2) jtsaved += jtsiz; if (phase != DISK1) movmem (code.b, codend.b, flen); codend.b += flen; } relocate (param, body, jtsiz, base, offset, flen) /* do a relocation!! */ unsigned param, jtsiz, base, offset, flen; union ptr body; { union ptr instr, /* instruction involved */ ref; /* jump table link */ struct funct *fnct; /* if (param == 1) return; /* don't reloc jt skip */*/ instr.b = body.b + param - 1; if (instr.i->address >= jtsiz) instr.i->address += base - jtsiz; /* vanilla case */ else { ref.b = instr.i->address + body.u; if (instr.i->opcode == LHLD) { instr.i->opcode = LXIH; --ref.b; } fnct = ref.i->address; instr.i->address = fnct->faddr; /* link in */ if (!fnct->flinkedp && phase == INMEM) fnct->faddr = instr.b + 1 - offset; /* new list head */ } } intern (name) /* intern a function name in the table */ char *name; { struct funct *fptr; if (*name == 0x9D) name = "MAIN"; /* Why, Leor, WHY??? */ for (fptr = &ftab[nfuncts-1]; fptr >= ftab; --fptr) if (!strcmp7 (name, fptr->fname)) break; if (fptr < ftab) { if (nfuncts >= maxfuncts) Fatal("Too many functions (limit is %d)!\n", maxfuncts); fptr = &ftab[nfuncts]; strcpy7 (fptr->fname, name); str7tont (fptr->fname); fptr->flinkedp = FALSE; fptr->faddr = NULL; fptr->fdebug = !SysStat; ++nfuncts; } return (fptr); } dirsearch (name) /* search directory for a function */ char *name; { union ptr temp; for (temp.b = &fdir; *temp.b != 0x80; nextd (&temp)) if (!strcmp7 (name, temp.b)) return (temp.b); return (NULL); } nextd (ptrp) /* move this pointer to the next dir entry */ union ptr *ptrp; { skip7 (ptrp); ++(*ptrp).w; } chase (head, loc) /* chase chain of refs to function */ union ptr head; unsigned loc; { union ptr temp; while (head.w) { temp.w = *head.w; *head.w = loc; head.u = temp.u; } } wrtcom() /* write out com file (from in-mem link) */ { hackccc(); if (!ovlp) makeext (&progfiles[0], "COM"); else makeext (&progfiles[0], "OVL"); if (lastdata > codend.b) codend.b = lastdata; if (!ccreat (&obuf, &progfiles[0]) < 0 || cwrite (&obuf, lspace, codend.b - lspace) == -1 || cflush (&obuf) < 0) Fatal ("Disk write error!\n"); cclose (&obuf); stats (STDOUT); } hackccc() /* store various goodies in C.CCC code */ { union ptr temp; struct funct *fptr; temp.b = lspace; fptr = intern (&mainfunct); if (!ovlp) { #ifdef MARC if (!marcp) { #endif if (Tflag) { temp.i->opcode = LXISP; temp.i->address = Tval; } else if (Nflag) { temp.i->opcode = JMP; temp.i->address = SNOBSP; temp.b = lspace + 0x09; temp.i->opcode = JMP; temp.i->address = NOBRET; } else { temp.i->opcode = LHLD; temp.i->address = origin - 0x100 + 6; (++temp.i)->opcode = SPHL; } if (Zflag) { temp.b = lspace + 0x41; temp.i->opcode = RET; } temp.b = lspace + 0xF; /* main function address */ temp.i->address = fptr->faddr; #ifdef MARC } #endif temp.b = lspace + 0x15; *temp.w++ = exts.u; ++temp.w; *temp.w++ = acodend.u; if (extspc < maxdata.u) extspc = maxdata.u; *temp.w++ = exts.u + extspc; } else temp.i->address = fptr->faddr; /* that's a JMP */ #ifdef MARC if (maxmemp) { temp.b = lspace + 0x258; temp.i->opcode = CALL; temp.i->address = 0x50; } #endif } wrtsyms() /* write out symbol table */ { int i, fd, compar(); qsort (ftab, nfuncts, sizeof(*ftab), &compar); makeext (&progfiles[0], "SYM"); if (fcreat (&progfiles[0], &symbuf) < 0) Fatal ("Can't create .SYM file\n"); for (i=0; i < nfuncts; ++i) { puthex (ftab[i].faddr, &symbuf); putc (' ', &symbuf); fputs (&ftab[i].fname, &symbuf); if (i % 4 == 3) fputs ("\n", &symbuf); else { if (strlen (&ftab[i].fname) < 3) putc ('\t', &symbuf); putc ('\t', &symbuf); } } if (i % 4) fputs ("\n", &symbuf); if (appstatsp) stats (&symbuf); putc (CPMEOF, &symbuf); fflush (&symbuf); fclose (&symbuf); if (sepstatsp) { makeext (&progfiles[0], "LNK"); if (fcreat (&progfiles[0], &symbuf) < 0) Fatal ("Can't create .LNK file\n"); stats (&symbuf); putc (CPMEOF, &symbuf); fflush (&symbuf); fclose (&symbuf); } } compar (f1, f2) /* compare two symbol table entries by name */ struct funct *f1, *f2; { /* return (strcmp (&f1->fname, &f2->fname)); alphabetical order */ return (f1->faddr > f2->faddr); /* memory order */ } #ifdef OVERLAYS loadsyms() /* load base symbol table (for overlay) */ { /* symbol table must be empty! */ int nread; FLAG done; char *c; makeext (&symsfile, "SYM"); if (fopen (&symsfile, &symbuf) < 0) Fatal ("Can't open %s.\n", &symsfile); done = FALSE; while (!done) { nread = fscanf (&symbuf, "%x %s\t%x %s\t%x %s\t%x %s\n", &(ftab[nfuncts].faddr), &(ftab[nfuncts].fname), &(ftab[nfuncts+1].faddr), &(ftab[nfuncts+1].fname), &(ftab[nfuncts+2].faddr), &(ftab[nfuncts+2].fname), &(ftab[nfuncts+3].faddr), &(ftab[nfuncts+3].fname)); nread /= 2; if (nread < 4) done = TRUE; while (nread-- > 0) ftab[nfuncts++].flinkedp = EXTERNAL; } fclose (&symbuf); } #endif stats (chan) /* print statistics on chan */ int chan; { unsigned temp, *tptr; tptr = 6; fprintf (chan, "\n\nLink statistics:\n"); fprintf (chan, " Number of functions: %d\n", nfuncts); fprintf (chan, " Code ends at: 0x%x\n", acodend.u); fprintf (chan, " Externals begin at: 0x%x\n", exts.u); fprintf (chan, " Externals end at: 0x%x\n", exts.u + extspc); fprintf (chan, " End of current TPA: 0x%x\n", *tptr); fprintf (chan, " Jump table bytes saved: 0x%x\n", jtsaved); temp = lspcend; if (phase == INMEM) fprintf (chan, " Link space remaining: %dK\n", (temp - codend.u) / 1024); } /*3 force extension to .CRL unless is .REL */ makecrl(fname) char *fname; { /*3 keep REL extension and raise flag */ if ( index(fname,".REL") > 0 || index(fname,".rel") > 0 ) relflag = TRUE; else { makeext (fname, "CRL"); relflag = FALSE; } } makeext (fname, ext) /* force a file extension to ext */ char *fname, *ext; { while (*fname && (*fname != '.')) { *fname = toupper (*fname); /* upcase as well */ ++fname; } *fname++ = '.'; strcpy (fname, ext); } strcmp7(s1, s2) char *s1, *s2; { /* compare two strings, either bit-7-terminated or null-terminated */ for (; (_c1 = *s1) == *s2; s1++, s2++) if ( (0x80 & _c1) || !_c1) return 0; /*3 8th char of REL symbol is wild */ if ( (_c1 &= 0x7F) == WILDC || (_c2 = 0x7F & *s2) == WILDC ) return 0; /* if ((_c1 &= 0x7F) < (_c2 = 0x7F & *s2)) return -1;*/ if (_c1 < _c2) return -1; if (_c1 > _c2) return 1; _end1 = (*s1 & 0x80) || !*(s1+1) || *(s1+1) == WILDC; _end2 = (*s2 & 0x80) || !*(s2+1) || *(s2+1) == WILDC; if (_end2 && !_end1) return 1; if (_end1 && !_end2) return -1; /* if (_end1 && _end2) */ return 0; } strcpy7 (s1, s2) /* copy s2 into s1 */ char *s1, *s2; { do { *s1 = *s2; if (!*(s2+1)) { /* works even if */ *s1 |= 0x80; /* s2 is null-term */ break; } ++s1; } while (!(*s2++ & 0x80)); } skip7 (ptr7) /* move this pointer past a string */ char **ptr7; { while (!(*(*ptr7)++ & 0x80)); } str7tont (s) /* add null at end */ char *s; { while (!(*s & 0x80)) { if (!*s) return; /* already nul term! */ s++; } *s = *s & 0x7F; *++s = NUL; } puthex (n, obuf) /* output a hex word, with leading 0s */ unsigned n; char *obuf; { int i, nyb; for (i = 3; i >= 0; --i) { nyb = (n >> (i * 4)) & 0xF; nyb += (nyb > 9) ? 'A' - 10 : '0'; putc (nyb, obuf); } } Fatal (arg1, arg2, arg3, arg4) /* lose, lose */ char *arg1, *arg2, *arg3, *arg4; { printf (arg1, arg2, arg3, arg4); lexit (1); } lexit (status) /* exit the program */ int status; { if (status == 1) unlink (SUB_FILE); exit(); /* bye! */ } /* END OF L2.C */