
/*
   This software is copyright 1989 by John Dunning.  See the file
   'COPYLEFT.JRD' for the full copyright notice.
*/

/* Linker for .obj files produced by ra65 */

#define VERSION "1.0"

#include <stdio.h>
#include <file.h>

#ifdef M6502

/* for now, use debug version */
#define malloc xmalloc

#else

/* for other-than-atari, make this a no-op */
#define frob_name(name) name

#endif

#include "symtab.h"
#include "obj.h"
#include "libr.h"

#define MAX_MODULES 256
#define MAX_LIBS 4
#define MAX_FILES 32

struct obj_module
	{
	USHORT seg_type;	/* either OBJ_HEADER or LIBR_HEADER */
	char * seg_name;	/* the name of this file */
	USHORT lib_nbr;		/* the library number */
	USHORT lib_idx;		/* if libr file, the module nbr in the lib */
	USHORT seg_start;	/* segment start address */
	USHORT seg_size;	/* segment size */
	struct sym ** syms;	/* vector of pointers to symbols */
	};

struct obj_module * module[MAX_MODULES];
int nmodules;

struct libr_module
	{
	int used;		/* if this module used */
	int n_defs;		/* n symbols defined this obj module */
	char ** lsyms;		/* vector of pointers to syms defined */
	};
	
struct libr
	{
	char * l_name;		/* filename this libr */
	int n_mods;		/* n modules this libr */
	struct librmod * dict;	/* module dictionary this libr */
	struct libr_module * lm; /* vector of libr_module structs */
	};

struct libr * lib[MAX_LIBS];		/* the libraries we're using */
int n_libs;

char * input_file[MAX_FILES];
int n_input_files;

/* stuff for hashing syms */
#define HASHMOD 53
struct sym * symtab[HASHMOD];

/* stuff for the output file */
int exe_start = 0x2000;		/* executable start addr */
int exe_high_water;

char litbuf[32];

char * output_name = "foo.com";
int input_fd, output_fd;

char map_p = 0;			/* write a map */
char start_p = 1;		/* write an autostart vector */
char verbose = 0;		/* print debug msgs */

struct relfile rf;
struct librfile lf;

/* flags for searching libraries */
int n_undef;			/* current number undefined symbols */
int satisfied;			/* flag if anything satisfied this pass */
int libs_searched;

char symbuf[32];
int symflags, symval;

#ifdef M6502
/* debug code */

char * xmalloc(n)
int n;
{
  char * foo;
  
  foo = pmalloc(n);
/*  printf("xmalloc(%d)->%x; sp %x\n", n, foo, &foo); */
  bzero(foo, n);
  return(foo);
}

/* file-name frobber */
char * frob_name(name)
char * name;
{
  char * new_name;
  
  if (!strchr(name, ':'))
	{
	new_name = malloc(strlen(name)+3);
	strcpy(new_name, "D:");
	strcat(new_name, name);
	return(new_name);
	}
  return(name);
}

/* should put this in lib */
int strncmp(s1, s2, n)
char * s1;
char * s2;
int n;
{
  for ( ; n > 0 ; --n)
	if (*s1++ != *s2++)
		return(1);
  return(0);
}
#endif

unsigned char read8()
{
  unsigned char foo;
  read(input_fd, &foo, 1);
  return(foo);
}

USHORT read16()
{
  return(read8() | (read8() << 8));
}

readskip(nbytes)
int nbytes;
{
  char buf[32];

  while (nbytes > 0)
	{
	read(input_fd, buf, (nbytes > 32) ? 32 : nbytes);
	nbytes -= 32;
	}
}

write8(ch)
unsigned char ch;
{
  write(output_fd, &ch, 1);
}

write16(sh)
USHORT sh;
{
  write8(sh & 0xFF);
  write8(sh >> 8);
}

int open_carefully(name, mode)
char * name;
int mode;
{
  int fd;

  name = frob_name(name);
  if (verbose)
	printf("open('%s', %x)->", name, mode);
  fd = open(name, mode);
  if (fd < 0)
  	barf("Can't open '%s'\n", name);
  if (verbose)
	printf("%d\n", fd);
  return(fd);
}

/* read the header of a file into the appropriate data structure.
   return the header type word */
USHORT read_header()
{
  USHORT typ;

  typ = read16();		/* get the header word */
/* printf("read_header: typ=%x\n", typ); */
  switch (typ)
  	{
	case OBJ_HEADER:
		rf.header = typ;
		rf.nb_sym = read16();
		rf.nb_seg = read16();
		rf.nb_segdata = read16();
		rf.n_sym = read16();
/* printf("  obj: nb_sym %d. nb_seg %d. n_sym %d.\n",
	rf.nb_sym, rf.nb_seg, rf.n_sym); */
		break;
	case LIBR_HEADER:
		lf.l_header = typ;
		lf.type = read16();
		lf.n_modules = read16();
		lf.l_flags = read16();
/* printf("  lib: type %d. n_mod %d. flg %x\n", 
	lf.type, lf.n_modules, lf.l_flags); */
		break;
	}
  return(typ);
}

int iswhite(c)
char c;
{
  return((c == ' ') || (c == '\r') || (c == '\n') || (c == '\t'));
}

int read_token(f, buf)
FILE * f;
char * buf;
{
  int c;

  for ( ; ((c = fgetc(f)) != EOF) && iswhite(c) ; )
	;
  if (c == EOF)
	return(0);
  *buf++ = c;
  for ( ; ((c = fgetc(f)) != EOF) && !iswhite(c) ; )
	*buf++ = c;
  *buf = '\0';
  return(1);
}

char * clone(s)
char * s;
{
  char * ss;

  ss = (char * )malloc(strlen(s) + 1);
  strcpy(ss, s);
  return(ss);
}

#ifdef M6502
char xbuf[80];
#endif

add_file(name)
char * name;
{
  char * newname;
  char * buf;
  FILE * inf;

/* printf("add_file('%s')\n", name); */
  if (name[0] == '@')
	{
#ifdef M6502
	buf = xbuf;
#else
	buf = (char * )alloca(80);
#endif
	if ((inf = fopen(frob_name(++name), "r")) == NULL)
		barf("can't open indirect file %s\n", name);
	while (read_token(inf, buf) && (strlen(buf) > 0))
		{
		add_file(clone(buf));
		}
	fclose(inf);
	}
    else
	{
	/* maybe caononicalize name here? */
	input_file[n_input_files++] = name;
	}
}

struct obj_module * add_object_module(name)
char * name;
{
  module[nmodules] = (struct obj_module * )malloc(sizeof(struct obj_module));
  module[nmodules]->seg_name = frob_name(name);
  module[nmodules]->seg_start = 0;
  module[nmodules]->seg_size = 0;
  module[nmodules]->syms = (struct sym ** )0;
  nmodules++;
  return(module[nmodules - 1]);
}

/* intern a symbol */
struct sym * intern(str, flags, value)
char * str;
{
  int hash;
  char * p;
  struct sym * sym;

/* printf("intern %s val %04x flags %04x\n", str, value, flags); */
  for (p = str, hash = 0 ; *p ; )
	hash = (hash + *p++) % HASHMOD;
  for (sym = symtab[hash] ; sym ; sym = sym->next)
	if (!strcmp(str, sym->name))
		{
		if (flags & DEFINED)
			{
			if ((sym->flags & DEFINED) && 
			    !(sym->flags & ABS))	/* kludge */
				printf("%s multiply defined\n", str);
			sym->value = value;
			sym->flags = flags;
			}
		return(sym);
		}
  sym = (struct sym *)malloc((sizeof (struct sym) + strlen(str) + 1));
  strcpy(sym->name, str);
  sym->nbr = -1;
  sym->flags = flags;
  sym->value = value;
  sym->next = symtab[hash];
  symtab[hash] = sym;
  return(sym);
}

#ifdef M6502
barf()
{
  int * arg;
  
  arg = &arg;		/* point at self */
  arg += arg[1] + 1;	/* add next word; arg count, plus 1 */
  fprintf(stderr, *arg, *--arg, *--arg, *--arg);
  fprintf(stderr, "\n");
  exit(1);
}

#else
barf(msg, arg1, arg2, arg3)
{
  fprintf(stderr, msg, arg1, arg2, arg3);
  fprintf(stderr, "\n");
  exit(1);
}
#endif

/* a little frob to make referencing symbols easier */
USHORT reference(sym, m)
struct sym * sym;
struct obj_module * m;
{
  if (!(sym->flags & DEFINED))
	printf("Undefined symbol %s referenced in module %s\n",
		sym->name, m->seg_name);
  return(sym->value);
}

/* read all interesting data about an object file, open on input_fd */
struct obj_module * digest_object_file(name, module, base_addr)
char * name;
int module;		/* module index */
USHORT base_addr;
{
  struct obj_module * m;
  int j, k;
  
  m = add_object_module(name);
  m->syms = (struct sym ** )malloc(rf.n_sym * 
				         sizeof(struct sym *));
  for (j = 0 ; j < rf.n_sym ; j++)
	{
	k = read8();		/* size of symbol name */
	read(input_fd, symbuf, k);
	symbuf[k] = '\0';
	symval = read16();
	symflags = read16();
	m->syms[j] = intern(symbuf, symflags, symval);
	if (symflags & THIS_SEG)
		{
/* defined here.  adjust value by segment base */
		m->syms[j]->nbr = module;	/* zzz should check for multiple def */
		if (!(symflags & ABS))
			m->syms[j]->value += base_addr;
		m->syms[j]->flags &= ~THIS_SEG;
		m->syms[j]->flags |= DEFINED;
		}
	}
  return(m);
}

/* map over all syms, applying fun to sym and arg, until fun returns t */
int map_syms(fun, funarg)
int (* fun)();
char * funarg;
{
  int i, ret;
  struct sym * sym;

  for (i = 0 ; i < HASHMOD ; i++)
	{
/* printf("map_syms: bucket %d %x\n", i, symtab[i]); */
	for (sym = symtab[i] ; sym ; sym = sym->next)
		{
/* printf("  sym %x\n", sym); */
		if (ret = (*fun)(sym, funarg))
			return(ret);
		}
	}
}

int count_undef_sym(sym, arg)
struct sym * sym;
char * arg;		/* ignored */
{
  if (!(sym->flags & DEFINED))
	n_undef++;
  return(0);
}

int sym_needed(sym, sym_name)
struct sym * sym;
char * sym_name;		/* a textc string */
{
  return((!(sym->flags & DEFINED)) &&
	 (strlen(sym->name) == *sym_name) &&
	 (!strncmp(sym->name, sym_name+1, *sym_name)));
}

#ifdef old
search_lib(lib_nbr)
int lib_nbr;
{
  char * syms;			/* buf for a module's symbols */
  char * sym_p;			/* ptr into symbuf */
  int m;			/* module index */
  int s;			/* symbol number */
  int needed;			/* module needed flag */
  int m_type;
  int flags, value;
  unsigned short * iptr;
  struct obj_module * mod;

  syms = (char * )malloc(65536);	/* kludge */
  input_fd = open_carefully(lib[lib_nbr], O_RDONLY);
  read_header();		/* read the libr header */
  for (m = 0 ; m < lf.n_modules ; ++m)
	read(input_fd, symbuf, 14);	/* skip the libr dictionary */
  for (m = 0 ; m < lf.n_modules ; ++m)
	{
	if ((m_type = read_header()) != OBJ_HEADER)
		barf("Search: not an object module?!? type %04x", m_type);
	read(input_fd, syms, rf.nb_sym - 2);	/* read the whole symtab */
/*
printf("trying module %d\n", m);
{ int ii;
  for (ii = 0 ; ii < 16 ; ii++)
    printf(" %02x", syms[ii]);
  printf("\n");
}
*/
	/* walk syms in this module, seeing if we need any of them */
	sym_p = syms;
	needed = 0;		/* so far ... */
	for (s = 0 ; s < rf.n_sym ; s++)
		{
/*		iptr = sym_p + (*sym_p) + 1;	/* point at value */
/*		value = *iptr;
/*		flags = *++iptr;		*/
		strncpy(symbuf, sym_p + 1, *sym_p);
		if ((flags & THIS_SEG) &&	/* defined here? */
		    (map_syms(sym_needed, sym_p)))
			{
			needed = 1;
			break;
			}
		}
	if (needed)
	/* add the module, and intern all these syms */
		{
		mod = add_object_module(lib[lib_nbr]);
		mod->seg_type = LIBR_HEADER;
		mod->lib_idx = m;		/* module nbr in libr */
		mod->syms = (struct sym ** )malloc(rf.n_sym * 
				         sizeof(struct sym *));
		mod->seg_start = exe_high_water;
		mod->seg_size = rf.nb_seg;
		exe_high_water += rf.nb_seg;

		sym_p = syms;
		for (s = 0 ; s < rf.n_sym ; ++s)
			{
			iptr = sym_p + (*sym_p) + 1;	/* point at value */
			value = *iptr;
			flags = *++iptr;
			sym_p[*sym_p+1] = '\0';		/* so it's a C string */
			mod->syms[s] = intern(sym_p+1, flags, value);
			}
		}
	/* skip the segment data this obj module */
	read(input_fd, syms, rf.nb_segdata);
	}
  close(input_fd);		/* close the library */
  free(syms);
}
#endif

/* for debugging */
int describe_sym(sym, str)
struct sym * sym;
char * str;
{
  printf("%s: sym %x name %s\n", str, sym, sym->name);
  return(0);
}

/* search all libr structs for syms that satisfy refs from our symtab */
int search_lib(sym, ignore)
struct sym * sym;
int ignore;
{
  int libnum;
  int modnum;
  struct libr_module * mod;
  int symnum;
  char ** defname;

/* describe_sym(sym, "    search"); */
  if (!(sym->flags & DEFINED))		/* sym not defined? */
    for (libnum = 0 ; libnum < n_libs ; libnum++)
	for (modnum = 0 ; modnum < lib[libnum]->n_mods ; modnum++)
	    {
	    mod = &lib[libnum]->lm[modnum];
	    for (symnum = 0, defname = mod->lsyms ;
	         symnum < mod->n_defs ;
		 defname++, symnum++)
		{
	   	if (!(strcmp(sym->name, *defname)))
			{
			mod->used = 1;
			satisfied = 1;
			return(0);		/* keep scanning syms */
			}
		}
	    }
  return(0);
}

char * xsyms = 0;	/* ptr to temp buffer for syms */

add_library(name)
char * name;
{
/*  char * syms;		/* symtab buffer */
  char * sym_p;
  int s, m;		/* symbol number, module number */
  unsigned char * fp;
  USHORT flags;		/* symbol flags */
  struct libr * l;
  struct libr_module * mod;
  int i;

  l = lib[n_libs] = (struct libr * )malloc(sizeof(struct libr));
  
  l->l_name = frob_name(name);	/* record the name */
  l->n_mods = lf.n_modules;

  if (!xsyms)
#ifdef M6502
  xsyms = (char * )malloc(4096);	/* kludge */
#else
  xsyms = (char * )malloc(65536);	/* kludge */
#endif

  l->dict = (struct librmod *)malloc(lf.n_modules * sizeof(struct librmod));
/* printf("lib contains %d modules\n", lf.n_modules); */
  for (m = 0 ; m < lf.n_modules ; m++)
  	{
	read(input_fd, l->dict[m].m_name, 12);	/* get the module name */
	l->dict[m].m_nbytes = read16();		/* get module size */
/* printf("%d: %12s %d\n", m, l->dict[m].m_name, l->dict[m].m_nbytes); */
	}

  l->lm = (struct libr_module * )
  		malloc(lf.n_modules * sizeof(struct libr_module));

  for (m = 0 ; m < lf.n_modules ; m++)
	{
/* printf("digest module %d\n", m); */
	if ((i = read_header()) != OBJ_HEADER)	/* read obj header */
		barf("internal error, lib contains module type %04x\n", i);

	mod = &l->lm[m];
	mod->used = mod->n_defs = 0;

	if (rf.nb_sym > 2)
		read(input_fd, xsyms, rf.nb_sym - 2);	/* read the whole symtab */
	sym_p = xsyms;
	for (s = 0 ; s < rf.n_sym ; s++)
		{
		/* construct the flags word */
		fp = (unsigned char * )sym_p + *sym_p + 3;
		flags = *fp | (fp[1] << 8);
/* printf("    sym %d ptr %x flags %x\n", s, fp, flags); */

		if (flags & THIS_SEG)		/* defined here? */
			{
			/* count this one */
			mod->n_defs++;
			}
		sym_p = sym_p + *sym_p + 5;
		}
	/* make a vector to hold all the names */
/* printf("  %d syms defined here\n", mod->n_defs); */
	if (mod->n_defs > 0)
		{
		mod->lsyms = (char ** )malloc(mod->n_defs * sizeof(char **));
		sym_p = xsyms;
		for (s = 0, i = 0 ; s < rf.n_sym ; s++)
			{
			fp = (unsigned char * )sym_p + *sym_p + 3;
			flags = *fp | (fp[1] << 8);

			if (flags & THIS_SEG)		/* defined here? */
				{
				sym_p[*sym_p + 1] = '\0';
				mod->lsyms[i++] = clone(sym_p + 1);
				}
			sym_p = sym_p + *sym_p + 5;
			}
		}		
	readskip(rf.nb_segdata);	/* skip the segment data */
	}			/* loop for module... */
/*  free(syms); */
  close(input_fd);
  n_libs++;		/* count the library */
}

process_segdata(mod)
struct obj_module * mod;		/* struct corresponding to this data */
{
  int j, k, op;

  readskip(rf.nb_sym - 2);			/* skip symtab */

  for (j = 0 ; j < rf.nb_segdata ; )
	{
	op = read8();		/* get an opcode */
/* printf("  op %x\n", op); */
	j += 1;			/* count it */
	switch (op & OP_GEN_MASK)
	    {
	    case OP_LIT:
		{
		if (op == 0)
			op = 32;
		read(input_fd, litbuf, op);
		write(output_fd, litbuf, op);
		j += op;
		break;
		}
	    case OP_REL:
		{
		switch(op)
		    {
		    case OP_REL:
			{
			write16(read16() + mod->seg_start);
			j += 2;
			break;
			}
		    case OP_REL_HI:
			{
			write8((read16() + mod->seg_start) >> 8);
			j += 2;
			break;
			}
		    case OP_REL_LO:
			{
			write8((read16() + mod->seg_start) & 0xFF);
			j += 2;
			break;
			}
		    }
		break;
		}
	    default:		/* must be sym */
		{
		k = op & 0x1F;
		if (op & OP_SYM_EMASK)
			{
			k = (k << 8) | read8();
			j++;
			}
		switch (op & OP_SYM_MASK)
		    {
		    case OP_SYM:
			{
			int foo;

			foo = read16();
/* printf("generate word %X + '%s' value %X\n", foo,
	&module[i]->syms[k]->name, module[i]->syms[k]->value); */
			write16(foo + reference(mod->syms[k], mod));
			j += 2;
			break;
			}
		    case OP_SYM_HI:
			{
/*			write16((read16() + reference(mod->syms[k], mod))
				>> 8);		*/
			write8((read16() + reference(mod->syms[k], mod))
				>> 8);
			j += 2;
			break;
			}
		    case OP_SYM_LO:
			{
/*			write16((read16() + reference(mod->syms[k], mod))
				& 0xFF);		*/
			write8((read16() + reference(mod->syms[k], mod))
				& 0xFF);
			j += 2;
			break;
			}
		    }
		break;
		}
	    }
	}
}

#ifdef M6502
char argbuf[80];
#endif

main(argc, argv)
int argc;
char ** argv;
{
  int i, j, k, op;
  struct obj_module * m;

  struct sym * high_water;		/* special sym for top of executable */

#ifdef M6502
/* if no args, prompt for 'em */
  if (argc < 2)
	argc = readargs("LINK65>", argbuf, argv + 1) + 1;
#endif

/* init stuff */
  for (i = 0 ; i < HASHMOD ; i++)
	symtab[i] = (struct sym *)0;

  n_input_files = nmodules = n_libs = 0;

/* grok args */
  argv++;
  while (--argc)
	{
	if ((*argv)[0] == '-')
		switch ((*argv)[1])
			{
			case 'v': case 'V':
				{
				++verbose;
				break;
				}
			case 'b': case 'B':
				{
				char * x;
				char * hex;
				int base;
				int d;
				
				x = (*argv)+2;
				hex = "0123456789ABCDEF";
				d = 0;
/* printf("base string '%s'\n", x); */
				while (d = *x++)
					{
					if (d < 'A')
						d = d - '0';
					    else
						d = (d - 'A') + 10;
					base = (base * 16) + d;
					}
				exe_start = base;
				break;
				}
			case 'o': case 'O':
				{
				output_name = *++argv;
				argc--;
				break;
				}
			case 'm': case 'M':
				{
				map_p = 1;
				break;
				}
			case 'n': case 'N':
				{
				start_p = 0;
				break;
				}
			/* more later... */
			default:
				printf("don't grok arg %s\n", *argv);
			}
	    else
	 	add_file(*argv);
	argv++;
	}

  if (verbose)
	printf("Link65 v %s\n", VERSION);

  exe_high_water = exe_start;

/* intern special symbols */
  high_water = intern("__FREEMEM", DEFINED | ABS, 0);
  
/* Read each input file, classifying file type.  For obj files, read each
   object header, recording segment size, start addr etc, intern symbols
   for this module.  For librs, digest each module as though it were a
   distinct obj file, but leave the 'used' bit in the struct clear. */

  for (i = 0 ; i < n_input_files ; i++)
	{
	input_fd = open_carefully(input_file[i], O_RDONLY);
	j = read_header();
	if (j == OBJ_HEADER)
		{
		m = digest_object_file(input_file[i], i, exe_high_water);
		m->seg_start = exe_high_water;
		m->seg_size = rf.nb_seg;
		exe_high_water += rf.nb_seg;
		}
	    else
	if (j == LIBR_HEADER)
		{
		/* just record the name */
		add_library(input_file[i]);
		}
	    else
		barf("%s is not an object file or library: header word %04x\n",
			module[i]->seg_name, j);
	close(input_fd);
	}
/* if there are any undefined symbols, search libraries for them */
  satisfied = 1;		/* hot-wire loop */
  for( ; (satisfied != 0) ; )
	{
	satisfied = 0;
	map_syms(search_lib, NULL);

	if (satisfied)
		/* walk all librs, digesting modules that are used */
		{
		for (i = 0 ; i < n_libs ; i++)
			{
			input_fd = open_carefully(lib[i]->l_name, O_RDONLY);
			read_header();
			/* skip dictionary */
			readskip(lf.n_modules * 14);

			for (j = 0 ; j < lf.n_modules ; j++)
				{
				struct libr_module * lmod;
/*				struct obj_module * m;	*/
				
				if ((k = read_header()) != OBJ_HEADER)
					barf ("internal error, type %04x", k);
				lmod = &lib[i]->lm[j];
				if (lmod->used)
					{
					m = digest_object_file(lib[i]->l_name, 0, 
						exe_high_water);
					m->seg_type = LIBR_HEADER;
					m->lib_idx = j;
					m->seg_start = exe_high_water;
					m->seg_size = rf.nb_seg;
					exe_high_water += rf.nb_seg;
					readskip(rf.nb_segdata);
					lmod->used = 0;
/*					free(lmod->syms);	*/
					lmod->lsyms = NULL;
					lmod->n_defs = 0;
					}
				    else
					{
					readskip(rf.nb_segdata + rf.nb_sym - 2);
					}
				}				
			close(input_fd);
			}
		}
	}

/* set values for special symbols */
  if (verbose)
	printf("setting high water to %x sym %x\n", exe_high_water, high_water);
  high_water->value = exe_high_water;

/* all modules now digested.  read each seg data, interpreting fragment types */

  output_fd = open_carefully(output_name, O_WRONLY | O_CREAT | O_TRUNC);
/* write the atari executable header */
  write16(0xFFFF);
  write16(exe_start);
  write16(exe_high_water - 1);

  {
  char * curr_lib;			/* current library file open */
  int curr_idx;				/* current lib idx, if lib */
  
  curr_lib = NULL;
  curr_idx = -1;
  for (i = 0 ; i < nmodules ; i++)
	{
	m = module[i];
	if ((m->seg_type == LIBR_HEADER) &&
	    curr_lib &&
	    (!strcmp(m->seg_name, curr_lib)) &&
	    (curr_idx <= m->lib_idx))
		{
	/* skip modules til find the place in lib where this one starts */
		for ( ; curr_idx < m->lib_idx ; curr_idx++)
			{
			readskip(lib[m->lib_nbr]->dict[curr_idx].m_nbytes);
			}
		}
	  else
	    {
	    if (curr_lib)		/* we have a library open? */
		{
		close(input_fd);
		curr_lib = NULL;
		}
	/* just open the file */
	    input_fd = open_carefully(m->seg_name, O_RDONLY);
	    if (module[i]->seg_type == LIBR_HEADER)
		{
		curr_lib = m->seg_name;
		read_header();
		readskip(lf.n_modules * sizeof(struct librmod));
		curr_idx = 0;
	/* skip modules til find the place in lib where this one starts */
		for ( ; curr_idx < m->lib_idx ; curr_idx++)
			{
			readskip(lib[m->lib_nbr]->dict[curr_idx].m_nbytes);
			}
		}
	    }
	read_header();
	process_segdata(m);
	if (curr_lib)
		curr_idx++;
	if (!curr_lib)			/* if a libr, leave it open for now */
		close(input_fd);
	}
  if (start_p)				/* write autostart vec? */
	{
	write16(0x02E0);		/* autostart vec goes at #x2E0 */
	write16(0x02E1);
	write16(exe_start);		/* write the address */
	}
  close(output_fd);
  }

/* print map, if desired */
  if (map_p)
	{
	FILE * mapf;
/*	int i, j;	*/
	char * p, * q;
	struct sym * sym;
/*	struct obj_module * m; */

	for (p = output_name ; *p && (*p != '.') ; p++)
		;
	strcpy(p, ".map");
	output_name = frob_name(output_name);
	
	mapf = fopen(output_name, "w");
/* printf("map '%s'->%x\n", output_name, mapf); */
	fprintf(mapf, "Base address %04x\n", exe_start);
	for (i = 0 ; i < nmodules ; i++)
		{
		m = module[i];
		strcpy(symbuf, m->seg_name);
		/* if library, append libr element name */
		if (m->seg_type == LIBR_HEADER)
			{
			p = symbuf + strlen(symbuf);
			*p++ = '(';
#ifdef M6502
			q = lib[m->lib_nbr]->dict[m->lib_idx].m_name;
#else
			q = (char * )&lib[m->lib_nbr]->dict[m->lib_idx].m_name;
#endif
			for (j = 0 ; ((j < 12) && (*q != ' ')) ; j++)
				*p++ = *q++;
			*p++ = ')';
			*p='\0';
			}
		fprintf(mapf, " %-28s data from %04x to %04x: %5d. bytes\n",
			symbuf, m->seg_start, 
			m->seg_start + m->seg_size,
			m->seg_size);
		}
	j = 999;
	for (i = 0 ; i < HASHMOD ; i++)
		for (sym = symtab[i] ; sym ; sym = sym->next)
			{
			if (j > 3)
				{
				fprintf(mapf, "\n");
				j = 0;
				}
			fprintf(mapf, "%-11s%04x %c  ", sym->name, sym->value,
				sym->flags & DEFINED ? 
				  (sym->flags & ABS ? 'A' : 'R') :
				  'U');
			j++;
			}
	fclose(mapf);
	}
}
