nasm/output/outieee.c
H. Peter Anvin 8ac364139a NASM 0.98.30
2002-04-30 21:09:12 +00:00

1462 lines
36 KiB
C

/* outieee.c output routines for the Netwide Assembler to produce
* IEEE-std 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.
*/
/* notes: I have tried to make this correspond to the IEEE version
* of the standard, specifically the primary ASCII version. It should
* be trivial to create the binary version given this source (which is
* one of MANY things that have to be done to make this correspond to
* the hp-microtek version of the standard).
*
* 16-bit support is assumed to use 24-bit addresses
* The linker can sort out segmentation-specific stuff
* if it keeps track of externals
* in terms of being relative to section bases
*
* A non-standard variable type, the 'Yn' variable, has been introduced.
* Basically it is a reference to extern 'n'- denoting the low limit
* (L-variable) of the section that extern 'n' is defined in. Like the
* x variable, there may be no explicit assignment to it, it is derived
* from the public definition corresponding to the extern name. This
* is required because the one thing the mufom guys forgot to do well was
* take into account segmented architectures.
*
* I use comment classes for various things and these are undefined by
* the standard.
*
* Debug info should be considered totally non-standard (local labels are
* standard but linenum records are not covered by the standard.
* Type defs have the standard format but absolute meanings for ordinal
* types are not covered by the standard.)
*
* David Lindauer, LADsoft
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h> /* Note: we need the ANSI version of stdarg.h */
#include <ctype.h>
#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"
#ifdef OF_IEEE
#define ARRAY_BOT 0x1
static char ieee_infile[FILENAME_MAX];
static int ieee_uppercase;
static efunc error;
static ldfunc deflabel;
static FILE *ofp;
static int any_segs;
static int arrindex;
#define HUNKSIZE 1024 /* Size of the data hunk */
#define EXT_BLKSIZ 512
#define LDPERLINE 32 /* bytes per line in output */
struct ieeeSection;
struct LineNumber {
struct LineNumber *next;
struct ieeeSection *segment;
long offset;
long lineno;
};
static struct FileName {
struct FileName *next;
char *name;
long index;
} *fnhead, **fntail;
static struct Array {
struct Array *next;
unsigned size;
int basetype;
} *arrhead, **arrtail;
static struct ieeePublic {
struct ieeePublic *next;
char *name;
long offset;
long segment; /* only if it's far-absolute */
long index;
int type; /* for debug purposes */
} *fpubhead, **fpubtail, *last_defined;
static struct ieeeExternal {
struct ieeeExternal *next;
char *name;
long commonsize;
} *exthead, **exttail;
static int externals;
static struct ExtBack {
struct ExtBack *next;
int index[EXT_BLKSIZ];
} *ebhead, **ebtail;
/* NOTE: the first segment MUST be the lineno segment */
static struct ieeeSection {
struct ieeeObjData *data,*datacurr;
struct ieeeSection *next;
struct ieeeFixupp *fptr, * flptr;
long index; /* the NASM segment id */
long ieee_index; /* the OBJ-file segment index */
long currentpos;
long align; /* can be SEG_ABS + absolute addr */
long startpos;
enum {
CMB_PRIVATE = 0,
CMB_PUBLIC = 2,
CMB_COMMON = 6
} combine;
long use32; /* is this segment 32-bit? */
struct ieeePublic *pubhead, **pubtail, *lochead, **loctail;
char *name;
} *seghead, **segtail, *ieee_seg_needs_update;
struct ieeeObjData {
struct ieeeObjData *next;
unsigned char data[HUNKSIZE];
};
struct ieeeFixupp {
struct ieeeFixupp *next;
enum {
FT_SEG = 0,
FT_REL = 1,
FT_OFS = 2,
FT_EXT = 3,
FT_WRT = 4,
FT_EXTREL = 5,
FT_EXTWRT = 6,
FT_EXTSEG = 7
} ftype;
short size;
long id1;
long id2;
long offset;
long addend;
};
static long ieee_entry_seg, ieee_entry_ofs;
static int checksum;
extern struct ofmt of_ieee;
static void ieee_data_new(struct ieeeSection *);
static void ieee_write_fixup (long, long, struct ieeeSection *,
int, unsigned long, long);
static void ieee_install_fixup(struct ieeeSection *, struct ieeeFixupp *);
static long ieee_segment (char *, int, int *);
static void ieee_write_file(int debuginfo);
static void ieee_write_byte(struct ieeeSection *, int);
static void ieee_write_word(struct ieeeSection *, int);
static void ieee_write_dword(struct ieeeSection *, long);
static void ieee_putascii(char *, ...);
static void ieee_putcs(int);
static long ieee_putld(long, long, unsigned char *);
static long ieee_putlr(struct ieeeFixupp *);
static void ieee_unqualified_name(char *, char *);
/*
* pup init
*/
static void ieee_init (FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
(void) eval;
ofp = fp;
error = errfunc;
deflabel = ldef;
any_segs = FALSE;
fpubhead = NULL;
fpubtail = &fpubhead;
exthead = NULL;
exttail = &exthead;
externals = 1;
ebhead = NULL;
ebtail = &ebhead;
seghead = ieee_seg_needs_update = NULL;
segtail = &seghead;
ieee_entry_seg = NO_SEG;
ieee_uppercase = FALSE;
checksum = 0;
of_ieee.current_dfmt->init (&of_ieee,NULL,fp,errfunc);
}
static int ieee_set_info(enum geninfo type, char **val)
{
(void) type;
(void) val;
return 0;
}
/*
* Rundown
*/
static void ieee_cleanup (int debuginfo)
{
ieee_write_file(debuginfo);
of_ieee.current_dfmt->cleanup ();
fclose (ofp);
while (seghead) {
struct ieeeSection *segtmp = seghead;
seghead = seghead->next;
while (segtmp->pubhead) {
struct ieeePublic *pubtmp = segtmp->pubhead;
segtmp->pubhead = pubtmp->next;
nasm_free (pubtmp);
}
while (segtmp->fptr) {
struct ieeeFixupp *fixtmp = segtmp->fptr;
segtmp->fptr = fixtmp->next;
nasm_free(fixtmp);
}
while (segtmp->data) {
struct ieeeObjData *dattmp = segtmp->data;
segtmp->data = dattmp->next;
nasm_free(dattmp);
}
nasm_free (segtmp);
}
while (fpubhead) {
struct ieeePublic *pubtmp = fpubhead;
fpubhead = fpubhead->next;
nasm_free (pubtmp);
}
while (exthead) {
struct ieeeExternal *exttmp = exthead;
exthead = exthead->next;
nasm_free (exttmp);
}
while (ebhead) {
struct ExtBack *ebtmp = ebhead;
ebhead = ebhead->next;
nasm_free (ebtmp);
}
}
/*
* callback for labels
*/
static void ieee_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 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.
*
*
* (iii) `segment' is not one of our segments. Save the label
* position for later output of an EXTDEF.
*/
struct ieeeExternal *ext;
struct ExtBack *eb;
struct ieeeSection *seg;
int i;
if (special) {
error(ERR_NONFATAL, "unrecognised symbol type `%s'", special);
}
/*
* First check for the double-period, signifying something
* unusual.
*/
if (name[0] == '.' && name[1] == '.') {
if (!strcmp(name, "..start")) {
ieee_entry_seg = segment;
ieee_entry_ofs = offset;
}
return;
}
/*
* Case (i):
*/
if (ieee_seg_needs_update) {
ieee_seg_needs_update->name = name;
return;
}
if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
return;
/*
* case (ii)
*/
if (segment >= SEG_ABS) {
/*
* SEG_ABS subcase of (ii).
*/
if (is_global) {
struct ieeePublic *pub;
pub = *fpubtail = nasm_malloc(sizeof(*pub));
fpubtail = &pub->next;
pub->next = NULL;
pub->name = name;
pub->offset = offset;
pub->segment = segment & ~SEG_ABS;
}
return;
}
for (seg = seghead; seg && is_global; seg = seg->next)
if (seg->index == segment) {
struct ieeePublic *pub;
last_defined = pub = *seg->pubtail = nasm_malloc(sizeof(*pub));
seg->pubtail = &pub->next;
pub->next = NULL;
pub->name = name;
pub->offset = offset;
pub->index = seg->ieee_index;
pub->segment = -1;
return;
}
/*
* Case (iii).
*/
if (is_global) {
ext = *exttail = nasm_malloc(sizeof(*ext));
ext->next = NULL;
exttail = &ext->next;
ext->name = name;
if (is_global == 2)
ext->commonsize = offset;
else
ext->commonsize = 0;
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->index[i] = externals++;
}
}
/*
* Put data out
*/
static void ieee_out (long segto, void *data, unsigned long type,
long segment, long wrt) {
unsigned long size, realtype;
unsigned char *ucdata;
long ldata;
struct ieeeSection *seg;
/*
* 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 != ieee_segment("__NASMDEFSEG", 2, &tempint))
error (ERR_PANIC, "strange segment conditions in IEEE 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?");
size = type & OUT_SIZMASK;
realtype = type & OUT_TYPMASK;
if (realtype == OUT_RAWDATA) {
ucdata = data;
while (size--)
ieee_write_byte(seg,*ucdata++);
} else if (realtype == OUT_ADDRESS || realtype == OUT_REL2ADR ||
realtype == OUT_REL4ADR) {
if (segment == NO_SEG && realtype != OUT_ADDRESS)
error(ERR_NONFATAL, "relative call to absolute address not"
" supported by IEEE format");
ldata = *(long *)data;
if (realtype == OUT_REL2ADR)
ldata += (size-2);
if (realtype == OUT_REL4ADR)
ldata += (size-4);
ieee_write_fixup (segment, wrt, seg, size, realtype,ldata);
if (size == 2)
ieee_write_word (seg, ldata);
else
ieee_write_dword (seg, ldata);
}
else if (realtype == OUT_RESERVE) {
while (size--)
ieee_write_byte(seg,0);
}
}
static void ieee_data_new(struct ieeeSection *segto) {
if (!segto->data)
segto->data = segto->datacurr = nasm_malloc(sizeof(*(segto->datacurr)));
else
segto->datacurr = segto->datacurr->next = nasm_malloc(sizeof(*(segto->datacurr)));
segto->datacurr->next = NULL;
}
/*
* this routine is unalduterated bloatware. I usually don't do this
* but I might as well see what it is like on a harmless program.
* If anyone wants to optimize this is a good canditate!
*/
static void ieee_write_fixup (long segment, long wrt, struct ieeeSection * segto,
int size, unsigned long realtype, long offset) {
struct ieeeSection *target;
struct ieeeFixupp s;
/* Don't put a fixup for things NASM can calculate */
if (wrt == NO_SEG && segment == NO_SEG)
return;
s.ftype = -1;
/* if it is a WRT offset */
if (wrt != NO_SEG) {
s.ftype = FT_WRT;
s.addend = offset;
if (wrt >= SEG_ABS)
s.id1 = -(wrt-SEG_ABS);
else {
if (wrt %2 && realtype != OUT_REL2ADR && realtype != OUT_REL4ADR) {
wrt--;
for (target = seghead; target; target = target->next)
if (target->index == wrt)
break;
if (target) {
s.id1 = target->ieee_index;
for (target = seghead; target; target = target->next)
if (target->index == segment)
break;
if (target)
s.id2 = target->ieee_index;
else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
long i = segment/2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
s.ftype = FT_EXTWRT;
s.addend = 0;
s.id2 = eb->index[i];
}
else
error(ERR_NONFATAL,
"Source of WRT must be an offset");
}
}
else
error(ERR_PANIC,
"unrecognised WRT value in ieee_write_fixup");
}
else
error(ERR_NONFATAL,"target of WRT must be a section ");
}
s.size = size;
ieee_install_fixup(segto,&s);
return;
}
/* Pure segment fixup ? */
if (segment != NO_SEG) {
s.ftype = FT_SEG;
s.id1 = 0;
if (segment >= SEG_ABS) {
/* absolute far segment fixup */
s.id1 = -(segment -~SEG_ABS);
}
else if (segment % 2) {
/* fixup to named segment */
/* look it up */
for (target = seghead; target; target = target->next)
if (target->index == segment-1)
break;
if (target)
s.id1 = target->ieee_index;
else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
long i = segment/2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
error(ERR_PANIC,"Segment of a rel not supported in ieee_write_fixup");
}
else {
/* If we want the segment */
s.ftype = FT_EXTSEG;
s.addend = 0;
s.id1 = eb->index[i];
}
}
else
/* If we get here the seg value doesn't make sense */
error(ERR_PANIC,
"unrecognised segment value in ieee_write_fixup");
}
} else {
/* Assume we are offsetting directly from a section
* So look up the target segment
*/
for (target = seghead; target; target = target->next)
if (target->index == segment)
break;
if (target) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
/* PC rel to a known offset */
s.id1 = target->ieee_index;
s.ftype = FT_REL;
s.size = size;
s.addend = offset;
}
else {
/* We were offsetting from a seg */
s.id1 = target->ieee_index;
s.ftype = FT_OFS;
s.size = size;
s.addend = offset;
}
}
else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
long i = segment/2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
s.ftype = FT_EXTREL;
s.addend = 0;
s.id1 = eb->index[i];
}
else {
/* else we want the external offset */
s.ftype = FT_EXT;
s.addend = 0;
s.id1 = eb->index[i];
}
}
else
/* If we get here the seg value doesn't make sense */
error(ERR_PANIC,
"unrecognised segment value in ieee_write_fixup");
}
}
if (size != 2 && s.ftype == FT_SEG)
error(ERR_NONFATAL, "IEEE format can only handle 2-byte"
" segment base references");
s.size = size;
ieee_install_fixup(segto,&s);
return;
}
/* should never get here */
}
static void ieee_install_fixup(struct ieeeSection *seg, struct ieeeFixupp *fix)
{
struct ieeeFixupp *f;
f = nasm_malloc(sizeof(struct ieeeFixupp));
memcpy(f,fix,sizeof(struct ieeeFixupp));
f->offset = seg->currentpos;
seg->currentpos += fix->size;
f->next = NULL;
if (seg->fptr)
seg->flptr = seg->flptr->next = f;
else
seg->fptr = seg->flptr = f;
}
/*
* segment registry
*/
static long ieee_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 (!name) {
*bits = 16;
if (!any_segs)
return 0;
return seghead->index;
} else {
struct ieeeSection *seg;
int ieee_idx, 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++;
}
ieee_idx = 1;
for (seg = seghead; seg; seg = seg->next) {
ieee_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;
return seg->index;
}
}
*segtail = seg = nasm_malloc(sizeof(*seg));
seg->next = NULL;
segtail = &seg->next;
seg->index = seg_alloc();
seg->ieee_index = ieee_idx;
any_segs = TRUE;
seg->name = NULL;
seg->currentpos = 0;
seg->align = 1; /* default */
seg->use32 = *bits == 32; /* default to user spec */
seg->combine = CMB_PUBLIC; /* default */
seg->pubhead = NULL;
seg->pubtail = &seg->pubhead;
seg->data = NULL;
seg->fptr = NULL;
seg->lochead = NULL;
seg->loctail = &seg->lochead;
/*
* 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, "use16"))
seg->use32 = FALSE;
else if (!nasm_stricmp(p, "use32"))
seg->use32 = TRUE;
else if (!nasm_strnicmp(p, "align=", 6)) {
seg->align = readnum(p+6, &rn_error);
if (seg->align == 0)
seg->align = 1;
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 8:
case 32:
case 64:
case 128:
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");
}
}
ieee_seg_needs_update = seg;
if (seg->align >= SEG_ABS)
deflabel (name, NO_SEG, seg->align - SEG_ABS,
NULL, FALSE, FALSE, &of_ieee, error);
else
deflabel (name, seg->index+1, 0L,
NULL, FALSE, FALSE, &of_ieee, error);
ieee_seg_needs_update = NULL;
if (seg->use32)
*bits = 32;
else
*bits = 16;
return seg->index;
}
}
/*
* directives supported
*/
static int ieee_directive (char *directive, char *value, int pass)
{
(void) value;
(void) pass;
if (!strcmp(directive, "uppercase")) {
ieee_uppercase = TRUE;
return 1;
}
return 0;
}
/*
* Return segment data
*/
static long ieee_segbase (long segment)
{
struct ieeeSection *seg;
/*
* Find the segment in our list.
*/
for (seg = seghead; seg; seg = seg->next)
if (seg->index == segment-1)
break;
if (!seg)
return segment; /* not one of ours - leave it alone */
if (seg->align >= SEG_ABS)
return seg->align; /* absolute segment */
return segment; /* no special treatment */
}
/*
* filename
*/
static void ieee_filename (char *inname, char *outname, efunc error) {
strcpy(ieee_infile, inname);
standard_extension (inname, outname, ".o", error);
}
static void ieee_write_file (int debuginfo) {
struct tm *thetime;
time_t reltime;
struct FileName *fn;
struct ieeeSection *seg;
struct ieeePublic *pub, *loc;
struct ieeeExternal *ext;
struct ieeeObjData *data;
struct ieeeFixupp *fix;
struct Array *arr;
static char boast[] = "The Netwide Assembler " NASM_VER;
int i;
/*
* Write the module header
*/
ieee_putascii("MBFNASM,%02X%s.\r\n",strlen(ieee_infile),ieee_infile);
/*
* Write the NASM boast comment.
*/
ieee_putascii("CO0,%02X%s.\r\n",strlen(boast),boast);
/*
* write processor-specific information
*/
ieee_putascii("AD8,4,L.\r\n");
/*
* date and time
*/
time(&reltime);
thetime = localtime(&reltime);
ieee_putascii("DT%04d%02d%02d%02d%02d%02d.\r\n",
1900+thetime->tm_year,thetime->tm_mon+1,thetime->tm_mday,
thetime->tm_hour, thetime->tm_min, thetime->tm_sec);
/*
* if debugging, dump file names
*/
for (fn = fnhead; fn && debuginfo; fn = fn->next) {
ieee_putascii("C0105,%02X%s.\r\n",strlen(fn->name),fn->name);
}
ieee_putascii("CO101,07ENDHEAD.\r\n");
/*
* the standard doesn't specify when to put checksums,
* we'll just do it periodically.
*/
ieee_putcs(FALSE);
/*
* Write the section headers
*/
seg = seghead;
if (!debuginfo && !strcmp(seg->name,"??LINE"))
seg = seg->next;
while (seg) {
char buf[256];
char attrib;
switch(seg->combine) {
case CMB_PUBLIC:
default:
attrib = 'C';
break;
case CMB_PRIVATE:
attrib = 'S';
break;
case CMB_COMMON:
attrib = 'M';
break;
}
ieee_unqualified_name(buf,seg->name);
if (seg->align >= SEG_ABS) {
ieee_putascii("ST%X,A,%02X%s.\r\n",seg->ieee_index,strlen(buf), buf);
ieee_putascii("ASL%X,%lX.\r\n",seg->ieee_index, (seg->align - SEG_ABS)*16);
}
else {
ieee_putascii("ST%X,%c,%02X%s.\r\n",seg->ieee_index,attrib,strlen(buf), buf);
ieee_putascii("SA%X,%lX.\r\n",seg->ieee_index,seg->align);
ieee_putascii("ASS%X,%X.\r\n",seg->ieee_index, seg->currentpos);
}
seg = seg->next;
}
/*
* write the start address if there is one
*/
if (ieee_entry_seg) {
for (seg = seghead; seg; seg = seg->next)
if (seg->index == ieee_entry_seg)
break;
if (!seg)
error(ERR_PANIC,"Start address records are incorrect");
else
ieee_putascii("ASG,R%X,%lX,+.\r\n",seg->ieee_index, ieee_entry_ofs);
}
ieee_putcs(FALSE);
/*
* Write the publics
*/
i = 1;
for (seg = seghead; seg; seg = seg->next) {
for (pub = seg->pubhead; pub; pub = pub->next) {
char buf[256];
ieee_unqualified_name(buf,pub->name);
ieee_putascii("NI%X,%02X%s.\r\n",i, strlen(buf), buf);
if (pub->segment == -1)
ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,pub->offset);
else
ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment*16,pub->offset);
if (debuginfo) {
if (pub->type >= 0x100)
ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
else
ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
}
i++;
}
}
pub = fpubhead;
i = 1;
while (pub) {
char buf[256];
ieee_unqualified_name(buf,pub->name);
ieee_putascii("NI%X,%02X%s.\r\n",i, strlen(buf), buf);
if (pub->segment == -1)
ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,pub->offset);
else
ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment*16,pub->offset);
if (debuginfo) {
if (pub->type >= 0x100)
ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
else
ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
}
i++;
pub = pub->next;
}
/*
* Write the externals
*/
ext = exthead;
i = 1;
while (ext) {
char buf[256];
ieee_unqualified_name(buf,ext->name);
ieee_putascii("NX%X,%02X%s.\r\n",i++, strlen(buf), buf);
ext = ext->next;
}
ieee_putcs(FALSE);
/*
* IEEE doesn't have a standard pass break record
* so use the ladsoft variant
*/
ieee_putascii("CO100,06ENDSYM.\r\n");
/*
* now put types
*/
i = ARRAY_BOT;
for (arr = arrhead; arr && debuginfo; arr = arr->next) {
ieee_putascii("TY%X,20,%X,%lX.\r\n",i++,arr->basetype,arr->size);
}
/*
* now put locals
*/
i = 1;
for (seg = seghead; seg && debuginfo; seg = seg->next) {
for (loc = seg->lochead; loc; loc = loc->next) {
char buf[256];
ieee_unqualified_name(buf,loc->name);
ieee_putascii("NN%X,%02X%s.\r\n",i, strlen(buf), buf);
if (loc->segment == -1)
ieee_putascii("ASN%X,R%X,%lX,+.\r\n", i, loc->index,loc->offset);
else
ieee_putascii("ASN%X,%lX,%lX,+.\r\n", i, loc->segment*16,loc->offset);
if (debuginfo) {
if (loc->type >= 0x100)
ieee_putascii("ATN%X,T%X.\r\n", i, loc->type - 0x100);
else
ieee_putascii("ATN%X,%X.\r\n", i, loc->type);
}
i++;
}
}
/*
* put out section data;
*/
seg = seghead;
if (!debuginfo && !strcmp(seg->name,"??LINE"))
seg = seg->next;
while (seg) {
if (seg->currentpos) {
long size,org = 0;
data = seg->data;
ieee_putascii("SB%X.\r\n",seg->ieee_index);
fix = seg->fptr;
while (fix) {
size = HUNKSIZE - (org %HUNKSIZE);
size = size +org > seg->currentpos ? seg->currentpos-org : size;
size = fix->offset - org > size ? size : fix->offset-org;
org = ieee_putld(org,org+size,data->data);
if (org % HUNKSIZE == 0)
data = data->next;
if (org == fix->offset) {
org += ieee_putlr(fix);
fix = fix->next;
}
}
while (org < seg->currentpos && data) {
size = seg->currentpos-org > HUNKSIZE ? HUNKSIZE : seg->currentpos - org;
org = ieee_putld(org,org+size,data->data);
data = data->next;
}
ieee_putcs(FALSE);
}
seg = seg->next;
}
/*
* module end record
*/
ieee_putascii("ME.\r\n");
}
static void ieee_write_byte(struct ieeeSection *seg, int data) {
int temp;
if (!(temp = seg->currentpos++ % HUNKSIZE))
ieee_data_new(seg);
seg->datacurr->data[temp] = data;
}
static void ieee_write_word(struct ieeeSection *seg, int data) {
ieee_write_byte(seg, data & 0xFF);
ieee_write_byte(seg,(data >> 8) & 0xFF);
}
static void ieee_write_dword(struct ieeeSection *seg, long data) {
ieee_write_byte(seg, data & 0xFF);
ieee_write_byte(seg,(data >> 8) & 0xFF);
ieee_write_byte(seg,(data >> 16) & 0xFF);
ieee_write_byte(seg,(data >> 24) & 0xFF);
}
static void ieee_putascii(char *format, ...)
{
char buffer[256];
int i,l;
va_list ap;
va_start(ap, format);
vsprintf(buffer, format, ap);
l = strlen(buffer);
for (i=0; i < l; i++)
if ((buffer[i] & 0xff) > 31)
checksum+=buffer[i];
va_end(ap);
fprintf(ofp,buffer);
}
/*
* put out a checksum record */
static void ieee_putcs(int toclear)
{
if (toclear) {
ieee_putascii("CS.\r\n");
}
else {
checksum += 'C';
checksum += 'S';
ieee_putascii("CS%02X.\r\n",checksum & 127);
}
checksum = 0;
}
static long ieee_putld(long start, long end, unsigned char *buf)
{
long val;
if (start == end)
return(start);
val = start % HUNKSIZE;
/* fill up multiple lines */
while (end - start >= LDPERLINE) {
int i;
ieee_putascii("LD");
for (i=0; i < LDPERLINE; i++) {
ieee_putascii("%02X",buf[val++]);
start++;
}
ieee_putascii(".\r\n");
}
/* if no partial lines */
if (start == end)
return(start);
/* make a partial line */
ieee_putascii("LD");
while (start < end) {
ieee_putascii("%02X",buf[val++]);
start++;
}
ieee_putascii(".\r\n");
return(start);
}
static long ieee_putlr(struct ieeeFixupp *p)
{
/*
* To deal with the vagaries of segmentation the LADsoft linker
* defines two types of segments: absolute and virtual. Note that
* 'absolute' in this context is a different thing from the IEEE
* definition of an absolute segment type, which is also supported. If a
* sement is linked in virtual mode the low limit (L-var) is
* subtracted from each R,X, and P variable which appears in an
* expression, so that we can have relative offsets. Meanwhile
* in the ABSOLUTE mode this subtraction is not done and
* so we can use absolute offsets from 0. In the LADsoft linker
* this configuration is not done in the assemblker source but in
* a source the linker reads. Generally this type of thing only
* becomes an issue if real mode code is used. A pure 32-bit linker could
* get away without defining the virtual mode...
*/
char buf[40];
long size=p->size;
switch(p->ftype) {
case FT_SEG:
if (p->id1 < 0)
sprintf(buf,"%lX",-p->id1);
else
sprintf(buf,"L%lX,10,/",p->id1);
break;
case FT_OFS:
sprintf(buf,"R%lX,%lX,+",p->id1,p->addend);
break;
case FT_REL:
sprintf(buf,"R%lX,%lX,+,P,-,%X,-",p->id1,p->addend,p->size);
break;
case FT_WRT:
if (p->id2 < 0)
sprintf(buf,"R%lX,%lX,+,L%lX,+,%lX,-",p->id2,p->addend,p->id2,-p->id1*16);
else
sprintf(buf,"R%lX,%lX,+,L%lX,+,L%lX,-",p->id2,p->addend,p->id2,p->id1);
break;
case FT_EXT:
sprintf(buf,"X%lX",p->id1);
break;
case FT_EXTREL:
sprintf(buf,"X%lX,P,-,%lX,-",p->id1,size);
break;
case FT_EXTSEG:
/* We needed a non-ieee hack here.
* We introduce the Y variable, which is the low
* limit of the native segment the extern resides in
*/
sprintf(buf,"Y%lX,10,/",p->id1);
break;
case FT_EXTWRT:
if (p->id2 < 0)
sprintf(buf,"X%lX,Y%lX,+,%lX,-",p->id2,p->id2,-p->id1*16);
else
sprintf(buf,"X%lX,Y%lX,+,L%lX,-",p->id2,p->id2,p->id1);
break;
}
ieee_putascii("LR(%s,%lX).\r\n", buf, size);
return(size);
}
/* Dump all segment data (text and fixups )*/
static void ieee_unqualified_name(char *dest, char *source)
{
if (ieee_uppercase) {
while (*source)
*dest++ = toupper(*source++);
*dest = 0;
}
else strcpy(dest,source);
}
void dbgls_init(struct ofmt * of, void * id, FILE * fp, efunc error)
{
int tempint;
(void) of;
(void) id;
(void) fp;
(void) error;
fnhead = NULL;
fntail = &fnhead;
arrindex = ARRAY_BOT ;
arrhead = NULL;
arrtail = &arrhead;
ieee_segment("??LINE", 2, &tempint);
any_segs = FALSE;
}
static void dbgls_cleanup(void)
{
struct ieeeSection *segtmp;
while (fnhead) {
struct FileName *fntemp = fnhead;
fnhead = fnhead->next;
nasm_free (fntemp->name);
nasm_free (fntemp);
}
for (segtmp = seghead; segtmp; segtmp = segtmp->next) {
while (segtmp->lochead) {
struct ieeePublic *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);
}
}
/*
* because this routine is not bracketed in
* the main program, this routine will be called even if there
* is no request for debug info
* so, we have to make sure the ??LINE segment is avaialbe
* as the first segment when this debug format is selected
*/
static void dbgls_linnum (const char *lnfname, long lineno, long segto)
{
struct FileName *fn;
struct ieeeSection *seg;
int i = 0;
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 != ieee_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) {
if (!nasm_stricmp(lnfname,fn->name))
break;
i++;
}
if (!fn) {
fn = nasm_malloc ( sizeof( *fn));
fn->name = nasm_malloc ( strlen(lnfname) + 1) ;
fn->index = i;
strcpy (fn->name,lnfname);
fn->next = NULL;
*fntail = fn;
fntail = &fn->next;
}
ieee_write_byte(seghead, fn->index);
ieee_write_word(seghead, lineno);
ieee_write_fixup (segto, NO_SEG, seghead, 4, OUT_ADDRESS, seg->currentpos);
}
static void dbgls_deflabel (char *name, long segment,
long offset, int is_global, char *special)
{
struct ieeeSection *seg;
int used_special; /* have we used the special text? */
/* Keep compiler from warning about special and used_special */
used_special = special ? FALSE : FALSE;
/*
* 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 (ieee_seg_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 ieee_deflabel so we can skip that.
*/
for (seg = seghead; seg; seg = seg->next)
if (seg->index == segment) {
struct ieeePublic *loc;
/*
* Case (ii). Maybe MODPUB someday?
*/
if (!is_global) {
last_defined = loc = nasm_malloc (sizeof(*loc));
*seg->loctail = loc;
seg->loctail = &loc->next;
loc->next = NULL;
loc->name = nasm_strdup(name);
loc->offset = offset;
loc->segment = -1;
loc->index = seg->ieee_index;
}
}
}
static void dbgls_typevalue (long type)
{
int elem = TYM_ELEMENTS(type);
type = TYM_TYPE(type);
if (! last_defined)
return;
switch (type) {
case TY_BYTE:
last_defined->type = 1; /* unsigned char */
break;
case TY_WORD:
last_defined->type = 3; /* unsigned word */
break;
case TY_DWORD:
last_defined->type = 5; /* unsigned dword */
break;
case TY_FLOAT:
last_defined->type = 9; /* float */
break;
case TY_QWORD:
last_defined->type = 10; /* qword */
break;
case TY_TBYTE:
last_defined->type = 11; /* TBYTE */
break;
default:
last_defined->type = 0x10; /* near label */
break;
}
if (elem > 1) {
struct Array *arrtmp = nasm_malloc (sizeof(*arrtmp));
int vtype = last_defined->type;
arrtmp->size = elem;
arrtmp->basetype = vtype;
arrtmp->next = NULL;
last_defined->type = arrindex++ + 0x100;
*arrtail = arrtmp;
arrtail = & (arrtmp->next);
}
last_defined = NULL;
}
static void dbgls_output (int output_type, void *param)
{
(void) output_type;
(void) param;
}
static struct dfmt ladsoft_debug_form = {
"LADsoft Debug Records",
"ladsoft",
dbgls_init,
dbgls_linnum,
dbgls_deflabel,
null_debug_routine,
dbgls_typevalue,
dbgls_output,
dbgls_cleanup,
};
static struct dfmt *ladsoft_debug_arr[3] = {
&ladsoft_debug_form,
&null_debug_form,
NULL
};
struct ofmt of_ieee = {
"IEEE-695 (LADsoft variant) object file format",
"ieee",
NULL,
ladsoft_debug_arr,
&null_debug_form,
NULL,
ieee_init,
ieee_set_info,
ieee_out,
ieee_deflabel,
ieee_segment,
ieee_segbase,
ieee_directive,
ieee_filename,
ieee_cleanup
};
#endif /* OF_IEEE */