#include <stdio.h>
#include <ctype.h>
#include "assm.d1"
#include "assm.d2"

#define CPMEOF EOF

/*
 *  Two changes to version 1.4 have been made to "port" as6502 to CP/M(tm).
 *  A "tolower()" function call was add to the command line processing
 *  code to (re)map the command line arguments to lower case (CP/M
 *  converts all command line arguments to upper case).  The readline()
 *  function has code added to "ignore" the '\r' character (CP/M includes
 *  the \r character along with \n).
 *
 *  Also, the ability to process multiple files on the command line has been
 *  added.  Now one can do, for example:
 *
 *  as6502 -nisvo header.file source.file data.file ...
 *
 *    George V. Wilder
 *  IX 1A-360 x1937
 *  ihuxp!gvw1
 *
 *  source modified to output atari object file to 6502.obj with -a
 *  flag.  also prints usage with -h flag or incorrect usage.
 *  1/11/93  Todd Greenfield tgreen@iastate.edu
 *
 */

main(argc, argv)
   int  argc;
   char *argv[];
{
    char    c;
    int cnt;
    int i;
    int ac;
    char    **av;

    while (--argc > 0 && (*++argv)[0] == '-') {
        for (i = 1; (c = tolower((*argv)[i])) != '\0'; i++) {
            if (c == 'd')       /* debug flag */
                dflag++;
            if (c == 'i')       /* ignore .nlst flag */
                iflag++;
            if (c == 'l')       /* disable listing flag */
                lflag--;
            if (c == 'n')       /* normal/split address mode */
                nflag++;
            if (c == 'o')       /* object output flag */
                oflag++;
            if (c == 'a')       /* atari object output flag */
                aflag++;
            if (c == 's')       /* list symbol table flag */
                sflag++;
            if (c == 'v')       /* print assembler version */
                fprintf(stdout, "as6502 - version 4.1b - 11/22/84 - JHV [gvw]\n");
            if (c == 'h')       /* print usage */
                print_help();
        }
    }
    ac = argc;
    av = argv;
    pass = FIRST_PASS;
    for (i = 0; i < 128; i++)
        hash_tbl[i] = -1;
    errcnt = loccnt = slnum = 0;
    while (pass != DONE) {
        initialize(ac, av, argc);
        if(pass == LAST_PASS && ac == argc)
            errcnt = loccnt = slnum = 0;
        while (readline() != -1)
            assemble();
        if (errcnt != 0) {
            pass = DONE;
            fprintf(stderr, "Terminated with error counter = %d\n", errcnt);
        }
        switch (pass) {
        case FIRST_PASS:
            --ac;
            ++av;
            if(ac == 0) {
                pass = LAST_PASS;
                if (lflag == 0)
                    lflag++;
                ac = argc;
                av = argv;
            }
            break;
        case LAST_PASS:
            --ac;
            ++av;
            if(ac == 0) {
                pass = DONE;
                if (sflag != 0)
                    stprnt();
            }
        }
        wrapup();
        if ((dflag != 0) && (pass == LAST_PASS)) {
            fprintf(stdout, "nxt_free = %d\n", nxt_free);
            cnt = 0;
            for (i = 0; i < 128; i++)
                if (hash_tbl[i] == -1)
                    cnt++;
            fprintf(stdout, "%d unused hash table pointers out of 128\n", cnt);
        }
    }
    return(0);
}

/*****************************************************************************/

/* initialize opens files */

initialize(ac, av, argc)
   int  ac;
   char *av[];
   int  argc;
{

    if (ac == 0) {
        fprintf(stderr, "Invalid argument count (%d).\n", argc);
        print_help();
        exit(1);
    }
    if ((iptr = fopen(*av, "r")) == NULL) {
        fprintf(stderr, "Open error for file '%s'.\n", *av);
        exit(1);
    }
    if ((pass == LAST_PASS) && (oflag != 0) && ac == argc) {
        if ((optr = fopen("6502.out", "w")) == NULL) {
            fprintf(stderr, "Create error for object file 6502.out.\n");
            exit(1);
        }
    }
    if ((pass == LAST_PASS) && (aflag != 0) && ac == argc) {
        if ((aptr = fopen("6502.obj", "wb")) == NULL) {
            fprintf(stderr, "Create error for atari object file 6502.obj.\n");
            exit(1);
        }
        fputc(0xff,aptr);
        fputc(0xff,aptr);
    }
}

/* readline reads and formats an input line */

int field[] =
{
    SFIELD,
    SFIELD + 8,
    SFIELD + 14,
    SFIELD + 23,
    SFIELD + 43,
    SFIELD + 75
};

readline()
{
    int i;      /* pointer into prlnbuf */
    int j;      /* pointer to current field start   */
    int ch;     /* current character        */
    int cmnt;       /* comment line flag    */
    int spcnt;      /* consecutive space counter    */
    int string;     /* ASCII string flag    */
    int temp1;      /* temp used for line number conversion */

    temp1 = ++slnum;
    for (i = 0; i < LAST_CH_POS; i++)
        prlnbuf[i] = ' ';
    i = 3;
    while (temp1 != 0) {    /* put source line number into prlnbuf */
        prlnbuf[i--] = temp1 % 10 + '0';
        temp1 /= 10;
    }
    i = SFIELD;
    cmnt = spcnt = string = 0;
    j = 1;
    while ((ch = getc(iptr)) != '\n') {
        if(ch == '\r')
            continue;
        prlnbuf[i++] = ch;
        if ((ch == ' ') && (string == 0)) {
            if (spcnt != 0)
                --i;
            else if (cmnt == 0) {
                ++spcnt;
                if (i < field[j])
                    i = field[j];
                if (++j > 3) {
                    spcnt = 0;
                    ++cmnt;
                }
            }
        }
        else if (ch == '\t') {
            prlnbuf[i - 1] = ' ';
            spcnt = 0;
            if (cmnt == 0) {
                if (i < field[j])
                    i = field[j];
                if (++j > 3)
                    ++cmnt;
            }
            else i = (i + 8) & 0x78;
        }
        else if ((ch == ';') && (string == 0)) {
            spcnt = 0;
            if (i == SFIELD + 1)
                ++cmnt;
            else if (prlnbuf[i - 2] != '\'') {
                ++cmnt;
                prlnbuf[i-1] = ' ';
                if (i < field[3])
                    i = field[3];
                prlnbuf[i++] = ';';
            }
        }
        else if (ch == EOF || ch == CPMEOF)
            return(-1);
        else {
            if ((ch == '"') && (cmnt == 0))
                string = string ^ 1;
            spcnt = 0;
            if (i >= LAST_CH_POS - 1)
                --i;
        }
    }
    prlnbuf[i] = 0;
    return(0);
}

/*
 * wrapup() closes the source file
 */

wrapup()
{

    fclose(iptr);
    if ((pass == DONE) && (oflag != 0)) {
        fputc('\n', optr);
        fclose(optr);
    }
    if ((pass == DONE) && (aflag != 0)) {
        newlc_obj(0,1);  /* write out end addr of last block */
        fclose(aptr);
    }
    return;
}

/* symbol table print
 */

stprnt()
{
    int i;
    int j;
    int k;

    fputc('\014', stdout);
    fputc('\n', stdout);
    i = 0;
    while   ((j = symtab[i++]) != 0) {
        for (k = j; k > 0; k--)
            fputc(symtab[i++], stdout);
        for (k = 20 - j; k > 0; k--)
            fputc(' ', stdout);
        ++i;
        j = (symtab[i++] & 0xff);
        j += (symtab[i++] << 8);
        hexcon(4, j);
        fprintf(stdout, "\t%c%c:%c%c\t%c%c%c%c\n",
            hex[3], hex[4], hex[1], hex[2],
            hex[1], hex[2], hex[3], hex[4]);
        i += 2;
    }
}

print_help()
{

     printf("\n\
Usage:\n\
     as6502 {-ilnsv} {-h hexfile} {-o objfile} <source files descriptions>\n\
\n\
Options:\n\
\n\
     -i   ignore any .nlst pseudo operations\n\
     -l   list errors only\n\
     -n   print addresses as <high byte><low byte>,\n\
          rather than as <low byte>:<high byte>.\n\
     -a   generate atari DOS 2.0 object output in file 6502.obj\n\
     -o   generate ASCII hex object output in file 6502.out,\n\
          format is\n\
               ;<address lo><address hi><data>\n\
     -s   print symbol table at end of listing\n\
     -v   print version information\n\
     -h   print usage information (this message)\n\
");

     return 0;
}
