Logo Search packages:      
Sourcecode: nasm version File versions  Download package

outobj.c

/* outobj.c output routines for the Netwide Assembler to produce
 *          .OBJ object files
 *
 * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
 * Julian Hall. All rights reserved. The software is
 * redistributable under the licence given in the file "Licence"
 * distributed in the NASM archive.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"

#ifdef OF_OBJ

/*
 * outobj.c is divided into two sections.  The first section is low level
 * routines for creating obj records;  It has nearly zero NASM specific
 * code.  The second section is high level routines for processing calls and
 * data structures from the rest of NASM into obj format.
 *
 * It should be easy (though not zero work) to lift the first section out for
 * use as an obj file writer for some other assembler or compiler.
 */

/*
 * These routines are built around the ObjRecord data struture.  An ObjRecord
 * holds an object file record that may be under construction or complete.
 *
 * A major function of these routines is to support continuation of an obj
 * record into the next record when the maximum record size is exceeded.  The
 * high level code does not need to worry about where the record breaks occur.
 * It does need to do some minor extra steps to make the automatic continuation
 * work.  Those steps may be skipped for records where the high level knows no
 * continuation could be required.
 *
 * 1) An ObjRecord is allocated and cleared by obj_new, or an existing ObjRecord
 *    is cleared by obj_clear.
 *
 * 2) The caller should fill in .type.
 *
 * 3) If the record is continuable and there is processing that must be done at
 *    the start of each record then the caller should fill in .ori with the
 *    address of the record initializer routine.
 *
 * 4) If the record is continuable and it should be saved (rather than emitted
 *    immediately) as each record is done, the caller should set .up to be a
 *    pointer to a location in which the caller keeps the master pointer to the
 *    ObjRecord.  When the record is continued, the obj_bump routine will then
 *    allocate a new ObjRecord structure and update the master pointer.
 *
 * 5) If the .ori field was used then the caller should fill in the .parm with
 *    any data required by the initializer.
 *
 * 6) The caller uses the routines: obj_byte, obj_word, obj_rword, obj_dword,
 *    obj_x, obj_index, obj_value and obj_name to fill in the various kinds of
 *    data required for this record.
 *
 * 7) If the record is continuable, the caller should call obj_commit at each
 *    point where breaking the record is permitted.
 *
 * 8) To write out the record, the caller should call obj_emit2.  If the
 *    caller has called obj_commit for all data written then he can get slightly
 *    faster code by calling obj_emit instead of obj_emit2.
 *
 * Most of these routines return an ObjRecord pointer.  This will be the input
 * pointer most of the time and will be the new location if the ObjRecord
 * moved as a result of the call.  The caller may ignore the return value in
 * three cases:  It is a "Never Reallocates" routine;  or  The caller knows
 * continuation is not possible;  or  The caller uses the master pointer for the
 * next operation.
 */

#define RECORD_MAX (1024-3)   /* maximal size of any record except type+reclen */
#define OBJ_PARMS  3          /* maximum .parm used by any .ori routine */

#define FIX_08_LOW      0x8000      /* location type for various fixup subrecords */
#define FIX_16_OFFSET   0x8400
#define FIX_16_SELECTOR 0x8800
#define FIX_32_POINTER  0x8C00
#define FIX_08_HIGH     0x9000
#define FIX_32_OFFSET   0xA400
#define FIX_48_POINTER  0xAC00

enum RecordID {                      /* record ID codes */

    THEADR = 0x80,                   /* module header */
    COMENT = 0x88,                   /* comment record */

    LINNUM = 0x94,                     /* line number record */
    LNAMES = 0x96,                   /* list of names */

    SEGDEF = 0x98,                   /* segment definition */
    GRPDEF = 0x9A,                   /* group definition */
    EXTDEF = 0x8C,                   /* external definition */
    PUBDEF = 0x90,                   /* public definition */
    COMDEF = 0xB0,                   /* common definition */

    LEDATA = 0xA0,                   /* logical enumerated data */
    FIXUPP = 0x9C,                   /* fixups (relocations) */
    FIXU32 = 0x9D,                   /* 32-bit fixups (relocations) */

    MODEND = 0x8A,                   /* module end */
    MODE32 = 0x8B              /* module end for 32-bit objects */
};

enum ComentID {                        /* ID codes for comment records */

     dEXTENDED = 0xA1,                 /* tells that we are using translator-specific extensions */
     dLINKPASS = 0xA2,                 /* link pass 2 marker */
     dTYPEDEF = 0xE3,                  /* define a type */
     dSYM = 0xE6,                      /* symbol debug record */
     dFILNAME = 0xE8,                  /* file name record */
     dCOMPDEF = 0xEA                   /* compiler type info */

};

typedef struct ObjRecord ObjRecord;
typedef void ORI(ObjRecord *orp);

struct ObjRecord {
    ORI           *ori;             /* Initialization routine           */
    int            used;            /* Current data size                */
    int            committed;       /* Data size at last boundary       */
    int            x_size;          /* (see obj_x)                      */
    unsigned int   type;            /* Record type                      */
    ObjRecord     *child;           /* Associated record below this one */
    ObjRecord    **up;              /* Master pointer to this ObjRecord */
    ObjRecord     *back;            /* Previous part of this record     */
    unsigned long  parm[OBJ_PARMS]; /* Parameters for ori routine       */
    unsigned char  buf[RECORD_MAX+3];
};

static void obj_fwrite(ObjRecord *orp);
static void ori_ledata(ObjRecord *orp);
static void ori_pubdef(ObjRecord *orp);
static void ori_null(ObjRecord *orp);
static ObjRecord *obj_commit(ObjRecord *orp);

static int obj_uppercase;           /* Flag: all names in uppercase */
static int obj_use32;               /* Flag: at least one segment is 32-bit */

/*
 * Clear an ObjRecord structure.  (Never reallocates).
 * To simplify reuse of ObjRecord's, .type, .ori and .parm are not cleared.
 */
static ObjRecord *obj_clear(ObjRecord *orp) 
{
    orp->used = 0;
    orp->committed = 0;
    orp->x_size = 0;
    orp->child = NULL;
    orp->up = NULL;
    orp->back = NULL;
    return (orp);
}

/*
 * Emit an ObjRecord structure.  (Never reallocates).
 * The record is written out preceeded (recursively) by its previous part (if
 * any) and followed (recursively) by its child (if any).
 * The previous part and the child are freed.  The main ObjRecord is cleared,
 * not freed.
 */
static ObjRecord *obj_emit(ObjRecord *orp) 
{
    if (orp->back) {
      obj_emit(orp->back);
      nasm_free(orp->back);
    }

    if (orp->committed)
      obj_fwrite(orp);

    if (orp->child) {
      obj_emit(orp->child);
      nasm_free(orp->child);
    }

    return (obj_clear(orp));
}

/*
 * Commit and Emit a record.  (Never reallocates).
 */
static ObjRecord *obj_emit2(ObjRecord *orp) 
{
    obj_commit(orp);
    return (obj_emit(orp));
}

/*
 * Allocate and clear a new ObjRecord;  Also sets .ori to ori_null
 */
static ObjRecord *obj_new(void) 
{
    ObjRecord *orp;
    
    orp = obj_clear( nasm_malloc(sizeof(ObjRecord)) );
    orp->ori = ori_null;
    return (orp);
}
    
/*
 * Advance to the next record because the existing one is full or its x_size
 * is incompatible.
 * Any uncommited data is moved into the next record.
 */
static ObjRecord *obj_bump(ObjRecord *orp) 
{
    ObjRecord *nxt;
    int used = orp->used;
    int committed = orp->committed;

    if (orp->up) {
      *orp->up = nxt = obj_new();
      nxt->ori = orp->ori;
      nxt->type = orp->type;
      nxt->up = orp->up;
      nxt->back = orp;
      memcpy( nxt->parm, orp->parm, sizeof(orp->parm));
    } else
      nxt = obj_emit(orp);

    used -= committed;
    if (used) {
      nxt->committed = 1;
      nxt->ori (nxt);
      nxt->committed = nxt->used;
      memcpy( nxt->buf + nxt->committed, orp->buf + committed, used);
      nxt->used = nxt->committed + used;
    }

    return (nxt);
}

/*
 * Advance to the next record if necessary to allow the next field to fit.
 */
static ObjRecord *obj_check(ObjRecord *orp, int size) 
{
    if (orp->used + size > RECORD_MAX)
      orp = obj_bump(orp);

    if (!orp->committed) {
      orp->committed = 1;
      orp->ori (orp);
      orp->committed = orp->used;
    }

    return (orp);
}

/*
 * All data written so far is commited to the current record (won't be moved to
 * the next record in case of continuation).
 */
static ObjRecord *obj_commit(ObjRecord *orp) 
{
    orp->committed = orp->used;
    return (orp);
}

/*
 * Write a byte
 */
static ObjRecord *obj_byte(ObjRecord *orp, unsigned char val) 
{
    orp = obj_check(orp, 1);
    orp->buf[orp->used] = val;
    orp->used++;
    return (orp);
}

/*
 * Write a word
 */
static ObjRecord *obj_word(ObjRecord *orp, unsigned int val) 
{
    orp = obj_check(orp, 2);
    orp->buf[orp->used] = val;
    orp->buf[orp->used+1] = val >> 8;
    orp->used += 2;
    return (orp);
}

/*
 * Write a reversed word
 */
static ObjRecord *obj_rword(ObjRecord *orp, unsigned int val) 
{
    orp = obj_check(orp, 2);
    orp->buf[orp->used] = val >> 8;
    orp->buf[orp->used+1] = val;
    orp->used += 2;
    return (orp);
}

/*
 * Write a dword
 */
static ObjRecord *obj_dword(ObjRecord *orp, unsigned long val) 
{
    orp = obj_check(orp, 4);
    orp->buf[orp->used] = val;
    orp->buf[orp->used+1] = val >> 8;
    orp->buf[orp->used+2] = val >> 16;
    orp->buf[orp->used+3] = val >> 24;
    orp->used += 4;
    return (orp);
}

/*
 * All fields of "size x" in one obj record must be the same size (either 16
 * bits or 32 bits).  There is a one bit flag in each record which specifies
 * which.
 * This routine is used to force the current record to have the desired
 * x_size.  x_size is normally automatic (using obj_x), so that this
 * routine should be used outside obj_x, only to provide compatibility with
 * linkers that have bugs in their processing of the size bit.
 */

static ObjRecord *obj_force(ObjRecord *orp, int x)
{
    if (orp->x_size == (x^48))
      orp = obj_bump(orp);
    orp->x_size = x;
      return (orp);
}

/*
 * This routine writes a field of size x.  The caller does not need to worry at
 * all about whether 16-bits or 32-bits are required.
 */
static ObjRecord *obj_x(ObjRecord *orp, unsigned long val) 
{
    if (orp->type & 1)
      orp->x_size = 32;
    if (val > 0xFFFF)
      orp = obj_force(orp, 32);
    if (orp->x_size == 32)
      return (obj_dword(orp, val));
    orp->x_size = 16;
    return (obj_word(orp, val));
}

/*
 * Writes an index
 */
static ObjRecord *obj_index(ObjRecord *orp, unsigned int val) 
{
    if (val < 128)
      return ( obj_byte(orp, val) );
    return (obj_word(orp, (val>>8) | (val<<8) | 0x80));
}

/*
 * Writes a variable length value
 */
static ObjRecord *obj_value(ObjRecord *orp, unsigned long val) 
{
    if (val <= 128)
      return ( obj_byte(orp, val) );
    if (val <= 0xFFFF) {
      orp = obj_byte(orp, 129);
      return ( obj_word(orp, val) );
    }
    if (val <= 0xFFFFFF)
      return ( obj_dword(orp, (val<<8) + 132 ) );
    orp = obj_byte(orp, 136);
    return ( obj_dword(orp, val) );
}

/*
 * Writes a counted string
 */
static ObjRecord *obj_name(ObjRecord *orp, char *name) 
{
    int len = strlen(name);
    unsigned char *ptr;

    orp = obj_check(orp, len+1);
    ptr = orp->buf + orp->used;
    *ptr++ = len;
    orp->used += len+1;
    if (obj_uppercase)
      while (--len >= 0) {
          *ptr++ = toupper(*name);
          name++;
    } else
      memcpy(ptr, name, len);
    return (orp);
}

/*
 * Initializer for an LEDATA record.
 * parm[0] = offset
 * parm[1] = segment index
 * During the use of a LEDATA ObjRecord, parm[0] is constantly updated to
 * represent the offset that would be required if the record were split at the
 * last commit point.
 * parm[2] is a copy of parm[0] as it was when the current record was initted.
 */
static void ori_ledata(ObjRecord *orp) 
{
    obj_index (orp, orp->parm[1]);
    orp->parm[2] = orp->parm[0];
    obj_x (orp, orp->parm[0]);
}

/*
 * Initializer for a PUBDEF record.
 * parm[0] = group index
 * parm[1] = segment index
 * parm[2] = frame (only used when both indexes are zero)
 */
static void ori_pubdef(ObjRecord *orp) 
{
    obj_index (orp, orp->parm[0]);
    obj_index (orp, orp->parm[1]);
    if ( !(orp->parm[0] | orp->parm[1]) )
      obj_word (orp, orp->parm[2]);
}

/*
 * Initializer for a LINNUM record.
 * parm[0] = group index
 * parm[1] = segment index
 */
static void ori_linnum(ObjRecord *orp) 
{
    obj_index (orp, orp->parm[0]);
    obj_index (orp, orp->parm[1]);
}
/*
 * Initializer for a local vars record.
 */
static void ori_local(ObjRecord *orp) 
{
    obj_byte (orp, 0x40);
    obj_byte (orp, dSYM);
}

/*
 * Null initializer for records that continue without any header info
 */
static void ori_null(ObjRecord *orp) 
{
    (void) orp;  /* Do nothing */
}

/*
 * This concludes the low level section of outobj.c
 */

static char obj_infile[FILENAME_MAX];

static efunc error;
static evalfunc evaluate;
static ldfunc deflabel;
static FILE *ofp;
static long first_seg;
static int any_segs;
static int passtwo;
static int arrindex;

#define GROUP_MAX 256                /* we won't _realistically_ have more
                              * than this many segs in a group */
#define EXT_BLKSIZ 256               /* block size for externals list */

struct Segment;                      /* need to know these structs exist */
struct Group;

struct LineNumber {
    struct LineNumber *next;
    struct Segment *segment;
    long offset;
    long lineno;
};

static struct FileName {
    struct FileName *next;
    char *name;
    struct LineNumber *lnhead, **lntail;
    int index;
} *fnhead, **fntail;

static struct Array {
    struct Array *next;
    unsigned size;
    int basetype;
} *arrhead, **arrtail;

#define ARRAYBOT 31 /* magic number  for first array index */


static struct Public {
    struct Public *next;
    char *name;
    long offset;
    long segment;              /* only if it's far-absolute */
    int type;                          /* only for local debug syms */
} *fpubhead, **fpubtail, *last_defined;

static struct External {
    struct External *next;
    char *name;
    long commonsize;
    long commonelem;                 /* element size if FAR, else zero */
    int index;                       /* OBJ-file external index */
    enum {
      DEFWRT_NONE,                   /* no unusual default-WRT */
      DEFWRT_STRING,                 /* a string we don't yet understand */
      DEFWRT_SEGMENT,                /* a segment */
      DEFWRT_GROUP                   /* a group */
    } defwrt_type;
    union {
      char *string;
      struct Segment *seg;
      struct Group *grp;
    } defwrt_ptr;
    struct External *next_dws;             /* next with DEFWRT_STRING */
} *exthead, **exttail, *dws;

static int externals;

static struct ExtBack {
    struct ExtBack *next;
    struct External *exts[EXT_BLKSIZ];
} *ebhead, **ebtail;

static struct Segment {
    struct Segment *next;
    long index;                      /* the NASM segment id */
    long obj_index;                  /* the OBJ-file segment index */
    struct Group *grp;               /* the group it belongs to */
    unsigned long currentpos;
    long align;                      /* can be SEG_ABS + absolute addr */
    enum {
      CMB_PRIVATE = 0,
      CMB_PUBLIC = 2,
      CMB_STACK = 5,
      CMB_COMMON = 6
    } combine;
    long use32;                      /* is this segment 32-bit? */
    struct Public *pubhead, **pubtail, *lochead, **loctail;
    char *name;
    char *segclass, *overlay;        /* `class' is a C++ keyword :-) */
    ObjRecord *orp;
} *seghead, **segtail, *obj_seg_needs_update;

static struct Group {
    struct Group *next;
    char *name;
    long index;                      /* NASM segment id */
    long obj_index;                  /* OBJ-file group index */
    long nentries;                   /* number of elements... */
    long nindices;                   /* ...and number of index elts... */
    union {
      long index;
      char *name;
    } segs[GROUP_MAX];               /* ...in this */
} *grphead, **grptail, *obj_grp_needs_update;

static struct ImpDef {
    struct ImpDef *next;
    char *extname;
    char *libname;
    unsigned int impindex;
    char *impname;
} *imphead, **imptail;

static struct ExpDef {
    struct ExpDef *next;
    char *intname;
    char *extname;
    unsigned int ordinal;
    int flags;
} *exphead, **exptail;

#define EXPDEF_FLAG_ORDINAL  0x80
#define EXPDEF_FLAG_RESIDENT 0x40
#define EXPDEF_FLAG_NODATA   0x20
#define EXPDEF_MASK_PARMCNT  0x1F

static long obj_entry_seg, obj_entry_ofs;

struct ofmt of_obj;

/* The current segment */
static struct Segment *current_seg;

static long obj_segment (char *, int, int *);
static void obj_write_file(int debuginfo);
static int obj_directive (char *, char *, int);

static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval) 
{
    ofp = fp;
    error = errfunc;
    evaluate = eval;
    deflabel = ldef;
    first_seg = seg_alloc();
    any_segs = FALSE;
    fpubhead = NULL;
    fpubtail = &fpubhead;
    exthead = NULL;
    exttail = &exthead;
    imphead = NULL;
    imptail = &imphead;
    exphead = NULL;
    exptail = &exphead;
    dws = NULL;
    externals = 0;
    ebhead = NULL;
    ebtail = &ebhead;
    seghead = obj_seg_needs_update = NULL;
    segtail = &seghead;
    grphead = obj_grp_needs_update = NULL;
    grptail = &grphead;
    obj_entry_seg = NO_SEG;
    obj_uppercase = FALSE;
    obj_use32 = FALSE;
    passtwo = 0;
    current_seg = NULL;

    of_obj.current_dfmt->init (&of_obj,NULL,fp,errfunc);
}

static int obj_set_info(enum geninfo type, char **val)
{
    (void) type;
    (void) val;

    return 0;
}
static void obj_cleanup (int debuginfo) 
{
    obj_write_file(debuginfo);
    of_obj.current_dfmt->cleanup();
    fclose (ofp);
    while (seghead) {
      struct Segment *segtmp = seghead;
      seghead = seghead->next;
      while (segtmp->pubhead) {
          struct Public *pubtmp = segtmp->pubhead;
          segtmp->pubhead = pubtmp->next;
          nasm_free (pubtmp->name);
          nasm_free (pubtmp);
      }
      nasm_free (segtmp->segclass);
      nasm_free (segtmp->overlay);
      nasm_free (segtmp);
    }
    while (fpubhead) {
      struct Public *pubtmp = fpubhead;
      fpubhead = fpubhead->next;
      nasm_free (pubtmp->name);
      nasm_free (pubtmp);
    }
    while (exthead) {
      struct External *exttmp = exthead;
      exthead = exthead->next;
      nasm_free (exttmp);
    }
    while (imphead) {
      struct ImpDef *imptmp = imphead;
      imphead = imphead->next;
      nasm_free (imptmp->extname);
      nasm_free (imptmp->libname);
      nasm_free (imptmp->impname);   /* nasm_free won't mind if it's NULL */
      nasm_free (imptmp);
    }
    while (exphead) {
      struct ExpDef *exptmp = exphead;
      exphead = exphead->next;
      nasm_free (exptmp->extname);
      nasm_free (exptmp->intname);
      nasm_free (exptmp);
    }
    while (ebhead) {
      struct ExtBack *ebtmp = ebhead;
      ebhead = ebhead->next;
      nasm_free (ebtmp);
    }
    while (grphead) {
      struct Group *grptmp = grphead;
      grphead = grphead->next;
      nasm_free (grptmp);
    }
}

static void obj_ext_set_defwrt (struct External *ext, char *id) 
{
    struct Segment *seg;
    struct Group *grp;

    for (seg = seghead; seg; seg = seg->next)
      if (!strcmp(seg->name, id)) {
          ext->defwrt_type = DEFWRT_SEGMENT;
          ext->defwrt_ptr.seg = seg;
          nasm_free (id);
          return;
      }

    for (grp = grphead; grp; grp = grp->next)
      if (!strcmp(grp->name, id)) {
          ext->defwrt_type = DEFWRT_GROUP;
          ext->defwrt_ptr.grp = grp;
          nasm_free (id);
          return;
      }

    ext->defwrt_type = DEFWRT_STRING;
    ext->defwrt_ptr.string = id;
    ext->next_dws = dws;
    dws = ext;
}

static void obj_deflabel (char *name, long segment,
                    long offset, int is_global, char *special) 
{
    /*
     * We have three cases:
     *
     * (i) `segment' is a segment-base. If so, set the name field
     * for the segment or group structure it refers to, and then
     * return.
     *
     * (ii) `segment' is one of our segments, or a SEG_ABS segment.
     * Save the label position for later output of a PUBDEF record.
     * (Or a MODPUB, if we work out how.)
     *
     * (iii) `segment' is not one of our segments. Save the label
     * position for later output of an EXTDEF, and also store a
     * back-reference so that we can map later references to this
     * segment number to the external index.
     */
    struct External *ext;
    struct ExtBack *eb;
    struct Segment *seg;
    int i;
    int used_special = FALSE;        /* have we used the special text? */

#if defined(DEBUG) && DEBUG>2
fprintf(stderr, " obj_deflabel: %s, seg=%ld, off=%ld, is_global=%d, %s\n",
      name, segment, offset, is_global, special);
#endif

    /*
     * If it's a special-retry from pass two, discard it.
     */
    if (is_global == 3)
      return;

    /*
     * First check for the double-period, signifying something
     * unusual.
     */
    if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
      if (!strcmp(name, "..start")) {
          obj_entry_seg = segment;
          obj_entry_ofs = offset;
          return;
      }
      error (ERR_NONFATAL, "unrecognised special symbol `%s'", name);
    }

    /*
     * Case (i):
     */
    if (obj_seg_needs_update) {
      obj_seg_needs_update->name = name;
      return;
    } else if (obj_grp_needs_update) {
      obj_grp_needs_update->name = name;
      return;
    }
    if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
      return;

    if (segment >= SEG_ABS || segment == NO_SEG) {
      /*
       * SEG_ABS subcase of (ii).
       */
      if (is_global) {
          struct Public *pub;

          pub = *fpubtail = nasm_malloc(sizeof(*pub));
          fpubtail = &pub->next;
          pub->next = NULL;
          pub->name = nasm_strdup(name);
          pub->offset = offset;
          pub->segment = (segment == NO_SEG ? 0 : segment & ~SEG_ABS);
      }
      if (special)
          error(ERR_NONFATAL, "OBJ supports no special symbol features"
              " for this symbol type");
      return;
    }

    /*
     * If `any_segs' is still FALSE, we might need to define a
     * default segment, if they're trying to declare a label in
     * `first_seg'.
     */
    if (!any_segs && segment == first_seg) {
      int tempint;                   /* ignored */
      if (segment != obj_segment("__NASMDEFSEG", 2, &tempint))
          error (ERR_PANIC, "strange segment conditions in OBJ driver");
    }

    for (seg = seghead; seg && is_global; seg = seg->next)
      if (seg->index == segment) {
          struct Public *loc = nasm_malloc (sizeof(*loc));
          /*
           * Case (ii). Maybe MODPUB someday?
           */
          *seg->pubtail = loc;
          seg->pubtail = &loc->next;
          loc->next = NULL;
          loc->name = nasm_strdup(name);
          loc->offset = offset;
                  
          if (special)
            error(ERR_NONFATAL, "OBJ supports no special symbol features"
                  " for this symbol type");
          return;
      }

    /*
     * Case (iii).
     */
    if (is_global) {
        ext = *exttail = nasm_malloc(sizeof(*ext));
        ext->next = NULL;
        exttail = &ext->next;
        ext->name = name;
      /* Place by default all externs into the current segment */
        ext->defwrt_type = DEFWRT_NONE;

/* 28-Apr-2002 - John Coffman
  The following code was introduced on 12-Aug-2000, and breaks fixups
  on code passed thru the MSC 5.1 linker (3.66) and MSC 6.00A linker
  (5.10).  It was introduced after FIXUP32 was added, and may be needed
  for 32-bit segments.  The following will get 16-bit segments working
  again, and maybe someone can correct the 'if' condition which is
  actually needed.
*/
#if 0
      if (current_seg) {
#else
      if (current_seg && current_seg->use32) {
          if (current_seg->grp) {
            ext->defwrt_type = DEFWRT_GROUP;
            ext->defwrt_ptr.grp = current_seg->grp;
          } else {
            ext->defwrt_type = DEFWRT_SEGMENT;
            ext->defwrt_ptr.seg = current_seg;
          }
      }
#endif

        if (is_global == 2) {
          ext->commonsize = offset;
          ext->commonelem = 1;             /* default FAR */
        } else
          ext->commonsize = 0;
    }
    else
      return;

    /*
     * Now process the special text, if any, to find default-WRT
     * specifications and common-variable element-size and near/far
     * specifications.
     */
    while (special && *special) {
      used_special = TRUE;

      /*
       * We might have a default-WRT specification.
       */
      if (!nasm_strnicmp(special, "wrt", 3)) {
          char *p;
          int len;
          special += 3;
          special += strspn(special, " \t");
          p = nasm_strndup(special, len = strcspn(special, ":"));
          obj_ext_set_defwrt (ext, p);
          special += len;
          if (*special && *special != ':')
            error(ERR_NONFATAL, "`:' expected in special symbol"
                  " text for `%s'", ext->name);
          else if (*special == ':')
            special++;
      }

      /*
       * The NEAR or FAR keywords specify nearness or
       * farness. FAR gives default element size 1.
       */
      if (!nasm_strnicmp(special, "far", 3)) {
          if (ext->commonsize)
            ext->commonelem = 1;
          else
            error(ERR_NONFATAL, "`%s': `far' keyword may only be applied"
                  " to common variables\n", ext->name);
          special += 3;
          special += strspn(special, " \t");
      } else if (!nasm_strnicmp(special, "near", 4)) {
          if (ext->commonsize)
            ext->commonelem = 0;
          else
            error(ERR_NONFATAL, "`%s': `far' keyword may only be applied"
                  " to common variables\n", ext->name);
          special += 4;
          special += strspn(special, " \t");
      }

      /*
       * If it's a common, and anything else remains on the line
       * before a further colon, evaluate it as an expression and
       * use that as the element size. Forward references aren't
       * allowed.
       */
      if (*special == ':')
          special++;
      else if (*special) {
          if (ext->commonsize) {
            expr *e;
            struct tokenval tokval;

            stdscan_reset();
            stdscan_bufptr = special;
            tokval.t_type = TOKEN_INVALID;
            e = evaluate(stdscan, NULL, &tokval, NULL, 1, error, NULL);
            if (e) {
                if (!is_simple(e))
                  error (ERR_NONFATAL, "cannot use relocatable"
                         " expression as common-variable element size");
                else
                  ext->commonelem = reloc_value(e);
            }
            special = stdscan_bufptr;
          } else {
            error (ERR_NONFATAL, "`%s': element-size specifications only"
                   " apply to common variables", ext->name);
            while (*special && *special != ':')
                special++;
            if (*special == ':')
                special++;
          }
      }
    }

    i = segment/2;
    eb = ebhead;
    if (!eb) {
      eb = *ebtail = nasm_malloc(sizeof(*eb));
      eb->next = NULL;
      ebtail = &eb->next;
    }
    while (i >= EXT_BLKSIZ) {
      if (eb && eb->next)
          eb = eb->next;
      else {
          eb = *ebtail = nasm_malloc(sizeof(*eb));
          eb->next = NULL;
          ebtail = &eb->next;
      }
      i -= EXT_BLKSIZ;
    }
    eb->exts[i] = ext;
    ext->index = ++externals;

    if (special && !used_special)
      error(ERR_NONFATAL, "OBJ supports no special symbol features"
            " for this symbol type");
}

/* forward declaration */
static void obj_write_fixup (ObjRecord *orp, int bytes,
    int segrel, long seg, long wrt, struct Segment *segto);

static void obj_out (long segto, const void *data, unsigned long type,
                 long segment, long wrt) 
{
    unsigned long size, realtype;
    const unsigned char *ucdata;
    long ldata;
    struct Segment *seg;
    ObjRecord *orp;

    /*
     * handle absolute-assembly (structure definitions)
     */
    if (segto == NO_SEG) {
      if ((type & OUT_TYPMASK) != OUT_RESERVE)
          error (ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]"
               " space");
      return;
    }

    /*
     * If `any_segs' is still FALSE, we must define a default
     * segment.
     */
    if (!any_segs) {
      int tempint;                   /* ignored */
      if (segto != obj_segment("__NASMDEFSEG", 2, &tempint))
          error (ERR_PANIC, "strange segment conditions in OBJ driver");
    }

    /*
     * Find the segment we are targetting.
     */
    for (seg = seghead; seg; seg = seg->next)
      if (seg->index == segto)
          break;
    if (!seg)
      error (ERR_PANIC, "code directed to nonexistent segment?");

    orp = seg->orp;
    orp->parm[0] = seg->currentpos;

    size = type & OUT_SIZMASK;
    realtype = type & OUT_TYPMASK;
    if (realtype == OUT_RAWDATA) {
      ucdata = data;
      while (size > 0) {
          unsigned int len;
          orp = obj_check(seg->orp, 1);
          len = RECORD_MAX - orp->used;
          if (len > size)
            len = size;
          memcpy (orp->buf+orp->used, ucdata, len);
          orp->committed = orp->used += len;
          orp->parm[0] = seg->currentpos += len;
          ucdata += len;
          size -= len;
      }
    }
    else if (realtype == OUT_ADDRESS || realtype == OUT_REL2ADR ||
           realtype == OUT_REL4ADR) 
    {
      int rsize;

      if (segment == NO_SEG && realtype != OUT_ADDRESS)
          error(ERR_NONFATAL, "relative call to absolute address not"
              " supported by OBJ format");
      if (segment >= SEG_ABS)
          error(ERR_NONFATAL, "far-absolute relocations not supported"
              " by OBJ format");
      ldata = *(long *)data;
      if (realtype == OUT_REL2ADR) {
          ldata += (size-2);
          size = 2;
      }
      if (realtype == OUT_REL4ADR) {
          ldata += (size-4);
          size = 4;
      }
      if (size == 2)
          orp = obj_word (orp, ldata);
      else
          orp = obj_dword (orp, ldata);
      rsize = size;
      if (segment < SEG_ABS && (segment != NO_SEG && segment % 2) &&
          size == 4) {
          /*
           * This is a 4-byte segment-base relocation such as
           * `MOV EAX,SEG foo'. OBJ format can't actually handle
           * these, but if the constant term has the 16 low bits
           * zero, we can just apply a 2-byte segment-base
           * relocation to the low word instead.
           */
          rsize = 2;
          if (ldata & 0xFFFF)
            error(ERR_NONFATAL, "OBJ format cannot handle complex"
                  " dword-size segment base references");
      }
      if (segment != NO_SEG)
          obj_write_fixup (orp, rsize,
                       (realtype == OUT_ADDRESS  ? 0x4000 : 0),
                       segment, wrt, seg);
      seg->currentpos += size;
    } else if (realtype == OUT_RESERVE) {
      if (orp->committed)
          orp = obj_bump(orp);
      seg->currentpos += size;
    }
    obj_commit(orp);
}

static void obj_write_fixup (ObjRecord *orp, int bytes,
    int segrel, long seg, long wrt, struct Segment *segto)
{
    unsigned locat;
    int method;
    int base;
    long tidx, fidx;
    struct Segment *s = NULL;
    struct Group *g = NULL;
    struct External *e = NULL;
    ObjRecord *forp;

    if (bytes == 1) {
      error(ERR_NONFATAL, "`obj' output driver does not support"
            " one-byte relocations");
      return;
    }

    forp = orp->child;
    if (forp == NULL) {
      orp->child = forp = obj_new();
      forp->up = &(orp->child);
      /* We should choose between FIXUPP and FIXU32 record type */
      /* If we're targeting a 32-bit segment, use a FIXU32 record */
      if (segto->use32)
          forp->type = FIXU32;
      else
          forp->type = FIXUPP;
    }

    if (seg % 2) {
      base = TRUE;
      locat = FIX_16_SELECTOR;
      seg--;
      if (bytes != 2)
          error(ERR_PANIC, "OBJ: 4-byte segment base fixup got"
              " through sanity check");
    }
    else {
      base = FALSE;
      locat = (bytes == 2) ? FIX_16_OFFSET : FIX_32_OFFSET;
      if (!segrel)
          /*
           * There is a bug in tlink that makes it process self relative
           * fixups incorrectly if the x_size doesn't match the location
           * size.
           */
          forp = obj_force(forp, bytes<<3);
    }

    forp = obj_rword (forp, locat | segrel | (orp->parm[0]-orp->parm[2]));

    tidx = fidx = -1, method = 0;      /* placate optimisers */

    /*
     * See if we can find the segment ID in our segment list. If
     * so, we have a T4 (LSEG) target.
     */
    for (s = seghead; s; s = s->next)
      if (s->index == seg)
          break;
    if (s)
      method = 4, tidx = s->obj_index;
    else {
      for (g = grphead; g; g = g->next)
          if (g->index == seg)
            break;
      if (g)
          method = 5, tidx = g->obj_index;
      else {
          long i = seg/2;
          struct ExtBack *eb = ebhead;
          while (i > EXT_BLKSIZ) {
            if (eb)
                eb = eb->next;
            else
                break;
            i -= EXT_BLKSIZ;
          }
          if (eb)
            method = 6, e = eb->exts[i], tidx = e->index;
          else
            error(ERR_PANIC,
                  "unrecognised segment value in obj_write_fixup");
      }
    }

    /*
     * If no WRT given, assume the natural default, which is method
     * F5 unless:
     *
     * - we are doing an OFFSET fixup for a grouped segment, in
     *   which case we require F1 (group).
     *
     * - we are doing an OFFSET fixup for an external with a
     *   default WRT, in which case we must honour the default WRT.
     */
    if (wrt == NO_SEG) {
      if (!base && s && s->grp)
          method |= 0x10, fidx = s->grp->obj_index;
      else if (!base && e && e->defwrt_type != DEFWRT_NONE) {
          if (e->defwrt_type == DEFWRT_SEGMENT)
            method |= 0x00, fidx = e->defwrt_ptr.seg->obj_index;
          else if (e->defwrt_type == DEFWRT_GROUP)
            method |= 0x10, fidx = e->defwrt_ptr.grp->obj_index;
          else {
            error(ERR_NONFATAL, "default WRT specification for"
                  " external `%s' unresolved", e->name);
            method |= 0x50, fidx = -1; /* got to do _something_ */
          }
      } else
          method |= 0x50, fidx = -1;
    } else {
      /*
       * See if we can find the WRT-segment ID in our segment
       * list. If so, we have a F0 (LSEG) frame.
       */
      for (s = seghead; s; s = s->next)
          if (s->index == wrt-1)
            break;
      if (s)
          method |= 0x00, fidx = s->obj_index;
      else {
          for (g = grphead; g; g = g->next)
            if (g->index == wrt-1)
                break;
          if (g)
            method |= 0x10, fidx = g->obj_index;
          else {
            long i = wrt/2;
            struct ExtBack *eb = ebhead;
            while (i > EXT_BLKSIZ) {
                if (eb)
                  eb = eb->next;
                else
                  break;
                i -= EXT_BLKSIZ;
            }
            if (eb)
                method |= 0x20, fidx = eb->exts[i]->index;
            else
                error(ERR_PANIC,
                    "unrecognised WRT value in obj_write_fixup");
          }
      }
    }

    forp = obj_byte (forp, method);
    if (fidx != -1)
      forp = obj_index (forp, fidx);
    forp = obj_index (forp, tidx);
    obj_commit (forp);
}

static long obj_segment (char *name, int pass, int *bits) 
{
    /*
     * We call the label manager here to define a name for the new
     * segment, and when our _own_ label-definition stub gets
     * called in return, it should register the new segment name
     * using the pointer it gets passed. That way we save memory,
     * by sponging off the label manager.
     */
#if defined(DEBUG) && DEBUG>=3
fprintf(stderr," obj_segment: < %s >, pass=%d, *bits=%d\n",
      name, pass, *bits);
#endif     
    if (!name) {
      *bits = 16;
      current_seg = NULL;
      return first_seg;
    } else {
      struct Segment *seg;
      struct Group *grp;
      struct External **extp;
      int obj_idx, i, attrs, rn_error;
      char *p;

      /*
       * Look for segment attributes.
       */
      attrs = 0;
      while (*name == '.')
          name++;              /* hack, but a documented one */
            p = name;
      while (*p && !isspace(*p))
          p++;
      if (*p) {
          *p++ = '\0';
          while (*p && isspace(*p))
            *p++ = '\0';
      }
      while (*p) {
          while (*p && !isspace(*p))
            p++;
          if (*p) {
            *p++ = '\0';
            while (*p && isspace(*p))
                *p++ = '\0';
          }

          attrs++;
      }

      obj_idx = 1;
      for (seg = seghead; seg; seg = seg->next) {
          obj_idx++;
          if (!strcmp(seg->name, name)) {
            if (attrs > 0 && pass == 1)
                error(ERR_WARNING, "segment attributes specified on"
                    " redeclaration of segment: ignoring");
            if (seg->use32)
                *bits = 32;
            else
                *bits = 16;
            current_seg = seg;
            return seg->index;
          }
      }

      *segtail = seg = nasm_malloc(sizeof(*seg));
      seg->next = NULL;
      segtail = &seg->next;
      seg->index = (any_segs ? seg_alloc() : first_seg);
      seg->obj_index = obj_idx;
      seg->grp = NULL;
      any_segs = TRUE;
      seg->name = NULL;
      seg->currentpos = 0;
      seg->align = 1;                /* default */
      seg->use32 = FALSE;            /* default */
      seg->combine = CMB_PUBLIC;     /* default */
      seg->segclass = seg->overlay = NULL;
      seg->pubhead = NULL;
      seg->pubtail = &seg->pubhead;
      seg->lochead = NULL;
      seg->loctail = &seg->lochead;
      seg->orp = obj_new();
      seg->orp->up = &(seg->orp);
      seg->orp->ori = ori_ledata;
      seg->orp->type = LEDATA;
      seg->orp->parm[1] = obj_idx;

      /*
       * Process the segment attributes.
       */
      p = name;
      while (attrs--) {
          p += strlen(p);
          while (!*p) p++;

          /*
           * `p' contains a segment attribute.
           */
          if (!nasm_stricmp(p, "private"))
            seg->combine = CMB_PRIVATE;
          else if (!nasm_stricmp(p, "public"))
            seg->combine = CMB_PUBLIC;
          else if (!nasm_stricmp(p, "common"))
            seg->combine = CMB_COMMON;
          else if (!nasm_stricmp(p, "stack"))
            seg->combine = CMB_STACK;
          else if (!nasm_stricmp(p, "use16"))
            seg->use32 = FALSE;
          else if (!nasm_stricmp(p, "use32"))
            seg->use32 = TRUE;
          else if (!nasm_stricmp(p, "flat")) {
            /*
             * This segment is an OS/2 FLAT segment. That means
             * that its default group is group FLAT, even if
             * the group FLAT does not explicitly _contain_ the
             * segment.
             * 
             * When we see this, we must create the group
             * `FLAT', containing no segments, if it does not
             * already exist; then we must set the default
             * group of this segment to be the FLAT group.
             */
            struct Group *grp;
            for (grp = grphead; grp; grp = grp->next)
                if (!strcmp(grp->name, "FLAT"))
                  break;
            if (!grp) {
                obj_directive ("group", "FLAT", 1);
                for (grp = grphead; grp; grp = grp->next)
                  if (!strcmp(grp->name, "FLAT"))
                      break;
                if (!grp)
                  error (ERR_PANIC, "failure to define FLAT?!");
            }
            seg->grp = grp;
          } else if (!nasm_strnicmp(p, "class=", 6))
            seg->segclass = nasm_strdup(p+6);
          else if (!nasm_strnicmp(p, "overlay=", 8))
            seg->overlay = nasm_strdup(p+8);
          else if (!nasm_strnicmp(p, "align=", 6)) {
            seg->align = readnum(p+6, &rn_error);
            if (rn_error) {
                seg->align = 1;
                error (ERR_NONFATAL, "segment alignment should be"
                     " numeric");
            }
            switch ((int) seg->align) {
              case 1:          /* BYTE */
              case 2:          /* WORD */
              case 4:          /* DWORD */
              case 16:         /* PARA */
              case 256:        /* PAGE */
              case 4096:             /* PharLap extension */
                break;
              case 8:
                error(ERR_WARNING, "OBJ format does not support alignment"
                    " of 8: rounding up to 16");
                seg->align = 16;
                break;
              case 32:
              case 64:
              case 128:
                error(ERR_WARNING, "OBJ format does not support alignment"
                    " of %d: rounding up to 256", seg->align);
                seg->align = 256;
                break;
              case 512:
              case 1024:
              case 2048:
                error(ERR_WARNING, "OBJ format does not support alignment"
                    " of %d: rounding up to 4096", seg->align);
                seg->align = 4096;
                break;
              default:
                error(ERR_NONFATAL, "invalid alignment value %d",
                    seg->align);
                seg->align = 1;
                break;
            }
          } else if (!nasm_strnicmp(p, "absolute=", 9)) {
            seg->align = SEG_ABS + readnum(p+9, &rn_error);
            if (rn_error)
                error (ERR_NONFATAL, "argument to `absolute' segment"
                     " attribute should be numeric");
          }
      }

        /* We need to know whenever we have at least one 32-bit segment */
        obj_use32 |= seg->use32;

      obj_seg_needs_update = seg;
      if (seg->align >= SEG_ABS)
          deflabel (name, NO_SEG, seg->align - SEG_ABS,
                  NULL, FALSE, FALSE, &of_obj, error);
      else
          deflabel (name, seg->index+1, 0L,
                  NULL, FALSE, FALSE, &of_obj, error);
      obj_seg_needs_update = NULL;

      /*
       * See if this segment is defined in any groups.
       */
      for (grp = grphead; grp; grp = grp->next) {
          for (i = grp->nindices; i < grp->nentries; i++) {
            if (!strcmp(grp->segs[i].name, seg->name)) {
                nasm_free (grp->segs[i].name);
                grp->segs[i] = grp->segs[grp->nindices];
                grp->segs[grp->nindices++].index = seg->obj_index;
                if (seg->grp)
                  error(ERR_WARNING, "segment `%s' is already part of"
                        " a group: first one takes precedence",
                        seg->name);
                else
                  seg->grp = grp;
            }
          }
      }

      /*
       * Walk through the list of externals with unresolved
       * default-WRT clauses, and resolve any that point at this
       * segment.
       */
      extp = &dws;
      while (*extp) {
          if ((*extp)->defwrt_type == DEFWRT_STRING &&
            !strcmp((*extp)->defwrt_ptr.string, seg->name)) {
            nasm_free((*extp)->defwrt_ptr.string);
            (*extp)->defwrt_type = DEFWRT_SEGMENT;
            (*extp)->defwrt_ptr.seg = seg;
            *extp = (*extp)->next_dws;
          } else
            extp = &(*extp)->next_dws;
      }

      if (seg->use32)
          *bits = 32;
      else
          *bits = 16;
      current_seg = seg;
      return seg->index;
    }
}

static int obj_directive (char *directive, char *value, int pass) 
{
    if (!strcmp(directive, "group")) {
      char *p, *q, *v;
      if (pass == 1) {
          struct Group *grp;
          struct Segment *seg;
          struct External **extp;
          int obj_idx;

          q = value;
          while (*q == '.')
            q++;               /* hack, but a documented one */
          v = q;
          while (*q && !isspace(*q))
            q++;
          if (isspace(*q)) {
            *q++ = '\0';
            while (*q && isspace(*q))
                q++;
          }
          /*
           * Here we used to sanity-check the group directive to
           * ensure nobody tried to declare a group containing no
           * segments. However, OS/2 does this as standard
           * practice, so the sanity check has been removed.
           *
           * if (!*q) {
           *     error(ERR_NONFATAL,"GROUP directive contains no segments");
           *     return 1;
           * }
           */

          obj_idx = 1;
          for (grp = grphead; grp; grp = grp->next) {
            obj_idx++;
            if (!strcmp(grp->name, v)) {
                error(ERR_NONFATAL, "group `%s' defined twice", v);
                return 1;
            }
          }

          *grptail = grp = nasm_malloc(sizeof(*grp));
          grp->next = NULL;
          grptail = &grp->next;
          grp->index = seg_alloc();
          grp->obj_index = obj_idx;
          grp->nindices = grp->nentries = 0;
          grp->name = NULL;

          obj_grp_needs_update = grp;
          deflabel (v, grp->index+1, 0L,
                  NULL, FALSE, FALSE, &of_obj, error);
          obj_grp_needs_update = NULL;

          while (*q) {
            p = q;
            while (*q && !isspace(*q))
                q++;
            if (isspace(*q)) {
                *q++ = '\0';
                while (*q && isspace(*q))
                  q++;
            }
            /*
             * Now p contains a segment name. Find it.
             */
            for (seg = seghead; seg; seg = seg->next)
                if (!strcmp(seg->name, p))
                  break;
            if (seg) {
                /*
                 * We have a segment index. Shift a name entry
                 * to the end of the array to make room.
                 */
                grp->segs[grp->nentries++] = grp->segs[grp->nindices];
                grp->segs[grp->nindices++].index = seg->obj_index;
                if (seg->grp)
                  error(ERR_WARNING, "segment `%s' is already part of"
                        " a group: first one takes precedence",
                        seg->name);
                else
                  seg->grp = grp;
            } else {
                /*
                 * We have an as-yet undefined segment.
                 * Remember its name, for later.
                 */
                grp->segs[grp->nentries++].name = nasm_strdup(p);
            }
          }

          /*
           * Walk through the list of externals with unresolved
           * default-WRT clauses, and resolve any that point at
           * this group.
           */
          extp = &dws;
          while (*extp) {
            if ((*extp)->defwrt_type == DEFWRT_STRING &&
                !strcmp((*extp)->defwrt_ptr.string, grp->name)) {
                nasm_free((*extp)->defwrt_ptr.string);
                (*extp)->defwrt_type = DEFWRT_GROUP;
                (*extp)->defwrt_ptr.grp = grp;
                *extp = (*extp)->next_dws;
          } else
                extp = &(*extp)->next_dws;
          }
      }
      return 1;
    }
    if (!strcmp(directive, "uppercase")) {
      obj_uppercase = TRUE;
      return 1;
    }
    if (!strcmp(directive, "import")) {
      char *q, *extname, *libname, *impname;

      if (pass == 2)
          return 1;                  /* ignore in pass two */
      extname = q = value;
      while (*q && !isspace(*q))
          q++;
      if (isspace(*q)) {
          *q++ = '\0';
          while (*q && isspace(*q))
            q++;
      }

      libname = q;
      while (*q && !isspace(*q))
          q++;
      if (isspace(*q)) {
          *q++ = '\0';
          while (*q && isspace(*q))
            q++;
      }

      impname = q;

      if (!*extname || !*libname)
          error(ERR_NONFATAL, "`import' directive requires symbol name"
              " and library name");
      else {
          struct ImpDef *imp;
          int err = FALSE;

          imp = *imptail = nasm_malloc(sizeof(struct ImpDef));
          imptail = &imp->next;
          imp->next = NULL;
          imp->extname = nasm_strdup(extname);
          imp->libname = nasm_strdup(libname);
          imp->impindex = readnum(impname, &err);
          if (!*impname || err)
            imp->impname = nasm_strdup(impname);
          else
            imp->impname = NULL;
      }

      return 1;
    }
    if (!strcmp(directive, "export")) {
      char *q, *extname, *intname, *v;
      struct ExpDef *export;
      int flags = 0;
      unsigned int ordinal = 0;

      if (pass == 2)
          return 1;                  /* ignore in pass two */
      intname = q = value;
      while (*q && !isspace(*q))
          q++;
      if (isspace(*q)) {
          *q++ = '\0';
          while (*q && isspace(*q))
            q++;
      }

      extname = q;
      while (*q && !isspace(*q))
          q++;
      if (isspace(*q)) {
          *q++ = '\0';
          while (*q && isspace(*q))
            q++;
      }

      if (!*intname) {
          error(ERR_NONFATAL, "`export' directive requires export name");
          return 1;
      }
      if (!*extname) {
          extname = intname;
          intname = "";
      }
      while (*q) {
          v = q;
          while (*q && !isspace(*q))
            q++;
          if (isspace(*q)) {
            *q++ = '\0';
            while (*q && isspace(*q))
                q++;
          }
          if (!nasm_stricmp(v, "resident"))
            flags |= EXPDEF_FLAG_RESIDENT;
          else if (!nasm_stricmp(v, "nodata"))
            flags |= EXPDEF_FLAG_NODATA;
          else if (!nasm_strnicmp(v, "parm=", 5)) {
            int err = FALSE;
            flags |= EXPDEF_MASK_PARMCNT & readnum(v+5, &err);
            if (err) {
                error(ERR_NONFATAL,
                    "value `%s' for `parm' is non-numeric", v+5);
                return 1;
            }
          } else {
            int err = FALSE;
            ordinal = readnum(v, &err);
            if (err) {
                error(ERR_NONFATAL, "unrecognised export qualifier `%s'",
                    v);
                return 1;
            }
            flags |= EXPDEF_FLAG_ORDINAL;
          }
      }

      export = *exptail = nasm_malloc(sizeof(struct ExpDef));
      exptail = &export->next;
      export->next = NULL;
      export->extname = nasm_strdup(extname);
      export->intname = nasm_strdup(intname);
      export->ordinal = ordinal;
      export->flags = flags;

      return 1;
    }
    return 0;
}

static long obj_segbase (long segment) 
{
    struct Segment *seg;

    /*
     * Find the segment in our list.
     */
    for (seg = seghead; seg; seg = seg->next)
      if (seg->index == segment-1)
          break;

    if (!seg) {
      /*
       * Might be an external with a default WRT.
       */
      long i = segment/2;
      struct ExtBack *eb = ebhead;
      struct External *e;

      while (i > EXT_BLKSIZ) {
          if (eb)
            eb = eb->next;
          else
            break;
          i -= EXT_BLKSIZ;
      }
      if (eb) {
          e = eb->exts[i];
          if (e->defwrt_type == DEFWRT_NONE)
            return segment;          /* fine */
          else if (e->defwrt_type == DEFWRT_SEGMENT)
            return e->defwrt_ptr.seg->index+1;
          else if (e->defwrt_type == DEFWRT_GROUP)
            return e->defwrt_ptr.grp->index+1;
          else
            return NO_SEG;           /* can't tell what it is */
      }

      return segment;                /* not one of ours - leave it alone */
    }

    if (seg->align >= SEG_ABS)
      return seg->align;             /* absolute segment */
    if (seg->grp)
      return seg->grp->index+1;      /* grouped segment */

    return segment;                  /* no special treatment */
}

static void obj_filename (char *inname, char *outname, efunc lerror) 
{
    strcpy(obj_infile, inname);
    standard_extension (inname, outname, ".obj", lerror);
}

static void obj_write_file (int debuginfo) 
{
    struct Segment *seg, *entry_seg_ptr = 0;
    struct FileName *fn;
    struct LineNumber *ln;
    struct Group *grp;
    struct Public *pub, *loc;
    struct External *ext;
    struct ImpDef *imp;
    struct ExpDef *export;
    static char boast[] = "The Netwide Assembler " NASM_VER;
    int lname_idx;
    ObjRecord *orp;

    /*
     * Write the THEADR module header.
     */
    orp = obj_new();
    orp->type = THEADR;
    obj_name (orp, obj_infile);
    obj_emit2 (orp);

    /*
     * Write the NASM boast comment.
     */
    orp->type = COMENT;
    obj_rword (orp, 0);   /* comment type zero */
    obj_name (orp, boast);
    obj_emit2 (orp);

    orp->type = COMENT;
    /*
     * Write the IMPDEF records, if any.
     */
    for (imp = imphead; imp; imp = imp->next) {
      obj_rword (orp, 0xA0);   /* comment class A0 */
      obj_byte (orp, 1);   /* subfunction 1: IMPDEF */
      if (imp->impname)
          obj_byte (orp, 0);   /* import by name */
      else
          obj_byte (orp, 1);   /* import by ordinal */
      obj_name (orp, imp->extname);
      obj_name (orp, imp->libname);
      if (imp->impname)
          obj_name (orp, imp->impname);
      else
          obj_word (orp, imp->impindex);
      obj_emit2 (orp);
    }

    /*
     * Write the EXPDEF records, if any.
     */
    for (export = exphead; export; export = export->next) {
      obj_rword (orp, 0xA0);   /* comment class A0 */
      obj_byte (orp, 2);   /* subfunction 2: EXPDEF */
      obj_byte (orp, export->flags);
      obj_name (orp, export->extname);
      obj_name (orp, export->intname);
      if (export->flags & EXPDEF_FLAG_ORDINAL)
          obj_word (orp, export->ordinal);
      obj_emit2 (orp);
    }

    /* we're using extended OMF if we put in debug info*/
    if (debuginfo) {
      orp->type = COMENT;
      obj_byte (orp, 0x40);
      obj_byte (orp, dEXTENDED);
      obj_emit2 (orp);
    }

    /*
     * Write the first LNAMES record, containing LNAME one, which
     * is null. Also initialise the LNAME counter.
     */
    orp->type = LNAMES;
    obj_byte (orp, 0);
    lname_idx = 1;
    /*
     * Write some LNAMES for the segment names
     */
    for (seg = seghead; seg; seg = seg->next) {
      orp = obj_name (orp, seg->name);
      if (seg->segclass)
          orp = obj_name (orp, seg->segclass);
      if (seg->overlay)
          orp = obj_name (orp, seg->overlay);
      obj_commit (orp);
    }
    /*
     * Write some LNAMES for the group names
     */
    for (grp = grphead; grp; grp = grp->next) {
      orp = obj_name (orp, grp->name);
      obj_commit (orp);
    }
    obj_emit (orp);


    /*
     * Write the SEGDEF records.
     */
    orp->type = SEGDEF;
    for (seg = seghead; seg; seg = seg->next) {
      int acbp;
      unsigned long seglen = seg->currentpos;

      acbp = (seg->combine << 2);    /* C field */

      if (seg->use32)
          acbp |= 0x01;        /* P bit is Use32 flag */
      else if (seglen == 0x10000L) {
          seglen = 0;                /* This special case may be needed for old linkers */
          acbp |= 0x02;        /* B bit */
      }


      /* A field */
      if (seg->align >= SEG_ABS)
          /* acbp |= 0x00 */;
      else if (seg->align >= 4096) {
          if (seg->align > 4096)
            error(ERR_NONFATAL, "segment `%s' requires more alignment"
                  " than OBJ format supports", seg->name);
          acbp |= 0xC0;        /* PharLap extension */
      } else if (seg->align >= 256) {
          acbp |= 0x80;
      } else if (seg->align >= 16) {
          acbp |= 0x60;
      } else if (seg->align >= 4) {
          acbp |= 0xA0;
      } else if (seg->align >= 2) {
          acbp |= 0x40;
      } else
          acbp |= 0x20;

      obj_byte (orp, acbp);
      if (seg->align & SEG_ABS) {
          obj_x (orp, seg->align - SEG_ABS);  /* Frame */
          obj_byte (orp, 0);  /* Offset */
      }
      obj_x (orp, seglen);
      obj_index (orp, ++lname_idx);
      obj_index (orp, seg->segclass ? ++lname_idx : 1);
      obj_index (orp, seg->overlay ? ++lname_idx : 1);
      obj_emit2 (orp);
    }

    /*
     * Write the GRPDEF records.
     */
    orp->type = GRPDEF;
    for (grp = grphead; grp; grp = grp->next) {
      int i;

      if (grp->nindices != grp->nentries) {
          for (i = grp->nindices; i < grp->nentries; i++) {
            error(ERR_NONFATAL, "group `%s' contains undefined segment"
                  " `%s'", grp->name, grp->segs[i].name);
            nasm_free (grp->segs[i].name);
            grp->segs[i].name = NULL;
          }
      }
      obj_index (orp, ++lname_idx);
      for (i = 0; i < grp->nindices; i++) {
          obj_byte (orp, 0xFF);
          obj_index (orp, grp->segs[i].index);
      }
      obj_emit2 (orp);
    }

    /*
     * Write the PUBDEF records: first the ones in the segments,
     * then the far-absolutes.
     */
    orp->type = PUBDEF;
    orp->ori = ori_pubdef;
    for (seg = seghead; seg; seg = seg->next) {
      orp->parm[0] = seg->grp ? seg->grp->obj_index : 0;
      orp->parm[1] = seg->obj_index;
      for (pub = seg->pubhead; pub; pub = pub->next) {
          orp = obj_name (orp, pub->name);
          orp = obj_x (orp, pub->offset);
          orp = obj_byte (orp, 0);  /* type index */
          obj_commit (orp);
      }
      obj_emit (orp);
    }
    orp->parm[0] = 0;
    orp->parm[1] = 0;
    for (pub = fpubhead; pub; pub = pub->next) {   /* pub-crawl :-) */
      if (orp->parm[2] != pub->segment) {
          obj_emit (orp);
          orp->parm[2] = pub->segment;
      }
      orp = obj_name (orp, pub->name);
      orp = obj_x (orp, pub->offset);
      orp = obj_byte (orp, 0);  /* type index */
      obj_commit (orp);
    }
    obj_emit (orp);

    /*
     * Write the EXTDEF and COMDEF records, in order.
     */
    orp->ori = ori_null;
    for (ext = exthead; ext; ext = ext->next) {
      if (ext->commonsize == 0) {
          if (orp->type != EXTDEF) {
            obj_emit (orp);
            orp->type = EXTDEF;
          }
          orp = obj_name (orp, ext->name);
          orp = obj_index (orp, 0);
      } else {
          if (orp->type != COMDEF) {
            obj_emit (orp);
            orp->type = COMDEF;
          }
          orp = obj_name (orp, ext->name);
          orp = obj_index (orp, 0);
          if (ext->commonelem) {
            orp = obj_byte (orp, 0x61);/* far communal */
            orp = obj_value (orp, (ext->commonsize / ext->commonelem));
            orp = obj_value (orp, ext->commonelem);
          } else {
            orp = obj_byte (orp, 0x62);/* near communal */
            orp = obj_value (orp, ext->commonsize);
          }
      }
      obj_commit (orp);
    }
    obj_emit (orp);

    /*
     * Write a COMENT record stating that the linker's first pass
     * may stop processing at this point. Exception is if our
     * MODEND record specifies a start point, in which case,
     * according to some variants of the documentation, this COMENT
     * should be omitted. So we'll omit it just in case.
     * But, TASM puts it in all the time so if we are using
     * TASM debug stuff we are putting it in
     */
    if (debuginfo || obj_entry_seg == NO_SEG) {
      orp->type = COMENT;
        obj_byte (orp, 0x40);
        obj_byte (orp, dLINKPASS);
      obj_byte (orp, 1);
      obj_emit2 (orp);
    } 

    /*
     * 1) put out the compiler type
     * 2) Put out the type info.  The only type we are using is near label #19
     */
    if (debuginfo) {
      int i;
      struct Array *arrtmp = arrhead;
      orp->type = COMENT;
      obj_byte (orp, 0x40);
      obj_byte (orp, dCOMPDEF);
      obj_byte (orp, 4);
      obj_byte (orp, 0);
      obj_emit2 (orp);

      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x18); /* type # for linking */
      obj_word (orp, 6);    /* size of type */
      obj_byte (orp, 0x2a); /* absolute type for debugging */
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x19); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x24); /* absolute type for debugging */
      obj_byte (orp, 0);    /* near/far specifier */
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x1A); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x24); /* absolute type for debugging */
      obj_byte (orp, 1);    /* near/far specifier */
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x1b); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x23); /* absolute type for debugging */
      obj_byte (orp, 0);
      obj_byte (orp, 0);
      obj_byte (orp, 0);
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x1c); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x23); /* absolute type for debugging */
      obj_byte (orp, 0);
      obj_byte (orp, 4);
      obj_byte (orp, 0);
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x1d); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x23); /* absolute type for debugging */
      obj_byte (orp, 0);
      obj_byte (orp, 1);
      obj_byte (orp, 0);
      obj_emit2 (orp);
      obj_byte (orp, 0x40);
      obj_byte (orp, dTYPEDEF);
      obj_word (orp, 0x1e); /* type # for linking */
      obj_word (orp, 0);    /* size of type */
      obj_byte (orp, 0x23); /* absolute type for debugging */
      obj_byte (orp, 0);
      obj_byte (orp, 5);
      obj_byte (orp, 0);
      obj_emit2 (orp);

      /* put out the array types */
      for (i= ARRAYBOT; i < arrindex; i++) {
        obj_byte (orp, 0x40);
            obj_byte (orp, dTYPEDEF);
            obj_word (orp, i ); /* type # for linking */
            obj_word (orp, arrtmp->size);    /* size of type */
            obj_byte (orp, 0x1A); /* absolute type for debugging (array)*/
            obj_byte (orp, arrtmp->basetype ); /* base type */
            obj_emit2 (orp);
        arrtmp = arrtmp->next ;
      }
    }
    /*
     * write out line number info with a LINNUM record
     * switch records when we switch segments, and output the
     * file in a pseudo-TASM fashion.  The record switch is naive; that
     * is that one file may have many records for the same segment
     * if there are lots of segment switches
     */
    if (fnhead && debuginfo) {
      seg = fnhead->lnhead->segment;

      for (fn = fnhead; fn; fn = fn->next) {
          /* write out current file name */
            orp->type = COMENT;
            orp->ori = ori_null;
          obj_byte (orp, 0x40);
          obj_byte (orp, dFILNAME);
            obj_byte( orp,0);
            obj_name( orp,fn->name);
            obj_dword(orp, 0);
          obj_emit2 (orp);

          /* write out line numbers this file */

            orp->type = LINNUM;
            orp->ori = ori_linnum;
          for (ln = fn->lnhead; ln; ln = ln->next) {
            if (seg != ln->segment) {
                /* if we get here have to flush the buffer and start
                     * a new record for a new segment
                 */
                seg = ln->segment;
                obj_emit ( orp );
            }
            orp->parm[0] = seg->grp ? seg->grp->obj_index : 0;
            orp->parm[1] = seg->obj_index;
              orp = obj_word(orp, ln->lineno);
                orp = obj_x(orp, ln->offset);
              obj_commit (orp);
          }
          obj_emit (orp);
      }
    }
    /*
     * we are going to locate the entry point segment now
     * rather than wait until the MODEND record, because,
     * then we can output a special symbol to tell where the
     * entry point is.
     *
     */
    if (obj_entry_seg != NO_SEG) {
      for (seg = seghead; seg; seg = seg->next) {
          if (seg->index == obj_entry_seg) {
                entry_seg_ptr = seg;
            break;
          }
      }
      if (!seg)
          error(ERR_NONFATAL, "entry point is not in this module");
    }

    /*
     * get ready to put out symbol records
     */
    orp->type = COMENT;
    orp->ori = ori_local;
   
    /*
     * put out a symbol for the entry point
     * no dots in this symbol, because, borland does
     * not (officially) support dots in label names
     * and I don't know what various versions of TLINK will do
     */
    if (debuginfo && obj_entry_seg != NO_SEG) {
        orp = obj_name (orp,"start_of_program");
      orp = obj_word (orp,0x19);  /* type: near label */
      orp = obj_index (orp, seg->grp ? seg->grp->obj_index : 0);
      orp = obj_index (orp, seg->obj_index);
      orp = obj_x (orp, obj_entry_ofs);
      obj_commit (orp);
    } 
 
    /*
     * put out the local labels
     */
    for (seg = seghead; seg && debuginfo; seg = seg->next) {
        /* labels this seg */
        for (loc = seg->lochead; loc; loc = loc->next) {
            orp = obj_name (orp,loc->name);
          orp = obj_word (orp, loc->type);
          orp = obj_index (orp, seg->grp ? seg->grp->obj_index : 0);
          orp = obj_index (orp, seg->obj_index);
          orp = obj_x (orp,loc->offset);
          obj_commit (orp);
        }
    }
    if (orp->used)
        obj_emit (orp);

    /*
     * Write the LEDATA/FIXUPP pairs.
     */
    for (seg = seghead; seg; seg = seg->next) {
      obj_emit (seg->orp);
      nasm_free (seg->orp);
    }

    /*
     * Write the MODEND module end marker.
     */
    orp->type = obj_use32 ? MODE32 : MODEND;
    orp->ori = ori_null;
    if (entry_seg_ptr) {
      orp->type = entry_seg_ptr->use32 ? MODE32 : MODEND;
      obj_byte (orp, 0xC1);
      seg = entry_seg_ptr;
      if (seg->grp) {
          obj_byte (orp, 0x10);
          obj_index (orp, seg->grp->obj_index);
      } else {
          /*
           * the below changed to prevent TLINK crashing.
           * Previous more efficient version read:
           *
           *  obj_byte (orp, 0x50);
           */
          obj_byte (orp, 0x00);
          obj_index (orp, seg->obj_index);
      }
      obj_index (orp, seg->obj_index);
      obj_x (orp, obj_entry_ofs);
    } else
      obj_byte (orp, 0);
    obj_emit2 (orp);
    nasm_free (orp);
}

void obj_fwrite(ObjRecord *orp) 
{
    unsigned int cksum, len;
    unsigned char *ptr;

    cksum = orp->type;
    if (orp->x_size == 32)
      cksum |= 1;
    fputc (cksum, ofp);
    len = orp->committed+1;
    cksum += (len & 0xFF) + ((len>>8) & 0xFF);
    fwriteshort (len, ofp);
    fwrite (orp->buf, 1, len-1, ofp);
    for (ptr=orp->buf; --len; ptr++)
      cksum += *ptr;
    fputc ( (-cksum) & 0xFF, ofp);
}

static const char *obj_stdmac[] = {
    "%define __SECT__ [section .text]",
    "%imacro group 1+.nolist",
    "[group %1]",
    "%endmacro",
    "%imacro uppercase 0+.nolist",
    "[uppercase %1]",
    "%endmacro",
    "%imacro export 1+.nolist",
    "[export %1]",
    "%endmacro",
    "%imacro import 1+.nolist",
    "[import %1]",
    "%endmacro",
    "%macro __NASM_CDecl__ 1",
    "%endmacro",
    NULL
};

void dbgbi_init(struct ofmt * of, void * id, FILE * fp, efunc error)
{
    (void) of;
    (void) id;
    (void) fp;
    (void) error;

    fnhead = NULL;
    fntail = &fnhead;
    arrindex = ARRAYBOT ;
    arrhead = NULL;
    arrtail = &arrhead;
}
static void dbgbi_cleanup(void)
{
    struct Segment *segtmp;
    while (fnhead) {
      struct FileName *fntemp = fnhead;
      while (fnhead->lnhead) {
          struct LineNumber *lntemp = fnhead->lnhead;
          fnhead->lnhead = lntemp->next;
          nasm_free( lntemp);
      }
      fnhead = fnhead->next;
      nasm_free (fntemp->name);
      nasm_free (fntemp);
    }
    for (segtmp=seghead; segtmp; segtmp=segtmp->next) {
      while (segtmp->lochead) {
          struct Public *loctmp = segtmp->lochead;
          segtmp->lochead = loctmp->next;
          nasm_free (loctmp->name);
          nasm_free (loctmp);
      }
    }
    while (arrhead) {
      struct Array *arrtmp = arrhead;
        arrhead = arrhead->next;
        nasm_free (arrtmp);
    }
}

static void dbgbi_linnum (const char *lnfname, long lineno, long segto)
{
    struct FileName *fn;
    struct LineNumber *ln;
    struct Segment *seg;

    if (segto == NO_SEG)
      return;

    /*
     * If `any_segs' is still FALSE, we must define a default
     * segment.
     */
    if (!any_segs) {
      int tempint;                   /* ignored */
      if (segto != obj_segment("__NASMDEFSEG", 2, &tempint))
          error (ERR_PANIC, "strange segment conditions in OBJ driver");
    }

    /*
     * Find the segment we are targetting.
     */
    for (seg = seghead; seg; seg = seg->next)
      if (seg->index == segto)
          break;
    if (!seg)
      error (ERR_PANIC, "lineno directed to nonexistent segment?");

/*    for (fn = fnhead; fn; fn = fnhead->next) */
    for (fn = fnhead; fn; fn = fn->next) /* fbk - Austin Lunnen - John Fine*/
      if (!nasm_stricmp(lnfname,fn->name))
          break;
    if (!fn) {
      fn = nasm_malloc ( sizeof( *fn));
      fn->name = nasm_malloc ( strlen(lnfname) + 1) ;
        strcpy (fn->name,lnfname);
      fn->lnhead = NULL;
      fn->lntail = & fn->lnhead;
      fn->next = NULL;
      *fntail = fn;
      fntail = &fn->next;
    }
    ln = nasm_malloc ( sizeof( *ln));
    ln->segment = seg;
    ln->offset = seg->currentpos;
    ln->lineno = lineno;
    ln->next = NULL;
    *fn->lntail = ln;
    fn->lntail = &ln->next;

}
static void dbgbi_deflabel (char *name, long segment,
                    long offset, int is_global, char *special) 
{
    struct Segment *seg;

    (void) special;

    /*
     * If it's a special-retry from pass two, discard it.
     */
    if (is_global == 3)
      return;

    /*
     * First check for the double-period, signifying something
     * unusual.
     */
    if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
      return;
    }

    /*
     * Case (i):
     */
    if (obj_seg_needs_update) {
      return;
    } else if (obj_grp_needs_update) {
      return;
    }
    if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
      return;

    if (segment >= SEG_ABS || segment == NO_SEG) {
      return;
    }

    /*
     * If `any_segs' is still FALSE, we might need to define a
     * default segment, if they're trying to declare a label in
     * `first_seg'.  But the label should exist due to a prior
     * call to obj_deflabel so we can skip that.
     */

    for (seg = seghead; seg; seg = seg->next)
      if (seg->index == segment) {
          struct Public *loc = nasm_malloc (sizeof(*loc));
          /*
           * Case (ii). Maybe MODPUB someday?
           */
          last_defined = *seg->loctail = loc;
          seg->loctail = &loc->next;
          loc->next = NULL;
          loc->name = nasm_strdup(name);
          loc->offset = offset;
      }
}
static void dbgbi_typevalue (long type)
{
    int vsize;
    int elem = TYM_ELEMENTS(type);
    type = TYM_TYPE(type);

    if (!last_defined)
      return;

    switch (type) {
      case TY_BYTE:
          last_defined->type = 8; /* unsigned char */
          vsize = 1;
          break;
      case TY_WORD:
          last_defined->type = 10; /* unsigned word */
          vsize = 2;
          break;
      case TY_DWORD:
          last_defined->type = 12; /* unsigned dword */
          vsize = 4;
          break;
      case TY_FLOAT:
          last_defined->type = 14; /* float */
          vsize = 4;
          break;
      case TY_QWORD:
          last_defined->type = 15; /* qword */
          vsize = 8;
          break;
      case TY_TBYTE:
          last_defined->type = 16; /* TBYTE */
          vsize = 10;
          break;
      default:
          last_defined->type = 0x19; /*label */
          vsize = 0;
          break;
    }
                
    if (elem > 1) {
        struct Array *arrtmp = nasm_malloc (sizeof(*arrtmp));
        int vtype = last_defined->type;
        arrtmp->size = vsize * elem;
        arrtmp->basetype = vtype;
        arrtmp->next = NULL;
        last_defined->type = arrindex++;
        *arrtail = arrtmp;
        arrtail = & (arrtmp->next);
    }
    last_defined = NULL;
}
static void dbgbi_output (int output_type, void *param)
{
    (void) output_type;
    (void) param;
}
static struct dfmt borland_debug_form = {
    "Borland Debug Records",
    "borland",
    dbgbi_init,
    dbgbi_linnum,
    dbgbi_deflabel,
    null_debug_routine,
    dbgbi_typevalue,
    dbgbi_output,
    dbgbi_cleanup,
};

static struct dfmt *borland_debug_arr[3] = {
      &borland_debug_form,
      &null_debug_form,
      NULL
};

struct ofmt of_obj = {
    "MS-DOS 16-bit/32-bit OMF object files",
    "obj",
    NULL,
    borland_debug_arr,
    &null_debug_form,
    obj_stdmac,
    obj_init,
    obj_set_info,
    obj_out,
    obj_deflabel,
    obj_segment,
    obj_segbase,
    obj_directive,
    obj_filename,
    obj_cleanup
};
#endif /* OF_OBJ */

Generated by  Doxygen 1.6.0   Back to index