diff options
Diffstat (limited to 'zoutieee.c')
-rw-r--r-- | zoutieee.c | 1457 |
1 files changed, 1457 insertions, 0 deletions
diff --git a/zoutieee.c b/zoutieee.c new file mode 100644 index 0000000..b36098a --- /dev/null +++ b/zoutieee.c @@ -0,0 +1,1457 @@ +/* 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; + +static 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, 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) { + 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, 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%X,10,/",p->id1); + break; + case FT_OFS: + sprintf(buf,"R%X,%lX,+",p->id1,p->addend); + break; + case FT_REL: + sprintf(buf,"R%X,%lX,+,P,-,%X,-",p->id1,p->addend,p->size); + break; + + case FT_WRT: + if (p->id2 < 0) + sprintf(buf,"R%X,%lX,+,L%X,+,%lX,-",p->id2,p->addend,p->id2,-p->id1*16); + else + sprintf(buf,"R%X,%lX,+,L%X,+,L%X,-",p->id2,p->addend,p->id2,p->id1); + break; + case FT_EXT: + sprintf(buf,"X%X",p->id1,p->id1); + break; + case FT_EXTREL: + sprintf(buf,"X%X,P,-,%X,-",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%X,10,/",p->id1); + break; + case FT_EXTWRT: + if (p->id2 < 0) + sprintf(buf,"X%X,Y%X,+,%lX,-",p->id2,p->id2,-p->id1*16); + else + sprintf(buf,"X%X,Y%X,+,L%X,-",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 = FALSE; /* have we used the special text? */ + + (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 (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 */ |