ld09: updates
authorAlan Cox <alan@linux.intel.com>
Wed, 29 Jun 2016 16:41:56 +0000 (17:41 +0100)
committerAlan Cox <alan@linux.intel.com>
Wed, 29 Jun 2016 16:41:56 +0000 (17:41 +0100)
Applications/ld09/Makefile.z80
Applications/ld09/mkar.c
Applications/ld09/objdump86.c
Applications/ld09/writefuzix.c [new file with mode: 0644]

index a8f90c0..f859d4d 100644 (file)
@@ -8,11 +8,14 @@ DEFS  =
 OBJS= dumps.rel io.rel ld.rel readobj.rel table.rel typeconv.rel linksyms.rel \
       writefuzix.rel
 
-all: ld09
+all: ld09 mkar
 
 ld09: $(OBJS)
        $(CC) -o $@ $(OBJS)
 
+mkar: mkar.rel
+       $(CC) -o $@ $<
+
 clean realclean clobber:
        rm -f *.rel *.lst *.sym *.asm ld09 ld09r objchop catimage objdump09 *~ *.bin *.lk *.map *.noi
 
index 2ca9855..622f19d 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef __STDC__
 #include <stdlib.h>
 #include <unistd.h>
-#else
-#include <malloc.h>
-#endif
+#include <fcntl.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
-#include "type.h"
 #include "ar.h"
 
 static struct ar_hdr arbuf;
 
-#ifdef __STDC__
-void
-ld86r(int argc, char ** argv)
-#else
-ld86r(argc, argv)
-   int argc; char ** argv;
-#endif
+static char buf[512];
+static const char *path;
+static char space = ' ';
+
+static void twrite(int fd, const void *p, int len)
+{
+       if (write(fd, p, len) != len)
+               err(1, "%s: write failed", path);
+}
+
+static void copy_file(const char *p, int fd)
+{
+       int ifd;
+       ssize_t l;
+
+       if ((ifd = open(p, O_RDONLY)) == -1) {
+               perror(p);
+               exit(1);
+       }
+       while ((l = read(ifd, buf, sizeof(buf))) > 1)
+               twrite(fd, buf, l);
+       if (l == 1)
+               twrite(fd, &space, 1);
+       if (l == -1) {
+               perror("read");
+               exit(1);
+       }
+       close(ifd);
+}
+
+int main(int argc, char *argv[])
 {
-char buf[128];
-   FILE * fd, * ifd;
-   struct stat st;
-   int ar, libarg=0, need_o = 0, got_o = 0;
-
-   for(ar=1; ar<argc; ar++) if( argv[ar][0] == '-' )
-   {
-      if( argv[ar][1] == 'r' ) need_o = 1;
-      if( argv[ar][1] == 'o' ) { got_o++; libarg = 0; }
-   }
-   else
-   {
-      if( libarg == 0 ) libarg = ar;
-   }
-
-   if( libarg == 0 || got_o > 1 || need_o > got_o )
-      fatalerror("-o option required for -r");
-
-   if( (fd =fopen(argv[libarg], "wb")) == 0 ) fatalerror("Cannot open archive");
-   if( fwrite(ARMAG, 1, SARMAG, fd) != SARMAG)  fatalerror("Cannot write magic");
-
-   for(ar=1; ar<argc; ar++) if( ar != libarg && argv[ar][0] != '-' )
-   {
-      char * ptr;
-      /* FIXME: strlcpy etc */
-      if( stat(argv[ar], &st) < 0 ) fatalerror("Cannot stat object");
-      if((ptr=strchr(argv[ar], '/'))) ptr++; else ptr=argv[ar];
-      memset(&arbuf, ' ', sizeof(arbuf));
-      strcpy(buf, ptr); strcat(buf, "/                 ");
-      strncpy(arbuf.ar_name, buf, sizeof(arbuf.ar_name));
-     
-      /* FIXME: use ltoa ita for Fuzix */
-      snprintf(arbuf.ar_date, 12, "%-12ld", (long)st.st_mtime);
-      snprintf(arbuf.ar_uid, 6, "%-6d", (int)(st.st_uid%1000000L));
-      snprintf(arbuf.ar_gid, 6, "%-6d", (int)(st.st_gid%1000000L));
-      snprintf(arbuf.ar_mode, 8, "%-8lo", (long)st.st_mode);
-      snprintf(arbuf.ar_size, 10, "%-10ld", (long)st.st_size);
-      memcpy(arbuf.ar_fmag, ARFMAG, sizeof(arbuf.ar_fmag));
-
-      if( fwrite(&arbuf, 1, sizeof(arbuf), fd) != sizeof(arbuf) )
-         fatalerror("Cannot write header");
-
-      /* FIXME: can overflow and off_t may not be size_t*/
-      ptr = malloc(st.st_size+2);
-      if( ptr == 0 ) fatalerror("Out of memory");
-      ptr[st.st_size] = ' ';
-      if( (ifd = fopen(argv[ar], "rb")) == 0 ) fatalerror("Cannot open input");
-      if( fread(ptr, 1, st.st_size, ifd) != st.st_size )
-         fatalerror("Cannot read input file");
-      fclose(ifd);
-
-      /* FIXME: may overflow */
-      if( st.st_size&1 ) st.st_size++;
-      if( fwrite(ptr, 1, st.st_size, fd) != st.st_size )
-         fatalerror("Cannot write output file");
-      free(ptr);
-   }
-   fclose(fd);
-   exit(0);
+       char buf[128];
+       int fd;
+       struct stat st;
+       int ar, libarg = 0, need_o = 0, got_o = 0;
+
+       for (ar = 1; ar < argc; ar++) {
+               if (argv[ar][0] == '-') {
+                       if (argv[ar][1] == 'r')
+                               need_o = 1;
+                       if (argv[ar][1] == 'o') {
+                               got_o++;
+                               libarg = 0;
+                       }
+               } else {
+                       if (libarg == 0)
+                               libarg = ar;
+               }
+        }
+       if (libarg == 0 || got_o > 1 || need_o > got_o)
+               errx(1, "-o option required for -r");
+
+        path = argv[libarg];
+
+       if ((fd = open(path, O_WRONLY)) == -1)
+               err(1, "unable to open %s", path);
+
+
+       twrite(fd, ARMAG, SARMAG);
+
+       for (ar = 1; ar < argc; ar++) {
+               if (ar != libarg && argv[ar][0] != '-') {
+                       char *ptr;
+                       if (stat(argv[ar], &st) < 0)
+                               err(1, "cannot stat object %s", argv[ar]);
+                       if ((ptr = strrchr(argv[ar], '/')))
+                               ptr++;
+                       else
+                               ptr = argv[ar];
+                       memset(&arbuf, ' ', sizeof(arbuf));
+                       snprintf(buf, sizeof(buf), "%s/                 ", ptr);
+                       strncpy(arbuf.ar_name, buf, sizeof(arbuf.ar_name));
+
+                       snprintf(arbuf.ar_date, 12, "%-12ld",
+                                (long) st.st_mtime);
+                       snprintf(arbuf.ar_uid, 6, "%-6d",
+                                (int) (st.st_uid % 1000000L));
+                       snprintf(arbuf.ar_gid, 6, "%-6d",
+                                (int) (st.st_gid % 1000000L));
+                       snprintf(arbuf.ar_mode, 8, "%-8lo",
+                                (long) st.st_mode);
+                       snprintf(arbuf.ar_size, 10, "%-10ld",
+                                (long) st.st_size);
+                       memcpy(arbuf.ar_fmag, ARFMAG,
+                              sizeof(arbuf.ar_fmag));
+
+                       twrite(fd, &arbuf, sizeof(arbuf));
+                       copy_file(argv[ar], fd);
+               }
+       }
+       close(fd);
+       exit(0);
 }
index 89f4e2d..f73d820 100644 (file)
  *
  * Copyright (c) 1999 by Greg Haerr <greg@censoft.com>
  * Added archive file reading capabilties
+ *
+ * Modified for Fuzix by Alan Cox 2016.
  */
 
 #include <stdio.h>
-#ifdef __STDC__
 #include <stdlib.h>
-#else
-#include <malloc.h>
-#endif
 #include <string.h>
 #include "const.h"
 #include "ar.h"
 #include "obj.h"
 
-FILE * ifd;
-char * ifname;
-
-#ifdef __STDC__
-#define _(x) x
-#else 
-#define _(x) ()
-#endif 
-
-long get_long _((void));
-long get_sized _((int sz));
-unsigned int get_word _((void));
-int get_byte _((void));
-int main _((int argc, char**argv));
-void do_file _((char * fname));
-long read_arheader _((char *archentry));
-void do_module _((char * fname, char *archive));
-int error _((char * str));
-int read_objheader _((void));
-int read_sectheader _((void));
-int read_syms _((void));
-void disp_sectheader _((void));
-int disp_syms _((void));
-void read_databytes _((void));
-void hex_output _((int ch));
-void fetch_aout_hdr _((void));
-void dump_aout _((void));
-void size_aout _((void));
-void nm_aout _((void));
+FILE *ifd;
+char *ifname;
+
+long get_long(void);
+long get_sized(int sz);
+unsigned int get_word(void);
+int get_byte(void);
+int main(int argc, char**argv);
+void do_file(char * fname);
+long read_arheader(char *archentry);
+void do_module(char * fname, char *archive);
+int error(char * str);
+int read_objheader(void);
+int read_sectheader(void);
+int read_syms(void);
+void disp_sectheader(void);
+int disp_syms(void);
+void read_databytes(void);
+void hex_output(int ch);
+void fetch_aout_hdr(void);
+void dump_aout(void);
+void size_aout(void);
+void nm_aout(void);
 
 int  obj_ver;
 int  sections;
@@ -84,10 +76,7 @@ int opt_o;
 long size_text, size_data, size_bss;
 long tot_size_text=0, tot_size_data=0, tot_size_bss=0;
 
-int
-main(argc, argv)
-int argc;
-char ** argv;
+int main(int argc, char *argv[])
 {
    int ar;
    char * p;
@@ -132,9 +121,7 @@ char ** argv;
    return 0;
 }
 
-void
-do_file(fname)
-char * fname;
+void do_file(char * fname)
 {
    unsigned int magic;
    long        filelength;
@@ -178,9 +165,7 @@ char * fname;
 }
 
 /* read archive header and return length */
-long
-read_arheader(archentry)
-char *archentry;
+long read_arheader(char *archentry)
 {
    char *        endptr;
    struct ar_hdr  arheader;
@@ -197,10 +182,7 @@ char *archentry;
    return strtoul(arheader.ar_size, (char **)NULL, 0);
 }
 
-void
-do_module(fname, archive)
-char * fname;
-char * archive;
+void do_module(char *fname, char *archive)
 {
    int  modno, i;
 
@@ -285,9 +267,7 @@ char * archive;
    symnames = 0;
 }
 
-int
-error(str)
-char * str;
+int error(char *str)
 {
    switch( display_mode )
    {
@@ -300,14 +280,14 @@ char * str;
    return -1;
 }
 
-int
-read_objheader()
+int read_objheader(void)
 {
    unsigned char buf[5];
 
    if( fread(buf, 1, 5, ifd) != 5 )
       return error("Cannot read object header");
-
+#if 0
+   /* FIXME: Fuzix not ELKS support needed */
    if( buf[0] != 0xA3 || buf[1] != 0x86 )
    {
       if( buf[0] == 1 && buf[1] == 3 )
@@ -317,7 +297,7 @@ read_objheader()
       }
       return error("Bad magic number");
    }
-
+#endif
    if( (unsigned char)(buf[0] + buf[1] + buf[2] + buf[3]) != buf[4] )
       return error("Bad header checksum");
 
@@ -326,8 +306,7 @@ read_objheader()
    return 0;
 }
 
-int
-read_sectheader()
+int read_sectheader(void)
 {
    long ssenc;
    int i;
@@ -353,8 +332,7 @@ read_sectheader()
    return 0;
 }
 
-void
-disp_sectheader()
+void disp_sectheader(void)
 {
    int i;
    if( display_mode ) return;
@@ -373,13 +351,13 @@ disp_sectheader()
    printf("SYMS %u\n", num_syms);
 }
 
-int
-read_syms()
+int read_syms(void)
 {
    int i;
 
    if( num_syms < 0 ) return error("Bad symbol table");
 
+   /* FIXME: overflows */
    symnames = malloc(num_syms*sizeof(char*)+1);
    if( symnames == 0 ) return error("Out of memory");
 
@@ -407,8 +385,7 @@ read_syms()
    return 0;
 }
 
-int
-disp_syms()
+int disp_syms(void)
 {
    int i;
 
@@ -482,10 +459,10 @@ disp_syms()
    return 0;
 }
 
-void
-read_databytes()
-{
 static char * relstr[] = {"ERR", "DB", "DW", "DD"};
+
+void read_databytes(void)
+{
    long l, cpos;
    int ch, i;
    int curseg = 0;
@@ -568,9 +545,7 @@ break_break:;
    fseek(ifd, cpos, 0);
 }
 
-long
-get_sized(sz)
-int sz;
+long get_sized(int sz)
 {
    switch(sz)
    {
@@ -582,8 +557,7 @@ int sz;
    return -1;
 }
 
-long
-get_long()
+long get_long(void)
 {
    long retv = 0;
    int i;
@@ -598,8 +572,7 @@ get_long()
    return retv;
 }
 
-unsigned int
-get_word()
+unsigned int get_word(void)
 {
    long retv = 0;
    int i;
@@ -616,8 +589,7 @@ get_word()
    return retv;
 }
 
-int
-get_byte()
+int get_byte(void)
 {
    int v = getc(ifd);
    if (v == EOF) return -1; 
@@ -625,9 +597,7 @@ get_byte()
    return v;
 }
 
-void
-hex_output(ch)
-int ch;
+void hex_output(int ch)
 {
 static char linebuf[80];
 static char buf[20];
@@ -658,13 +628,14 @@ static int pos = 0;
 
 /************************************************************************/
 /* ELKS a.out versions
+ *
+ * TODO: Fuzix headers
  */
 
 long header[12];
 int  h_len, h_flgs, h_cpu;
 
-void
-fetch_aout_hdr()
+void fetch_aout_hdr(void)
 {
    int i;
 
@@ -685,8 +656,7 @@ fetch_aout_hdr()
    }
 }
 
-void
-dump_aout()
+void dump_aout(void)
 {
 static char * cpu[] = { "unknown", "8086", "m68k", "ns16k", "i386", "sparc" };
 static char * byteord[] = { "LITTLE_ENDIAN", "(2143)","(3412)","BIG_ENDIAN" };
@@ -756,8 +726,7 @@ static char * byteord[] = { "LITTLE_ENDIAN", "(2143)","(3412)","BIG_ENDIAN" };
    hex_output(EOF);
 }
 
-void
-size_aout()
+void size_aout(void)
 {
    if( display_mode == 0 )
       printf("text\tdata\tbss\tdec\thex\tfilename\n");
@@ -773,8 +742,7 @@ size_aout()
    tot_size_bss  += header[4];
 }
 
-void
-nm_aout()
+void nm_aout(void)
 {
    char n_name[10];
    long n_value;
diff --git a/Applications/ld09/writefuzix.c b/Applications/ld09/writefuzix.c
new file mode 100644 (file)
index 0000000..c842bd6
--- /dev/null
@@ -0,0 +1,502 @@
+
+/* writefuzix.c - write binary file for linker */
+
+/* Based upon writebin Copyright (C) 1994 Bruce Evans */
+
+/*
+ * TODO
+ * - add support for debug symbol tables
+ * - figure out how banked binaries should look
+ * - add a discard segment that is put between data and bss and the
+ *   bss size shrunk by its size (so the bootstrap code jumps to discard,
+ *   relocates and then it gets wiped)
+ */
+
+#include "syshead.h"
+#include "bindef.h"
+#include "const.h"
+#include "obj.h"
+#include "type.h"
+#include "globvar.h"
+
+#define btextoffset (text_base_value)
+#define bdataoffset (data_base_value)
+
+struct fuzix16_hdr {
+    uint8_t jmp;
+    uint16_t addr;
+    uint8_t magic[4];
+    uint8_t load;
+    uint16_t chmem;
+    uint16_t code;
+    uint16_t data;
+    uint16_t bss;
+    uint16_t mbz;
+    /* Header can be extended as the jump goes over it */
+};
+    
+#define HEADERSIZE sizeof(struct fuzix16_hdr)
+/* It's part of the binary */
+#define FILEHEADERLENGTH       0
+/* Must match AS09 */
+#define DPSEG 3
+
+#define CM_MASK 0xC0
+#define MODIFY_MASK 0x3F
+#define S_MASK 0x04
+#define OF_MASK 0x03
+
+#define CM_SPECIAL 0
+#define CM_ABSOLUTE 0x40
+#define CM_OFFSET_RELOC 0x80
+#define CM_SYMBOL_RELOC 0xC0
+
+#define CM_EOT 0
+#define CM_BYTE_SIZE 1
+#define CM_WORD_SIZE 2
+#define CM_LONG_SIZE 3
+#define CM_1_SKIP 17
+#define CM_2_SKIP 18
+#define CM_4_SKIP 19
+#define CM_0_SEG 32
+
+#define ABS_TEXT_MAX 64
+
+#define memsizeof(struc, mem) sizeof(((struc *) 0)->mem)
+
+static bool_t bits32;          /* nonzero for 32-bit executable */
+static bin_off_t combase[NSEG];/* bases of common parts of segments */
+static bin_off_t comsz[NSEG];  /* sizes of common parts of segments */
+static fastin_t curseg;        /* current segment, 0 to $F */
+static bin_off_t edataoffset;  /* end of data */
+static bin_off_t endoffset;    /* end of bss */
+static bin_off_t etextoffset;  /* end of text */
+static bin_off_t etextpadoff;  /* end of padded text */
+static unsigned nsym;          /* number of symbols written */
+static unsigned relocsize;     /* current relocation size 1, 2 or 4 */
+static bin_off_t segadj[NSEG]; /* adjusts (file offset - seg offset) */
+                               /* depends on zero init */
+static bin_off_t segbase[NSEG];/* bases of data parts of segments */
+static char segboundary[9] = "__seg0DH";
+                               /* name of seg boundary __seg0DL to __segfCH */
+static bin_off_t segpos[NSEG]; /* segment positions for current module */
+static bin_off_t segsz[NSEG];  /* sizes of data parts of segments */
+                               /* depends on zero init */
+static bool_t stripflag;       /* nonzero to strip symbols */
+static bin_off_t spos;         /* position in current seg */
+static bool_t uzp;             /* nonzero for unmapped zero page */
+static bool_t sepid;           /* Separate I & D space (8086 etc) */
+
+static void linkmod(struct modstruct *modptr);
+static void padmod(struct modstruct *modptr);
+static void setsym(char *name, bin_off_t value);
+static void symres(char *name);
+static void setseg(fastin_pt newseg);
+static void skip(unsigned countsize);
+static void writeheader(void);
+static void writenulls(bin_off_t count);
+
+void write_fuzix(char *outfilename, bool_pt argsepid, bool_pt argbits32,
+                     bool_pt argstripflag, bool_pt arguzp)
+{
+    char *cptr;
+    struct modstruct *modptr;
+    fastin_t seg;
+    unsigned sizecount;
+    bin_off_t tempoffset;
+
+    sepid = argsepid;
+    bits32 = argbits32;
+    stripflag = argstripflag;
+    uzp = arguzp;
+
+    /* reserve special symbols use curseg to pass parameter to symres() */
+    for (curseg = 0; curseg < NSEG; ++curseg)
+    {
+       segboundary[5] = hexdigit[curseg];      /* to __segX?H */
+       segboundary[6] = 'D';
+       symres(segboundary);    /* __segXDH */
+       segboundary[7] = 'L';
+       symres(segboundary);    /* __segXDL */
+       segboundary[6] = 'C';
+       symres(segboundary);    /* __segXCL */
+       segboundary[7] = 'H';
+       symres(segboundary);    /* __segXCH */
+#ifndef DATASEGS
+        if( curseg > 3 )
+       {
+          segboundary[6] = 'S';
+          segboundary[7] = 'O';
+          symres(segboundary); /* __segXSO */
+        }
+#endif
+    }
+    /* FIXME: we need a BSS segment in here ??? */
+    curseg = 3;
+    symres("__edata");
+    symres("__end");
+    curseg = 0;                        /* text seg, s.b. variable */
+    symres("__etext");
+    symres("__segoff");
+
+    /* calculate segment and common sizes (sum over loaded modules) */
+    /* use zero init of segsz[] */
+    /* also relocate symbols relative to starts of their segments */
+    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
+       if (modptr->loadflag)
+       {
+           register struct symstruct **symparray;
+           register struct symstruct *symptr;
+
+           for (symparray = modptr->symparray;
+                (symptr = *symparray) != NUL_PTR; ++symparray)
+               if (symptr->modptr == modptr && !(symptr->flags & A_MASK))
+               {
+                   if (!(symptr->flags & (I_MASK | SA_MASK)))
+                   {
+                       /* relocate by offset of module in segment later */
+                       /* relocate by offset of segment in memory special */
+                       /* symbols get relocated improperly */
+                       symptr->value += segsz[symptr->flags & SEGM_MASK];
+                   }
+                   else if (symptr->value == 0)
+                           undefined(symptr->name);
+                   else
+                   {
+                       tempoffset = ld_roundup(symptr->value, 4, bin_off_t);
+                       /* temp kludge quad alignment for 386 */
+                       symptr->value = comsz[seg = symptr->flags & SEGM_MASK];
+                       comsz[seg] += tempoffset;
+                       if (!(symptr->flags & SA_MASK))
+                           symptr->flags |= C_MASK;
+                   }
+               }
+           for (seg = 0, cptr = modptr->segsize; seg < NSEG; ++seg)
+           {
+               segsz[seg] += cntooffset(cptr,
+                         sizecount = segsizecount((unsigned) seg, modptr));
+               cptr += sizecount;
+           }
+       }
+
+    /* calculate seg positions now their sizes are known */
+    /* temp use fixed order 0D 0C 1D 1C 2D 2C ... */
+    /*
+#ifdef DATASEGS
+     * Assume seg 0 is text and rest are data
+#else
+     * Assume seg 1..3 are data, Seg 0 is real text, seg 4+ are far text
+#endif
+     */
+    segpos[0] = segbase[0] = spos = btextoffset;
+    combase[0] = segbase[0] + segsz[0];
+    segadj[1] = segadj[0] = -btextoffset;
+    etextpadoff = etextoffset = combase[0] + comsz[0];
+    /* No rounding needed currently - but keep for x86 etc when we get
+       to them */
+#if 0    
+    if (sepid)
+    {
+       etextpadoff = ld_roundup(etextoffset, 0x10, bin_off_t);
+       segadj[1] += etextpadoff - bdataoffset;
+    } else
+#endif    
+    if (bdataoffset == 0)
+       bdataoffset = etextpadoff;
+    segpos[1] = segbase[1] = edataoffset = bdataoffset;
+    combase[1] = segbase[1] + segsz[1];
+#ifndef DATASEGS
+    for (seg = 4; seg < NSEG; ++seg)
+    {
+       segpos[seg] = segbase[seg] = 0;
+       combase[seg] = segbase[seg] + segsz[seg];
+       segadj[seg] = etextpadoff;
+
+       etextpadoff += ld_roundup(segsz[seg] + comsz[seg], 0x10, bin_off_t);
+       segadj[1]   += ld_roundup(segsz[seg] + comsz[seg], 0x10, bin_off_t);
+    }
+    for (seg = 2; seg < 4; ++seg)
+#else
+    for (seg = 2; seg < NSEG; ++seg)
+#endif
+    {
+       segpos[seg] = segbase[seg] = combase[seg - 1] + comsz[seg - 1];
+#if defined(MC6809) || defined(MC6502)
+       if (seg == DPSEG)
+       {
+           /* temporarily have fixed DP seg */
+           /* adjust if nec so it only spans 1 page */
+           tempoffset = segsz[seg] + comsz[seg];
+           if (tempoffset > 0x100)
+               fatalerror("direct page segment too large");
+           if ((((segbase[seg] + tempoffset) ^ segbase[seg])
+                & ~(bin_off_t) 0xFF) != 0)
+               segpos[seg] = segbase[seg] = (segbase[seg] + 0xFF)
+                                            & ~(bin_off_t) 0xFF;
+       }
+#endif
+
+       combase[seg] = segbase[seg] + segsz[seg];
+       segadj[seg] = segadj[seg - 1];
+    }
+
+    /* relocate symbols by offsets of segments in memory */
+    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
+       if (modptr->loadflag)
+       {
+           register struct symstruct **symparray;
+           register struct symstruct *symptr;
+
+           for (symparray = modptr->symparray;
+                (symptr = *symparray) != NUL_PTR; ++symparray)
+               if (symptr->modptr == modptr && !(symptr->flags & A_MASK))
+               {
+                   if (symptr->flags & (C_MASK | SA_MASK))
+                           symptr->value += combase[symptr->flags & SEGM_MASK];
+                   else
+                       symptr->value += segbase[symptr->flags & SEGM_MASK];
+               }
+       }
+
+    /* adjust special symbols */
+    for (seg = 0; seg < NSEG; ++seg)
+    {
+#ifdef DATASEGS
+       if (segsz[seg] != 0)
+           /* only count data of nonzero length */
+#else
+       if (segsz[seg] != 0 && seg < 4)
+#endif
+           edataoffset = segbase[seg] + segsz[seg];
+       segboundary[5] = hexdigit[seg];         /* to __segX?H */
+       segboundary[6] = 'D';
+       setsym(segboundary, (tempoffset = segbase[seg]) + segsz[seg]);
+                                               /* __segXDH */
+       segboundary[7] = 'L';
+       setsym(segboundary, tempoffset);        /* __segXDL */
+       segboundary[6] = 'C';
+       setsym(segboundary, tempoffset = combase[seg]);
+                                               /* __segXCL */
+       segboundary[7] = 'H';
+       setsym(segboundary, tempoffset + comsz[seg]);
+                                               /* __segXCH */
+#ifndef DATASEGS
+        if( seg > 3 )
+       {
+          segboundary[6] = 'S';
+          segboundary[7] = 'O';
+          setsym(segboundary, (bin_off_t)(segadj[seg]-segadj[0])/0x10);
+          /* __segXSO */
+        }
+#endif
+    }
+    setsym("__etext", etextoffset);
+    setsym("__edata", edataoffset);
+#ifdef DATASEGS
+    setsym("__end", endoffset = combase[NSEG - 1] + comsz[NSEG - 1]);
+#else
+    setsym("__end", endoffset = combase[3] + comsz[3]);
+#endif
+    setsym("__segoff", (bin_off_t)(segadj[1]-segadj[0])/0x10);
+    if( !bits32 )
+    {
+        if( etextoffset > 65536L )
+            fatalerror("text segment too large for 16bit");
+        if( endoffset > 65536L )
+            fatalerror("data segment too large for 16bit");
+    }
+
+    openout(outfilename);
+    writeheader();
+    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext) {     if (modptr->loadflag)
+       {
+           linkmod(modptr);
+           padmod(modptr);
+       }
+    }
+    closeout();
+    executable();
+}
+
+static void linkmod(struct modstruct *modptr)
+{
+    char buf[ABS_TEXT_MAX];
+    int command;
+    unsigned char modify;
+    bin_off_t offset;
+    int symbolnum;
+    struct symstruct **symparray;
+    struct symstruct *symptr;
+
+    setseg(0);
+    relocsize = 2;
+    symparray = modptr->symparray;
+    openin(modptr->filename);  /* does nothing if already open */
+    seekin(modptr->textoffset);
+    while (TRUE)
+    {
+       if ((command = readchar()) < 0)
+           prematureeof();
+       modify = command & MODIFY_MASK;
+       switch (command & CM_MASK)
+       {
+       case CM_SPECIAL:
+           switch (modify)
+           {
+           case CM_EOT:
+               segpos[curseg] = spos;
+               return;
+           case CM_BYTE_SIZE:
+               relocsize = 1;
+               break;
+           case CM_WORD_SIZE:
+               relocsize = 2;
+               break;
+           case CM_LONG_SIZE:
+#ifdef LONG_OFFSETS
+               relocsize = 4;
+               break;
+#else
+               fatalerror("relocation by long offsets not implemented");
+#endif
+           case CM_1_SKIP:
+               skip(1);
+               break;
+           case CM_2_SKIP:
+               skip(2);
+               break;
+           case CM_4_SKIP:
+               skip(4);
+               break;
+           default:
+               if ((modify -= CM_0_SEG) >= NSEG)
+                   inputerror("bad data in");
+               setseg(modify);
+               break;
+           }
+           break;
+       case CM_ABSOLUTE:
+           if (modify == 0)
+               modify = ABS_TEXT_MAX;
+           readin(buf, (unsigned) modify);
+           writeout(buf, (unsigned) modify);
+           spos += (int) modify;
+           break;
+       case CM_OFFSET_RELOC:
+           offset = readsize(relocsize);
+           if (modify & R_MASK)
+           {
+#ifndef DATASEGS
+                int m = (modify & SEGM_MASK);
+               if( curseg != m && m != SEGM_MASK )
+                  interseg(modptr->filename, modptr->archentry, (char*)0);
+#endif
+               offset -= (spos + relocsize);
+            }
+           offtocn(buf, segbase[modify & SEGM_MASK] + offset, relocsize);
+           writeout(buf, relocsize);
+           spos += relocsize;
+           break;
+       case CM_SYMBOL_RELOC:
+           symptr = symparray[symbolnum = readconvsize((unsigned)
+                                           (modify & S_MASK ? 2 : 1))];
+           offset = readconvsize((unsigned) modify & OF_MASK);
+           if (modify & R_MASK)
+           {
+#ifndef DATASEGS
+                int m = (symptr->flags & SEGM_MASK);
+               if( curseg != m && m != SEGM_MASK )
+                  interseg(modptr->filename, modptr->archentry, symptr->name);
+#endif
+               offset -= (spos + relocsize);
+           }
+               offset += symptr->value;            
+           offtocn(buf, offset, relocsize);
+           writeout(buf, relocsize);
+           spos += relocsize;
+       }
+    }
+}
+
+static void padmod(struct modstruct *modptr)
+{
+    bin_off_t count;
+    fastin_t seg;
+    bin_off_t size;
+    unsigned sizecount;
+    char *sizeptr;
+
+    for (seg = 0, sizeptr = modptr->segsize; seg < NSEG; ++seg)
+    {
+       size = cntooffset(sizeptr,
+                         sizecount = segsizecount((unsigned) seg, modptr));
+       sizeptr += sizecount;
+       if ((count = segpos[seg] - segbase[seg]) != size)
+           size_error(seg, count, size);
+
+       /* pad to quad boundary */
+       /* not padding in-between common areas which sometimes get into file */
+       if ((size = ld_roundup(segpos[seg], 4, bin_off_t) - segpos[seg]) != 0)
+       {
+           setseg(seg);
+           writenulls(size);
+           segpos[seg] = spos;
+       }
+       segbase[seg] = segpos[seg];
+    }
+}
+
+static void setsym(char *name, bin_off_t value)
+{
+    struct symstruct *symptr;
+
+    if ((symptr = findsym(name)) != NUL_PTR)
+        symptr->value = value;
+}
+
+static void symres(char *name)
+{
+    register struct symstruct *symptr;
+
+    if ((symptr = findsym(name)) != NUL_PTR)
+    {
+       if ((symptr->flags & SEGM_MASK) == SEGM_MASK)
+           symptr->flags &= ~SEGM_MASK | curseg;
+       if (symptr->flags != (I_MASK | curseg) || symptr->value != 0)
+           reserved(name);
+        symptr->flags = E_MASK | curseg;       /* show defined, not common */
+    }
+}
+
+/* set new segment */
+
+static void setseg(fastin_pt newseg)
+{
+    if (newseg != curseg)
+    {
+       segpos[curseg] = spos;
+       spos = segpos[curseg = newseg];
+       seekout(FILEHEADERLENGTH + (unsigned long) spos
+               + (unsigned long) segadj[curseg]);
+    }
+}
+
+static void skip(unsigned countsize)
+{
+    writenulls((bin_off_t) readsize(countsize));
+}
+
+static void writeheader(void)
+{
+}
+
+static void writenulls(bin_off_t count)
+{
+    long lcount = count;
+    if( lcount < 0 )
+       fatalerror("org command requires reverse seek");
+    spos += count;
+    while (count-- > 0)
+       writechar(0);
+}
+