b36098a6352efb1bcda7f05d9c12922715cf81f5
[platform/upstream/nasm.git] / zoutieee.c
1 /* outieee.c    output routines for the Netwide Assembler to produce
2  *              IEEE-std object files
3  *
4  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
5  * Julian Hall. All rights reserved. The software is
6  * redistributable under the licence given in the file "Licence"
7  * distributed in the NASM archive.
8  */
9
10 /* notes: I have tried to make this correspond to the IEEE version
11  * of the standard, specifically the primary ASCII version.  It should
12  * be trivial to create the binary version given this source (which is
13  * one of MANY things that have to be done to make this correspond to
14  * the hp-microtek version of the standard).
15  *
16  * 16-bit support is assumed to use 24-bit addresses
17  * The linker can sort out segmentation-specific stuff
18  * if it keeps track of externals
19  * in terms of being relative to section bases
20  *
21  * A non-standard variable type, the 'Yn' variable, has been introduced.
22  * Basically it is a reference to extern 'n'- denoting the low limit
23  * (L-variable) of the section that extern 'n' is defined in.  Like the
24  * x variable, there may be no explicit assignment to it, it is derived
25  * from the public definition corresponding to the extern name.  This
26  * is required because the one thing the mufom guys forgot to do well was
27  * take into account segmented architectures.
28  *
29  * I use comment classes for various things and these are undefined by
30  * the standard.
31  *
32  * Debug info should be considered totally non-standard (local labels are
33  * standard but linenum records are not covered by the standard.
34  * Type defs have the standard format but absolute meanings for ordinal
35  * types are not covered by the standard.)
36  *
37  * David Lindauer, LADsoft
38  */
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <stdarg.h>  /* Note: we need the ANSI version of stdarg.h */
44 #include <ctype.h>
45
46 #include "nasm.h"
47 #include "nasmlib.h"
48 #include "outform.h"
49
50 #ifdef OF_IEEE
51
52 #define ARRAY_BOT 0x1
53
54 static char ieee_infile[FILENAME_MAX];
55 static int ieee_uppercase;
56
57 static efunc error;
58 static ldfunc deflabel;
59 static FILE *ofp;
60 static int any_segs;
61 static int arrindex;
62
63 #define HUNKSIZE 1024                  /* Size of the data hunk */
64 #define EXT_BLKSIZ 512
65 #define LDPERLINE 32                    /* bytes per line in output */
66
67 struct ieeeSection;
68
69 struct LineNumber {
70     struct LineNumber *next;
71     struct ieeeSection *segment;
72     long offset;
73     long lineno;
74 };
75
76 static struct FileName {
77     struct FileName *next;
78     char *name;
79     long index;
80 } *fnhead, **fntail;
81
82 static struct Array {
83     struct Array *next;
84     unsigned size;
85     int basetype;
86 } *arrhead, **arrtail;
87
88 static struct ieeePublic {
89     struct ieeePublic *next;
90     char *name;
91     long offset;
92     long segment;                      /* only if it's far-absolute */
93     long index;
94     int type;                           /* for debug purposes */        
95 } *fpubhead, **fpubtail, *last_defined;
96
97 static struct ieeeExternal {
98     struct ieeeExternal *next;
99     char *name;
100     long commonsize;
101 } *exthead, **exttail;
102
103 static int externals;
104
105 static struct ExtBack {
106     struct ExtBack *next;
107     int index[EXT_BLKSIZ];
108 } *ebhead, **ebtail;
109
110 /* NOTE: the first segment MUST be the lineno segment */
111 static struct ieeeSection {
112     struct ieeeObjData *data,*datacurr;
113     struct ieeeSection *next;
114     struct ieeeFixupp *fptr, * flptr;
115     long index;                        /* the NASM segment id */
116     long ieee_index;                   /* the OBJ-file segment index */
117     long currentpos;
118     long align;                        /* can be SEG_ABS + absolute addr */
119     long startpos;
120     enum {
121         CMB_PRIVATE = 0,
122         CMB_PUBLIC = 2,
123         CMB_COMMON = 6
124     } combine;
125     long use32;                        /* is this segment 32-bit? */
126     struct ieeePublic *pubhead, **pubtail, *lochead, **loctail;
127     char *name;
128 } *seghead, **segtail, *ieee_seg_needs_update;
129
130 static struct ieeeObjData {
131     struct ieeeObjData *next;
132     unsigned char data[HUNKSIZE];
133 };
134
135 struct ieeeFixupp {
136     struct ieeeFixupp *next;
137     enum { 
138         FT_SEG = 0,
139         FT_REL = 1,
140         FT_OFS = 2,
141         FT_EXT = 3,
142         FT_WRT = 4,
143         FT_EXTREL = 5,
144         FT_EXTWRT = 6,
145         FT_EXTSEG = 7
146     } ftype;
147     short size;
148     long id1;
149     long id2;
150     long offset;
151     long addend;
152 };
153                 
154 static long ieee_entry_seg, ieee_entry_ofs;
155 static int checksum;
156
157 extern struct ofmt of_ieee;
158
159 static void ieee_data_new(struct ieeeSection *);
160 static void ieee_write_fixup (long, long, struct ieeeSection *,
161                                 int, long, long);
162 static void ieee_install_fixup(struct ieeeSection *, struct ieeeFixupp *);
163 static long ieee_segment (char *, int, int *);
164 static void ieee_write_file(int debuginfo);
165 static void ieee_write_byte(struct ieeeSection *, int);
166 static void ieee_write_word(struct ieeeSection *, int);
167 static void ieee_write_dword(struct ieeeSection *, long);
168 static void ieee_putascii(char *, ...);
169 static void ieee_putcs(int);
170 static long ieee_putld(long, long, unsigned char *);
171 static long ieee_putlr(struct ieeeFixupp *);
172 static void ieee_unqualified_name(char *, char *);
173
174
175 /* 
176  * pup init 
177  */
178 static void ieee_init (FILE *fp, efunc errfunc, ldfunc ldef, evalfunc eval) 
179 {
180     (void) eval;
181     ofp = fp;
182     error = errfunc;
183     deflabel = ldef;
184     any_segs = FALSE;
185     fpubhead = NULL;
186     fpubtail = &fpubhead;
187     exthead = NULL;
188     exttail = &exthead;
189     externals = 1;
190     ebhead = NULL;
191     ebtail = &ebhead;
192     seghead = ieee_seg_needs_update = NULL;
193     segtail = &seghead;
194     ieee_entry_seg = NO_SEG;
195     ieee_uppercase = FALSE;
196     checksum = 0;
197     of_ieee.current_dfmt->init (&of_ieee,NULL,fp,errfunc);
198 }
199 static int ieee_set_info(enum geninfo type, char **val)
200 {
201     (void) type;
202     (void) val;
203
204     return 0;
205 }
206 /*
207  * Rundown
208  */
209 static void ieee_cleanup (int debuginfo) 
210 {
211     ieee_write_file(debuginfo);
212     of_ieee.current_dfmt->cleanup ();
213     fclose (ofp);
214     while (seghead) {
215         struct ieeeSection *segtmp = seghead;
216         seghead = seghead->next;
217         while (segtmp->pubhead) {
218             struct ieeePublic *pubtmp = segtmp->pubhead;
219             segtmp->pubhead = pubtmp->next;
220             nasm_free (pubtmp);
221         }
222         while (segtmp->fptr) {
223             struct ieeeFixupp *fixtmp = segtmp->fptr;
224             segtmp->fptr = fixtmp->next;
225             nasm_free(fixtmp);
226         }
227         while (segtmp->data) {
228             struct ieeeObjData *dattmp = segtmp->data;
229             segtmp->data = dattmp->next;
230             nasm_free(dattmp);
231         }
232         nasm_free (segtmp);
233     }
234     while (fpubhead) {
235         struct ieeePublic *pubtmp = fpubhead;
236         fpubhead = fpubhead->next;
237         nasm_free (pubtmp);
238     }
239     while (exthead) {
240         struct ieeeExternal *exttmp = exthead;
241         exthead = exthead->next;
242         nasm_free (exttmp);
243     }
244     while (ebhead) {
245         struct ExtBack *ebtmp = ebhead;
246         ebhead = ebhead->next;
247         nasm_free (ebtmp);
248     }
249 }
250 /*
251  * callback for labels
252  */
253 static void ieee_deflabel (char *name, long segment,
254                           long offset, int is_global, char *special) {
255     /*
256      * We have three cases:
257      *
258      * (i) `segment' is a segment-base. If so, set the name field
259      * for the segment structure it refers to, and then
260      * return.
261      *
262      * (ii) `segment' is one of our segments, or a SEG_ABS segment.
263      * Save the label position for later output of a PUBDEF record.
264      * 
265      *
266      * (iii) `segment' is not one of our segments. Save the label
267      * position for later output of an EXTDEF.
268      */
269     struct ieeeExternal *ext;
270     struct ExtBack *eb;
271     struct ieeeSection *seg;
272     int i;
273
274     if (special) {
275         error(ERR_NONFATAL, "unrecognised symbol type `%s'", special);
276     }
277     /*
278      * First check for the double-period, signifying something
279      * unusual.
280      */
281     if (name[0] == '.' && name[1] == '.') {
282         if (!strcmp(name, "..start")) {
283             ieee_entry_seg = segment;
284             ieee_entry_ofs = offset;
285         }
286         return;
287     }
288
289     /*
290      * Case (i):
291      */
292     if (ieee_seg_needs_update) {
293         ieee_seg_needs_update->name = name;
294         return;
295     } 
296     if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
297         return;
298
299     /*
300      * case (ii)
301      */
302     if (segment >= SEG_ABS) {
303         /*
304          * SEG_ABS subcase of (ii).
305          */
306         if (is_global) {
307             struct ieeePublic *pub;
308
309             pub = *fpubtail = nasm_malloc(sizeof(*pub));
310             fpubtail = &pub->next;
311             pub->next = NULL;
312             pub->name = name;
313             pub->offset = offset;
314             pub->segment = segment & ~SEG_ABS;
315         }
316         return;
317     }
318
319     for (seg = seghead; seg && is_global; seg = seg->next)
320         if (seg->index == segment) {
321                 struct ieeePublic *pub;
322
323             last_defined = pub = *seg->pubtail = nasm_malloc(sizeof(*pub));
324             seg->pubtail = &pub->next;
325             pub->next = NULL;
326             pub->name = name;
327             pub->offset = offset;
328             pub->index = seg->ieee_index;
329             pub->segment = -1;
330             return;
331         }
332
333     /*
334      * Case (iii).
335      */
336     if (is_global) {
337         ext = *exttail = nasm_malloc(sizeof(*ext));
338         ext->next = NULL;
339         exttail = &ext->next;
340         ext->name = name;
341         if (is_global == 2)
342             ext->commonsize = offset;
343         else
344             ext->commonsize = 0;
345         i = segment/2;
346         eb = ebhead;
347         if (!eb) {
348             eb = *ebtail = nasm_malloc(sizeof(*eb));
349             eb->next = NULL;
350             ebtail = &eb->next;
351         }
352         while (i > EXT_BLKSIZ) {
353             if (eb && eb->next)
354                 eb = eb->next;
355             else {
356                 eb = *ebtail = nasm_malloc(sizeof(*eb));
357                 eb->next = NULL;
358                 ebtail = &eb->next;
359             }
360             i -= EXT_BLKSIZ;
361         }
362         eb->index[i] = externals++;
363     }
364         
365 }
366
367 /*
368  * Put data out
369  */
370 static void ieee_out (long segto, void *data, unsigned long type,
371                      long segment, long wrt) {
372     long size, realtype;
373     unsigned char *ucdata;
374     long ldata;
375     struct ieeeSection *seg;
376
377     /*
378      * handle absolute-assembly (structure definitions)
379      */
380     if (segto == NO_SEG) {
381         if ((type & OUT_TYPMASK) != OUT_RESERVE)
382             error (ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]"
383                    " space");
384         return;
385     }
386
387     /*
388      * If `any_segs' is still FALSE, we must define a default
389      * segment.
390      */
391     if (!any_segs) {
392         int tempint;                   /* ignored */
393         if (segto != ieee_segment("__NASMDEFSEG", 2, &tempint))
394             error (ERR_PANIC, "strange segment conditions in IEEE driver");
395     }
396
397     /*
398      * Find the segment we are targetting.
399      */
400     for (seg = seghead; seg; seg = seg->next)
401         if (seg->index == segto)
402             break;
403     if (!seg)
404         error (ERR_PANIC, "code directed to nonexistent segment?");
405
406     size = type & OUT_SIZMASK;
407     realtype = type & OUT_TYPMASK;
408     if (realtype == OUT_RAWDATA) {
409         ucdata = data;
410         while (size--)
411             ieee_write_byte(seg,*ucdata++);
412     } else if (realtype == OUT_ADDRESS || realtype == OUT_REL2ADR ||
413                realtype == OUT_REL4ADR) {
414         if (segment == NO_SEG && realtype != OUT_ADDRESS)
415             error(ERR_NONFATAL, "relative call to absolute address not"
416                   " supported by IEEE format");
417         ldata = *(long *)data;
418         if (realtype == OUT_REL2ADR)
419             ldata += (size-2);
420         if (realtype == OUT_REL4ADR)
421             ldata += (size-4);
422         ieee_write_fixup (segment, wrt, seg, size, realtype,ldata);
423         if (size == 2)
424             ieee_write_word (seg, ldata);
425         else
426             ieee_write_dword (seg, ldata);
427     } 
428     else if (realtype == OUT_RESERVE) {
429         while (size--)
430             ieee_write_byte(seg,0);
431     }
432 }
433
434 static void ieee_data_new(struct ieeeSection *segto) {
435     
436     if (!segto->data)
437         segto->data = segto->datacurr = nasm_malloc(sizeof(*(segto->datacurr)));
438     else
439         segto->datacurr = segto->datacurr->next = nasm_malloc(sizeof(*(segto->datacurr)));
440     segto->datacurr->next = NULL;
441 }
442
443
444 /*
445  * this routine is unalduterated bloatware.  I usually don't do this
446  * but I might as well see what it is like on a harmless program.
447  * If anyone wants to optimize this is a good canditate!
448  */
449 static void ieee_write_fixup (long segment, long wrt, struct ieeeSection * segto,
450                                 int size, long realtype, long offset) {
451     struct ieeeSection *target;
452     struct ieeeFixupp s;
453
454     /* Don't put a fixup for things NASM can calculate */
455     if (wrt == NO_SEG && segment == NO_SEG)
456         return;
457
458     s.ftype = -1;
459     /* if it is a WRT offset */
460     if (wrt != NO_SEG) {
461         s.ftype = FT_WRT;
462         s.addend = offset;
463         if (wrt >= SEG_ABS)
464             s.id1 = -(wrt-SEG_ABS);
465         else {
466             if (wrt %2 && realtype != OUT_REL2ADR && realtype != OUT_REL4ADR) {
467                 wrt--;
468
469                 for (target = seghead; target; target = target->next)
470                     if (target->index == wrt)
471                         break;
472                 if (target) {
473                     s.id1 = target->ieee_index;
474                     for (target = seghead; target; target = target->next)
475                         if (target->index == segment)
476                             break;
477                 
478                     if (target) 
479                         s.id2 = target->ieee_index;
480                     else {
481                         /*
482                          * Now we assume the segment field is being used
483                          * to hold an extern index
484                          */
485                         long i = segment/2;
486                         struct ExtBack *eb = ebhead;
487                         while (i > EXT_BLKSIZ) {
488                             if (eb)
489                                 eb = eb->next;
490                             else
491                                 break;
492                             i -= EXT_BLKSIZ;
493                         }
494                                 /* if we have an extern decide the type and make a record
495                          */
496                         if (eb) {
497                                 s.ftype = FT_EXTWRT;
498                                 s.addend = 0;
499                                 s.id2 = eb->index[i];
500                         }
501                         else
502                             error(ERR_NONFATAL,
503                                 "Source of WRT must be an offset");
504                     }
505                         
506                 }
507                 else 
508                     error(ERR_PANIC,
509                           "unrecognised WRT value in ieee_write_fixup");
510             }
511             else 
512                 error(ERR_NONFATAL,"target of WRT must be a section ");
513         }
514         s.size = size;
515         ieee_install_fixup(segto,&s);
516         return;
517     }
518     /* Pure segment fixup ? */
519     if (segment != NO_SEG) {
520         s.ftype = FT_SEG;
521         s.id1 = 0;
522         if (segment >= SEG_ABS) {
523             /* absolute far segment fixup */
524             s.id1 = -(segment -~SEG_ABS);
525         }
526         else if (segment % 2) {
527             /* fixup to named segment */
528             /* look it up */
529             for (target = seghead; target; target = target->next)
530                 if (target->index == segment-1)
531                     break;
532             if (target)
533                 s.id1 = target->ieee_index;
534             else {
535                 /*
536                  * Now we assume the segment field is being used
537                  * to hold an extern index
538                  */
539                 long i = segment/2;
540                 struct ExtBack *eb = ebhead;
541                 while (i > EXT_BLKSIZ) {
542                     if (eb)
543                         eb = eb->next;
544                     else
545                         break;
546                     i -= EXT_BLKSIZ;
547                 }
548                 /* if we have an extern decide the type and make a record
549                  */
550                 if (eb) {
551                     if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
552                         error(ERR_PANIC,"Segment of a rel not supported in ieee_write_fixup");
553                     }
554                     else {
555                                 /* If we want the segment */
556                             s.ftype = FT_EXTSEG;
557                             s.addend = 0;
558                             s.id1 = eb->index[i];
559                     }
560                     
561                 }
562                 else 
563                     /* If we get here the seg value doesn't make sense */
564                     error(ERR_PANIC,
565                           "unrecognised segment value in ieee_write_fixup");
566             }
567
568         } else {
569             /* Assume we are offsetting directly from a section
570              * So look up the target segment
571              */
572             for (target = seghead; target; target = target->next)
573                 if (target->index == segment)
574                     break;
575             if (target) {
576                 if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
577                     /* PC rel to a known offset */
578                     s.id1 = target->ieee_index;
579                     s.ftype = FT_REL;
580                     s.size = size;
581                     s.addend = offset;
582                 }
583                 else {
584                     /* We were offsetting from a seg */
585                     s.id1 = target->ieee_index;
586                     s.ftype = FT_OFS;
587                     s.size = size;
588                     s.addend = offset;
589                 }
590             }
591             else {
592                 /*
593                  * Now we assume the segment field is being used
594                  * to hold an extern index
595                  */
596                 long i = segment/2;
597                 struct ExtBack *eb = ebhead;
598                 while (i > EXT_BLKSIZ) {
599                     if (eb)
600                         eb = eb->next;
601                     else
602                         break;
603                     i -= EXT_BLKSIZ;
604                 }
605                 /* if we have an extern decide the type and make a record
606                  */
607                 if (eb) {
608                     if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
609                         s.ftype = FT_EXTREL;
610                         s.addend = 0;
611                         s.id1 = eb->index[i];
612                     }
613                     else {
614                             /* else we want the external offset */
615                             s.ftype = FT_EXT;
616                             s.addend = 0;
617                             s.id1 = eb->index[i];
618                     }
619                     
620                 }
621                 else 
622                     /* If we get here the seg value doesn't make sense */
623                     error(ERR_PANIC,
624                           "unrecognised segment value in ieee_write_fixup");
625             }
626         }
627         if (size != 2 && s.ftype == FT_SEG)
628             error(ERR_NONFATAL, "IEEE format can only handle 2-byte"
629                      " segment base references");
630         s.size = size;
631         ieee_install_fixup(segto,&s);
632         return;
633     }
634     /* should never get here */
635 }
636 static void ieee_install_fixup(struct ieeeSection *seg, struct ieeeFixupp *fix)
637 {
638     struct ieeeFixupp *f;
639     f = nasm_malloc(sizeof(struct ieeeFixupp));
640     memcpy(f,fix,sizeof(struct ieeeFixupp));
641     f->offset = seg->currentpos;
642     seg->currentpos += fix->size;
643     f->next = NULL;
644     if (seg->fptr)
645         seg->flptr = seg->flptr->next = f;
646     else
647         seg->fptr = seg->flptr = f;
648
649 }
650
651 /*
652  * segment registry
653  */
654 static long ieee_segment (char *name, int pass, int *bits) {
655     /*
656      * We call the label manager here to define a name for the new
657      * segment, and when our _own_ label-definition stub gets
658      * called in return, it should register the new segment name
659      * using the pointer it gets passed. That way we save memory,
660      * by sponging off the label manager.
661      */
662     if (!name) {
663         *bits = 16;
664         if (!any_segs)
665                 return 0;
666         return seghead->index;
667     } else {
668         struct ieeeSection *seg;
669         int ieee_idx, attrs, rn_error;
670         char *p;
671
672         /*
673          * Look for segment attributes.
674          */
675         attrs = 0;
676         while (*name == '.')
677             name++;                    /* hack, but a documented one */
678         p = name;
679         while (*p && !isspace(*p))
680             p++;
681         if (*p) {
682             *p++ = '\0';
683             while (*p && isspace(*p))
684                 *p++ = '\0';
685         }
686         while (*p) {
687             while (*p && !isspace(*p))
688                 p++;
689             if (*p) {
690                 *p++ = '\0';
691                 while (*p && isspace(*p))
692                     *p++ = '\0';
693             }
694
695             attrs++;
696         }
697
698         ieee_idx = 1;
699         for (seg = seghead; seg; seg = seg->next) {
700             ieee_idx++;
701             if (!strcmp(seg->name, name)) {
702                 if (attrs > 0 && pass == 1)
703                     error(ERR_WARNING, "segment attributes specified on"
704                           " redeclaration of segment: ignoring");
705                 if (seg->use32)
706                     *bits = 32;
707                 else
708                     *bits = 16;
709                 return seg->index;
710             }
711         }
712
713         *segtail = seg = nasm_malloc(sizeof(*seg));
714         seg->next = NULL;
715         segtail = &seg->next;
716         seg->index = seg_alloc();
717         seg->ieee_index = ieee_idx;
718         any_segs = TRUE;
719         seg->name = NULL;
720         seg->currentpos = 0;
721         seg->align = 1;                /* default */
722         seg->use32 = *bits == 32;      /* default to user spec */
723         seg->combine = CMB_PUBLIC;     /* default */
724         seg->pubhead = NULL;
725         seg->pubtail = &seg->pubhead;
726         seg->data = NULL;
727         seg->fptr = NULL;
728         seg->lochead = NULL;
729         seg->loctail = &seg->lochead;
730
731         /*
732          * Process the segment attributes.
733          */
734         p = name;
735         while (attrs--) {
736             p += strlen(p);
737             while (!*p) p++;
738
739             /*
740              * `p' contains a segment attribute.
741              */
742             if (!nasm_stricmp(p, "private"))
743                 seg->combine = CMB_PRIVATE;
744             else if (!nasm_stricmp(p, "public"))
745                 seg->combine = CMB_PUBLIC;
746             else if (!nasm_stricmp(p, "common"))
747                 seg->combine = CMB_COMMON;
748             else if (!nasm_stricmp(p, "use16"))
749                 seg->use32 = FALSE;
750             else if (!nasm_stricmp(p, "use32"))
751                 seg->use32 = TRUE;
752             else if (!nasm_strnicmp(p, "align=", 6)) {
753                 seg->align = readnum(p+6, &rn_error);
754                 if (seg->align == 0)
755                     seg->align = 1;
756                 if (rn_error) {
757                     seg->align = 1;
758                     error (ERR_NONFATAL, "segment alignment should be"
759                            " numeric");
760                 }
761                 switch ((int) seg->align) {
762                   case 1:              /* BYTE */
763                   case 2:              /* WORD */
764                   case 4:              /* DWORD */
765                   case 16:             /* PARA */
766                   case 256:            /* PAGE */
767                   case 8:
768                   case 32:
769                   case 64:
770                   case 128:
771                     break;
772                   default:
773                     error(ERR_NONFATAL, "invalid alignment value %d",
774                           seg->align);
775                     seg->align = 1;
776                     break;
777                 }
778             } else if (!nasm_strnicmp(p, "absolute=", 9)) {
779                 seg->align = SEG_ABS + readnum(p+9, &rn_error);
780                 if (rn_error)
781                     error (ERR_NONFATAL, "argument to `absolute' segment"
782                            " attribute should be numeric");
783             }
784         }
785
786         ieee_seg_needs_update = seg;
787         if (seg->align >= SEG_ABS)
788             deflabel (name, NO_SEG, seg->align - SEG_ABS, 
789                         NULL, FALSE, FALSE, &of_ieee, error);
790         else
791             deflabel (name, seg->index+1, 0L, 
792                         NULL, FALSE, FALSE, &of_ieee, error);
793         ieee_seg_needs_update = NULL;
794
795         if (seg->use32)
796             *bits = 32;
797         else
798             *bits = 16;
799         return seg->index;
800     }
801 }
802 /*
803  * directives supported
804  */
805 static int ieee_directive (char *directive, char *value, int pass) 
806 {
807     
808     (void) value;
809     (void) pass;
810     if (!strcmp(directive, "uppercase")) {
811         ieee_uppercase = TRUE;
812         return 1;
813     }
814     return 0;
815 }
816 /*
817  * Return segment data
818  */
819 static long ieee_segbase (long segment) 
820 {
821     struct ieeeSection *seg;
822
823     /*
824      * Find the segment in our list.
825      */
826     for (seg = seghead; seg; seg = seg->next)
827         if (seg->index == segment-1)
828             break;
829
830     if (!seg)
831         return segment;                /* not one of ours - leave it alone */
832
833     if (seg->align >= SEG_ABS)
834         return seg->align;             /* absolute segment */
835
836     return segment;                    /* no special treatment */
837 }
838 /*
839  * filename
840  */
841 static void ieee_filename (char *inname, char *outname, efunc error) {
842     strcpy(ieee_infile, inname);
843     standard_extension (inname, outname, ".o", error);
844 }
845
846 static void ieee_write_file (int debuginfo) {
847     struct tm *thetime;
848     time_t reltime;
849     struct FileName *fn;
850     struct ieeeSection *seg;
851     struct ieeePublic *pub, *loc;
852     struct ieeeExternal *ext;
853     struct ieeeObjData *data;
854     struct ieeeFixupp *fix;
855     struct Array *arr;
856     static char boast[] = "The Netwide Assembler " NASM_VER;
857     int i;
858
859     /*
860      * Write the module header
861      */
862     ieee_putascii("MBFNASM,%02X%s.\r\n",strlen(ieee_infile),ieee_infile);
863
864     /*
865      * Write the NASM boast comment.
866      */
867     ieee_putascii("CO0,%02X%s.\r\n",strlen(boast),boast);
868
869     /* 
870      * write processor-specific information
871      */
872     ieee_putascii("AD8,4,L.\r\n");
873
874     /*
875      * date and time
876      */
877     time(&reltime);
878     thetime = localtime(&reltime);
879     ieee_putascii("DT%04d%02d%02d%02d%02d%02d.\r\n", 
880                         1900+thetime->tm_year,thetime->tm_mon+1,thetime->tm_mday,
881                         thetime->tm_hour, thetime->tm_min, thetime->tm_sec);
882     /* 
883      * if debugging, dump file names
884      */
885     for (fn = fnhead; fn && debuginfo; fn = fn->next) {
886         ieee_putascii("C0105,%02X%s.\r\n",strlen(fn->name),fn->name);
887     }
888      
889     ieee_putascii("CO101,07ENDHEAD.\r\n");
890     /*
891      * the standard doesn't specify when to put checksums,
892      * we'll just do it periodically.
893      */
894     ieee_putcs(FALSE);
895
896     /* 
897      * Write the section headers
898      */
899     seg = seghead;
900     if (!debuginfo && !strcmp(seg->name,"??LINE"))
901         seg = seg->next;
902     while (seg) {
903         char buf[256];
904         char attrib;
905         switch(seg->combine) {
906             case CMB_PUBLIC:
907             default:
908                 attrib = 'C';
909                 break;
910             case CMB_PRIVATE:
911                 attrib = 'S';
912                 break;
913             case CMB_COMMON:
914                 attrib = 'M';
915                 break;
916         }
917         ieee_unqualified_name(buf,seg->name);
918         if (seg->align >= SEG_ABS) {
919             ieee_putascii("ST%X,A,%02X%s.\r\n",seg->ieee_index,strlen(buf), buf);
920             ieee_putascii("ASL%X,%lX.\r\n",seg->ieee_index, (seg->align - SEG_ABS)*16);
921         }
922         else {
923             ieee_putascii("ST%X,%c,%02X%s.\r\n",seg->ieee_index,attrib,strlen(buf), buf);
924             ieee_putascii("SA%X,%lX.\r\n",seg->ieee_index,seg->align);
925             ieee_putascii("ASS%X,%X.\r\n",seg->ieee_index, seg->currentpos);
926         }
927         seg = seg->next;
928     }
929     /*
930      * write the start address if there is one
931      */
932     if (ieee_entry_seg) {
933         for (seg = seghead; seg; seg = seg->next)
934             if (seg->index == ieee_entry_seg)
935                 break;
936         if (!seg)
937             error(ERR_PANIC,"Start address records are incorrect");
938         else 
939             ieee_putascii("ASG,R%X,%lX,+.\r\n",seg->ieee_index, ieee_entry_ofs);
940     }   
941
942
943     ieee_putcs(FALSE);
944     /*
945      * Write the publics
946      */
947     i = 1;
948     for (seg = seghead; seg; seg = seg->next) {
949         for (pub = seg->pubhead; pub; pub = pub->next) {
950             char buf[256];
951             ieee_unqualified_name(buf,pub->name);
952             ieee_putascii("NI%X,%02X%s.\r\n",i, strlen(buf), buf);
953             if (pub->segment == -1)  
954                 ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,pub->offset);
955             else
956                 ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment*16,pub->offset);
957             if (debuginfo)
958                 if (pub->type >= 0x100)
959                     ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
960                 else
961                     ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
962             i++;
963         }
964     }
965     pub = fpubhead;
966     i = 1;
967     while (pub) {
968         char buf[256];
969         ieee_unqualified_name(buf,pub->name);
970         ieee_putascii("NI%X,%02X%s.\r\n",i, strlen(buf), buf);
971         if (pub->segment == -1)  
972             ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,pub->offset);
973         else
974             ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment*16,pub->offset);
975         if (debuginfo)
976             if (pub->type >= 0x100)
977                 ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
978             else
979                 ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
980         i++;
981         pub = pub->next;
982     }
983     /*
984      * Write the externals
985      */
986     ext = exthead;
987     i = 1;
988     while (ext) {
989         char buf[256];
990         ieee_unqualified_name(buf,ext->name);
991         ieee_putascii("NX%X,%02X%s.\r\n",i++, strlen(buf), buf);
992         ext = ext->next;
993     }
994     ieee_putcs(FALSE);
995
996     /*
997      * IEEE doesn't have a standard pass break record
998      * so use the ladsoft variant
999      */
1000     ieee_putascii("CO100,06ENDSYM.\r\n");
1001
1002     /*
1003      * now put types
1004      */
1005     i = ARRAY_BOT;
1006     for (arr = arrhead; arr && debuginfo; arr = arr->next) {
1007         ieee_putascii("TY%X,20,%X,%lX.\r\n",i++,arr->basetype,arr->size);
1008     }
1009     /*
1010      * now put locals
1011      */
1012     i = 1;
1013     for (seg = seghead; seg && debuginfo; seg = seg->next) {
1014         for (loc = seg->lochead; loc; loc = loc->next) {
1015             char buf[256];
1016             ieee_unqualified_name(buf,loc->name);
1017             ieee_putascii("NN%X,%02X%s.\r\n",i, strlen(buf), buf);
1018             if (loc->segment == -1)  
1019                 ieee_putascii("ASN%X,R%X,%lX,+.\r\n", i, loc->index,loc->offset);
1020             else
1021                 ieee_putascii("ASN%X,%lX,%lX,+.\r\n", i, loc->segment*16,loc->offset);
1022             if (debuginfo)
1023                 if (loc->type >= 0x100)
1024                     ieee_putascii("ATN%X,T%X.\r\n", i, loc->type - 0x100);
1025                 else
1026                     ieee_putascii("ATN%X,%X.\r\n", i, loc->type);
1027             i++;
1028         }
1029     }
1030
1031     /*
1032      *  put out section data;
1033      */ 
1034     seg = seghead;
1035     if (!debuginfo && !strcmp(seg->name,"??LINE"))
1036         seg = seg->next;
1037     while (seg) {
1038         if (seg->currentpos) {
1039             long size,org = 0;
1040             data = seg->data;
1041             ieee_putascii("SB%X.\r\n",seg->ieee_index);
1042             fix = seg->fptr;
1043             while (fix) {
1044                 size = HUNKSIZE - (org %HUNKSIZE);
1045                 size = size +org > seg->currentpos ? seg->currentpos-org : size;
1046                 size = fix->offset - org > size ? size : fix->offset-org;
1047                 org = ieee_putld(org,org+size,data->data);
1048                 if (org % HUNKSIZE == 0)
1049                     data = data->next;
1050                 if (org == fix->offset) {
1051                     org += ieee_putlr(fix);
1052                     fix = fix->next;
1053                 }
1054             }
1055             while (org < seg->currentpos && data) {
1056                 size = seg->currentpos-org > HUNKSIZE ? HUNKSIZE : seg->currentpos - org;
1057                 org = ieee_putld(org,org+size,data->data);
1058                 data = data->next;
1059             }
1060             ieee_putcs(FALSE);
1061                 
1062         }
1063         seg = seg->next;
1064     }
1065     /*
1066      * module end record
1067      */
1068     ieee_putascii("ME.\r\n");
1069 }
1070
1071 static void ieee_write_byte(struct ieeeSection *seg, int data) {
1072     int temp;
1073     if (!(temp = seg->currentpos++ % HUNKSIZE))
1074         ieee_data_new(seg);
1075     seg->datacurr->data[temp] = data;
1076 }
1077
1078 static void ieee_write_word(struct ieeeSection *seg, int data) {
1079     ieee_write_byte(seg, data & 0xFF);
1080     ieee_write_byte(seg,(data >> 8) & 0xFF);
1081 }
1082
1083 static void ieee_write_dword(struct ieeeSection *seg, long data) {
1084     ieee_write_byte(seg, data & 0xFF);
1085     ieee_write_byte(seg,(data >> 8) & 0xFF);
1086     ieee_write_byte(seg,(data >> 16) & 0xFF);
1087     ieee_write_byte(seg,(data >> 24) & 0xFF);
1088 }
1089 static void ieee_putascii(char *format, ...)
1090 {
1091         char buffer[256];
1092         int i,l;
1093         va_list ap;
1094
1095         va_start(ap, format);
1096         vsprintf(buffer, format, ap);
1097         l = strlen(buffer);
1098         for (i=0; i < l; i++)
1099                 if ((buffer[i] & 0xff) > 31)
1100                         checksum+=buffer[i];
1101         va_end(ap);
1102         fprintf(ofp,buffer);
1103 }
1104
1105 /*
1106  * put out a checksum record */
1107 static void ieee_putcs(int toclear)
1108 {
1109         if (toclear) {
1110                 ieee_putascii("CS.\r\n");
1111         }
1112         else {
1113                 checksum += 'C';
1114                 checksum += 'S';
1115                 ieee_putascii("CS%02X.\r\n",checksum & 127);
1116         }
1117         checksum = 0;
1118 }
1119
1120 static long ieee_putld(long start, long end, unsigned char *buf)
1121 {
1122         long val;
1123         if (start == end)
1124                 return(start);
1125         val = start % HUNKSIZE;
1126         /* fill up multiple lines */
1127         while (end - start >= LDPERLINE) {
1128                 int i;
1129                 ieee_putascii("LD");
1130                 for (i=0; i < LDPERLINE; i++) {
1131                         ieee_putascii("%02X",buf[val++]);
1132                         start++;
1133                 }
1134                 ieee_putascii(".\r\n");
1135         }
1136         /* if no partial lines */
1137         if (start == end)
1138                 return(start);
1139         /* make a partial line */
1140         ieee_putascii("LD");
1141         while (start < end) {
1142                 ieee_putascii("%02X",buf[val++]);
1143                 start++;
1144         }
1145         ieee_putascii(".\r\n");
1146         return(start);
1147 }
1148 static long ieee_putlr(struct ieeeFixupp *p)
1149 {
1150 /*
1151  * To deal with the vagaries of segmentation the LADsoft linker
1152  * defines two types of segments: absolute and virtual.  Note that
1153  * 'absolute' in this context is a different thing from the IEEE
1154  * definition of an absolute segment type, which is also supported. If a
1155  * sement is linked in virtual mode the low limit (L-var) is
1156  * subtracted from each R,X, and P variable which appears in an
1157  * expression, so that we can have relative offsets.  Meanwhile
1158  * in the ABSOLUTE mode this subtraction is not done and
1159  * so we can use absolute offsets from 0.  In the LADsoft linker
1160  * this configuration is not done in the assemblker source but in
1161  * a source the linker reads.  Generally this type of thing only
1162  * becomes an issue if real mode code is used.  A pure 32-bit linker could
1163  * get away without defining the virtual mode...
1164  */
1165         char buf[40];
1166         long size=p->size;
1167         switch(p->ftype) {
1168             case FT_SEG:
1169                 if (p->id1 < 0)
1170                     sprintf(buf,"%lX",-p->id1);
1171                 else
1172                     sprintf(buf,"L%X,10,/",p->id1);
1173                 break;
1174             case FT_OFS:
1175                 sprintf(buf,"R%X,%lX,+",p->id1,p->addend);
1176                 break;
1177             case FT_REL:
1178                 sprintf(buf,"R%X,%lX,+,P,-,%X,-",p->id1,p->addend,p->size);
1179                 break;
1180                 
1181             case FT_WRT:
1182                 if (p->id2 < 0)
1183                     sprintf(buf,"R%X,%lX,+,L%X,+,%lX,-",p->id2,p->addend,p->id2,-p->id1*16);
1184                 else
1185                     sprintf(buf,"R%X,%lX,+,L%X,+,L%X,-",p->id2,p->addend,p->id2,p->id1);
1186                 break;
1187             case FT_EXT:
1188                 sprintf(buf,"X%X",p->id1,p->id1);
1189                 break;
1190             case FT_EXTREL:
1191                 sprintf(buf,"X%X,P,-,%X,-",p->id1,size);
1192                 break;
1193             case FT_EXTSEG:
1194                 /* We needed a non-ieee hack here.
1195                  * We introduce the Y variable, which is the low
1196                  * limit of the native segment the extern resides in
1197                  */
1198                 sprintf(buf,"Y%X,10,/",p->id1);
1199                 break;
1200             case FT_EXTWRT:
1201                 if (p->id2 < 0)
1202                     sprintf(buf,"X%X,Y%X,+,%lX,-",p->id2,p->id2,-p->id1*16);
1203                 else
1204                     sprintf(buf,"X%X,Y%X,+,L%X,-",p->id2,p->id2,p->id1);
1205                 break;
1206         }
1207         ieee_putascii("LR(%s,%lX).\r\n", buf, size);
1208
1209         return(size);
1210 }
1211 /* Dump all segment data (text and fixups )*/
1212
1213 static void ieee_unqualified_name(char *dest, char *source)
1214 {
1215     if (ieee_uppercase) {
1216         while (*source)
1217             *dest++ = toupper(*source++);
1218         *dest = 0;
1219     }
1220     else strcpy(dest,source);
1221 }
1222 void dbgls_init(struct ofmt * of, void * id, FILE * fp, efunc error)
1223 {
1224     int tempint;
1225     (void) of;
1226     (void) id;
1227     (void) fp;
1228     (void) error;
1229
1230     fnhead = NULL;
1231     fntail = &fnhead;
1232     arrindex = ARRAY_BOT ;
1233     arrhead = NULL;
1234     arrtail = &arrhead;
1235     ieee_segment("??LINE", 2, &tempint);
1236     any_segs = FALSE;
1237 }
1238 static void dbgls_cleanup(void)
1239 {
1240     struct ieeeSection *segtmp;
1241     while (fnhead) {
1242         struct FileName *fntemp = fnhead;
1243         fnhead = fnhead->next;
1244         nasm_free (fntemp->name);
1245         nasm_free (fntemp);
1246     }
1247     for (segtmp = seghead; segtmp; segtmp = segtmp->next) {
1248         while (segtmp->lochead) {
1249             struct ieeePublic *loctmp = segtmp->lochead;
1250             segtmp->lochead = loctmp->next;
1251             nasm_free (loctmp->name);
1252             nasm_free (loctmp);
1253         }
1254     }
1255     while (arrhead) {
1256         struct Array *arrtmp = arrhead;
1257         arrhead = arrhead->next;
1258         nasm_free (arrtmp);
1259     }
1260 }
1261
1262 /*
1263  * because this routine is not bracketed in
1264  * the main program, this routine will be called even if there
1265  * is no request for debug info
1266  * so, we have to make sure the ??LINE segment is avaialbe
1267  * as the first segment when this debug format is selected
1268  */
1269 static void dbgls_linnum (const char *lnfname, long lineno, long segto)
1270 {
1271     struct FileName *fn;
1272     struct ieeeSection *seg;
1273     int i = 0;
1274     if (segto == NO_SEG)
1275         return;
1276
1277     /*
1278      * If `any_segs' is still FALSE, we must define a default
1279      * segment.
1280      */
1281     if (!any_segs) {
1282         int tempint;                   /* ignored */
1283         if (segto != ieee_segment("__NASMDEFSEG", 2, &tempint))
1284             error (ERR_PANIC, "strange segment conditions in OBJ driver");
1285     }
1286
1287     /*
1288      * Find the segment we are targetting.
1289      */
1290     for (seg = seghead; seg; seg = seg->next)
1291         if (seg->index == segto)
1292             break;
1293     if (!seg)
1294         error (ERR_PANIC, "lineno directed to nonexistent segment?");
1295
1296     for (fn = fnhead; fn; fn = fnhead->next) {
1297         if (!nasm_stricmp(lnfname,fn->name))
1298             break;
1299         i++;
1300     }
1301     if (!fn) {
1302         fn = nasm_malloc ( sizeof( *fn));
1303         fn->name = nasm_malloc ( strlen(lnfname) + 1) ;
1304         fn->index = i;
1305         strcpy (fn->name,lnfname);
1306         fn->next = NULL;
1307         *fntail = fn;
1308         fntail = &fn->next;
1309     }
1310     ieee_write_byte(seghead, fn->index);
1311     ieee_write_word(seghead, lineno);
1312     ieee_write_fixup (segto, NO_SEG, seghead, 4,OUT_ADDRESS,seg->currentpos);
1313
1314 }
1315 static void dbgls_deflabel (char *name, long segment,
1316                           long offset, int is_global, char *special) 
1317 {
1318     struct ieeeSection *seg;
1319     int used_special = FALSE;          /* have we used the special text? */
1320
1321     (void) special;
1322
1323     /*
1324      * If it's a special-retry from pass two, discard it.
1325      */
1326     if (is_global == 3)
1327         return;
1328
1329     /*
1330      * First check for the double-period, signifying something
1331      * unusual.
1332      */
1333     if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
1334         return;
1335     }
1336
1337     /*
1338      * Case (i):
1339      */
1340     if (ieee_seg_needs_update)
1341         return;
1342     if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
1343         return;
1344
1345     if (segment >= SEG_ABS || segment == NO_SEG) {
1346         return;
1347     }
1348
1349     /*
1350      * If `any_segs' is still FALSE, we might need to define a
1351      * default segment, if they're trying to declare a label in
1352      * `first_seg'.  But the label should exist due to a prior
1353      * call to ieee_deflabel so we can skip that.
1354      */
1355
1356     for (seg = seghead; seg; seg = seg->next)
1357         if (seg->index == segment) {
1358             struct ieeePublic *loc;
1359             /*
1360              * Case (ii). Maybe MODPUB someday?
1361              */
1362             if (!is_global) {
1363                 last_defined = loc  = nasm_malloc (sizeof(*loc));
1364                 *seg->loctail = loc;
1365                 seg->loctail = &loc->next;
1366                 loc->next = NULL;
1367                 loc->name = nasm_strdup(name);
1368                 loc->offset = offset;
1369                 loc->segment = -1;
1370                 loc->index = seg->ieee_index;
1371             }
1372         }
1373 }
1374 static void dbgls_typevalue (long type)
1375 {
1376     int elem = TYM_ELEMENTS(type);
1377     type = TYM_TYPE(type);
1378
1379     if (! last_defined)
1380         return;
1381
1382     switch (type) {
1383         case TY_BYTE:
1384             last_defined->type = 1; /* unsigned char */
1385             break;
1386         case TY_WORD:
1387             last_defined->type = 3; /* unsigned word */
1388             break;
1389         case TY_DWORD:
1390             last_defined->type = 5; /* unsigned dword */
1391             break;
1392         case TY_FLOAT:
1393             last_defined->type = 9; /* float */
1394             break;
1395         case TY_QWORD:
1396             last_defined->type = 10; /* qword */
1397             break;
1398         case TY_TBYTE:
1399             last_defined->type = 11; /* TBYTE */
1400             break;
1401         default:
1402             last_defined->type = 0x10; /* near label */
1403             break;
1404     }
1405                
1406     if (elem > 1) {
1407         struct Array *arrtmp = nasm_malloc (sizeof(*arrtmp));
1408         int vtype = last_defined->type;
1409         arrtmp->size = elem;
1410         arrtmp->basetype = vtype;
1411         arrtmp->next = NULL;
1412         last_defined->type = arrindex++ + 0x100;
1413         *arrtail = arrtmp;
1414         arrtail = & (arrtmp->next);
1415     }
1416     last_defined = NULL;
1417 }
1418 static void dbgls_output (int output_type, void *param)
1419 {
1420     (void) output_type;
1421     (void) param;
1422 }
1423 static struct dfmt ladsoft_debug_form = {
1424     "LADsoft Debug Records",
1425     "ladsoft",
1426     dbgls_init,
1427     dbgls_linnum,
1428     dbgls_deflabel,
1429     null_debug_routine,
1430     dbgls_typevalue,
1431     dbgls_output,
1432     dbgls_cleanup,
1433 };
1434 static struct dfmt *ladsoft_debug_arr[3] = {
1435         &ladsoft_debug_form,
1436         &null_debug_form,
1437         NULL
1438 };
1439 struct ofmt of_ieee = {
1440     "IEEE-695 (LADsoft variant) object file format",
1441     "ieee",
1442     NULL,
1443     ladsoft_debug_arr,
1444     &null_debug_form,
1445     NULL,
1446     ieee_init,
1447     ieee_set_info,
1448     ieee_out,
1449     ieee_deflabel,
1450     ieee_segment,
1451     ieee_segbase,
1452     ieee_directive,
1453     ieee_filename,
1454     ieee_cleanup
1455 };
1456
1457 #endif /* OF_IEEE */