/* aslist.c */

/*
 *  Copyright (C) 1989-2009  Alan R. Baldwin
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 *
 *   With enhancements from
 *
 *      John L. Hartman (JLH)
 *      jhartman at compuserve dot com
*/

#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#include "asxxxx.h"

/*)Module       aslist.c
 *
 *      The module aslist.c contains all the functions used
 *      to generate the assembler list and symbol output files.
 *
 *      aslist.c contains the following functions:
 *              VOID    list()
 *              VOID    list1()
 *              VOID    list2()
 *              VOID    slew()
 *              VOID    lstsym()
 *
 *      The module aslist.c contains no local/static variables
 */

/*)Function     VOID    list()
 *
 *      The function list() generates the listing output
 *      which includes the input source, line numbers,
 *      and generated code.  Numerical output may be selected
 *      as hexadecimal, decimal, or octal.
 * 
 *      local variables:
 *              int *   wp              pointer to the assembled data bytes
 *              int *   wpt             pointer to the data byte mode
 *              int     nb              computed number of assembled bytes
 *
 *      global variables:
 *              int     cb[]            array of assembler output values
 *              int     cbt[]           array of assembler relocation types
 *                                      describing the data in cb[]
 *              int *   cp              pointer to assembler output array cb[]
 *              int *   cpt             pointer to assembler relocation type
 *                                      output array cbt[]
 *              char    eb[]            array of generated error codes
 *              char *  ep              pointer into error list
 *                                      array eb[]
 *              char    ib[]            assembler-source text line
 *              FILE *  lfp             list output file handle
 *              int     line            current assembler source line number
 *              int     lmode           listing mode
 *              int     xflag           -x, listing radix flag
 *
 *      functions called:
 *              int     fprintf()       c_library
 *              VOID    list1()         aslist.c
 *              int     putc()          c_library
 *              VOID    slew()          asslist.c
 *
 *      side effects:
 *              Listing or symbol output updated.
 */

VOID
list(void)
{
        char *wp;
        int *wpt;
        int nb;

        if (lfp == NULL || lmode == NLIST)
                return;

        /*
         * Get Correct Line Number
         */
        if (incfil >= 0) {
                line = incline[incfil];
                if (line == 0) {
                        if (incfil > 0) {
                                line = incline[incfil-1];
                        } else {
                                line = srcline[cfile];
                        }
                }
        } else {
                line = srcline[cfile];
        }

        /*
         * Move to next line.
         */
        slew(lfp, pflag);

        /*
         * Output a maximum of NERR error codes with listing.
         */
        while (ep < &eb[NERR])
                *ep++ = ' ';
        fprintf(lfp, "%.2s", eb);

        /*
         * Source listing only option.
         */
        if (lmode == SLIST) {
                fprintf(lfp, "%24s%5u %s\n", "", line, ib);
                return;
        }
        if (lmode == ALIST) {
                outchk(ASXXXX_HUGE,ASXXXX_HUGE);
        }

        /*
         * HEX output Option.
         */
        if (xflag == 0) {               /* HEX */
                /*
                 * Equate only
                 */
                if (lmode == ELIST) {
                        fprintf(lfp, "%18s%04X", "", laddr);
                        fprintf(lfp, "  %5u %s\n", line, ib);
                        return;
                }

                /*
                 * Address (with allocation)
                 */
                fprintf(lfp, " %04X", laddr);
                if (lmode == ALIST || lmode == BLIST) {
                        fprintf(lfp, "%19s%5u %s\n", "", line, ib);
                        outdot();
                        return;
                }
                wp = cb;
                wpt = cbt;
                nb = (int) (cp - cb);

                /*
                 * First line of output for this source line with data.
                 */
                list1(wp, wpt, nb, 1);
                fprintf(lfp, " %5u %s\n", line, ib);

                /*
                 * Subsequent lines of output if more data.
                 */
                while ((nb -= 6) > 0) {
                        wp += 6;
                        wpt += 6;
                        slew(lfp, 0);
                        fprintf(lfp, "%7s", "");
                        list1(wp, wpt, nb, 0);
                        putc('\n', lfp);
                }
        } else
        /*
         * OCTAL output Option.
         */
        if (xflag == 1) {               /* OCTAL */
                /*
                 * Equate only
                 */
                if (lmode == ELIST) {
                        fprintf(lfp, "%16s%06o", "", laddr);
                        fprintf(lfp, "  %5u %s\n", line, ib);
                        return;
                }

                /*
                 * Address (with allocation)
                 */
                fprintf(lfp, " %06o", laddr);
                if (lmode == ALIST || lmode == BLIST) {
                        fprintf(lfp, "%17s%5u %s\n", "", line, ib);
                        outdot();
                        return;
                }
                wp = cb;
                wpt = cbt;
                nb = (int) (cp - cb);

                /*
                 * First line of output for this source line with data.
                 */
                list1(wp, wpt, nb, 1);
                fprintf(lfp, " %5u %s\n", line, ib);

                /*
                 * Subsequent lines of output if more data.
                 */
                while ((nb -= 4) > 0) {
                        wp += 4;
                        wpt += 4;
                        slew(lfp, 0);
                        fprintf(lfp, "%9s", "");
                        list1(wp, wpt, nb, 0);
                        putc('\n', lfp);
                }
        } else
        /*
         * DECIMAL output Option.
         */
        if (xflag == 2) {               /* DECIMAL */
                /*
                 * Equate only
                 */
                if (lmode == ELIST) {
                        fprintf(lfp, "%16s%05u", "", laddr);
                        fprintf(lfp, "   %5u %s\n", line, ib);
                        return;
                }

                /*
                 * Address (with allocation)
                 */
                fprintf(lfp, "  %05u", laddr);
                if (lmode == ALIST || lmode == BLIST) {
                        fprintf(lfp, "%17s%5u %s\n", "", line, ib);
                        outdot();
                        return;
                }
                wp = cb;
                wpt = cbt;
                nb = (int) (cp - cb);

                /*
                 * First line of output for this source line with data.
                 */
                list1(wp, wpt, nb, 1);
                fprintf(lfp, " %5u %s\n", line, ib);

                /*
                 * Subsequent lines of output if more data.
                 */
                while ((nb -= 4) > 0) {
                        wp += 4;
                        wpt += 4;
                        slew(lfp, 0);
                        fprintf(lfp, "%9s", "");
                        list1(wp, wpt, nb, 0);
                        putc('\n', lfp);
                }
        }
}

/*)Function     VOID    list1(wp, wpt, nw, f)
 *
 *              int     f               fill blank fields (1)
 *              int     nb              number of data bytes
 *              int *   wp              pointer to data bytes
 *              int *   wpt             pointer to data byte mode
 *
 *      local variables:
 *              int     i               loop counter
 *
 *      global variables:
 *              int     xflag           -x, listing radix flag
 *
 *      functions called:
 *              VOID    list2()         asslist.c
 *              int     fprintf()       c_library
 *
 *      side effects:
 *              Data formatted and output to listing.
 */

VOID
list1(char *wp, int *wpt, int nb, int f)
{
        int i;

        /*
         * HEX output Option.
         */
        if (xflag == 0) {               /* HEX */
                /*
                 * Bound number of words to HEX maximum per line.
                 */
                if (nb > 6)
                        nb = 6;

                /*
                 * Output bytes.
                 */
                for (i=0; i<nb; ++i) {
                        list2(*wpt++);
                        fprintf(lfp, "%02X", (*wp++)&0377);
                }

                /*
                 * Output blanks if required.
                 */
                if (f) {
                        while (i < 6) {
                                fprintf(lfp, "   ");
                                ++i;
                        }
                }
        } else
        /*
         * OCTAL output Option.
         */
        if (xflag == 1) {               /* OCTAL */
                /*
                 * Bound number of words to OCTAL maximum per line.
                 */
                if (nb > 4)
                        nb = 4;

                /*
                 * Output bytes.
                 */
                for (i=0; i<nb; ++i) {
                        list2(*wpt++);
                        fprintf(lfp, "%03o", (*wp++)&0377);
                }

                /*
                 * Output blanks if required.
                 */
                if (f) {
                        while (i < 4) {
                                fprintf(lfp, "    ");
                                ++i;
                        }
                }
        } else
        /*
         * DECIMAL output Option.
         */
        if (xflag == 2) {               /* DECIMAL */
                /*
                 * Bound number of words to DECIMAL maximum per line.
                 */
                if (nb > 4)
                        nb = 4;

                /*
                 * Output bytes.
                 */
                for (i=0; i<nb; ++i) {
                        list2(*wpt++);
                        fprintf(lfp, "%03u", (*wp++)&0377);
                }

                /*
                 * Output blanks if required.
                 */
                if (f) {
                        while (i < 4) {
                                fprintf(lfp, "    ");
                                ++i;
                        }
                }
        }
}

/*)Function     VOID    list2(wpt)
 *
 *              int *   wpt             pointer to relocation mode
 *
 *      The function list2() outputs the selected
 *      relocation flag as specified by fflag.
 *
 *      local variables:
 *              int     c               relocation flag character
 *              int     t               relocation mode
 *
 *      global variables:
 *              int     fflag           -f(f), relocations flagged flag
 *
 *      functions called:
 *              int     putc()          c_library
 *
 *      side effects:
 *              Relocation flag output to listing file.
 */

VOID
list2(int t)
{
        int c;

        c = ' ';

        /*
         * Designate a relocatable word by `.
         */
        if (fflag == 1) {
                if (t & R_RELOC) {
                        c = '`';
                }
        } else
        /*
         * Designate a relocatable word by its mode:
         *      page0 or paged          *
         *      unsigned                u (v)
         *      operand offset          p (q)
         *      relocatable symbol      r (s)
         */
        if (fflag >= 2) {
                if (t & R_RELOC) {
                        if (t & (R3_PAG0|R3_PAG)) {
                                c = '*';
                        } else if (t & R3_USGN) {
                                c = 'u';
                        } else if (t & R3_PCR) {
                                c = 'p';
                        } else {
                                c = 'r';
                        }
                        if (t & R_HIGH) c += 1;
                }
        }

        /*
         * Output the selected mode.
         */
        putc(c, lfp);
}

/*)Function     VOID    slew(fp, flag)
 *
 *              FILE *  fp              file handle for listing
 *              int     flag            enable pagination
 *
 *      The function slew() increments the page line count.
 *      If the page overflows and pagination is enabled:
 *              1)      put out a page skip,
 *              2)      a title,
 *              3)      a subtitle,
 *              4)      and reset the line count.
 *
 *      local variables:
 *              none
 *
 *      global variables:
 *              char    cpu[]           cpu type string
 *              int     lop             current line number on page
 *              int     page            current page number
 *              char    stb[]           Subtitle string buffer
 *              char    tb[]            Title string buffer
 *
 *      functions called:
 *              int     fprintf()       c_library
 *
 *      side effects:
 *              Increments page line counter, on overflow
 *              a new page header is output to the listing file.
 */

VOID
slew(FILE *fp, int flag)
{
        if ((lop++ >= NLPP) && flag) {
                fprintf(fp, "\fASxxxx Assembler %s  (%s), page %u.\n",
                        VERSION, cpu, ++page);
                fprintf(fp, "%s\n", tb);
                fprintf(fp, "%s\n\n", stb);
                lop = 5;
        }
}

/* sdas specific */
/* Used for qsort call in lstsym */
static int _cmpSym(const void *p1, const void *p2)
{
    struct sym **s1 = (struct sym **)(p1);
    struct sym **s2 = (struct sym **)(p2);
    return strcmp((*s1)->s_id,(*s2)->s_id);
}
/* end sdas specific */

/*)Function     VOID    lstsym(fp)
 *
 *              FILE *  fp              file handle for output
 *
 *      The function lstsym() outputs alphabetically
 *      sorted symbol and area tables.
 *
 *      local variables:
 *              int     c               temporary
 *              int     i               loop counter
 *              int     j               temporary
 *              int     k               temporary
 *              char *  ptr             pointer to an id string
 *              int     nmsym           number of symbols
 *              int     narea           number of areas
 *              sym *   sp              pointer to symbol structure
 *              sym **  p               pointer to an array of
 *                                      pointers to symbol structures
 *              area *  ap              pointer to an area structure
 *
 *      global variables:
 *              area *  areap           pointer to an area structure
 *              char    aretbl[]        string "Area Table"
 *              sym     dot             defined as sym[0]
 *              char    stb[]           Subtitle string buffer
 *              sym * symhash[]         array of pointers to NHASH
 *                                      linked symbol lists
 *              char    symtbl[]        string "Symbol Table"
 *              FILE *  tfp             symbol table output file handle
 *              int     wflag           -w, wide listing flag
 *              int     xflag           -x, listing radix flag
 *
 *      functions called:
 *              int     fprintf()       c_library
 *              int     putc()          c_library
 *              VOID    slew()          aslist.c
 *              int     strcmp()        c_library
 *              char *  strcpy()        c_library
 *
 *      side effects:
 *              Symbol and area tables output.
 */

VOID
lstsym(FILE *fp)
{
        int i, j, k;
        char *ptr;
        int nmsym, narea;
        struct sym *sp;
        struct sym **p;
        struct area *ap;

        /*
         * Symbol Table Header
         */
        strcpy(stb, &symtbl[0]);
        lop = NLPP;
        if (fp == tfp)
                page = 0;
        slew(fp, 1);

        /*
         * Find number of symbols
         */
        nmsym = 0;
        for (i=0; i<NHASH; i++) {
                sp = symhash[i];
                while (sp) {
                        if (sp != &dot)
                                ++nmsym;
                        sp = sp->s_sp;
                }
        }
        if (nmsym == 0)
                goto atable;

        /*
         * Allocate space for an array of pointers to symbols
         * and load array.
         */
        if ((p = (struct sym **) malloc(sizeof((struct sym *) sp)*nmsym))
                == NULL) {
                fprintf(fp, "Insufficient space to build Symbol Table.\n");
                return;
        }
        nmsym = 0;
        for (i=0; i<NHASH; i++) {
                sp = symhash[i];
                while (sp) {
                        if (sp != &dot)
                                p[nmsym++] = sp;
                        sp = sp->s_sp;
                }
        }

/* sdas specific */
#if 0
        /* BUBBLE SORT?? WTF??? */
        /*
         * Bubble Sort on Symbol Table Array
         */
        j = 1;
        c = nmsym - 1;
        while (j) {
                j = 0;
                for (i=0; i<c; ++i) {
                        if (strcmp(&p[i]->s_id[0],&p[i+1]->s_id[0]) > 0) {
                                j = 1;
                                sp = p[i+1];
                                p[i+1] = p[i];
                                p[i] = sp;
                        }
                }
        }
#else

        qsort(p, nmsym, sizeof(struct sym *), _cmpSym);
#endif  
/* end sdas specific */

        /*
         * Symbol Table Output
         */
        for (i=0; i<nmsym;) {
                sp = p[i];
                if (sp->s_area) {
                        j = sp->s_area->a_ref;
                        if (xflag == 0) {
                                fprintf(fp, " %2X ", j);
                        } else
                        if (xflag == 1) {
                                fprintf(fp, "%3o ", j);
                        } else
                        if (xflag == 2) {
                                fprintf(fp, "%3u ", j);
                        }
                } else {
                        fprintf(fp, "    ");
                }
                ptr = &sp->s_id[0];
                if (wflag) {
                        fprintf(fp, "%-60.60s", ptr );  /* JLH */
                } else {
                        fprintf(fp, "%-8.8s", ptr);
                }
                if (sp->s_flag & S_ASG) {
                        putc('=', fp);
                } else {
                        putc(' ', fp);
                }
                if (sp->s_type == S_NEW) {
                        if (xflag == 0) {
                                fprintf(fp, "  **** ");
                        } else
                        if (xflag == 1) {
                                fprintf(fp, "****** ");
                        } else
                        if (xflag == 2) {
                                fprintf(fp, " ***** ");
                        }
                } else {
                        j = sp->s_addr;
                        if (xflag == 0) {
                                fprintf(fp, "  %04X ", j);
                        } else
                        if (xflag == 1) {
                                fprintf(fp, "%06o ", j);
                        } else
                        if (xflag == 2) {
                                fprintf(fp, " %05u ", j);
                        }
                }
                j = 0;
                if (sp->s_flag & S_GBL) {
                        putc('G', fp);
                        ++j;
                }
                if (sp->s_area != NULL) {
                        putc('R', fp);
                        ++j;
                }
                if (sp->s_type == S_NEW) {
                        putc('X', fp);
                        ++j;
                }
                if (wflag) {
                        putc('\n', fp);         /* JLH */
                        slew(fp, 0);
                        ++i;
                } else {
                        if (++i % 3 == 0) {
                                putc('\n', fp);
                                slew(fp, pflag);
                        } else
                        if (i < nmsym) {
                                while (j++ < 4)
                                        putc(' ', fp);
                                fprintf(fp, "| ");
                        }
                }
        }
        putc('\n', fp);

        /*
         * Area Table Header
         */

atable:
        strcpy(stb, &aretbl[0]);
        lop = NLPP;
        slew(fp, 1);

        /*
         * Area Table Output
         */
        narea = 0;
        ap = areap;
        while (ap) {
                ++narea;
                ap = ap->a_ap;
        }
        for (i=0; i<narea; ++i) {
                ap = areap;
                for (j=i+1; j<narea; ++j)
                        ap = ap->a_ap;
                j = ap->a_ref;
                if (xflag == 0) {
                        fprintf(fp, "  %2X ", j);
                } else
                if (xflag == 1) {
                        fprintf(fp, " %3o ", j);
                } else
                if (xflag == 2) {
                        fprintf(fp, " %3u ", j);
                }
                ptr = &ap->a_id[0];
                if (wflag) {
                        fprintf(fp, "%-40.40s", ptr );
                } else {
                        fprintf(fp, "%-8.8s", ptr);
                }

                j = ap->a_size;
                k = ap->a_flag;
                if (xflag==0) {
                        fprintf(fp, "   size %4X   flags %X\n", j, k);
                } else
                if (xflag==1) {
                        fprintf(fp, "   size %6o   flags %o\n", j, k);
                } else
                if (xflag==2) {
                        fprintf(fp, "   size %5u   flags %u\n", j, k);
                }
        }               
}
