/* asmemit.c -- microsoft 80x86 assembler
**
** microsoft (r) macro assembler
** copyright (c) microsoft corp 1986.  all rights reserved
**
** randy nevin
**
** 10/90 - Quick conversion to 32 bit by Jeff Spencer
*/

#include <stdio.h>
#include <io.h>
#include <string.h>
#include "asm86.h"
#include "asmfcn.h"

#define LINBUFSIZE EMITBUFSIZE - 20         /* line # records can't as big */

#define OMFBYTE(c)      *ebufpos++ = (unsigned char)(c)
#define FIXBYTE(c)      *efixpos++ = (unsigned char)(c)
#define LINBYTE(c)      *elinpos++ = (unsigned char)(c)
#define EBUFOPEN(c)     (ebufpos+(c) <= emitbuf+EMITBUFSIZE)
#define EFIXOPEN(c)     (efixpos+(c) < efixbuffer+EMITBUFSIZE)
#define ELINOPEN(c)     (elinpos+(c) <= elinbuffer+LINBUFSIZE)

UCHAR   emitbuf[EMITBUFSIZE];
UCHAR   efixbuffer[EMITBUFSIZE];
UCHAR   elinbuffer[LINBUFSIZE];
UCHAR   *ebufpos = emitbuf;
UCHAR   *efixpos = efixbuffer;
UCHAR   *elinpos = elinbuffer;
UCHAR   ehoffset = 0;                   /* number of bytes into segment index */
UCHAR   emitrecordtype = 0;
OFFSET  ecuroffset;
USHORT  ecursegment;
long    oOMFCur;
SHORT   FixupOp = 0;
SHORT    LedataOp = 0xA0;

USHORT  linSegment;
UCHAR   fLineUsed32;
SHORT   fUnderScore;
UCHAR   fIniter = TRUE;
UCHAR   fNoMap;                    /* hack to disable fixup mapping for CV */

extern PFPOSTRUCT  pFpoHead;
extern PFPOSTRUCT  pFpoTail;
extern unsigned long numFpoRecords;

VOID CODESIZE edump( VOID );
VOID PASCAL CODESIZE emitoffset( OFFSET, SHORT );

/* The calls to the routines in this module appear in the following group
   order.  Ordering within a group is unspecified:

  Group 1:
           emodule (Pname)

  Group 2:
           emitsymbol (Psymb)

  Group 3:
           emitsegment (Psymb)
           emitglobal (Psymb)
           emitextern (Psymb)

  Group 4:
           emitcbyte (BYTE)
           emitobject(pDSC)
           emitcword (WORD)
           emitdup (???)

  Group 5:
           emitdone (Psymb)

 */




/***    emitsword - feed a word into the buffer
 *
 *      emitsword (w);
 *
 *      Entry   w = word to feed into omf buffer
 *      Exit    word placed in buffer low byte first
 *      Returns none
 */


VOID PASCAL CODESIZE
emitsword (
        USHORT w
){
        OMFBYTE(w);
        OMFBYTE(w >> 8);
}


/* emitoff - write an offset, either 16 or 32 bits, depending upon
 * use32.  note conditional compilation trick with sizeof(OFFSET).
 * with more cleverness, this could be a macro. -Hans */

VOID PASCAL CODESIZE
emitoffset(
        OFFSET off,
        SHORT use32
){
        emitsword((USHORT)off);
        if (sizeof(OFFSET) > 2 && use32)
                emitsword((USHORT)highWord(off));
}

/***    emitSymbol - output name string
 *
 *      Entry
 *          pSY - pointer to symbol table entry to dump
 */


VOID PASCAL CODESIZE
emitSymbol(
        SYMBOL FARSYM *pSY
){
        if (pSY->attr & M_CDECL)
            fUnderScore++;

        if (pSY->lcnamp)
                emitname ((NAME FAR *)pSY->lcnamp);
        else
                emitname (pSY->nampnt);
}

/***    emitname - write FAR name preceeded by length into omf buffer
 *
 *      emitname (name);
 *
 *      Entry   name = FAR pointer to name string
 *      Exit    length of name followed by name written to omf buffer
 *      Returns none
 */


VOID PASCAL CODESIZE
emitname (
        NAME FAR *nam
){
        char FAR *p;

        OMFBYTE(STRFLEN ((char FAR *)nam->id)+fUnderScore);

        if (fUnderScore) {      /* leading _ for C language */
            fUnderScore = 0;
            OMFBYTE('_');
        }

        for (p = (char FAR *)nam->id; *p; p++)
                OMFBYTE(*p);
}


/***    flushbuffer - write out linker record
 *
 *      flushbuffer ();
 *
 *      Entry   ebufpos = next address in emitbuf
 *              emitbuf = data buffer
 *              emitrecordtype = type of omf data in buffer
 *              ehoffset = length of segment index data in buffer
 *      Exit    data written to obj->fil if data in buffer
 *              buffer set empty (ebufpos = emitbuf)
 *              segment index length set to 0
 *      Returns none
 */


VOID PASCAL CODESIZE
flushbuffer ()
{
        /* Don't put out an empty data record, but can do empty
         * anything else */

        if ((emitrecordtype&~1) != 0xA0 ||
            (ebufpos - emitbuf) != ehoffset) /* RN */
                ebuffer (emitrecordtype, ebufpos, emitbuf);

        ebufpos = emitbuf;
        ehoffset = 0;
}




/***    flushfixup, flushline, write out fixup/line record
 *
 *      flushfixup ();
 *
 *      Entry   efixbuffer = fixup buffer
 *              efixpos = address of next byte in fixup buffer
 *      Exit    fixup buffer written to file if not empty
 *              fixup buffer set empty (efixpos = efixbuffer)
 *      Returns none
 */


VOID PASCAL CODESIZE
flushfixup ()
{
        ebuffer (FixupOp, efixpos, efixbuffer);
        FixupOp = 0;
        efixpos = efixbuffer;
}

VOID PASCAL CODESIZE
flushline ()
{
        USHORT recordT;

        recordT = emitrecordtype;

        ebuffer ( (USHORT)(fLineUsed32? 0x95: 0x94), elinpos, elinbuffer);
        elinpos = elinbuffer;
        emitrecordtype = (unsigned char)recordT;
}




/***    emitsetrecordtype - set record type and flush buffers if necessary
 *
 *      emitsetrecordtype (t);
 *
 *      Entry   t = type of omf record
 *      Exit    emit and fixup buffers flushed if t != current record type
 *              segment index written to buffer if data or dup omf record
 *              emit segment set to current segment
 *              emit offset set to offset within current segment
 *      Returns none
 */


VOID PASCAL CODESIZE
emitsetrecordtype (
        UCHAR t
){
        if (emitrecordtype && emitrecordtype != t) {
                /* flush emit and fixup buffers if active and not of same type */
                flushbuffer ();
                flushfixup ();
                switch(t)
                {
                case 0xA0:
                case 0xA1:      /* LEDATA or */
                case 0xA2:      /* LIDATA (dup) record */
                case 0xA3:
                    if (pcsegment) {

                        /* create a new header */
                        ecursegment = pcsegment->symu.segmnt.segIndex;
                        ecuroffset = pcoffset;
                        emitsindex (pcsegment->symu.segmnt.segIndex);

                        /* if we are getting to the end of the buffer
                         * and its a 32 bit segment, we need to start
                         * using 32 bit offsets in the LEDATA header.
                         * -Hans */

                        if (wordsize == 4)
                        {
                                if (t>= 0xA2)
                                        t = 0xA3;
                                /* there is a bug in the current linker--
                                 * all ledata or lidata records within
                                 * a module have to be either 16 or 32.
                                 * comment out optimization until this
                                 * is fixed */
                                else /* if (pcoffset>0x0ffffL-EMITBUFSIZE) */
                                        LedataOp = t = 0xA1;
                        }
                        emitoffset((OFFSET)pcoffset, (SHORT)(t&1));
                        if (t&1)
                                ehoffset += 2; /* RN */
                        break;
                    }
                    else
                        errorc (E_ENS);

                default:
                        break;
                }
        }
        if (t == 0xA4) {
            t = 0xA1;
        }
        emitrecordtype = t;
}




/***    emitsindex - output 'index' of segment, external, etc.
 *
 *      emitsindex (i);
 *
 *      Entry   i = index
 *      Exit    index written to emit buffer
 *              ehoffset = 3 if single byte index
 *              ehoffset = 4 if double byte index
 *      Returns none
 */


VOID PASCAL CODESIZE
emitsindex (
        register USHORT i
){
        ehoffset = 3;
        if (i >= 0x80) {
                OMFBYTE((i >> 8) + 0x80);
                ehoffset++;
        }
        OMFBYTE(i);
}




/***    emodule - output module name record
 *
 *      emodule (pmodule);
 *
 *      Entry   pmodule = pointer to module name
 *      Exit    module name written to obj->fil
 *              current emit segment and offset set to 0
 *      Returns none
 */


VOID PASCAL CODESIZE
emodule (
        NAME FAR *pmodule
){
        char FAR *p;

        emitsetrecordtype (0x80);
        emitname (pmodule);
        flushbuffer ();

        if (fDosSeg) {

            emitsetrecordtype (0x88);
            emitsword((USHORT)(0x9E00 | 0x80));  /* nopurge + class = DOSSEG */
            flushbuffer ();
        }

        if (codeview == CVSYMBOLS){

            /* output speical comment record to handle symbol section */

            emitsetrecordtype (0x88);
            OMFBYTE(0);
            emitsword(0x1a1);
            emitsword('V'<< 8 | 'C');
            flushbuffer ();
        }

        while (pLib) {

            emitsetrecordtype (0x88);
            emitsword((USHORT) (0x9F00 | 0x80));  /* nopurge + class = Library*/

            for (p = (char FAR *)pLib->text; *p; p++)
                   OMFBYTE(*p);

            flushbuffer ();
            pLib = pLib->strnext;
        }

        ecuroffset = 0;             /* initial for pass2 */
        ecursegment = 0;
}




/***    emitlname - put symbols into bufer to form 'lnames' record
 *
 *      emitlname (psym);
 *
 *      Entry   psym = pointer to symbol structure
 *      Exit    current record type set to LNAMES and buffer flushed if
 *              necessary.  The name string is written to the emit buffer.
 *      Returns none
 */


VOID PASCAL CODESIZE
emitlname (
        SYMBOL FARSYM *psym
){
        emitsetrecordtype (0x96);
        if (lnameIndex == 3)        /* 1st time around */
                OMFBYTE(0);         /* output the NULL name */

        if (!EBUFOPEN(STRFLEN (psym->nampnt->id) + 1)) {
                flushbuffer ();
                emitsetrecordtype (0x96);
        }
        emitSymbol(psym);
}




/***    emitsegment - output a segment definition record
 *
 *      emitsegment (pseg);
 *
 *      Entry   pseg = pointer to segment name
 *      Exit    record type set to SEGDEF and emit buffer flushed if necessary.
 *              SEGDEF record written to emit buffer
 *      Returns none
 */


VOID PASCAL CODESIZE
emitsegment (
        SYMBOL FARSYM *pseg
){
        UCHAR   comb;
        UCHAR   algn;
        SHORT   use32=0;

        /* use32 is whether to put out 16 or 32 bit offsets.  it
         * only works if segmnt.use32 is enabled.  the D bit
         * is keyed off segmnt.use32 -Hans */

        if (sizeof(OFFSET)>2 &&
            (pseg->symu.segmnt.use32 > 2) &&
            pseg->symu.segmnt.seglen > 0xffffL)
                use32 = 1;

        emitsetrecordtype ((UCHAR)(0x98+use32)); /* SEGDEF */

        algn = pseg->symu.segmnt.align;
        comb = pseg->symu.segmnt.combine;

#ifdef V386
        if (!use32 && pseg->symu.segmnt.seglen == 0x10000L) /* add 'big' bit? */
                if (pseg->symu.segmnt.use32 > 2)
                        OMFBYTE((algn<<5) + (comb<<2) + 3); /* add 'D' bit */
                else
                        OMFBYTE((algn<<5) + (comb<<2) + 2);
        else
#endif
                if (pseg->symu.segmnt.use32 > 2)
                        OMFBYTE((algn<<5) + (comb<<2) + 1); /* add 'D' bit */
                else
                        OMFBYTE((algn<<5) + (comb<<2));

        if (algn == 0 || algn == (UCHAR)-1) {
                /* segment number of seg */
                emitsword ((USHORT)pseg->symu.segmnt.locate);
                OMFBYTE(0);
        }
        emitoffset(pseg->symu.segmnt.seglen, use32);

        emitsindex (pseg->symu.segmnt.lnameIndex);
        pseg->symu.segmnt.segIndex = segmentnum++;

        /* seg, class number */
        if (!pseg->symu.segmnt.classptr)   /* Use blank name */
                emitsindex (1);
        else
                emitsindex((USHORT)((pseg->symu.segmnt.classptr->symkind == SEGMENT) ?
                            pseg->symu.segmnt.classptr->symu.segmnt.lnameIndex:
                            pseg->symu.segmnt.classptr->symu.ext.extIndex));

        emitsindex (1);
        flushbuffer ();
}




/***    emitgroup - output a group record
 *
 *      emitgroup (pgrp);
 *
 *      Entry   pgrp = pointer to group name
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitgroup (
        SYMBOL FARSYM *pgrp
){
        SYMBOL FARSYM *pseg;

        emitsetrecordtype (0x9A);

        emitsindex (pgrp->symu.grupe.groupIndex);
        pgrp->symu.grupe.groupIndex = groupnum++;

        pseg = pgrp->symu.grupe.segptr;
        while (pseg) {
                if (pseg->symu.segmnt.segIndex){

                        OMFBYTE(((pseg->attr == XTERN)? 0xFE: 0xFF));
                        emitsindex (pseg->symu.segmnt.segIndex);
                }
                pseg = pseg->symu.segmnt.nxtseg;
        }
        flushbuffer ();
}




/***    emitglobal - output a global declaration
 *
 *      emitglobal (pglob);
 *
 *      Entry   pglob = pointer to global name
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitglobal (
        SYMBOL FARSYM *pglob
){
        static SHORT gIndexCur = -1, sIndexCur = -1;
        register SHORT gIndex, sIndex, cbNeeded;
        OFFSET pubvalue;
        SHORT use32 = 0x90;

        pubvalue = pglob->offset;
        if ((unsigned long)pubvalue >= 0x10000l)
                use32 = 0x91;

        /*  A public EQU can be negative, so must adjust value */
        /* this is happening because masm keeps numbers
         * in 17/33 bit sign magnitude representation */

        if ((pglob->symkind == EQU) && pglob->symu.equ.equrec.expr.esign)
                pubvalue = (short)(((use32==0x91? 0xffffffffl : 65535) - pglob->offset) + 1);


        /* Match Intel action, If a global is code, it should
           belong to the group of the CS assume at the time of
           definition, if there is one */

        /* Output group index for data labels too */

        sIndex = gIndex = 0;

        if (((1 << pglob->symkind) & (M_PROC | M_CLABEL))
            && pglob->symu.clabel.csassume
            && pglob->symu.clabel.csassume->symkind == GROUP
            && pglob->symsegptr && pglob->symsegptr->symu.segmnt.grouptr)

            gIndex = pglob->symu.clabel.csassume->symu.grupe.groupIndex;


        if (pglob->symsegptr)
            sIndex = pglob->symsegptr->symu.segmnt.segIndex;

        cbNeeded = STRFLEN ((char FAR *)pglob->nampnt->id) + 13;

        if (gIndex != gIndexCur ||
            sIndex != sIndexCur ||
            emitrecordtype != use32 ||
            !EBUFOPEN(cbNeeded)) {     /* start a new record */

            flushbuffer();
            emitsetrecordtype ((UCHAR)use32);

            gIndexCur = gIndex;
            sIndexCur = sIndex;

            emitsindex (gIndexCur);
            emitsindex (sIndexCur);

            if (sIndex == 0)            /* absolutes require a 0 frame # */
                emitsword (sIndex);
        }

        emitSymbol(pglob);

        emitoffset(pubvalue, (SHORT)(use32&1));
        if (codeview == CVSYMBOLS) {

            if (pglob->symkind == EQU)    /* type not stored */

                emitsindex(typeFet(pglob->symtype));
            else
                emitsindex (pglob->symu.clabel.type);
        }
        else
            emitsindex(0);              /* untyped */
}




/***    emitextern - emit an external
 *
 *      emitextern (psym)
 *
 *      Entry   *psym = symbol entry for external
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitextern (
        SYMBOL FARSYM *psym
){
        USHORT recType;

        recType = 0x8c;

        if (psym->symkind == EQU){

            /* this an extrn lab:abs definition, which is allocated as
             * an EQU record which doesn't have space for a index so
             * it stored in the unused length field */

            psym->length = externnum++;
        }
        else {
            psym->symu.ext.extIndex = externnum++;

            if (psym->symu.ext.commFlag)
                recType = 0xb0;
        }

        fKillPass1 |= pass2;

        emitsetrecordtype ((UCHAR)recType);

        if (!EBUFOPEN(STRFLEN (psym->nampnt->id) + 9)) {
                flushbuffer ();
                emitsetrecordtype ((UCHAR)recType);
        }

        emitSymbol(psym);

        if (codeview == CVSYMBOLS)

            emitsindex(typeFet(psym->symtype));
        else
            OMFBYTE(0);

        if (recType == 0xb0) {          /* output commdef variate */

            if (psym->symu.ext.commFlag == 1) {    /* near item */

                OMFBYTE(0x62);
                                        /* size of field */
                OMFBYTE(0x81);
                emitsword((USHORT)(psym->symu.ext.length * psym->symtype));
            }
            else {
                OMFBYTE(0x61);          /* far item */

                OMFBYTE(0x84);              /* # of elements */
                emitsword((USHORT)psym->symu.ext.length);
                OMFBYTE(psym->symu.ext.length >> 16);

                OMFBYTE(0x81);              /* element size */
                emitsword(psym->symtype);
            }


        }
}


/***    emitfltfix - emit fixup for floating point
 *
 *      emitfltfix (group, extidx);
 *
 *      Entry
 *              *extidx = external id (0 if not assigned)
 *      Exit
 *      Returns
 *      Calls
 */

VOID PASCAL CODESIZE
emitfltfix (
        USHORT group,
        USHORT item,
        USHORT *extidx
){
        register SHORT i;

        if (*extidx == 0) {
                /* Must define it */
                if (!moduleflag)
                        dumpname ();
                /* All fixups are FxyRQQ */
                *extidx = externnum++;
                if (!EBUFOPEN(7))
                        flushbuffer ();
                emitsetrecordtype (0x8C);
                /* Name length */
                OMFBYTE(6);
                OMFBYTE('F');
                OMFBYTE(group);         /* Group I or J */
                OMFBYTE(item);          /* Item  D, W, E, C, S, A */
                OMFBYTE('R');
                OMFBYTE('Q');
                OMFBYTE('Q');
                OMFBYTE(0);
        }
        if (pass2) {            /* Must put out a extern ref */
                if (!EFIXOPEN(5))
                        emitdumpdata ( (UCHAR)LedataOp);
                emitsetrecordtype ( (UCHAR) LedataOp);

                FixupOp = 0x9C + (LedataOp & 1);

                /* output location */
                i = (SHORT)(ebufpos - emitbuf - ehoffset);
                FIXBYTE(0xC4 + (i >> 8));
                FIXBYTE(i);
                FIXBYTE(0x46);

                if (*extidx >= 0x80)      /* Output 2 byte link # */
                        FIXBYTE ((UCHAR)((*extidx >> 8) + 0x80));

                FIXBYTE(*extidx);
        }
}



/***    emitline - output a line number offset pair
 *
 *      emitline(pcOffset)
 *
 *      Entry   pcoffset: code offset to output
 *              src->line: for the current line number
 *      Exit    none
 */

VOID PASCAL CODESIZE
emitline()
{
    static UCHAR fCurrent32;

    if (codeview < CVLINE || !pass2 || !objing || !pcsegment)
        return;

    if (macrolevel == 0 ||
        !fPutFirstOp) {

        fCurrent32 = (emitrecordtype == 0xA1);

        if (linSegment != pcsegment->symu.segmnt.segIndex ||
            ! ELINOPEN(2 + wordsize) ||
            fLineUsed32 != fCurrent32 ) {

            flushline();

            /* Start a new line # segment */

            linSegment = pcsegment->symu.segmnt.segIndex;
            fLineUsed32 = fCurrent32;

            /* start record with group index and segment index */

            LINBYTE(0);                 /* no group */

            if (linSegment >= 0x80)      /* Output 2 byte link # */
                LINBYTE ((UCHAR)((linSegment >> 8) + 0x80));

            LINBYTE(linSegment);
        }

        LINBYTE(pFCBCur->line);             /* First line # */
        LINBYTE(pFCBCur->line >> 8);

        LINBYTE(pcoffset);              /* then offset */
        LINBYTE(pcoffset >> 8);

        if (fLineUsed32) {              /* 32 bit offset for large segments */

            LINBYTE(highWord(pcoffset));
            LINBYTE(highWord(pcoffset) >> 8);
        }
    }
    if (macrolevel != 0)
        fPutFirstOp = TRUE;
}



/***    fixroom - check for space in fix buffer
 *
 *      flag = fixroom (n);
 *
 *      Entry   n = number of bytes to insert in buffer
 *      Exit    none
 *      Returns TRUE if n bytes will fit in buffer
 *              FALSE if n bytes will not fit in buffer
 */


UCHAR PASCAL CODESIZE
fixroom (
        register UCHAR   n
){
        return (EFIXOPEN(n));
}




/***    emitcleanq - check for buffer cleaning
 *
 *      flag = emitcleanq (n);
 *
 *      Entry   n = number of bytes to insert in buffer
 *      Exit    E_ENS error message issued if pcsegment is null
 *      Returns TRUE if
 */


UCHAR PASCAL CODESIZE
emitcleanq (
        UCHAR n
){
        if (!pcsegment)

            errorc (E_ENS);
        else
            return (ecursegment != pcsegment->symu.segmnt.segIndex ||
                pcoffset != ecuroffset ||
                !EBUFOPEN(n));
}




/***    emitdumpdata - clean data buffer and set up for data record
 *
 *      emitdumpdata (recordnum);
 *
 *      Entry   recordnum = record type
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitdumpdata (
        UCHAR recordnum
){
        flushbuffer ();
        /* force dump of buffer */
        emitrecordtype = 0xFF;
        emitsetrecordtype (recordnum);
}




/***    emitcbyte - emit constant byte into segment
 *
 *      emitcbyte (b)
 *
 *      Entry   b = byte
 *              pcsegment = pointer to segment symbol entry
 *              pcoffset = offset into segment
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitcbyte (
        UCHAR b
){
        /* if the segment is changed or the offset is not current or there
           is no room in the buffer then flush buffer and start over */

        if (emitcleanq (1))
                emitdumpdata ((UCHAR)LedataOp);

        emitsetrecordtype ((UCHAR)LedataOp);
        OMFBYTE(b);
        ecuroffset++;
}



/***    emitcword - place a constant word into data record
 *
 *      emitcword (w);
 *
 *      Entry   w = word
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitcword (
        OFFSET w
){
        if (emitcleanq (2))
                emitdumpdata ((UCHAR)LedataOp);

        emitsetrecordtype ((UCHAR)LedataOp);
        emitsword ((USHORT)w);
        ecuroffset += 2;
}

/***    emitcdword - place a constant word into data record
 *
 *      emitcword (w);
 *
 *      Entry   w = word
 *      Exit
 *      Returns
 *      Calls
 */
VOID PASCAL CODESIZE
emitcdword (
        OFFSET w
){
        if (emitcleanq (4))
                emitdumpdata ((UCHAR)LedataOp);

        emitsetrecordtype ((UCHAR)LedataOp);
        emitsword ((USHORT)w);
        emitsword (highWord(w));
        ecuroffset += 4;
}



/***    emitlong - emit a long constant
 *
 *      emitlong (pdsc);
 *
 *      Entry   *pdsc = duprecord
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitlong (
        struct duprec FARSYM *pdsc
){
        UCHAR *cp;
        OFFSET tmpstart;
        OFFSET tmpcurr;
        OFFSET tmplimit;

        tmpstart = pcoffset;
        cp = pdsc->duptype.duplong.ldata;
        tmplimit = (pcoffset + pdsc->duptype.duplong.llen) - 1;
        for (tmpcurr = tmpstart; tmpcurr <= tmplimit; ++tmpcurr) {
                pcoffset = tmpcurr;
                emitcbyte ((UCHAR)*cp++);
        }
        pcoffset = tmpstart;
}


VOID PASCAL CODESIZE
emitnop ()
{
        errorc(E_NOP);
        emitopcode(0x90);
}



/***    emitobject - emit object in dup or iter record to OMF stream
 *
 *      emitobject (pdesc);
 *
 *      Entry   *pdesc = parse stack entry
 *              Global - fInter -> FALSE if in iterated DUP
 */


VOID PASCAL CODESIZE
emitobject (
        register struct psop *pso
){
        register SHORT i;

        if (!pcsegment) {
                errorc (E_ENS);
                return;
        }
        mapFixup(pso);

        if (fIniter) {

            i = LedataOp;
            if (wordsize == 4 || pso->fixtype >= F32POINTER)
                i |= 1;

            emitsetrecordtype ((UCHAR)i);
         }

        /* Data or DUP record */
         if (pso->fixtype == FCONSTANT) {

            if (!fIniter) {
                    if (pso->dsize == 1)
                            OMFBYTE(pso->doffset);
                    else if (pso->dsize == 2)
                            emitsword ((USHORT)pso->doffset);
                    else
                            for (i = pso->dsize; i; i--)
                                    OMFBYTE(0);
            }
            else switch(pso->dsize) {

                case 1:
                        emitcbyte ((UCHAR)pso->doffset);
                        break;
                case 2:
                emit2:
                        emitcword (pso->doffset);
                        break;
                case 4:
                emit4:
                        emitcdword (pso->doffset);
                        break;
                default:
                        /* the indeterminate case had been set up
                           by emit2byte as 2.  we are now leaving
                           it as zero  and doing the mapping here. */

                        if (wordsize==4)
                                goto emit4;
                        else
                                goto emit2;
            }
        }
        else
            emitfixup (pso);
}



/***    emitfixup - emit fixup data into fixup buffer
 *
 *      emitfixup (pso)
 *
 *      Entry  PSO for object
 */


VOID PASCAL CODESIZE
emitfixup (
        register struct psop *pso
){
        UCHAR   fixtype;
        USHORT  dlen;           /* length of operand */
        UCHAR   flen;           /* length of fixup */
        SYMBOL FARSYM *pframe;
        SYMBOL FARSYM *ptarget;
        register USHORT   tmp;
        SHORT i;

        fixtype = fixvalues[pso->fixtype];

        emitgetspec (&pframe, &ptarget, pso);

        /* magic numbers for omf types. */

        dlen = pso->dsize;

        if (ptarget){
            if ((M_XTERN & ptarget->attr) &&
                !pframe && (fixtype == 2 || fixtype == 3))
                    pframe = ptarget;
        }
        else
            return;

        flen = 7;
        if (pso->doffset)                /* target displacement */
                flen += 2 + ((emitrecordtype&1) << 1);

        /* make sure that we have enough room in the various buffers */
        if (fIniter)
                if (emitcleanq ((UCHAR)dlen) || !EFIXOPEN(flen))
                        emitdumpdata ((UCHAR)(LedataOp +2 - 2 * fIniter)); /* RN */

        /* set fixup type--32 or 16 */
        if (emitrecordtype&1)
        {
                if (FixupOp == 0x9C)
                        errorc(E_PHE); /* is there a better message? */
                FixupOp = 0x9D;
        }
        else
        {
                if (FixupOp == 0x9D)
                        errorc(E_PHE); /* is there a better message? */
                FixupOp = 0x9C;
        }
        /* build high byte of location */
        tmp = 0x80 + (fixtype << 2);
        if (!(M_SHRT & pso->dtype))          /* set 'M' bit */
                tmp |= 0x40;

        i = (SHORT)(ebufpos - emitbuf - ehoffset);
        FIXBYTE(tmp + (i >> 8));

        /* build low byte of location */
        FIXBYTE(i);

        /* output fixup data */
        FIXBYTE(efixdat (pframe, ptarget, pso->doffset));

        tmp = (pframe->symkind == EQU) ?
               pframe->length: pframe->symu.ext.extIndex;

        if (tmp >= 0x80)
                FIXBYTE((tmp >> 8) + 0x80);

        FIXBYTE(tmp);

        tmp = (ptarget->symkind == EQU) ?
               ptarget->length: ptarget->symu.ext.extIndex;

        /* send target spec */
        if (tmp >= 0x80)
                FIXBYTE((tmp >> 8) + 0x80);

        FIXBYTE(tmp);

        if (pso->doffset) {
                FIXBYTE(pso->doffset);
                FIXBYTE((UCHAR)(pso->doffset >> 8));
#ifdef V386
                if (FixupOp == 0x9D)
                {
                        FIXBYTE((UCHAR)highWord(pso->doffset));
                        FIXBYTE((UCHAR)(highWord(pso->doffset) >> 8));
                }
#endif
        }
        ecuroffset += dlen;

        /* put zero bytes into data buffer */
        while (dlen--)
                OMFBYTE(0);
}



/***    mapFixup - map relocatable objects into the correct fixup type
 *
 *
 *      Entry   *pdesc = parse stack entry
 *      Returns
 *          Sets fixtype and dsize
 */


VOID PASCAL CODESIZE
mapFixup (
        register struct psop *pso
){

        if (fNoMap)
            return;

        if ((1 << pso->fixtype & (M_FCONSTANT | M_FNONE)) &&
            (pso->dsegment || pso->dflag == XTERNAL))

            pso->fixtype = FOFFSET;

#ifdef V386

         /* Remap OFFSET and POINTERS into there 32 bit types */

        if (pso->mode > 4 || pso->dsize > 4 ||
            (pso->dsegment && pso->dsegment->symkind == SEGMENT &&
             pso->dsegment->symu.segmnt.use32 == 4) ||
            pso->dcontext == pFlatGroup && pso->dsize == 4)

            switch(pso->fixtype) {

            case FOFFSET:

                    if (pso->dsize != 4)
                        errorc(E_IIS & ~E_WARN1);

                    else
                        pso->fixtype = F32OFFSET;
                    break;

            case FPOINTER:
                    if (pso->dsize != 6)
                        errorc(E_IIS & ~E_WARN1);

                    else
                        pso->fixtype = F32POINTER;
                    break;

            /* default is to do no mapping */
            }
#endif
}


/***    emitgetspec - set frame and target of parse entry
 *
 *      emitgetspec (pframe, ptarget, pdesc);
 *
 *      Entry   pframe
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
emitgetspec (
        SYMBOL FARSYM * *pframe,
        SYMBOL FARSYM * *ptarget,
        register struct psop *pso
){

        if (pso->fixtype != FCONSTANT &&
            pso->dflag == XTERNAL) {

                   *ptarget = pso->dextptr;
                   *pframe = pso->dsegment;

#ifndef FEATURE
                   /* externs without segments and the current assume is to
                    * flat space get a flat space segment frame */

                   if (! *pframe && pso->dextptr &&
                      regsegment[isCodeLabel(pso->dextptr) ? CSSEG: DSSEG] == pFlatGroup)

                        *pframe = pFlatGroup;

#endif
                   if (pso->dcontext)
                           *pframe = pso->dcontext;
        }
        else {

            *ptarget = pso->dsegment;   /* containing segment */
            *pframe = pso->dcontext;    /* context(?) of value */
        }

        if (!*pframe)
                *pframe = *ptarget;
}




/***    efixdat - return fixdat byte
 *
 *      routine  (pframe, ptarget, roffset);
 *
 *      Entry   *pframe =
 *              *ptarget =
 *              roffset =
 *      Exit
 *      Returns
 *      Calls
 */


UCHAR PASCAL CODESIZE
efixdat (
        SYMBOL FARSYM *pframe,
        SYMBOL FARSYM *ptarget,
        OFFSET roffset
){
        register UCHAR   tmp;

        /* build fixdat byte */
        tmp = 0;
        /* 'F' bit is off */
        /* 'T' bit is off */
        if (roffset == 0)       /* 'P' bit is on */
                tmp = 4;

        if (pframe)
                if (M_XTERN & pframe->attr)
                       tmp += 2 << 4;
                else if (pframe->symkind == GROUP)
                       tmp += 1 << 4;

        /* frame part of fixdat */

        if (ptarget)
                if (M_XTERN & ptarget->attr)
                       tmp += 2;
                else if (ptarget->symkind == GROUP)
                       tmp += 1;

        return (tmp);
}



/***    edupitem - emit single dup item and count size
 *
 *      edupitem (pdup);
 *
 *      Entry   *pdup = dup record
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL CODESIZE
edupitem (
        struct duprec FARSYM    *pdup
){
        register USHORT len;
        UCHAR *cp;

        if (nestCur > nestMax)
            nestMax++;

        if (ebufpos - emitbuf != EMITBUFSIZE + 1) {
                len = wordsize+2;

            if (pdup->dupkind == LONG)
                len += pdup->duptype.duplong.llen + 1;

            else if (pdup->dupkind == ITEM)
                len += pdup->duptype.dupitem.ddata->dsckind.opnd.dsize + 1;

            if (!EBUFOPEN(len))
                ebufpos = emitbuf + EMITBUFSIZE + 1;

            else {
                emitsword ((USHORT)(pdup->rptcnt));
                /* repeat count */
                if (emitrecordtype & 1)
                        emitsword((USHORT)(pdup->rptcnt >> 16));

                /* block count */
                emitsword (pdup->itemcnt);

                if (pdup->dupkind == LONG) {
                    cp = pdup->duptype.duplong.ldata;
                    len = pdup->duptype.duplong.llen;

                    OMFBYTE(len);

                    do {
                            OMFBYTE(*cp++);
                    } while (--len);
                }
                else if (pdup->dupkind == ITEM) {
                    OMFBYTE(pdup->duptype.dupitem.ddata->dsckind.opnd.dsize);

                        fIniter--;
                        emitobject (&pdup->duptype.dupitem.ddata->dsckind.opnd);
                        fIniter++;
                }
            }
        }
}




/***    emitdup - emit dup record and appropriate fixup record
 *
 *      emitdup (pdup);
 *
 *      Entry   *pdup = dup record
 *      Exit
 *      Returns FALSE if dup is too large to fit in buffer
 *      Calls
 */


UCHAR PASCAL CODESIZE
emitdup (
        struct duprec FARSYM *pdup
){
        SHORT op;

        op = (f386already) ? 0xA3 : 0xA2;
        nestCur = nestMax = 0;

        emitdumpdata ((UCHAR)op);
        emitsetrecordtype ((UCHAR)op);

        /* scan dup tree and emit dup items */
        scandup (pdup, edupitem);

        if (ebufpos - emitbuf == EMITBUFSIZE + 1) {
                ebufpos = emitbuf;
                ehoffset = 0;
                efixpos = efixbuffer;
                return(FALSE);
        }
        else {
                flushbuffer ();
                flushfixup ();
                emitrecordtype = 0xFF;
        }
        return (nestMax <= 18);
}


/***    emitEndPass1 - emit end of pass1 info
 *
 */


VOID PASCAL emitEndPass1()
{

        emitsetrecordtype (0x88);
        oEndPass1 = oOMFCur + 5;   /* note offset of end of pass1 OMF record */

        OMFBYTE(0);
        emitsword(0x100 | 0xA2);
        flushbuffer ();
}



/***    emitdone - produce end record
 *
 *      emitdone (pdesc);
 *
 *      Entry   *pdesc = parse tree entry
 *      Exit
 *      Returns
 *      Calls
 */


VOID PASCAL
emitdone (
        DSCREC *pdesc
){
        SYMBOL FARSYM *pframe;
        SYMBOL FARSYM *ptarget;
        OFFSET u;
        UCHAR endOMFtype;

        flushline();

        if (!pdesc)
        {
                emitsetrecordtype (0x8A); /* RN */
                /* emit null entry point marked in MOD TYP */
                /* there is a point of contention here.  some people
                 * (and decode.c, old assemblers and other things) say
                 * the low order bit is zero.  others, such as the
                 * omf documentation, say the low order bit should be
                 * 1.  since I dont know, and am trying to be compatable,
                 * I will obey the old tools.  maybe I'll change this
                 * later...             -Hans
                 * OMFBYTE(1); /* RN */

                OMFBYTE(0);
        }
        else {
                fKillPass1++;
                u = pdesc->dsckind.opnd.doffset;
                emitgetspec (&pframe, &ptarget, &pdesc->dsckind.opnd);

                if (!ptarget || !pframe)
                    return;

                endOMFtype = (cputype & P386)? 0x8B: 0x8A;

                if (M_XTERN & ptarget->attr)
                        pframe = ptarget;

                emitsetrecordtype (endOMFtype);

                /* emit entry point information */
                OMFBYTE(0xC1);
                OMFBYTE(efixdat (pframe, ptarget, u) & ~4);

                emitsindex (pframe->symu.segmnt.segIndex);
                emitsindex (ptarget->symu.segmnt.segIndex);

                emitsword((USHORT)u);       /* output offset */

#ifdef V386
                if (endOMFtype == 0x8B)
                        emitsword((USHORT)highWord(u));
#endif
        }
        flushbuffer ();
}

#ifndef M8086OPT

/***    EBYTE - Emit byte macro
 *
 *      EBYTE ( ch )
 *
 *      The bytes are buffered in obj.buf until the buffer fills
 *      then the buffer is written to disk via edump.
 *
 */

#define EBYTE( ch ){\
    if( !obj.cnt){\
        edump();\
    }\
    obj.cnt--;\
    checksum += *obj.pos++ = (char)ch;\
}

/***    ebuffer - write out object buffer
 *
 *      Writes the record type, record length, record data, and checksum to
 *      the obj file. This is done via EBYTE which buffers the writes into
 *      obj.buf.
 *
 *      Modifies    obj.cnt, obj.pos, objerr, emitrecordtype
 *      Exit        none
 *      Returns
 *      Calls       farwrite
 */

VOID CODESIZE
ebuffer (
        USHORT rectyp,
        UCHAR *bufpos,
        UCHAR *buffer
){
    register UCHAR   checksum;
    register i;
    USHORT  nb;


    if ((bufpos != buffer) && objing) {
        nb = (USHORT)(bufpos - buffer + 1);
        oOMFCur += nb + 3;
        checksum = 0;
        EBYTE(rectyp)
        i = nb & 0xFF;
        EBYTE( i )
        i = nb >> 8;
        EBYTE( i )
        while (buffer < bufpos){
            EBYTE( *buffer++ )
        }
        checksum = -checksum;
        EBYTE( checksum );
    }
    emitrecordtype = 0;
}


/***    edump - dump the emit buffer
 *
 *      edump ();
 *
 *      The bytes buffered in obj.buf are dumped to disk. And
 *      the count and buffer position are reinitialized.
 *
 *      Modifies    obj.cnt, obj.pos, objerr
 *      Exit        none
 *      Returns
 *      Calls       farwrite
 */

VOID CODESIZE
edump()
{

# if defined MSDOS && !defined FLATMODEL
    farwrite( obj.fh, obj.buf, (SHORT)(obj.siz - obj.cnt) );
# else
    if (_write( obj.fh, obj.buf, obj.siz - obj.cnt )
            != obj.siz - obj.cnt)
            objerr = -1;
# endif /* MSDOS */

    obj.cnt = obj.siz;
    obj.pos = obj.buf;
}
#endif /* M8086OPT */


#if !defined M8086OPT && !defined FLATMODEL

unsigned short _far _pascal DosWrite( unsigned short, unsigned char far *, unsigned short, unsigned short far *);

VOID farwrite( handle, buffer, count )
 int handle;
 UCHAR FAR * buffer;
 SHORT count;
{
  USHORT usWritten;

    if( DosWrite( handle, buffer, count, &usWritten ) ){
        objerr = -1;
    }
}

#endif

int emitFpo()
{
        struct nameStruct {
                SHORT   hashval;
                char    id[20];
        } nam = {0, ".debug$F"};

        PFPOSTRUCT    pFpo        = pFpoHead;
        SYMBOL        sym;
        UCHAR         comb        = 2;  // public
        UCHAR         algn        = 5;  // relocatable
        USHORT        tmp         = 0;
        unsigned long offset      = 0;
        unsigned long data_offset = 0;

        if (!pFpo) {
            return TRUE;
        }

        /*
         * write out the externs for all fpo procs
         * this must be done during pass1 so that the extdefs
         * are written to the omf file before the pubdefs
         */
        if (!pass2) {
            flushbuffer();
            for (pFpo=pFpoHead; pFpo; pFpo=pFpo->next) {
                pFpo->extidx = externnum++;
                emitsetrecordtype (0x8C);
                emitSymbol(pFpo->pSym);
                OMFBYTE(0);
                flushbuffer();
            }
            return TRUE;
        }

        /*
         * create the lnames record for the .debug$F section
         */
        emitsetrecordtype (0x96);
        memset(&sym,0,sizeof(SYMBOL));
        sym.nampnt = (NAME*) &nam;
        emitSymbol(&sym);
        flushbuffer();

        /*
         * create the segdef record for the .debug$F section
         */
        emitsetrecordtype (0x98);
        OMFBYTE((algn<<5) + (comb<<2) + 1);
        emitoffset(numFpoRecords*sizeof(FPO_DATA), 0);
        emitsindex (lnameIndex);
        emitsindex (1);
        emitsindex (1);
        flushbuffer();

        /*
         * now we have to cruise thru the list of fpo directives and
         * fixup any cases where there are multiple fpo directives for
         * a single procedure.  the procedure size needs to be changed
         * to account for the multiple directives.
         */
        pFpo=pFpoHead;
        flushbuffer();
        do {
            if ((pFpo->next) && (pFpo->next->pSym == pFpo->pSym)) {
                // we must have a group (2 or more) fpo directives
                // that are in the same function so lets fix them
                do {
                    pFpo->fpoData.cbProcSize =
                      pFpo->next->fpoData.ulOffStart - pFpo->fpoData.ulOffStart;
                    pFpo = pFpo->next;
                    // now we must output a pubdef and a extdef for the
                    // fpo record.  this is necessary because otherwise the
                    // linker will resolve the fixups to the first fpo record
                    // function.
                    pFpo->extidx = externnum++;
                    emitsetrecordtype (0x8C);
                    emitSymbol(pFpo->pSymAlt);
                    OMFBYTE(0);
                    flushbuffer();
                    emitglobal(pFpo->pSymAlt);
                } while ((pFpo->next) && (pFpo->next->pSym == pFpo->pSym));
                pFpo->fpoData.cbProcSize =
                   (pFpo->pSym->offset + pFpo->pSym->symu.plabel.proclen) -
                   pFpo->fpoData.ulOffStart;
            }
            else {
                pFpo->fpoData.cbProcSize = pFpo->pSym->symu.plabel.proclen;
            }
            pFpo = pFpo->next;
        } while (pFpo);

        /*
         * finally we scan the list of fpo directives and output the
         * actual fpo records and associated fixups
         */
        for (pFpo=pFpoHead; pFpo; pFpo=pFpo->next) {
            /*
             * emit the fpo record
             */
            emitsetrecordtype (0xA4);
            emitsindex (segmentnum);
            emitoffset(data_offset,1);
            data_offset += sizeof(FPO_DATA);
            offset = pFpo->fpoData.ulOffStart;
            pFpo->fpoData.ulOffStart = 0;
            memcpy((void*)ebufpos, (void*)&pFpo->fpoData, sizeof(FPO_DATA));
            ebufpos += sizeof(FPO_DATA);
            /*
             * emit the fixup record
             */
            emitsetrecordtype (0x9D);
            OMFBYTE(0xB8);   // m=0, loc=14, offset=0
            OMFBYTE(0x00);   // offset=0
            OMFBYTE(0x92);   // f=1, frame=1, t=0, p=0, target=2
            tmp = pFpo->extidx;
            if (tmp >= 0x80) {
                OMFBYTE((tmp >> 8) + 0x80);
            }
            OMFBYTE(tmp);
            OMFBYTE(offset);
            OMFBYTE(offset >>  8);
            OMFBYTE(offset >> 16);
            OMFBYTE(offset >> 24);
        }
        flushbuffer();

        lnameIndex++;
        segmentnum++;

        return TRUE;
}