root/usr/src/uts/common/io/audio/drv/audioemu10k/dsp/asm10k.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Assembler for Emu10k1
 */
/*
 * Copyright (C) 4Front Technologies 1996-2008.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/param.h>

#define MAX_GPR 256
#define MAX_GPR_PARMS   60
#define MAX_CONST_PARMS 128
#define GPR_NAME_SIZE 32

typedef struct {
        char name[GPR_NAME_SIZE];
        unsigned int num;
        int type;
        int def;
} gpr_t;

typedef struct {
        unsigned int gpr;
        unsigned int value;
} const_t;

typedef struct {
        unsigned int ngpr;

        gpr_t gpr[MAX_GPR_PARMS];
} gpr_info;

typedef struct {
        unsigned int nconst;

        const_t consts[MAX_CONST_PARMS];
} const_info;

typedef struct {
        unsigned int code[1024];
        gpr_info parms;
        const_info consts;
        int     ninit;
        struct {
                uint32_t        gpr;
                uint32_t        value;
                char            name[GPR_NAME_SIZE];
        } init[MAX_GPR];
} emu10k1_file;

#define MAX_NAME        64
#define MAX_SYMBOLS     1024

static int parms_only = 0;
static int is_audigy = 0;
static int verbose = 0;

static int gpr_base = 0x100;
static int input_base = 0x10;
static int output_base = 0x20;

static char *progname;

typedef struct {
        char name[MAX_NAME];
        int type;
#define SY_DUMMY        0
#define SY_GPR          1
#define SY_INPUT        2
#define SY_OUTPUT       3
#define SY_CONST        4
#define SY_FX           5
#define SY_ACCUM        6
#define SY_PARM         7
        int arg;
} sym_t;

typedef struct {
        char *name;
        int opcode;
} instruction_t;

static char remarks[2048] = "";
static char *banner =
        "/*\n"
        " * Note: This file was automatically generated by %s\n"
        " * on %s.\n"
        " */\n";

/*
 * Instructions.  Each instruction takes 4 arguments, R, A, X, and Y.
 */
static instruction_t instructions[] = {
        { "MACS",       0x0},   /* R = A + (X * Y >> 31); saturation */
        { "MACS1",      0x1},   /* R = A + (-X * Y >> 31); saturation */
        { "MACW",       0x2},   /* R = A + (X * Y >> 31); wraparound */
        { "MACW1",      0x3},   /* R = A + (-X * Y >> 31); wraparound */
        { "MACINTS",    0x4},   /* R = A + (X * Y); saturation */
        { "MACINTW",    0x5},   /* R = A + (X * Y); wraparound */
        { "SUM",        0x6},   /* R = A + X + Y; saturation */
        { "ACC3",       0x6},   /* R = A + X + Y; saturation */
        { "MACMV",      0x7},   /* R = A, acc += X * Y >> 31 */
        { "ANDXOR",     0x8},   /* R = (A & X) ^ Y */
        { "TSTNEG",     0x9},   /* R = (A >= Y) ? X : ~X */
        { "LIMIT",      0xa},   /* R = (A >= Y) ? X : Y */
        { "LIMIT1",     0xb},   /* R = (A < Y) ? X : Y */
        { "LOG",        0xc},   /* R = ... (log?) */
        { "EXP",        0xd},   /* R = ... (exp?) */
        { "INTERP",     0xe},   /* R = A + (X * (Y - A) >> 31) */
        { "SKIP",       0xf},   /* R, CCR, CC_TEST, COUNT */
        { NULL, 0}
};

#define CHECK_COUNT(tokens, cnt, mincnt, maxcnt)                        \
        if (cnt < mincnt) {                                             \
                error("Too few parameters for '%s' (have %d, min %d)",  \
                    tokens[0], cnt - 1, mincnt - 1);                    \
                return;                                                 \
        }                                                               \
        if (cnt > maxcnt) {                                             \
                error("Too many parameters for '%s' (have %d, max %d)", \
                    tokens[0], cnt - 1, maxcnt - 1);                    \
                return;                                                 \
        }

static sym_t symtab[MAX_SYMBOLS];
static int nsyms = 0;

static int lineno = 0, errors = 0;
static emu10k1_file fle;
static int pc;

static int ngpr = 0;
static char *infile;

static int
getaline(FILE *input, char **tokens)
{
        char *s, *ls;
        static char *stmt = NULL, *lasts = NULL;
        static char line[4096];
        int cnt, tokcnt;

        for (;;) {

                if (stmt == NULL) {
                        if (fgets(line, sizeof (line), input) == NULL)
                                return (-1);
                        lineno++;

                        /*
                         * Special handling for .' comments.  We use
                         * .' as a keyword to ensure that entire
                         * comment makes it through the C preprocessor
                         * unmolested.  We also need to make sure *we*
                         * don't molest it either.  The comment will
                         * be exported to any resulting header,
                         * allowing us to pass through copyright and
                         * other information from the source file to
                         * the resulting header.
                         */
                        s = line;
                        s += strspn(s, " \t");
                        if ((strncmp(s, ".'", 2) == 0) &&
                            (strchr(" \t\n", s[2]) != NULL)) {
                                /* chop off trailing new line */
                                (void) strtok(line, "\n");
                                tokens[0] = s;
                                s += 2;
                                s += strspn(s, " \t");
                                if ((s[0] == '\'') &&
                                    (s[strlen(s) - 1] == '\'')) {
                                        s[strlen(s) - 1] = 0;
                                        s++;
                                }
                                tokens[1] = s;
                                tokens[0][2] = 0;
                                tokens[2] = NULL;
                                stmt = NULL;
                                return (strlen(tokens[1]) ? 2 : 1);
                        }

                        /* strip off any C++ style comments that CPP missed */
                        if ((s = strstr(line, "//")) != NULL) {
                                *s = '\0';
                        }
                        stmt = strtok_r(line, ";\n", &lasts);
                } else {
                        stmt = strtok_r(NULL, ";\n", &lasts);
                }

                if (stmt != NULL) {
                        break;
                }
        }

        /*
         * Ok, we have a statement, lets tokenize it.  For
         * simplicities sake we convert "OPCODE(arg1, arg2)" into
         * "OPCODE arg1 arg2".  This means that commas and parens are
         * treated as whitespace.  This can lead to some really messed
         * up syntaxes that get assembled properly (such as nested
         * calls, empty arguments, etc.)  Hopefully people don't abuse
         * this.
         */
        ls = NULL;
        s = strtok_r(stmt, " \t\n(),", &ls);
        cnt = 0;
        tokcnt = 0;
        while (cnt < 10) {
                tokens[cnt++] = s;
                if (s != NULL) {
                        tokcnt++;
                        s = strtok_r(NULL, " \t\n(),", &ls);
                }
        }
        return (tokcnt);
}

static void
error(char *msg, ...)
{
        va_list va;
        char msgbuf[1024];

        va_start(va, msg);
        (void) vsnprintf(msgbuf, sizeof (msgbuf), msg, va);
        va_end(va);

        (void) fprintf(stderr, "Error: %s on line %d of %s\n", msgbuf, lineno,
            infile);
        errors++;
}

static sym_t *
find_symbol(char *name)
{
        int i;

        for (i = 0; i < nsyms; i++)
                if (strcmp(symtab[i].name, name) == 0) {
                        return (&symtab[i]);
                }

        return (NULL);
}

static void
add_symbol(char *name, int type, int arg)
{
        sym_t *sym;

        if (nsyms >= MAX_SYMBOLS) {
                error("Symbol table full");
                exit(-1);
        }

        if (find_symbol(name) != NULL) {
                error("Dublicate symbol '%s'", name);
                return;
        }

        if (strlen(name) >= MAX_NAME) {
                error("Symbol name '%s' too long", name);
                exit(-1);
        }

        sym = &symtab[nsyms++];

        (void) strcpy(sym->name, name);
        sym->type = type;
        sym->arg = arg;
}

static void
add_init(uint32_t gpr, uint32_t val, const char *name)
{
        int     n;

        n = fle.ninit;
        if (n >= MAX_GPR) {
                error("Too many GPRs");
                return;
        }
        fle.init[n].gpr = gpr;
        fle.init[n].value = val;
        if (name)
                (void) strlcpy(fle.init[n].name, name,
                    sizeof (fle.init[n].name));
        fle.ninit++;
}

static void
compile_gpr(char **tokens, int cnt)
{
        CHECK_COUNT(tokens, cnt, 2, 2);

        if (ngpr >= MAX_GPR)
                error("Too many GPR variables");

        add_symbol(tokens[1], SY_GPR, gpr_base + ngpr++);
}

static void
compile_rem(char **tokens, int cnt)
{
        int i;

        (void) strlcat(remarks, " *", sizeof (remarks));
        for (i = 1; i < cnt; i++) {
                (void) strlcat(remarks, " ", sizeof (remarks));
                (void) strlcat(remarks, tokens[i], sizeof (remarks));
        }
        (void) strlcat(remarks, "\n", sizeof (remarks));
}

static void
declare_const(unsigned int gpr, char *value)
{
        int n, intv;
        float v;

        n = fle.consts.nconst;

        if (n >= MAX_CONST_PARMS) {
                error("Too many constant parameters");
                return;
        }

        if (*value == 'I') {
                if (sscanf(&value[1], "%g", &v) != 1) {
                        error("Bad floating point value (%s)", value);
                        return;
                }
                intv = (int)v;
        } else if (*value == '0' && value[1] == 'x') {
                if (sscanf(&value[2], "%x", (unsigned *)&intv) != 1) {
                        error("Bad hexadecimal value (%s)", value);
                        return;
                }
        } else {
                if (sscanf(value, "%g", &v) != 1) {
                        error("Bad floating point value (%s)", value);
                        return;
                }
                intv = (int)(v * 0x7fffffff);
        }

        fle.consts.consts[n].gpr = gpr;
        fle.consts.consts[n].value = intv;
        fle.consts.nconst = n + 1;

        add_init(gpr, intv, NULL);
}

static void
compile_const(char **tokens, int cnt)
{
        CHECK_COUNT(tokens, cnt, 2, 3);
        char *name = tokens[1];
        char *value = tokens[2] ? tokens[2] : tokens[1];

        if (ngpr >= MAX_GPR)
                error("Too many GPR variables");

        declare_const(ngpr, value);

        add_symbol(name, SY_GPR, gpr_base + ngpr++);
}

static void
compile_bool(char **tokens, int cnt)
{
        char *parm, *def;
        int n, num;

        CHECK_COUNT(tokens, cnt, 3, 3);

        parm = tokens[1];
        def = tokens[2];

        n = fle.parms.ngpr;
        if (n >= MAX_GPR_PARMS) {
                error("Too many GPR parameters");
                return;
        }

        if (sscanf(def, "%d", &num) != 1) {
                error("Bad integer value near '%s'", def);
                return;
        }

        (void) strcpy(fle.parms.gpr[n].name, parm);
        fle.parms.gpr[n].num = ngpr;
        fle.parms.gpr[n].def = num;
        fle.parms.ngpr = n + 1;

        add_init(ngpr, num, parm);

        add_symbol(parm, SY_PARM, gpr_base + ngpr++);
}

static void
compile_mono(char **tokens, int cnt)
{
        char *parm, *def;
        int n, num;

        CHECK_COUNT(tokens, cnt, 3, 3);

        parm = tokens[1];
        def = tokens[2];

        n = fle.parms.ngpr;
        if (n >= MAX_GPR_PARMS) {
                error("Too many GPR parameters");
                return;
        }

        if (sscanf(def, "%d", &num) != 1) {
                error("Bad integer value near '%s'", def);
                return;
        }

        (void) strcpy(fle.parms.gpr[n].name, parm);
        fle.parms.gpr[n].num = ngpr;
        fle.parms.gpr[n].def = num;
        fle.parms.ngpr = n + 1;

        add_init(ngpr, num, parm);

        add_symbol(parm, SY_PARM, gpr_base + ngpr++);
}

static void
compile_stereo(char **tokens, int cnt)
{
        char *parm, *def;
        int n, num;
        char tmp[128];

        CHECK_COUNT(tokens, cnt, 3, 3);

        parm = tokens[1];
        def = tokens[2];

        n = fle.parms.ngpr;
        if (n >= MAX_GPR_PARMS) {
                error("Too many GPR parameters");
                return;
        }

        if (sscanf(def, "%d", &num) != 1) {
                error("Bad integer value near '%s'", def);
                return;
        }

        (void) strcpy(fle.parms.gpr[n].name, parm);
        fle.parms.gpr[n].num = ngpr;
        fle.parms.gpr[n].def = num | (num << 8);
        fle.parms.ngpr = n + 1;

        add_init(ngpr, num, parm);
        add_init(ngpr + 1, num, NULL);

        (void) sprintf(tmp, "%s_L", parm);
        add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
        (void) sprintf(tmp, "%s_R", parm);
        add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
}

static void
compile_input(char **tokens, int cnt)
{
        int num;

        CHECK_COUNT(tokens, cnt, 3, 3);

        if (sscanf(tokens[2], "%d", &num) != 1) {
                error("Bad integer value near '%s'", tokens[2]);
                return;
        }

        add_symbol(tokens[1], SY_INPUT, input_base + num);
}

static void
compile_send(char **tokens, int cnt)
{
        int num;

        CHECK_COUNT(tokens, cnt, 3, 3);

        if (sscanf(tokens[2], "%d", &num) != 1) {
                error("Bad integer near '%s'", tokens[2]);
                return;
        }

        add_symbol(tokens[1], SY_FX, num);
}

static void
compile_output(char **tokens, int cnt)
{
        int num;

        CHECK_COUNT(tokens, cnt, 3, 3);

        if (sscanf(tokens[2], "%d", &num) != 1) {
                error("Bad integer value near '%s'", tokens[2]);
                return;
        }

        add_symbol(tokens[1], SY_OUTPUT, output_base + num);
}

static void
compile_directive(char **tokens, int cnt)
{
        if (strcmp(tokens[0], ".gpr") == 0) {
                compile_gpr(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".const") == 0) {
                compile_const(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".stereo") == 0) {
                compile_stereo(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".mono") == 0) {
                compile_mono(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".bool") == 0) {
                compile_bool(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".input") == 0) {
                compile_input(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".send") == 0) {
                compile_send(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".output") == 0) {
                compile_output(tokens, cnt);
                return;
        }

        if (strcmp(tokens[0], ".rem") == 0) {
                compile_rem(tokens, cnt);
                return;
        }
        if (strcmp(tokens[0], ".'") == 0) {
                compile_rem(tokens, cnt);
                return;
        }

        error("Unknown directive '%s'", tokens[0]);
}

static void
compile_asm(char **tokens, int cnt)
{
        sym_t *symbols[4];
#define EMIT(o, r, a, x, y) \
        fle.code[pc*2] =  ((x) << 10) | (y);                    \
        fle.code[pc*2+1] = ((o) << 20) | ((r) << 10) | a; pc++
#define EMIT_AUDIGY(o, r, a, x, y) \
        fle.code[pc*2] =  ((x) << 12) | (y);                    \
        fle.code[pc*2+1] = ((o) << 24) | ((r) << 12) | a; pc++

        int i, nerr = 0;
        int ninputs = 0;

        CHECK_COUNT(tokens, cnt, 5, 5);

        for (i = 0; i < 4; i++) {
                if ((symbols[i] = find_symbol(tokens[i+1])) == NULL) {
                        (void) fprintf(stderr, "%s\n", tokens[i+1]);
                        nerr++;
                        error("Undefined symbol '%s'", tokens[i + 1]);
                        continue;
                }

                if (symbols[i]->type == SY_INPUT)
                        ninputs++;

                if (symbols[i]->type == SY_ACCUM && i != 1)
                        error("Bad usage of 'accum' operand.");
        }

        if (nerr > 0)
                return;

        if (ninputs > 1) {
                error("Attempt to access more than one input "
                    "GPRs by the same instruction");
        }

        for (i = 0; instructions[i].name != NULL; i++)
                if (strcasecmp(tokens[0], instructions[i].name) == 0)  {

                        if (is_audigy) {
                                EMIT_AUDIGY(instructions[i].opcode,
                                    symbols[0]->arg,
                                    symbols[1]->arg,
                                    symbols[2]->arg,
                                    symbols[3]->arg);
                        } else {
                                EMIT(instructions[i].opcode,
                                    symbols[0]->arg,
                                    symbols[1]->arg,
                                    symbols[2]->arg,
                                    symbols[3]->arg);
                        }

                        return;
                }

        error("Unrecognized instruction '%s'", tokens[0]);
}

static void
init_compiler(void)
{
        char tmp[100];
        int i;

        (void) memset(&fle, 0, sizeof (fle));
        /*
         * Initialize few predefined GPR parameter registers. These
         * definitions have to be in sync with the GPR_* macros in
         * <sblive.h>.
         */

        /*
         * Make sure we start at gpr id 2 for now; 0 and 1 may be used
         * differently.
         */
        add_symbol("NULL", SY_DUMMY, gpr_base + ngpr++);
        add_symbol("NULL_", SY_DUMMY, gpr_base + ngpr++);

        pc = 0;

        if (is_audigy) {
                /* Initialize the code array with NOPs (AUDIGY) */
                for (i = 0; i < 512; i++) {
                        fle.code[i * 2 + 0] = (0xc0 << 12) | 0xc0;
                        fle.code[i * 2 + 1] =
                            (0x06 << 24) | (0xc0 << 12) | 0xc0;
                }

                for (i = 0; i < 32; i++) {
                        (void) sprintf(tmp, "fx%d", i);
                        add_symbol(tmp, SY_FX, i);
                }
        } else {
                /* Initialize the code array with NOPs (LIVE) */
                for (i = 0; i < 512; i++) {
                        fle.code[i * 2 + 0] = 0x10040;
                        fle.code[i * 2 + 1] = 0x610040;
                }

                for (i = 0; i < 16; i++) {
                        (void) sprintf(tmp, "fx%d", i);
                        add_symbol(tmp, SY_FX, i);
                }
        }

        /*
         * Constants
         */

        if (is_audigy) {
                /* Audigy symbols */
                add_symbol("0", SY_CONST, 0x0c0);
                add_symbol("1", SY_CONST, 0x0c1);
                add_symbol("2", SY_CONST, 0x0c2);
                add_symbol("3", SY_CONST, 0x0c3);
                add_symbol("4", SY_CONST, 0x0c4);
                add_symbol("8", SY_CONST, 0x0c5);
                add_symbol("16", SY_CONST, 0x0c6);
                add_symbol("32", SY_CONST, 0x0c7);
                add_symbol("256", SY_CONST, 0x0c8);
                add_symbol("65536", SY_CONST, 0x0c9);

                add_symbol("2048", SY_CONST, 0x0ca);
                add_symbol("0x800", SY_CONST, 0x0ca);

                add_symbol("2^28", SY_CONST, 0x0cb);
                add_symbol("0x10000000", SY_CONST, 0x0cb);

                add_symbol("2^29", SY_CONST, 0x0cc);
                add_symbol("0x20000000", SY_CONST, 0x0cc);

                add_symbol("2^30", SY_CONST, 0x0cd);
                add_symbol("0x40000000", SY_CONST, 0x0cd);

                add_symbol("2^31", SY_CONST, 0x0ce);
                add_symbol("0x80000000", SY_CONST, 0x0ce);

                add_symbol("0x7fffffff", SY_CONST, 0x0cf);

                add_symbol("0xffffffff", SY_CONST, 0x0d0);
                add_symbol("-1", SY_CONST, 0x0d0);

                add_symbol("0xfffffffe", SY_CONST, 0x0d1);
                add_symbol("-2", SY_CONST, 0x0d1);

                add_symbol("0xc0000000", SY_CONST, 0x0d2);

                add_symbol("0x4f1bbcdc", SY_CONST, 0x0d3);

                add_symbol("0x5a7ef9db", SY_CONST, 0x0d4);

                add_symbol("0x100000", SY_CONST, 0x0d5);
                add_symbol("accum", SY_ACCUM, 0x0d6);
                add_symbol("CCR", SY_CONST, 0x0d7);

                add_symbol("noise_L", SY_CONST, 0x0d8);
                add_symbol("noise_R", SY_CONST, 0x0d9);
                add_symbol("IRQREQ", SY_CONST, 0x0da);
        } else {
                /* SB Live symbols */
                add_symbol("0", SY_CONST, 0x040);
                add_symbol("1", SY_CONST, 0x041);
                add_symbol("2", SY_CONST, 0x042);
                add_symbol("3", SY_CONST, 0x043);
                add_symbol("4", SY_CONST, 0x044);
                add_symbol("8", SY_CONST, 0x045);
                add_symbol("16", SY_CONST, 0x046);
                add_symbol("32", SY_CONST, 0x047);
                add_symbol("256", SY_CONST, 0x048);
                add_symbol("65536", SY_CONST, 0x049);

                add_symbol("2^23", SY_CONST, 0x04a);
                add_symbol("0x80000", SY_CONST, 0x04a);

                add_symbol("2^28", SY_CONST, 0x04b);
                add_symbol("0x10000000", SY_CONST, 0x04b);

                add_symbol("2^29", SY_CONST, 0x04c);
                add_symbol("0x20000000", SY_CONST, 0x04c);

                add_symbol("2^30", SY_CONST, 0x04d);
                add_symbol("0x40000000", SY_CONST, 0x04d);

                add_symbol("2^31", SY_CONST, 0x04e);
                add_symbol("0x80000000", SY_CONST, 0x04e);

                add_symbol("0x7fffffff", SY_CONST, 0x04f);

                add_symbol("0xffffffff", SY_CONST, 0x050);
                add_symbol("-1", SY_CONST, 0x050);

                add_symbol("0xfffffffe", SY_CONST, 0x051);
                add_symbol("-2", SY_CONST, 0x051);

                add_symbol("accum", SY_ACCUM, 0x056);
                add_symbol("CCR", SY_CONST, 0x057);

                add_symbol("noise_L", SY_CONST, 0x058);
                add_symbol("noise_R", SY_CONST, 0x059);
                add_symbol("IRQREQ", SY_CONST, 0x05a);
        }
}

static void
produce_map(char *name)
{
        int i;
        FILE *f;

        if ((f = fopen(name, "w")) == NULL) {
                perror(name);
                return;
        }

        (void) fprintf(f, "%d\n", pc);

        for (i = 0; i < nsyms; i++) {
                (void) fprintf(f, "%04x %x %s\n",
                    symtab[i].arg, symtab[i].type, symtab[i].name);
        }

        (void) fclose(f);
        if (verbose) {
                (void) fprintf(stderr,
                    "No errors detected - Map written to %s\n", name);
        }
}

static void
produce_output(char *fname)
{
        int fd;

        if ((fd = creat(fname, 0644)) == -1) {
                perror(fname);
                exit(-1);
        }

        if (write(fd, &fle, sizeof (fle)) != sizeof (fle)) {
                perror(fname);
                exit(-1);
        }

        if (verbose) {
                (void) fprintf(stderr,
                    "No errors detected - Binary written to %s\n",
                    fname);
        }

        (void) close(fd);
}

static void
produce_header(char *fname, char *prefix)
{
        FILE *f;
        char *s;
        char sname[MAXPATHLEN + 1];
        char dname[MAXPATHLEN + 1];
        int i;
        clock_t now;
        char when[128];

        /* get basename */
        if (prefix == NULL) {
                s = strrchr(fname, '/');
                s = (s == NULL) ? fname : s + 1;
        } else {
                s = prefix;
        }
        (void) strlcpy(sname, s, sizeof (sname));

        /* strip off any extension */
        s = strchr(sname, '.');
        if (s != NULL) {
                *s = 0;
        }
        if ((f = fopen(fname, "w")) == NULL) {
                perror(fname);
                return;
        }

        if (remarks[0] != 0) {
                (void) fprintf(f, "/*\n%s */\n", remarks);
        }
        now = time(NULL);
        strftime(when, sizeof (when), "%c", localtime(&now));
        (void) fprintf(f, banner, progname, when);

        (void) strlcpy(dname, prefix ? prefix : sname, sizeof (dname));
        for (i = 0; dname[i]; i++) {
                dname[i] = toupper(dname[i]);
                if (!isalnum(dname[i])) {
                        dname[i] = '_';
                }
        }

        for (i = 0; i < fle.parms.ngpr; i++) {
                (void) fprintf(f, "#define\t%s_%s\t\t%d\n",
                    dname, fle.parms.gpr[i].name, fle.parms.gpr[i].num);
        }

        (void) fprintf(f, "\n");

        if (parms_only)
                goto done;

        (void) fprintf(f, "uint32_t %s_code[] = {\n", sname);

        for (i = 0; i < pc * 2; i++) {
                if (i == 0) {
                        (void) fprintf(f, "\t0x%08xU", fle.code[i]);
                } else if ((i % 4) == 0) {
                        (void) fprintf(f, ",\n\t0x%08xU", fle.code[i]);
                } else {
                        (void) fprintf(f, ", 0x%08xU", fle.code[i]);
                }
        }
        (void) fprintf(f, "\n};\n");

        (void) fprintf(f, "uint32_t %s_ninit = %d;\n", sname, fle.ninit);
        (void) fprintf(f, "uint32_t %s_init[] = {\n", sname);

        for (i = 0; i < fle.ninit; i++) {
                if (fle.init[i].name[0]) {
                        (void) fprintf(f, "\t%u, 0x%x%s,\t/* %s */\n",
                            fle.init[i].gpr, fle.init[i].value,
                            fle.init[i].value >= 0x80000000U ? "U" : "",
                            fle.init[i].name);
                } else {
                        (void) fprintf(f, "\t%u, 0x%x%s,\n",
                            fle.init[i].gpr, fle.init[i].value,
                            fle.init[i].value >= 0x80000000U ? "U" : "");
                }
        }
        (void) fprintf(f, "};\n");

done:
        (void) fclose(f);
        if (verbose) {
                (void) fprintf(stderr,
                    "No errors detected - Header written to %s\n",
                    fname);
        }
}

int
main(int argc, char *argv[])
{
        char *outfile;
        int i;
        FILE *input;
        char *tokens[10];
        int tokcnt;
        char *mapfile = NULL;
        char *header = NULL;
        char *prefix = NULL;

        outfile = NULL;
        infile = NULL;
        input = NULL;
        progname = argv[0];

        while ((i = getopt(argc, argv, "m:h:o:i:P:021v")) != EOF) {
                switch (i) {
                case 'o':
                        outfile = optarg;
                        break;
                case 'i':
                        infile = strdup(optarg);
                        break;
                case 'm':
                        mapfile = optarg;
                        break;
                case 'P':
                        prefix = optarg;
                        break;
                case 'h':
                        header = optarg;
                        break;
                case '0':
                        parms_only = 1;
                        break;
                case '2':
                        is_audigy = 1;
                        break;
                case '1':
                        is_audigy = 0;
                        break;
                case 'v':
                        verbose++;
                        break;
                default:
                        (void) fprintf(stderr,
                            "usage: %s [-m <map>] [-h <header>] "
                            "[-o <binary>] [-i <source>] [-2|-1]",
                            progname);
                        exit(-1);
                        break;
                }
        }

        if ((outfile == NULL) && (mapfile == NULL) && (header == NULL)) {
                outfile = "dsp.bin";
        }

        if (infile) {
                input = fopen(infile, "r");
                if (input == NULL) {
                        perror(infile);
                        exit(-1);
                }
        } else {
                infile = strdup("<stdin>");
                input = stdin;
        }

        if (is_audigy) {
                gpr_base = 0x400;
                input_base = 0x40;
                output_base = 0x60;
                if (verbose)
                        (void) fprintf(stderr, "Compiling for SB Audigy\n");
        } else {
                if (verbose)
                        (void) fprintf(stderr, "Compiling for SB Live\n");
        }

        init_compiler();

        while ((tokcnt = getaline(input, tokens)) != -1) {
                /* skip empty lines */
                if (tokcnt == 0) {
                        continue;
                }

                if (strcmp(tokens[0], "#") == 0) {
                        int     num;
                        if ((tokcnt >= 3) &&
                            (sscanf(tokens[1], "%d", &num) == 1)) {
                                lineno = num;
                                free(infile);
                                infile = strdup(tokens[2]);
                                /* we don't want to count the # directive */
                                lineno--;
                        }

                        /* unknown # directive? muddle on... */
                        continue;
                }
                if (*tokens[0] == '.') {
                        compile_directive(tokens, tokcnt);
                } else {
                        compile_asm(tokens, tokcnt);
                }
        }

        if (lineno < 1) {
                error("Empty input");
        }

        if (errors == 0) {
                if (verbose) {
                        (void) fprintf(stderr,
                            "%d instructions out of 512 assembled\n", pc);
                }

                if (outfile)
                        produce_output(outfile);
                if (mapfile)
                        produce_map(mapfile);
                if (header)
                        produce_header(header, prefix);
        }

        if (errors > 0) {
                (void) fprintf(stderr, "%d errors - compile failed\n", errors);
                exit(-1);
        }

        return (0);
}