/*
HEADER:        ;
TITLE:                 Frankenstein Cross Assemblers;
VERSION:       2.0;
DESCRIPTION: " Reconfigurable Cross-assembler producing Intel (TM)
               Hex format object records.  ";
SYSTEM:        UNIX, MS-Dos ;
FILENAME:      fraosub.c;
WARNINGS:      "This software is in the public domain.
               Any prior copyright claims are relinquished.

               This software is distributed with no warranty whatever.
               The author takes no responsibility for the consequences
               of its use."  ;
SEE-ALSO:      frasmain.c;
AUTHORS:       Mark Zenier;
*/

/*
       description     output pass utility routines
       history         September 27, 1987
                       March 15, 1988   release 1.1 WIDTH
                       September 14, 1990  Dosify, 6 char unique names
*/


#include <stdio.h>
#include "frasmdat.h"
#include "fragcon.h"
#include "procs.h"

#define OUTRESULTLEN 256
#define NUMHEXPERL 16
#define SOURCEOFFSET 24
#define NUMHEXSOURCE 6

int linenumber = 0;
char lineLbuff[INBUFFSZ];
int lineLflag = FALSE;

static unsigned char   outresult[OUTRESULTLEN];
static int     nextresult;
static long    genlocctr, resultloc;

static char    *oeptr;

#define        MAXIMPWID       24

static long widthmask[MAXIMPWID+1] =
{
/* 0 */                1L,
/* 1 */                1L,
/* 2 */                (1L <<  2 ) -1,
/* 3 */                (1L <<  3 ) -1,
/* 4 */                (1L <<  4 ) -1,
/* 5 */                (1L <<  5 ) -1,
/* 6 */                (1L <<  6 ) -1,
/* 7 */                (1L <<  7 ) -1,
/* 8 */                (1L <<  8 ) -1,
/* 9 */                (1L <<  9 ) -1,
/* 10 */       (1L <<  10 ) -1,
/* 11 */       (1L <<  11 ) -1,
/* 12 */       (1L <<  12 ) -1,
/* 13 */       (1L <<  13 ) -1,
/* 14 */       (1L <<  14 ) -1,
/* 15 */       (1L <<  15 ) -1,
/* 16 */       (1L <<  16 ) -1,
/* 17 */       (1L <<  17 ) -1,
/* 18 */       (1L <<  18 ) -1,
/* 19 */       (1L <<  19 ) -1,
/* 20 */       (1L <<  20 ) -1,
/* 21 */       (1L <<  21 ) -1,
/* 22 */       (1L <<  22 ) -1,
/* 23 */       (1L <<  23 ) -1,
/* 24 */       (1L <<  24 ) -1
};


static long dgethex(void)
/*
       description     convert the character string pointed to by
                       the output expression pointer to a long integer
       globals         oeptr, the output expression pointer
       return          the value
*/
{
       long rv = 0;

       while( *oeptr != '\0')
       {
               switch(*oeptr)
               {
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':
                       rv = (rv << 4) + ((*oeptr) - '0');
                       break;

               case 'a':
               case 'b':
               case 'c':
               case 'd':
               case 'e':
               case 'f':
                       rv = (rv << 4) + ((*oeptr) - 'a' + 10);
                       break;

               case 'A':
               case 'B':
               case 'C':
               case 'D':
               case 'E':
               case 'F':
                       rv = (rv << 4) + ((*oeptr) - 'A' + 10);
                       break;

               default:
                       return rv;
               }

               oeptr++;
       }

       return rv;
}


void outphase(void)
/*
       description     process all the lines in the intermediate file
       globals         the input line
                       the output expression pointer
                       line number
                       file name
                       the binary output array and counts
*/
{
       int firstchar;

       for(;;)
       {
               if((firstchar = fgetc(intermedf)) == EOF)
                       break;

               if(firstchar == 'L')
               {
                       if(listflag)
                               flushlisthex();

                       if( fgets(&lineLbuff[1], INBUFFSZ-1, intermedf)
                        == (char *)NULL)
                       {
               frp2error( "error or premature end of intermediate file");
                               break;
                       }

                       lineLflag = TRUE;
               }
               else
               {
                       finbuff[0] = firstchar;
                       if(fgets( &finbuff[1], INBUFFSZ-1, intermedf)
                        == (char *)NULL)
                       {
               frp2error("error or premature end of intermediate file");
                               break;
                       }
               }

               switch(firstchar)
               {
               case 'E': /* error */
                       if(listflag)
                       {
                               flushsourceline();
                               fputs(&finbuff[2], loutf);
                       }
                       else
                       {
                               fprintf(loutf, "%s - line %d - %s",
                                       currentfnm, linenumber, &finbuff[2]);
                       }
                       break;

               case 'L': /* listing */
                       linenumber++;
                       break;

               case 'C': /* comment / uncounted listing */
                       if(listflag)
                       {
                               char *stuff = strchr(finbuff, '\n');

                               if(stuff != NULL)
                                       *stuff = '\0';

                               fprintf(loutf,"%-*.*s",
                                SOURCEOFFSET, SOURCEOFFSET, &finbuff[2]);
                               if(lineLflag)
                               {
                                       fputs(&lineLbuff[2], loutf);
                                       lineLflag = FALSE;
                               }
                               else
                               {
                                       fputc('\n', loutf);
                               }
                       }
                       break;

               case 'P': /* location set */
                       oeptr = &finbuff[2];
                       currseg = dgethex();
                       oeptr++;
                       genlocctr = locctr = dgethex();
                       break;

               case 'D': /* data */
                       oeptr = &finbuff[2];
                       nextresult = 0;
                       resultloc = genlocctr;
                       outeval();
                       if(hexflag)
                               outhexblock();
                       if(listflag)
                               listhex();
                       break;

               case 'F': /* file start */
                       {
                               char *tp;
                               if( (tp = strchr(finbuff,'\n')) != (char *)NULL)
                                       *tp = '\0';
                               strncpy(currentfnm, &finbuff[2], 100);
                               currentfnm[99] = '\0';
                       }
                       lnumstk[currfstk++] = linenumber;
                       linenumber = 0;
                       break;

               case 'X': /* file resume */
                       {
                               char *tp;
                               if( (tp = strchr(finbuff,'\n')) != (char *)NULL)
                                       *tp = '\0';
                               strncpy(currentfnm, &finbuff[2], 100);
                               currentfnm[99] = '\0';
                       }
                       linenumber = lnumstk[--currfstk];
                       break;

               default:
                       frp2error("unknown intermediate file command");
                       break;
               }
       }

       if(hexflag)
               flushhex();

       if(listflag)
               flushlisthex();
}

void outeval(void)
/*
       description     convert the polish form character string in the
                       intermediate file 'D' line to binary values in the
                       output result array.
       globals         the output expression pointer
                       the output result array
*/
{
       register long etop = 0;

       register struct evstkel *estkm1p = &estk[0];

       while( *oeptr != '\0')
       {
               switch(*oeptr)
               {
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':
                       etop = (etop << 4) + ((*oeptr) - '0');
                       break;

               case 'a':
               case 'b':
               case 'c':
               case 'd':
               case 'e':
               case 'f':
                       etop = (etop << 4) + ((*oeptr) - 'a' + 10);
                       break;

               case 'A':
               case 'B':
               case 'C':
               case 'D':
               case 'E':
               case 'F':
                       etop = (etop << 4) + ((*oeptr) - 'A' + 10);
                       break;

#include "fraeuni.h"
#include "fraebin.h"
               case IFC_SYMB:
                       {
                               struct symel *tsy;

                               tsy = symbindex[etop];
                               if(tsy -> seg <= 0)
                               {
                                       frp2undef(tsy);
                                       etop = 0;
                               }
                               else
                               {
                                       if(tsy -> seg == SSG_EQU ||
                                          tsy -> seg == SSG_SET)
                                       {
                       frp2warn( "forward reference to SET/EQU symbol");
                                       }
                                       etop = tsy -> value;
                               }
                       }
                       break;

               case IFC_CURRLOC:
                       etop = genlocctr;
                       break;

               case IFC_PROGCTR:
                       etop = locctr;
                       break;

               case IFC_DUP:
                       if(estkm1p >= &estk[PESTKDEPTH-1])
                       {
                               frp2error("expression stack overflow");
                       }
                       else
                       {
                               (++estkm1p)->v = etop;
                       }
                       break;

               case IFC_LOAD:
                       if(estkm1p >= &estk[PESTKDEPTH-1])
                       {
                               frp2error("expression stack overflow");
                       }
                       else
                       {
                               (++estkm1p)->v = etop;
                       }
                       etop = 0;
                       break;

               case IFC_CLR:
                       etop = 0;
                       break;

               case IFC_CLRALL:
                       etop = 0;
                       estkm1p = &estk[0];
                       break;

               case IFC_POP:
                       etop = (estkm1p--)->v;
                       break;

               case IFC_TESTERR:
                       if(etop)
                       {
                               frp2error(
                       "expression fails validity test");
                       }
                       break;

               case IFC_SWIDTH:
                       if( etop > 0 && etop <= MAXIMPWID)
                       {
                               if( estkm1p->v < -(widthmask[etop-1]+1) ||
                                   estkm1p->v > widthmask[etop-1] )
                               {
                                       frp2error(
                               "expression exceeds available field width");
                               }
                               etop = ((estkm1p--)->v)  & widthmask[etop];
                       }
                       else
                               frp2error("unimplemented width");
                       break;

               case IFC_WIDTH:
                       if( etop > 0 && etop <= MAXIMPWID)
                       {
                               if( estkm1p->v < -(widthmask[etop-1]+1) ||
                                   estkm1p->v > widthmask[etop] )
                               {
                                       frp2error(
                               "expression exceeds available field width");
                               }
                               etop = ((estkm1p--)->v)  & widthmask[etop];
                       }
                       else
                               frp2error("unimplemented width");
                       break;

               case IFC_IWIDTH:
                       if( etop > 0 && etop <= MAXIMPWID)
                       {
                               if( estkm1p->v < 0 ||
                                   estkm1p->v > widthmask[etop] )
                               {
                                       frp2error(
                               "expression exceeds available field width");
                               }
                               etop = ((estkm1p--)->v)  & widthmask[etop];
                       }
                       else
                               frp2error("unimplemented width");
                       break;

               case IFC_EMU8:
                       if( etop >= -128 && etop <= 255)
                       {
                               outresult[nextresult++] = etop & 0xff;
                       }
                       else
                       {
                               outresult[nextresult++] = 0;
                               frp2error(
                       "expression exceeds available field width");
                       }
                       genlocctr ++;
                       etop = 0;
                       break;

               case IFC_EMS7:
                       if(etop >= -128 && etop <= 127)
                       {
                               outresult[nextresult++] = etop & 0xff;
                       }
                       else
                       {
                               outresult[nextresult++] = 0;
                               frp2error(
                       "expression exceeds available field width");
                       }
                       genlocctr ++;
                       etop = 0;
                       break;

               case IFC_EM16:
                       if(etop >= -32768L && etop <= 65535L)
                       {
                               outresult[nextresult++] = (etop >> 8) & 0xff;
                               outresult[nextresult++] = etop & 0xff;
                       }
                       else
                       {
                               outresult[nextresult++] = 0;
                               outresult[nextresult++] = 0;
                               frp2error(
                       "expression exceeds available field width");
                       }
                       genlocctr += 2;
                       etop = 0;
                       break;

               case IFC_EMBR16:
                       if(etop >= -32768L && etop <= 65535L)
                       {
                               outresult[nextresult++] = etop & 0xff;
                               outresult[nextresult++] = (etop >> 8) & 0xff;
                       }
                       else
                       {
                               outresult[nextresult++] = 0;
                               outresult[nextresult++] = 0;
                               frp2error(
                       "expression exceeds available field width");
                       }
                       genlocctr += 2;
                       etop = 0;
                       break;

               default:
                       break;
               }
               oeptr++;
       }
}

static long lhaddr, lhnextaddr;
static int lhnew, lhnext = 0;
static unsigned char listbuffhex[NUMHEXPERL];

void flushlisthex(void)
/*
       description     output the residue of the hexidecimal values for
                       the previous assembler statement.
       globals         the new hex list flag
*/
{
       listouthex();
       lhnew = TRUE;
}

void listhex(void)
/*
       description     buffer the output result to block the hexidecimal
                       listing on the output file to NUMHEXPERL bytes per
                       listing line.
       globals         The output result array and count
                       the hex line buffer and counts
*/
{
       register int cht;
       register long inhaddr = resultloc;

       if(lhnew)
       {
               lhaddr = lhnextaddr = resultloc;
               lhnew = FALSE;
       }

       for(cht = 0; cht < nextresult; cht++)
       {
               if(lhnextaddr != inhaddr
                || lhnext >= (lineLflag ? NUMHEXSOURCE : NUMHEXPERL ) )
               {
                       listouthex();
                       lhaddr = lhnextaddr = inhaddr;
               }
               listbuffhex[lhnext++] = outresult[cht];
               lhnextaddr ++;
               inhaddr ++;
       }
}

void listouthex(void)
/*
       description     print a line of hexidecimal on the listing
       globals         the hex listing buffer
*/
{
       register int cn;
       register int tc;

       if(lhnext > 0)
       {
               fputc(hexch((int)lhaddr>>12), loutf);
               fputc(hexch((int)lhaddr>>8), loutf);
               fputc(hexch((int)lhaddr>>4), loutf);
               fputc(hexch((int)lhaddr), loutf);
               fputc(' ', loutf);

               for(cn = 0; cn < lhnext; cn++)
               {
                       fputc(hexch((int)(tc = listbuffhex[cn])>>4), loutf);
                       fputc(hexch(tc), loutf);
                       fputc(' ', loutf);
               }

               if( ! lineLflag)
                       fputc('\n', loutf);
       }

       if(lineLflag)
       {
               if(lineLbuff[2] != '\n')
               {
                       switch(lhnext)
                       {
                       case 0:
                               fputs("\t\t\t",loutf);
                               break;
                       case 1:
                       case 2:
                       case 3:
                               fputs("\t\t",loutf);
                               break;
                       case 4:
                       case 5:
                       case 6:
                               fputs("\t",loutf);
                       default:
                               break;
                       }

                       fputs(&lineLbuff[2], loutf);
                       lineLflag = FALSE;
               }
               else
               {
                       fputc('\n', loutf);
               }
       }

       lhnext = 0;
}

#define INTELLEN 32

static long nextoutaddr, blockaddr;
static int hnextsub;
static char hlinebuff[INTELLEN];


void outhexblock(void)
/*
       description     buffer the output result to group adjacent output
                       data into longer lines.
       globals         the output result array
                       the intel hex line buffer
*/
{
       long inbuffaddr = resultloc;
       static int first = TRUE;

       int loopc;

       if(first)
       {
               nextoutaddr = blockaddr = resultloc;
               hnextsub = 0;
               first = FALSE;
       }

       for(loopc = 0; loopc < nextresult; loopc++)
       {
               if(nextoutaddr != inbuffaddr || hnextsub >= INTELLEN)
               {
                       intelout(0, blockaddr, hnextsub, hlinebuff);
                       blockaddr = nextoutaddr = inbuffaddr;
                       hnextsub = 0;
               }
               hlinebuff[hnextsub++] = outresult[loopc];
               nextoutaddr++;
               inbuffaddr++;
       }
}

void flushhex(void)
/*
       description     flush the intel hex line buffer at the end of
                       the second pass
       globals         the intel hex line buffer
*/
{
       if(hnextsub > 0)
               intelout(0, blockaddr, hnextsub, hlinebuff);
       if(endsymbol != SYMNULL && endsymbol -> seg > 0)
               intelout(1, endsymbol -> value, 0, "");
       else
               intelout(1, 0L, 0, "");

}


void intelout(int type, long addr, int count, char data[])
/*
       description     print a line of intel format hex data to the output
                       file
       parameters      see manual for record description
*/
{
       register int temp, checksum;

       fputc(':', hexoutf);
       fputc(hexch(count>>4),hexoutf);
       fputc(hexch(count),hexoutf);
       fputc(hexch((int)addr>>12),hexoutf);
       fputc(hexch((int)addr>>8),hexoutf);
       fputc(hexch((int)addr>>4),hexoutf);
       fputc(hexch((int)addr),hexoutf);
       fputc(hexch(type>>4),hexoutf);
       fputc(hexch(type),hexoutf);

       checksum = ((addr >> 8) & 0xff) + (addr & 0xff) + (count & 0xff);
       checksum += type & 0xff;

       for(temp = 0; temp < count; temp ++)
       {
               checksum += data[temp] & 0xff;
               fputc(hexch(data[temp] >> 4), hexoutf);
               fputc(hexch(data[temp]), hexoutf);
       }

       checksum = (-checksum) & 0xff;
       fputc(hexch(checksum>>4), hexoutf);
       fputc(hexch(checksum), hexoutf);
       fputc('\n',hexoutf);
}


void frp2undef(struct symel * symp)
/*
       description     second pass - print undefined symbol error message on
                       the output listing device.  If the the listing flag
                       is false, the output device is the standard output, and
                       the message format is different.
       parameters      a pointer to a symbol table element
       globals         the count of errors
*/
{
       if(listflag)
       {
               flushsourceline();
               fprintf(loutf," ERROR -  undefined symbol %s\n", symp ->symstr);
       }
       else
               fprintf(loutf, "%s - line %d - ERROR - undefined symbol  %s\n",
                       currentfnm, linenumber, symp -> symstr);
       errorcnt++;
}

void frp2warn(char * str)
/*
       description     second pass - print a warning message on the listing
                       file, varying the format for console messages.
       parameters      the message
       globals         the count of warnings
*/
{
       if(listflag)
       {
               flushsourceline();
               fprintf(loutf, " WARNING - %s\n", str);
       }
       else
               fprintf(loutf, "%s - line %d - WARNING - %s\n",
                       currentfnm, linenumber, str);
       warncnt++;
}


void frp2error(char * str)
/*
       description     second pass - print a message on the listing file
       parameters      message
       globals         count of errors
*/
{
       if(listflag)
       {
               flushsourceline();
               fprintf(loutf, " ERROR - %s\n", str);
       }
       else
               fprintf(loutf, "%s - line %d - ERROR - %s\n",
                       currentfnm, linenumber, str);
       errorcnt++;
}

void flushsourceline(void)
/*
       description     flush listing line buffer before an error for
                       that line is printed
*/
{
       if(listflag && lineLflag)
       {
               fputs("\t\t\t", loutf);
               fputs(&lineLbuff[2], loutf);
               lineLflag = FALSE;
       }
}
