/* genbin.c - binary code generation routines for assembler */

#include "syshead.h"
#include "const.h"
#include "type.h"
#include "address.h"
#include "file.h"
#include "globvar.h"

#ifdef USE_FIXED_HEAP
PRIVATE char *asmbeg;		/* beginning of assembler code */
				/* for overwrite check */
				/* bss-init to zero = NULL and not changed */
#endif

/* Sneaky stuff, the start of a binary file can be _negative_ for the I80386
   assembler. The -ve addresses are ones over 2GB (or "org -32") */
#ifdef I80386
PRIVATE soffset_t binfbuf;	/* binary code buffer for file (counter) */
PRIVATE soffset_t binmax;	/* maximum value of binmbuf for pass 1 */
PRIVATE soffset_t binmin;	/* minimum value of binmbuf for pass 1 */
#define PT soffset_t
#else
PRIVATE offset_t binfbuf;	/* binary code buffer for file (counter) */
PRIVATE offset_t binmax;	/* maximum value of binmbuf for pass 1 */
PRIVATE offset_t binmin;	/* minimum value of binmbuf for pass 1 */
#define PT offset_t
#endif

FORWARD void putbinoffset P((offset_t offset, count_t size));

/* write header to binary file */

PUBLIC void binheader()
{
#ifdef BINSYM
    if ((outfd = binfil) != 0x0 && binmbuf_set && binmax >= binmin)
    {
	writec(0x0);		/* binary header byte */
#ifdef LONG_BINHEADER
	writeoff(binmax - binmin);	/* program length */
	writeoff(binfbuf = binmin);	/* program start */
#else
	writew((unsigned) (binmax - binmin));	/* program length */
	writew((unsigned) (binfbuf = binmin));	/* program start */
#endif
    }
#else
    if ( ( outfd = symfil ) && binmbuf_set && binmax >= binmin)
    {
        int sft;
        writec('+'); writec(' ');
	for(sft=SIZEOF_OFFSET_T*8-4; sft >= 0; sft-=4)
           writec(hexdigit[(binmin>>sft) & 0xF]);
	writesn(" ----- $start");

        writec('+'); writec(' ');
	for(sft=SIZEOF_OFFSET_T*8-4; sft >= 0; sft-=4)
           writec(hexdigit[(binmax>>sft) & 0xF]);
	writesn(" ----- $end");

	binfbuf = binmin;	/* program start */
    }
#endif
}

/* write trailer to binary file */

PUBLIC void bintrailer()
{
#ifdef BINSYM
    if ((outfd = binfil) != 0x0 && (pedata & UNDBIT) != UNDBIT && binmbuf_set)
    {
	writec(0xFF);		/* binary trailer byte */
	writew(0x0);		/* further trailer bytes */
#ifdef LONG_BINHEADER
	writeoff(pedata & UNDBIT ? binmin : progent);	/* entry point */
#else
	writew(pedata & UNDBIT ? (unsigned) binmin : (unsigned) progent);
#endif
    }
#endif
}

/* generate binary code for current line */

PUBLIC void genbin()
{
    struct address_s *adrptr;
    char *bufptr;
    unsigned char remaining;

    if (binaryg && mcount != 0x0)
    {
	if (popflags)
	{
	    if (fcflag)
	    {
		bufptr = databuf.fcbuf;
		remaining = mcount;
		do
		    putbin(*bufptr++);
		while (--remaining != 0x0);
	    }
	    if (fdflag)
	    {
		adrptr = databuf.fdbuf;
		remaining = mcount;
		do
		{
		    putbinoffset(adrptr->offset, 0x2);
		    ++adrptr;
		}
		while ((remaining -= 0x2) != 0x0);
	    }
#if SIZEOF_OFFSET_T > 0x2
	    if (fqflag)
	    {
		adrptr = databuf.fqbuf;
		remaining = mcount;
		do
		{
		    putbinoffset(adrptr->offset, 0x4);
		    ++adrptr;
		}
		while ((remaining -= 0x4) != 0x0);
	    }
#endif
	}
	else
	{
	    remaining = mcount - 0x1;	/* count opcode immediately */
#ifdef I80386
	    if (aprefix != 0x0)
	    {
		putbin(aprefix);
		--remaining;
	    }
	    if (oprefix != 0x0)
	    {
		putbin(oprefix);
		--remaining;
	    }
	    if (sprefix != 0x0)
	    {
		putbin(sprefix);
		--remaining;
	    }
#endif
	    if (page != 0x0)
	    {
		putbin(page);
		--remaining;
	    }
	    putbin(opcode);
	    if (remaining != 0x0)
	    {
		if (postb != 0x0)
		{
		    putbin(postb);
		    --remaining;
		}
#ifdef I80386
		if (sib != NO_SIB)
		{
		    putbin(sib);
		    --remaining;
		}
#endif
		if (remaining != 0x0)
		    putbinoffset(lastexp.offset, remaining);
	    }
#ifdef I80386
	    if (immcount != 0x0)
		putbinoffset(immadr.offset, immcount);
#endif
	}
	/* else no code for this instruction, or already generated */
    }
}

/* initialise private variables */

PUBLIC void initbin()
{
#ifdef I80386
    binmin = ((offset_t)-1 >>1);	/* greater than anything */
#else
    binmin = -1;		/* greater than anything */
#endif
}

/* write char to binary file or directly to memory */

PUBLIC void putbin(ch)
opcode_pt ch;
{
    if (binfil != 0x0)
    {
	if (!binaryc)		/* pass 1, just record limits */
	{
	    if ((PT)binmbuf < binmin)
		binmin = binmbuf;
	    binmbuf++;
	    if ((PT)binmbuf > binmax)
		binmax = binmbuf;
	}
	else
	{
#if 0
	    if (binfbuf > (PT)binmbuf)
	    {
		error(BWRAP);	/* file buffer ahead of memory buffer */
	    }
	    else
#endif
	    {
#ifdef MSDOS
static PT zapptr = 0;
#endif
		outfd = binfil;
#ifdef MSDOS
		while (binfbuf < (PT)binmbuf && binfbuf >= zapptr+binmin)
		{
		    writec(0);
		    ++binfbuf;
		    ++zapptr;
		}
#endif
		if( binfbuf != (PT)binmbuf)
		    if( lseek(binfil, (long)((PT)binmbuf-binfbuf), 1) < 0 )
			error(BWRAP);
		binfbuf = binmbuf;
		writec(ch);
		binmbuf = ++binfbuf;
	    }
	}
    }
#ifdef USE_FIXED_HEAP
    else if (binaryc && !(lcdata & UNDBIT))
	/* memory output, and enabled */
    {
	register char *bufptr;

	if ((bufptr = (char *) binmbuf) >= asmbeg && bufptr < temp_buf())
	    error(OWRITE);
	else
	    *bufptr = ch;
	++binmbuf;
    }
#endif
}

/* write sized offset to binary file or directly to memory */

PRIVATE void putbinoffset(offset, size)
offset_t offset;
count_t size;
{
    char buf[sizeof offset];

#if SIZEOF_OFFSET_T > 0x2
    u4cn(buf, offset, size);
#else
    u2cn(buf, offset, size);
#endif
    putbin(buf[0]);
    if (size > 0x1)
	putbin(buf[1]);
    if (size > 0x2)
    {
	putbin(buf[2]);
	putbin(buf[3]);
    }
}
