/*      $NetBSD: ncr53cxxx.c,v 1.17 2022/04/21 21:31:11 andvar Exp $    */

/*
* Copyright (c) 1995,1999 Michael L. Hitch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*      ncr53cxxx.c     - SCSI SCRIPTS Assembler                */

#include <sys/cdefs.h>
__RCSID("$NetBSD: ncr53cxxx.c,v 1.17 2022/04/21 21:31:11 andvar Exp $");

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifndef AMIGA
#define strcmpi strcasecmp
#endif

#define MAXTOKENS       16
#define MAXINST         1024
#define MAXSYMBOLS      128

struct {
       int     type;
       char    *name;
} tokens[MAXTOKENS];
int     ntokens;
int     tokenix;

void    f_proc (void);
void    f_pass (void);
void    f_list (void);          /* ENTRY, EXTERNAL label list */
void    f_define (void);        /* ABSOLUTE, RELATIVE label list */
void    f_move (void);
void    f_jump (void);
void    f_call (void);
void    f_return (void);
void    f_int (void);
void    f_intfly (void);
void    f_select (void);
void    f_reselect (void);
void    f_wait (void);
void    f_disconnect (void);
void    f_set (void);
void    f_clear (void);
void    f_load (void);
void    f_store (void);
void    f_nop (void);
void    f_arch (void);

struct {
       char    *name;
       void    (*func)(void);
} directives[] = {
       {"PROC",        f_proc},
       {"PASS",        f_pass},
       {"ENTRY",       f_list},
       {"ABSOLUTE",    f_define},
       {"EXTERN",      f_list},
       {"EXTERNAL",    f_list},
       {"RELATIVE",    f_define},
       {"MOVE",        f_move},
       {"JUMP",        f_jump},
       {"CALL",        f_call},
       {"RETURN",      f_return},
       {"INT",         f_int},
       {"INTFLY",      f_intfly},
       {"SELECT",      f_select},
       {"RESELECT",    f_reselect},
       {"WAIT",        f_wait},
       {"DISCONNECT",  f_disconnect},
       {"SET",         f_set},
       {"CLEAR",       f_clear},
       {"LOAD",        f_load},
       {"STORE",       f_store},
       {"NOP",         f_nop},
       {"ARCH",        f_arch},
       {NULL, NULL}};

u_int32_t script[MAXINST];
int     dsps;
char    *script_name = "SCRIPT";
u_int32_t inst0, inst1, inst2;
unsigned int    ninsts;
unsigned int    npatches;

struct patchlist {
       struct patchlist *next;
       unsigned        offset;
} *patches;

#define S_LABEL         0x0000
#define S_ABSOLUTE      0x0001
#define S_RELATIVE      0x0002
#define S_EXTERNAL      0x0003
#define F_DEFINED       0x0001
#define F_ENTRY         0x0002
struct {
       short   type;
       short   flags;
       u_int32_t value;
       struct patchlist *patchlist;
       char    *name;
} symbols[MAXSYMBOLS];
int nsymbols;

char    *stypes[] = {"Label", "Absolute", "Relative", "External"};

char    *phases[] = {
       "data_out", "data_in", "cmd", "status",
       "res4", "res5", "msg_out", "msg_in"
};

struct ncrregs {
       char *name;
       int addr[5];
};
#define ARCH700 1
#define ARCH710 2
#define ARCH720 3
#define ARCH810 4
#define ARCH825 5

struct ncrregs  regs[] = {
       {"scntl0",      {0x00, 0x00, 0x00, 0x00, 0x00}},
       {"scntl1",      {0x01, 0x01, 0x01, 0x01, 0x01}},
       {"sdid",        {0x02, 0x02,   -1,   -1,   -1}},
       {"sien",        {0x03, 0x03,   -1,   -1,   -1}},
       {"scid",        {0x04, 0x04,   -1,   -1,   -1}},
       {"scntl2",      {  -1,   -1, 0x02, 0x02, 0x02}},
       {"scntl3",      {  -1,   -1, 0x03, 0x03, 0x03}},
       {"scid",        {  -1,   -1, 0x04, 0x04, 0x04}},
       {"sxfer",       {0x05, 0x05, 0x05, 0x05, 0x05}},
       {"sodl",        {0x06, 0x06,   -1,   -1,   -1}},
       {"socl",        {0x07, 0x07,   -1,   -1,   -1}},
       {"sdid",        {  -1,   -1, 0x06, 0x06, 0x06}},
       {"gpreg",       {  -1,   -1, 0x07, 0x07, 0x07}},
       {"sfbr",        {0x08, 0x08, 0x08, 0x08, 0x08}},
       {"sidl",        {0x09, 0x09,   -1,   -1,   -1}},
       {"sbdl",        {0x0a, 0x0a,   -1,   -1,   -1}},
       {"socl",        {  -1,   -1, 0x09, 0x09, 0x09}},
       {"ssid",        {  -1,   -1, 0x0a, 0x0a, 0x0a}},
       {"sbcl",        {0x0b, 0x0b, 0x0b, 0x0b, 0x0b}},
       {"dstat",       {0x0c, 0x0c, 0x0c, 0x0c, 0x0c}},
       {"sstat0",      {0x0d, 0x0d, 0x0d, 0x0d, 0x0d}},
       {"sstat1",      {0x0e, 0x0e, 0x0e, 0x0e, 0x0e}},
       {"sstat2",      {0x0f, 0x0f, 0x0f, 0x0f, 0x0f}},
       {"dsa0",        {  -1, 0x10, 0x10, 0x10, 0x10}},
       {"dsa1",        {  -1, 0x11, 0x11, 0x11, 0x11}},
       {"dsa2",        {  -1, 0x12, 0x12, 0x12, 0x12}},
       {"dsa3",        {  -1, 0x13, 0x13, 0x13, 0x13}},
       {"ctest0",      {0x14, 0x14, 0x18, 0x18, 0x18}},
       {"ctest1",      {0x15, 0x15, 0x19, 0x19, 0x19}},
       {"ctest2",      {0x16, 0x16, 0x1a, 0x1a, 0x1a}},
       {"ctest3",      {0x17, 0x17, 0x1b, 0x1b, 0x1b}},
       {"ctest4",      {0x18, 0x18, 0x21, 0x21, 0x21}},
       {"ctest5",      {0x19, 0x19, 0x22, 0x22, 0x22}},
       {"ctest6",      {0x1a, 0x1a, 0x23, 0x23, 0x23}},
       {"ctest7",      {0x1b, 0x1b,   -1,   -1,   -1}},
       {"temp0",       {0x1c, 0x1c, 0x1c, 0x1c, 0x1c}},
       {"temp1",       {0x1d, 0x1d, 0x1d, 0x1d, 0x1d}},
       {"temp2",       {0x1e, 0x1e, 0x1e, 0x1e, 0x1e}},
       {"temp3",       {0x1f, 0x1f, 0x1f, 0x1f, 0x1f}},
       {"dfifo",       {0x20, 0x20, 0x20, 0x20, 0x20}},
       {"istat",       {0x21, 0x21, 0x14, 0x14, 0x14}},
       {"ctest8",      {0x22, 0x22,   -1,   -1,   -1}},
       {"lcrc",        {  -1, 0x23,   -1,   -1,   -1}},
       {"ctest9",      {0x23,   -1,   -1,   -1,   -1}},
       {"dbc0",        {0x24, 0x24, 0x24, 0x24, 0x24}},
       {"dbc1",        {0x25, 0x25, 0x25, 0x25, 0x25}},
       {"dbc2",        {0x26, 0x26, 0x26, 0x26, 0x26}},
       {"dcmd",        {0x27, 0x27, 0x27, 0x27, 0x27}},
       {"dnad0",       {0x28, 0x28, 0x28, 0x28, 0x28}},
       {"dnad1",       {0x29, 0x29, 0x29, 0x29, 0x29}},
       {"dnad2",       {0x2a, 0x2a, 0x2a, 0x2a, 0x2a}},
       {"dnad3",       {0x2b, 0x2b, 0x2b, 0x2b, 0x2b}},
       {"dsp0",        {0x2c, 0x2c, 0x2c, 0x2c, 0x2c}},
       {"dsp1",        {0x2d, 0x2d, 0x2d, 0x2d, 0x2d}},
       {"dsp2",        {0x2e, 0x2e, 0x2e, 0x2e, 0x2e}},
       {"dsp3",        {0x2f, 0x2f, 0x2f, 0x2f, 0x2f}},
       {"dsps0",       {0x30, 0x30, 0x30, 0x30, 0x30}},
       {"dsps1",       {0x31, 0x31, 0x31, 0x31, 0x31}},
       {"dsps2",       {0x32, 0x32, 0x32, 0x32, 0x32}},
       {"dsps3",       {0x33, 0x33, 0x33, 0x33, 0x33}},
       {"scratch0",    {  -1, 0x34,   -1,   -1,   -1}},
       {"scratch1",    {  -1, 0x35,   -1,   -1,   -1}},
       {"scratch2",    {  -1, 0x36,   -1,   -1,   -1}},
       {"scratch3",    {  -1, 0x37,   -1,   -1,   -1}},
       {"scratcha0",   {0x10,   -1, 0x34, 0x34, 0x34}},
       {"scratcha1",   {0x11,   -1, 0x35, 0x35, 0x35}},
       {"scratcha2",   {0x12,   -1, 0x36, 0x36, 0x36}},
       {"scratcha3",   {0x13,   -1, 0x37, 0x37, 0x37}},
       {"dmode",       {0x34, 0x38, 0x38, 0x38, 0x38}},
       {"dien",        {0x39, 0x39, 0x39, 0x39, 0x39}},
       {"dwt",         {0x3a, 0x3a, 0x3a,   -1,   -1}},
       {"sbr",         {  -1,   -1,   -1, 0x3a, 0x3a}},
       {"dcntl",       {0x3b, 0x3b, 0x3b, 0x3b, 0x3b}},
       {"addr0",       {  -1, 0x3c, 0x3c, 0x3c, 0x3c}},
       {"addr1",       {  -1, 0x3d, 0x3d, 0x3d, 0x3d}},
       {"addr2",       {  -1, 0x3e, 0x3e, 0x3e, 0x3e}},
       {"addr3",       {  -1, 0x3f, 0x3f, 0x3f, 0x3f}},
       {"sien0",       {  -1,   -1, 0x40, 0x40, 0x40}},
       {"sien1",       {  -1,   -1, 0x41, 0x41, 0x41}},
       {"sist0",       {  -1,   -1, 0x42, 0x42, 0x42}},
       {"sist1",       {  -1,   -1, 0x43, 0x43, 0x43}},
       {"slpar",       {  -1,   -1, 0x44, 0x44, 0x44}},
       {"swide",       {  -1,   -1, 0x45,   -1, 0x45}},
       {"macntl",      {  -1,   -1, 0x46, 0x46, 0x46}},
       {"gpcntl",      {  -1,   -1, 0x47, 0x47, 0x47}},
       {"stime0",      {  -1,   -1, 0x48, 0x48, 0x48}},
       {"stime1",      {  -1,   -1, 0x49, 0x49, 0x49}},
       {"respid0",     {  -1,   -1, 0x4a, 0x4a, 0x4a}},
       {"respid1",     {  -1,   -1, 0x4b,   -1, 0x4b}},
       {"stest0",      {  -1,   -1, 0x4c, 0x4c, 0x4c}},
       {"stest1",      {  -1,   -1, 0x4d, 0x4d, 0x4d}},
       {"stest2",      {  -1,   -1, 0x4e, 0x4e, 0x4e}},
       {"stest3",      {  -1,   -1, 0x4f, 0x4f, 0x4f}},
       {"sidl0",       {  -1,   -1, 0x50, 0x50, 0x50}},
       {"sidl1",       {  -1,   -1, 0x51,   -1, 0x51}},
       {"sodl0",       {  -1,   -1, 0x54, 0x54, 0x54}},
       {"sodl1",       {  -1,   -1, 0x55,   -1, 0x55}},
       {"sbdl0",       {  -1,   -1, 0x58, 0x58, 0x58}},
       {"sbdl1",       {  -1,   -1, 0x59,   -1, 0x59}},
       {"scratchb0",   {0x3c,   -1, 0x5c, 0x5c, 0x5c}},
       {"scratchb1",   {0x3d,   -1, 0x5d, 0x5d, 0x5d}},
       {"scratchb2",   {0x3e,   -1, 0x5e, 0x5e, 0x5e}},
       {"scratchb3",   {0x3f,   -1, 0x5f, 0x5f, 0x5f}},
       {"scratchc0",   {  -1,   -1,   -1,   -1, 0x60}},
       {"scratchc1",   {  -1,   -1,   -1,   -1, 0x61}},
       {"scratchc2",   {  -1,   -1,   -1,   -1, 0x62}},
       {"scratchc3",   {  -1,   -1,   -1,   -1, 0x63}},
       {"scratchd0",   {  -1,   -1,   -1,   -1, 0x64}},
       {"scratchd1",   {  -1,   -1,   -1,   -1, 0x65}},
       {"scratchd2",   {  -1,   -1,   -1,   -1, 0x66}},
       {"scratchd3",   {  -1,   -1,   -1,   -1, 0x67}},
       {"scratche0",   {  -1,   -1,   -1,   -1, 0x68}},
       {"scratche1",   {  -1,   -1,   -1,   -1, 0x69}},
       {"scratche2",   {  -1,   -1,   -1,   -1, 0x6a}},
       {"scratche3",   {  -1,   -1,   -1,   -1, 0x6b}},
       {"scratchf0",   {  -1,   -1,   -1,   -1, 0x6c}},
       {"scratchf1",   {  -1,   -1,   -1,   -1, 0x6d}},
       {"scratchf2",   {  -1,   -1,   -1,   -1, 0x6e}},
       {"scratchf3",   {  -1,   -1,   -1,   -1, 0x6f}},
       {"scratchg0",   {  -1,   -1,   -1,   -1, 0x70}},
       {"scratchg1",   {  -1,   -1,   -1,   -1, 0x71}},
       {"scratchg2",   {  -1,   -1,   -1,   -1, 0x72}},
       {"scratchg3",   {  -1,   -1,   -1,   -1, 0x73}},
       {"scratchh0",   {  -1,   -1,   -1,   -1, 0x74}},
       {"scratchh1",   {  -1,   -1,   -1,   -1, 0x75}},
       {"scratchh2",   {  -1,   -1,   -1,   -1, 0x7e}},
       {"scratchh3",   {  -1,   -1,   -1,   -1, 0x77}},
       {"scratchi0",   {  -1,   -1,   -1,   -1, 0x78}},
       {"scratchi1",   {  -1,   -1,   -1,   -1, 0x79}},
       {"scratchi2",   {  -1,   -1,   -1,   -1, 0x7a}},
       {"scratchi3",   {  -1,   -1,   -1,   -1, 0x7b}},
       {"scratchj0",   {  -1,   -1,   -1,   -1, 0x7c}},
       {"scratchj1",   {  -1,   -1,   -1,   -1, 0x7d}},
       {"scratchj2",   {  -1,   -1,   -1,   -1, 0x7e}},
       {"scratchj3",   {  -1,   -1,   -1,   -1, 0x7f}},
};

int     lineno;
int     err_listed;
int     arch;
int     partial_flag;

char    inbuf[128];

char    *sourcefile;
char    *outputfile;
char    *listfile;
char    *errorfile;

FILE    *infp;
FILE    *outfp;
FILE    *listfp;
FILE    *errfp;

void    setarch(char *);
void    parse (void);
void    process (void);
void    emit_symbols (void);
void    list_symbols (void);
void    errout (char *);
void    define_symbol (char *, u_int32_t, short, short);
void    patch_label (void);
void    close_script (void);
void    new_script (char *);
void    store_inst (void);
int     expression (int *);
int     evaluate (int);
int     number (char *);
int     lookup (char *);
int     reserved (char *, int);
int     CheckPhase (int);
int     CheckRegister (int);
void    transfer (int, int);
void    select_reselect (int);
void    set_clear (u_int32_t);
void    block_move (void);
void    register_write (void);
void    memory_to_memory (void);
void    loadstore (int);
void    error_line(void);
char    *makefn(char *, char *);
void    usage(void);

int
main (int argc, char *argv[])
{
       int     i;
       struct patchlist *p;

       if (argc < 2 || argv[1][0] == '-')
               usage();
       sourcefile = argv[1];
       infp = fopen (sourcefile, "r");
       if (infp == NULL) {
               perror ("open source");
               fprintf (stderr, "scc: error opening source file %s\n", argv[1]);
               exit (1);
       }
       /*
        * process options
        * -l [listfile]
        * -o [outputfile]
        * -p [outputfile]
        * -z [debugfile]
        * -e [errorfile]
        * -a arch
        * -v
        * -u
        */
       for (i = 2; i < argc; ++i) {
               if (argv[i][0] != '-')
                       usage();
               switch (argv[i][1]) {
               case 'o':
               case 'p':
                       partial_flag = argv[i][1] == 'p';
                       if (i + 1 >= argc || argv[i + 1][0] == '-')
                               outputfile = makefn (sourcefile, "out");
                       else {
                               outputfile = argv[i + 1];
                               ++i;
                       }
                       break;
               case 'l':
                       if (i + 1 >= argc || argv[i + 1][0] == '-')
                               listfile = makefn (sourcefile, "lis");
                       else {
                               listfile = argv[i + 1];
                               ++i;
                       }
                       break;
               case 'e':
                       if (i + 1 >= argc || argv[i + 1][0] == '-')
                               errorfile = makefn (sourcefile, "err");
                       else {
                               errorfile = argv[i + 1];
                               ++i;
                       }
                       break;
               case 'a':
                       if (i + 1 == argc)
                               usage();
                       setarch(argv[i +1]);
                       if (arch == 0) {
                               fprintf(stderr,"%s: bad arch '%s'\n",
                                       argv[0], argv[i +1]);
                               exit(1);
                       }
                       ++i;
                       break;
               default:
                       fprintf (stderr, "scc: unrecognized option '%c'\n",
                           argv[i][1]);
                       usage();
               }
       }
       if (outputfile)
               outfp = fopen (outputfile, "w");
       if (listfile)
               listfp = fopen (listfile, "w");
       if (errorfile)
               errfp = fopen (errorfile, "w");
       else
               errfp = stderr;

       if (outfp) {
               time_t cur_time;

               fprintf(outfp, "/*\t$NetBSD: ncr53cxxx.c,v 1.17 2022/04/21 21:31:11 andvar Exp $\t*/\n");
               fprintf(outfp, "/*\n");
               fprintf(outfp, " *\tDO NOT EDIT - this file is automatically generated.\n");
               time(&cur_time);
               fprintf(outfp, " *\tcreated from %s on %s", sourcefile, ctime(&cur_time));
               fprintf(outfp, " */\n");
       }

       while (fgets (inbuf, sizeof (inbuf), infp)) {
               ++lineno;
               if (listfp)
                       fprintf (listfp, "%3d:  %s", lineno, inbuf);
               err_listed = 0;
               parse ();
               if (ntokens) {
#ifdef DUMP_TOKENS
                       int     i;

                       fprintf (listfp, "      %d tokens\n", ntokens);
                       for (i = 0; i < ntokens; ++i) {
                               fprintf (listfp, "      %d: ", i);
                               if (tokens[i].type)
                                       fprintf (listfp,"'%c'\n", tokens[i].type);
                               else
                                       fprintf (listfp, "%s\n", tokens[i].name);
                       }
#endif
                       if (ntokens >= 2 && tokens[0].type == 0 &&
                           tokens[1].type == ':') {
                               define_symbol (tokens[0].name, dsps, S_LABEL, F_DEFINED);
                               tokenix += 2;
                       }
                       if (tokenix < ntokens)
                               process ();
               }

       }
       close_script ();
       emit_symbols ();
       if (outfp && !partial_flag) {
               fprintf (outfp, "\nu_int32_t INSTRUCTIONS = 0x%08x;\n", ninsts);
               fprintf (outfp, "u_int32_t PATCHES = 0x%08x;\n", npatches);
               fprintf (outfp, "u_int32_t LABELPATCHES[] = {\n");
               p = patches;
               while (p) {
                       fprintf (outfp, "\t0x%08x,\n", p->offset / 4);
                       p = p->next;
               }
               fprintf (outfp, "};\n\n");
       }
       list_symbols ();
       exit(0);
}

void setarch(char *val)
{
       switch (atoi(val)) {
       case 700:
               arch = ARCH700;
               break;
       case 710:
               arch = ARCH710;
               break;
       case 720:
               arch = ARCH720;
               break;
       case 810:
               arch = ARCH810;
               break;
       case 825:
               arch = ARCH825;
               break;
       default:
               arch = 0;
       }
}

void emit_symbols ()
{
       int     i;
       struct  patchlist *p;

       if (nsymbols == 0 || outfp == NULL)
               return;

       for (i = 0; i < nsymbols; ++i) {
               char    *code;
               if ((symbols[i].flags & F_DEFINED) == 0 &&
                   symbols[i].type != S_EXTERNAL) {
                       fprintf(stderr, "warning: symbol %s undefined\n",
                           symbols[i].name);
               }
               if (symbols[i].type == S_ABSOLUTE)
                       code = "A_";
               else if (symbols[i].type == S_RELATIVE)
                       code = "R_";
               else if (symbols[i].type == S_EXTERNAL)
                       code = "E_";
               else if (symbols[i].flags & F_ENTRY)
                       code = "Ent_";
               else
                       continue;
               fprintf (outfp, "#define\t%s%s\t0x%08x\n", code, symbols[i].name,
                       symbols[i].value);
               if (symbols[i].flags & F_ENTRY || symbols[i].patchlist == NULL)
                       continue;
               fprintf (outfp, "u_int32_t %s%s_Used[] = {\n", code, symbols[i].name);
#if 1
               p = symbols[i].patchlist;
               while (p) {
                       fprintf (outfp, "\t0x%08x,\n", p->offset / 4);
                       p = p->next;
               }
#endif
               fprintf (outfp, "};\n\n");
       }
       /* patches ? */
}

void list_symbols ()
{
       int     i;

       if (nsymbols == 0 || listfp == NULL)
               return;
       fprintf (listfp, "\n\nValue     Type     Symbol\n");
       for (i = 0; i < nsymbols; ++i) {
               fprintf (listfp, "%08x: %-8s %s\n", symbols[i].value,
                       stypes[symbols[i].type], symbols[i].name);
       }
}

void errout (char *text)
{
       error_line();
       fprintf (errfp, "*** %s ***\n", text);
}

void parse ()
{
       char *p = inbuf;
       char c;
       char string[64];
       char *s;

       ntokens = tokenix = 0;
       while (1) {
               while ((c = *p++) && c != '\n' && (c <= ' ' || c == '\t'))
                       ;
               if (c == '\n' || c == 0 || c == ';')
                       break;
               if (ntokens >= MAXTOKENS) {
                       errout ("Token table full");
                       break;
               }
               if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
                   (c >= 'A' && c <= 'Z') || c == '$' || c == '_') {
                       s = string;
                       *s++ = c;
                       while (((c = *p) >= '0' && c <= '9') ||
                           (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
                           c == '_' || c == '$') {
                               *s++ = *p++;
                       }
                       *s = 0;
                       tokens[ntokens].name = malloc (strlen (string) + 1);
                       strcpy (tokens[ntokens].name, string);
                       tokens[ntokens].type = 0;
               }
               else {
                       tokens[ntokens].type = c;
               }
               ++ntokens;
       }
       return;
}

void    process ()
{
       int     i;

       if (tokens[tokenix].type) {
               error_line();
               fprintf (errfp, "Error: expected directive, found '%c'\n",
                       tokens[tokenix].type);
               return;
       }
       for (i = 0; directives[i].name; ++i) {
               if (strcmpi (directives[i].name, tokens[tokenix].name) == 0)
                       break;
       }
       if (directives[i].name == NULL) {
               error_line();
               fprintf (errfp, "Error: expected directive, found \"%s\"\n",
                       tokens[tokenix].name);
               return;
       }
       if (directives[i].func == NULL) {
               error_line();
               fprintf (errfp, "No function for directive \"%s\"\n", tokens[tokenix].name);
       } else {
#if 0
               fprintf (listfp, "Processing directive \"%s\"\n", directives[i].name);
#endif
               ++tokenix;
               (*directives[i].func) ();
       }
}

void define_symbol (char *name, u_int32_t value, short type, short flags)
{
       int     i;
       struct patchlist *p;

       for (i = 0; i < nsymbols; ++i) {
               if (symbols[i].type == type && strcmp (symbols[i].name, name) == 0) {
                       if (symbols[i].flags & F_DEFINED) {
                               error_line();
                               fprintf (errfp, "*** Symbol \"%s\" multiply defined\n",
                                       name);
                       } else {
                               symbols[i].flags |= flags;
                               symbols[i].value = value;
                               p = symbols[i].patchlist;
                               while (p) {
                                       if (p->offset > dsps)
                                               errout ("Whoops\007");
                                       else
                                               script[p->offset / 4] += dsps;
                                       p = p->next;
                               }
                       }
                       return;
               }
       }
       if (nsymbols >= MAXSYMBOLS) {
               errout ("Symbol table full");
               return;
       }
       symbols[nsymbols].type = type;
       symbols[nsymbols].flags = flags;
       symbols[nsymbols].value = value;
       symbols[nsymbols].patchlist = NULL;
       symbols[nsymbols].name = malloc (strlen (name) + 1);
       strcpy (symbols[nsymbols].name, name);
       ++nsymbols;
}

void patch_label (void)
{
       struct patchlist *p, **h;

       h = &patches;
       while(*h)
               h = &(*h)->next;
       p = (struct patchlist *) malloc (sizeof (struct patchlist));
       *h = p;
       p->next = NULL;
       p->offset = dsps + 4;
       npatches++;
}

void close_script ()
{
       int     i;

       if (dsps == 0)
               return;
       if (outfp) {
               fprintf (outfp, "const u_int32_t %s[] = {\n", script_name);
               for (i = 0; i < dsps / 4; i += 2) {
                       fprintf (outfp, "\t0x%08x, 0x%08x", script[i],
                               script[i + 1]);
                       /* check for memory move instruction */
                       if ((script[i] & 0xe0000000) == 0xc0000000)
                               fprintf (outfp, ", 0x%08x,", script[i + 2]);
                       else
                               if ((i + 2) <= dsps / 4) fprintf (outfp, ",\t\t");
                       fprintf (outfp, "\t/* %03x - %3d */\n", i * 4, i * 4);
                       if ((script[i] & 0xe0000000) == 0xc0000000)
                               ++i;
               }
               fprintf (outfp, "};\n\n");
       }
       dsps = 0;
}

void new_script (char *name)
{
       close_script ();
       script_name = malloc (strlen (name) + 1);
       strcpy (script_name, name);
}

int     reserved (char *string, int t)
{
       if (tokens[t].type == 0 && strcmpi (tokens[t].name, string) == 0)
               return (1);
       return (0);
}

int     CheckPhase (int t)
{
       int     i;

       for (i = 0; i < 8; ++i) {
               if (reserved (phases[i], t)) {
                       inst0 |= i << 24;
                       return (1);
               }
       }
       return (0);
}

int     CheckRegister (int t)
{
       int     i;

       if (arch <= 0) {
               errout("'ARCH' statement missing");
               return -1;
       }
       for (i = 0; i < (sizeof(regs) / sizeof(regs[0])); i++) {
               if (regs[i].addr[arch - 1] >= 0 && reserved(regs[i].name, t))
                       return regs[i].addr[arch-1];
       }
       return (-1);
}

int     expression (int *t)
{
       int     value;
       int     i = *t;

       value = evaluate (i++);
       while (i < ntokens) {
               if (tokens[i].type == '+')
                       value += evaluate (i + 1);
               else if (tokens[i].type == '-')
                       value -= evaluate (i + 1);
               else
                       errout ("Unknown identifier");
               i += 2;
       }
       *t = i;
       return (value);
}

int     evaluate (t)
{
       int     value;
       char    *name;

       if (tokens[t].type) {
               errout ("Expected an identifier");
               return (0);
       }
       name = tokens[t].name;
       if (*name >= '0' && *name <= '9')
               value = number (name);
       else
               value = lookup (name);
       return (value);
}

int     number (char *s)
{
       int     value;
       int     n;
       int     radix;

       radix = 10;
       if (*s == '0') {
               ++s;
               radix = 8;
               switch (*s) {
               case 'x':
               case 'X':
                       radix = 16;
                       break;
               case 'b':
               case 'B':
                       radix = 2;
               }
               if (radix != 8)
                       ++s;
       }
       value = 0;
       while (*s) {
               n = *s++;
               if (n >= '0' && n <= '9')
                       n -= '0';
               else if (n >= 'a' && n <= 'f')
                       n -= 'a' - 10;
               else if (n >= 'A' && n <= 'F')
                       n -= 'A' - 10;
               else {
                       error_line();
                       fprintf (errfp, "*** Expected digit\n");
                       n = 0;
               }
               if (n >= radix)
                       errout ("Expected digit");
               else
                       value = value * radix + n;
       }
       return (value);
}

int     lookup (char *name)
{
       int     i;
       struct patchlist *p;

       for (i = 0; i < nsymbols; ++i) {
               if (strcmp (name, symbols[i].name) == 0) {
                       if ((symbols[i].flags & F_DEFINED) == 0) {
                               p = (struct patchlist *) &symbols[i].patchlist;
                               while (p->next)
                                       p = p->next;
                               p->next = (struct patchlist *) malloc (sizeof (struct patchlist));
                               p = p->next;
                               p->next = NULL;
                               p->offset = dsps + 4;
                       }
                       return ((int) symbols[i].value);
               }
       }
       if (nsymbols >= MAXSYMBOLS) {
               errout ("Symbol table full");
               return (0);
       }
       symbols[nsymbols].type = S_LABEL;       /* assume forward reference */
       symbols[nsymbols].flags = 0;
       symbols[nsymbols].value = 0;
       p = (struct patchlist *) malloc (sizeof (struct patchlist));
       symbols[nsymbols].patchlist = p;
       p->next = NULL;
       p->offset = dsps + 4;
       symbols[nsymbols].name = malloc (strlen (name) + 1);
       strcpy (symbols[nsymbols].name, name);
       ++nsymbols;
       return (0);
}

void    f_arch (void)
{
       int i, archsave;

       i = tokenix;

       archsave = arch;
       setarch(tokens[i].name);
       if( arch == 0) {
               errout("Unrecognized ARCH");
               arch = archsave;
       }
}

void    f_proc (void)
{
       if (tokens[tokenix].type != 0 || tokens[tokenix + 1].type != ':')
               errout ("Invalid PROC statement");
       else
               new_script (tokens[tokenix].name);
}

void    f_pass (void)
{
       errout ("PASS option not implemented");
}

/*
*      f_list:  process list of symbols for the ENTRY and EXTERNAL directive
*/

void    f_list (void)
{
       int     i;
       short   type;
       short   flags;

       type = strcmpi (tokens[tokenix-1].name, "ENTRY") ? S_EXTERNAL : S_LABEL;
       flags = type == S_LABEL ? F_ENTRY : 0;
       for (i = tokenix; i < ntokens; ++i) {
               if (tokens[i].type != 0) {
                       errout ("Expected an identifier");
                       return;
               }
               define_symbol (tokens[i].name, 0, type, flags);
               if (i + 1 < ntokens) {
                       if (tokens[++i].type == ',')
                               continue;
                       errout ("Expected a separator");
                       return;
               }
       }
}

/*
*      f_define:       process list of definitions for ABSOLUTE and RELATIVE directive
*/

void    f_define (void)
{
       int     i;
       char    *name;
       u_int32_t value;
       int     type;

       type = strcmpi (tokens[tokenix-1].name, "ABSOLUTE") ? S_RELATIVE : S_ABSOLUTE;
       i = tokenix;
       while (i < ntokens) {
               if (tokens[i].type) {
                       errout ("Expected an identifier");
                       return;
               }
               if (tokens[i + 1].type != '=') {
                       errout ("Expected a separator");
                       return;
               }
               name = tokens[i].name;
               i += 2;
               value = expression (&i);
               define_symbol (name, value, type, F_DEFINED);
       }
}

void    store_inst ()
{
       int     i = dsps / 4;
       int     l = 8;

       if ((inst0 & 0xe0000000) == 0xc0000000)
               l = 12;                 /* Memory to memory move is 12 bytes */
       if ((dsps + l) / 4 > MAXINST) {
               errout ("Instruction table overflow");
               return;
       }
       script[i++] = inst0;
       script[i++] = inst1;
       if (l == 12)
               script[i++] = inst2;
       if (listfp) {
               fprintf (listfp, "\t%04x: %08x %08x", dsps, inst0, inst1);
               if (l == 12)
                       fprintf (listfp, " %08x", inst2);
               fprintf (listfp, "\n");
       }
       dsps += l;
       inst0 = inst1 = inst2 = 0;
       ++ninsts;
}

void    f_move (void)
{
       if (reserved ("memory", tokenix))
               memory_to_memory ();
       else if (reserved ("from", tokenix) || tokens[tokenix+1].type == ',')
               block_move ();
       else
               register_write ();
       store_inst ();
}

void    f_jump (void)
{
       transfer (0x80000000, 0);
}

void    f_call (void)
{
       transfer (0x88000000, 0);
}

void    f_return (void)
{
       transfer (0x90000000, 1);
}

void    f_int (void)
{
       transfer (0x98000000, 2);
}

void    f_intfly (void)
{
       transfer (0x98100000, 2);
}

void    f_select (void)
{
       int     t = tokenix;

       if (reserved ("atn", t)) {
               inst0 = 0x01000000;
               ++t;
       }
       select_reselect (t);
}

void    f_reselect (void)
{
       select_reselect (tokenix);
}

void    f_wait (void)
{
       int     i = tokenix;

       inst1 = 0;
       if (reserved ("disconnect", i)) {
               inst0 = 0x48000000;
       }
       else {
               if (reserved ("reselect", i))
                       inst0 = 0x50000000;
               else if (reserved ("select", i))
                       inst0 = 0x50000000;
               else
                       errout ("Expected SELECT or RESELECT");
               ++i;
               if (reserved ("rel", i)) {
#if 0 /* driver will fix relative dsps to absolute */
                       if (arch < ARCH710) {
                               errout ("Wrong arch for relative dsps");
                       }
#endif
                       i += 2;
                       inst1 = evaluate (i) - dsps - 8;
                       inst0 |= 0x04000000;
               }
               else {
                       inst1 = evaluate (i);
                       patch_label();
               }
       }
       store_inst ();
}

void    f_disconnect (void)
{
       inst0 = 0x48000000;
       store_inst ();
}

void    f_set (void)
{
       set_clear (0x58000000);
}

void    f_clear (void)
{
       set_clear (0x60000000);
}

void    f_load (void)
{
       inst0 = 0xe1000000;
       if (arch < ARCH810) {
               errout ("Wrong arch for load/store");
               return;
       }
       loadstore(tokenix);
}

void    f_store (void)
{
       int i;
       inst0 = 0xe0000000;
       if (arch < ARCH810) {
               errout ("Wrong arch for load/store");
               return;
       }
       i = tokenix;
       if (reserved("noflush", i)) {
               inst0 |= 0x2000000;
               i++;
       }
       loadstore(i);
}

void    f_nop (void)
{
       inst0 = 0x80000000;
       inst1 = 0x00000000;
       store_inst ();
}

void loadstore(int i)
{
       int reg, size;

       reg = CheckRegister(i);
       if (reg < 0)
               errout ("Expected register");
       else
               inst0 |= reg <<  16;
       if (reg == 8)
               errout ("Register can't be SFBR");
       i++;
       if (tokens[i].type == ',')
               i++;
       else
               errout ("expected ','");
       size = evaluate(i);
       if (i < 1 || i > 4)
               errout("wrong size");
       if ((reg & 0x3) + size > 4)
               errout("size too big for register");
       inst0 |= size;
       i++;
       if (tokens[i].type == ',')
               i++;
       else
               errout ("expected ','");
       if (reserved("from", i) || reserved("dsarel", i)) {
               if (arch < ARCH710) {
                       errout ("Wrong arch for table indirect");
                       return;
               }
               i++;
               inst0 |= 0x10000000;
       }
       inst1 = evaluate(i);
       store_inst ();
}

void    transfer (int word0, int type)
{
       int     i;

       i = tokenix;
       inst0 = word0;
       if (type == 0 && reserved ("rel", i)) {
#if 0 /* driver will fix relative dsps to absolute */
               if (arch < ARCH710) {
                       errout ("Wrong arch for relative dsps");
               }
#endif
               inst1 = evaluate (i + 2) - dsps - 8;
               i += 4;
               inst0 |= 0x00800000;
       }
       else if (type != 1) {
               inst1 = evaluate (i);
               ++i;
               if (type == 0)
                       patch_label();
       }
       if (i >= ntokens) {
               inst0 |= 0x00080000;
               store_inst ();
               return;
       }
       if (tokens[i].type != ',')
               errout ("Expected a separator, ',' assumed");
       else
               ++i;
       if (reserved("when", i))
               inst0 |= 0x00010000;
       else if (reserved ("if", i) == 0) {
               errout ("Expected a reserved word");
               store_inst ();
               return;
       }
       i++;
       if (reserved("false", i)) {
               store_inst ();
               return;
       }
       if (reserved ("not", i))
               ++i;
       else
               inst0 |= 0x00080000;
       if (reserved ("atn", i)) {
               inst0 |= 0x00020000;
               ++i;
       } else if (CheckPhase (i)) {
               inst0 |= 0x00020000;
               ++i;
       }
       if (i < ntokens && tokens[i].type != ',') {
               if (inst0 & 0x00020000) {
                       if (inst0 & 0x00080000 && reserved ("and", i)) {
                               ++i;
                       }
                       else if ((inst0 & 0x00080000) == 0 && reserved ("or", i)) {
                               ++i;
                       }
                       else
                               errout ("Expected a reserved word");
               }
               inst0 |= 0x00040000 + (evaluate (i++) & 0xff);
       }
       if (i < ntokens) {
               if (tokens[i].type == ',')
                       ++i;
               else
                       errout ("Expected a separator, ',' assumed");
               if (reserved ("and", i) && reserved ("mask", i + 1))
                       inst0 |= ((evaluate (i + 2) & 0xff) << 8);
               else
                       errout ("Expected , AND MASK");
       }
       store_inst ();
}

void    select_reselect (int t)
{
       inst0 |= 0x40000000;            /* ATN may be set from SELECT */
       if (reserved ("from", t)) {
               if (arch < ARCH710) {
                       errout ("Wrong arch for table indirect");
                       return;
               }
               ++t;
               inst0 |= 0x02000000 | evaluate (t++);
       }
       else
               inst0 |= (evaluate (t++) & 0xff) << 16;
       if (tokens[t++].type == ',') {
               if (reserved ("rel", t)) {
#if 0 /* driver will fix relative dsps to absolute */
                       if (arch < ARCH710) {
                               errout ("Wrong arch for relative dsps");
                       }
#endif
                       inst0 |= 0x04000000;
                       inst1 = evaluate (t + 2) - dsps - 8;
               }
               else {
                       inst1 = evaluate (t);
                       patch_label();
               }
       }
       else
               errout ("Expected separator");
       store_inst ();
}

void    set_clear (u_int32_t code)
{
       int     i = tokenix;
       short   need_and = 0;

       inst0 = code;
       while (i < ntokens) {
               if (need_and) {
                       if (reserved ("and", i))
                               ++i;
                       else
                               errout ("Expected AND");
               }
               if (reserved ("atn", i)) {
                       inst0 |= 0x0008;
                       ++i;
               }
               else if (reserved ("ack", i)) {
                       inst0 |= 0x0040;
                       ++i;
               }
               else if (reserved ("target", i)) {
                       inst0 |= 0x0200;
                       ++i;
               }
               else if (reserved ("carry", i)) {
                       inst0 |= 0x0400;
                       ++i;
               }
               else
                       errout ("Expected ATN, ACK, TARGET or CARRY");
               need_and = 1;
       }
       store_inst ();
}

void    block_move ()
{
       if (reserved ("from", tokenix)) {
               if (arch < ARCH710) {
                       errout ("Wrong arch for table indirect");
                       return;
               }
               inst1 = evaluate (tokenix+1);
               inst0 |= 0x10000000 | inst1;    /*** ??? to match Zeus script */
               tokenix += 2;
       }
       else {
               inst0 |= evaluate (tokenix++);  /* count */
               tokenix++;                      /* skip ',' */
               if (reserved ("ptr", tokenix)) {
                       ++tokenix;
                       inst0 |= 0x20000000;
               }
               inst1 = evaluate (tokenix++);   /* address */
       }
       if (tokens[tokenix].type != ',')
               errout ("Expected separator");
       if (reserved ("when", tokenix + 1)) {
               inst0 |= 0x08000000;
               CheckPhase (tokenix + 2);
       }
       else if (reserved ("with", tokenix + 1)) {
               CheckPhase (tokenix + 2);
       }
       else
               errout ("Expected WITH or WHEN");
}

void    register_write ()
{
       /*
        * MOVE reg/data8 TO reg                        register write
        * MOVE reg <op> data8 TO reg                   register write
        * MOVE reg + data8 TO reg WITH CARRY           register write
        */
       int     op;
       int     reg;
       int     data;

       if (reserved ("to", tokenix+1))
               op = 0;
       else if (reserved ("shl", tokenix+1))
               op = 1;
       else if (reserved ("shr", tokenix+1))
               op = 5;
       else if (tokens[tokenix+1].type == '|')
               op = 2;
       else if (reserved ("xor", tokenix+1))
               op = 3;
       else if (tokens[tokenix+1].type == '&')
               op = 4;
       else if (tokens[tokenix+1].type == '+')
               op = 6;
       else if (tokens[tokenix+1].type == '-')
               op = 8;
       else
               errout ("Unknown register operator");
       switch (op) {
       case 2:
       case 3:
       case 4:
       case 6:
       case 8:
               if (reserved ("to", tokenix+3) == 0)
                       errout ("Register command expected TO");
       }
       reg = CheckRegister (tokenix);
       if (reg < 0) {                  /* Not register, must be data */
               data = evaluate (tokenix);
               if (op)
                       errout ("Register operator not move");
               reg = CheckRegister (tokenix+2);
               if (reg < 0)
                       errout ("Expected register");
               inst0 = 0x78000000 | (data << 8) | reg << 16;
#if 0
fprintf (listfp, "Move data to register: %02x %d\n", data, reg);
#endif
       } else if (op) {
               switch (op) {
               case 2:
               case 3:
               case 4:
               case 6:
               case 8:
                       inst0 = 0;
                       /* A register read/write operator */
                       if (reserved("sfbr", tokenix+2)) {
                               if (arch < ARCH825)
                                       errout("wrong arch for add with SFBR");
                               if (op == 8)
                                       errout("can't subtract SFBR");
                               inst0 |= 0x00800000;
                               data = 0;
                       } else
                               data = evaluate (tokenix+2);
                       if (tokenix+5 < ntokens) {
                               if (!reserved("with", tokenix+5) ||
                                   !reserved("carry", tokenix+6)) {
                                       errout("Expected 'WITH CARRY'");
                               } else if (op != 6) {
                                       errout("'WITH CARRY' only valide "
                                           "with '+'");
                               }
                               op = 7;
                       }
                       if (op == 8) {
                               data = -data;
                               op = 6;
                       }
                       inst0 |= (data & 0xff) << 8;
                       data = CheckRegister (tokenix+4);
                       break;
               default:
                       data = CheckRegister (tokenix+2);
                       break;
               }
               if (data < 0)
                       errout ("Expected register");
               if (reg != data && reg != 8 && data != 8)
                       errout ("One register MUST be SBFR");
               if (reg == data) {      /* A register read/modify/write */
#if 0
fprintf (listfp, "Read/modify register: %02x %d %d\n", inst0 >> 8, op, reg);
#endif
                       inst0 |= 0x78000000 | (op << 24) | (reg << 16);
               }
               else {                  /* A move to/from SFBR */
                       if (reg == 8) { /* MOVE SFBR <> TO reg */
#if 0
fprintf (listfp, "Move SFBR to register: %02x %d %d\n", inst0 >> 8, op, data);
#endif
                               inst0 |= 0x68000000 | (op << 24) | (data << 16);
                       }
                       else {
#if 0
fprintf (listfp, "Move register to SFBR: %02x %d %d\n", inst0 >> 8, op, reg);
#endif
                               inst0 |= 0x70000000 | (op << 24) | (reg << 16);
                       }
               }
       } else {                                /* register to register */
               data = CheckRegister (tokenix+2);
               if (data < 0)
                       errout ("Expected register");
               if (reg == 8)           /* move SFBR to reg */
                       inst0 = 0x6a000000 | (data << 16);
               else if (data == 8)     /* move reg to SFBR */
                       inst0 = 0x72000000 | (reg << 16);
               else
                       errout ("One register must be SFBR");
       }
}

void    memory_to_memory ()
{
       inst0 = 0xc0000000 + evaluate (tokenix+1);
       inst1 = evaluate (tokenix+3);
       /*
        * need to hack dsps, otherwise patch offset will be wrong for
        * second pointer
        */
       dsps += 4;
       inst2 = evaluate (tokenix+5);
       dsps -= 4;
}

void    error_line()
{
       if (errfp != listfp && errfp && err_listed == 0) {
               fprintf (errfp, "%3d:  %s", lineno, inbuf);
               err_listed = 1;
       }
}

char *  makefn (base, sub)
       char *base;
       char *sub;
{
       char *fn;

       fn = malloc (strlen (base) + strlen (sub) + 2);
       strcpy (fn, base);
       base = strrchr(fn, '.');
       if (base)
               *base = 0;
       strcat (fn, ".");
       strcat (fn, sub);
       return (fn);
}

void    usage()
{
       fprintf (stderr, "usage: scc sourcfile [options]\n");
       exit(1);
}