Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* NCR53c8xx assembler */
%{
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <ctype.h>

#include "na.h"

#define COND_WAIT (1L << 16)
#define COND_TRUE (1L << 19)
#define COND_INTFLY (1L << 20)
#define COND_CARRY (1L << 21)
#define COND_REL (1L << 23)
#define COND_PHASE (1L << 17)
#define COND_DATA (1L << 18)

#define IO_REL (1L << 26)

#define MOVE_MODE (1L << 27)

int yylex(void);
int yyparse(void);
void assemble(void);
void yyerror(char *, ...);
void yywarn(char *, ...);
void p2error(int line, char *);

struct addr {
        int type; /* 0 - direct, 1 - indirect 2 - table indirect */
        unsigned long offset;
};

typedef enum Type { Const, Addr, Table, Extern, Reg, Unknown, Error } Type;

struct sym {
        char *name;
        int set;
        Type t;
        long value;
        struct sym *next;
};

struct sym *findsym(char *name);
struct sym *symlist;

void newsym(struct sym *s, Type t, long v);

struct binary {
        char len;
        unsigned long data[3];
        unsigned char patch[3];
};

#define MAXCPPOPTS 30
#define MAX_PATCHES 1000
struct na_patch patch[MAX_PATCHES];
int patches;

struct binary out;

struct expval {
        Type t;
        long value;
};

struct expval eval(struct expval a, struct expval b, char op);

int patchtype(Type t);
void fixup(void);

unsigned dot;
unsigned externs;
int errors, warnings;
struct sym *externp[100];

void regmove(unsigned char src_reg, unsigned char op,
    unsigned char dst_reg, struct expval *imm);

void preprocess(char *in, FILE *out);

int mk24bitssigned(long *l);
long mkreladdr(long value, int len);
long chkreladdr(int d, struct expval *e, int len, long relrv);
int pass2;
FILE *in_f;

int yyline = 0;
char yyfilename[200];
char line[500];
char *cppopts[MAXCPPOPTS];
int ncppopts;
int wflag;
%}

%union {
        long n;
        struct sym *s;
        struct expval e;
}

%token NUM MOVE WHEN SYMBOL SELECT WAIT DISCONNECT RESELECT SET CLEAR
%token DATA_OUT DATA_IN COMMAND STATUS RESERVED_OUT RESERVED_IN MESSAGE_OUT
%token MESSAGE_IN WITH ATN FAIL CARRY TARGET ACK COMMENT TO
%token SCNTL0 SCNTL1 SCNTL2 SCNTL3 SCID SXFER SDID GPREG
%token SFBR SOCL SSID SBCL DSTAT SSTAT0 SSTAT1 SSTAT2
%token ISTAT CTEST0 CTEST1 CTEST2 CTEST3 TEMP DFIFO CTEST4 CTEST5 CTEST6
%token DBC DCMD DNAD DSP DSPS DMODE DIEN DWT DCNTL ADDER
%token SIEN0 SIEN1 SIST0 SIST1 SLPAR MACNTL GPCNTL STIME0 STIME1 RESPID
%token STEST0 STEST1 STEST2 STEST3 SIDL SODL SBDL
%token SHL SHR AND OR XOR ADD ADDC
%token JUMP CALL RETURN INT INTFLY NOT ABSOLUTE MASK IF REL PTR
%token TABLE FROM MEMORY NOP EXTERN
%token SCRATCHA0 SCRATCHA1 SCRATCHA2 SCRATCHA3
%token SCRATCHB0 SCRATCHB1 SCRATCHB2 SCRATCHB3
%token SCRATCHC0 SCRATCHC1 SCRATCHC2 SCRATCHC3
%token DSA0 DSA1 DSA2 DSA3
%token DEFW

%left '-' '+'
%left '*' '/'
%left NEG     /* negation--unary minus */
%right '^'    /* exponentiation        */
%type <n> NUM phase .atn set_list set_bit regA reg
%type <n> set_cmd .cond condsfbr condphase
%type <n> jump_or_call .ptr
%type <s> SYMBOL
%type <e> exp byteexp regexp

/* Grammar follows */
%%
input:    /* empty string */
        | input line
;

line:   .label .opcode .comment '\n'
        {
                if (pass2) {
                        int x;
                        for (x = 0; x < out.len; x++) {
                                printf("/* %.4x */ 0x%.8lxL,",
                                    dot, out.data[x]);
                                if (x == 0) {
                                        printf(" /*\t");
                                        fwrite(line,
                                            strlen(line) - 1, 1, stdout);
                                        printf(" */");
                                }
                                printf("\n");
                                if (out.patch[x]) {
                                        patch[patches].lwoff = dot / 4;
                                        patch[patches].type = out.patch[x];
                                        patches++;
                                }
                                dot += 4;
                        }
                }
                else
                        dot += 4 * out.len;
        }
        | ABSOLUTE SYMBOL '=' exp .comment '\n'
        {
                setsym($2, $4.t, $4.value);
                if (pass2) {
                        printf("\t\t\t/*\t");
                        fwrite(line, strlen(line) - 1, 1, stdout);
                        printf(" */\n");
                }
        }
        | SYMBOL '=' exp .comment '\n'
        {
                setsym($1, $3.t, $3.value);
                if (pass2) {
                        printf("\t\t\t/*\t");
                        fwrite(line, strlen(line) - 1, 1, stdout);
                        printf(" */\n");
                }
        }
        | EXTERN SYMBOL {
                if (pass2) {
                        printf("\t\t\t/*\t");
                        fwrite(line, strlen(line) - 1, 1, stdout);
                        printf(" */\n");
                }
                else {
                        if (!pass2)
                                externp[externs] = $2;
                        setsym($2, Extern, externs++);
                }
        }
        ;

.comment: COMMENT
        | /* nothing */
        ;

.label: SYMBOL ':' {
                if ($1->t != Unknown)
                {
                        if (!pass2)
                                yyerror("multiply defined symbol");
                }
                else {
                        $1->t = Addr;
                        $1->value = dot;
                }
        }
        | /* nothing */
        ;

set_cmd: SET { $$ = 3; }
        | CLEAR { $$ = 4; }
        ;

set_bit: CARRY { $$ = 0x400; }
        | TARGET { $$ = 0x200; }
        | ACK { $$ = 0x40; }
        | ATN { $$ = 0x8; }
        ;
        
set_list: set_list ',' set_bit { $$ = $1 | $3; }
        | set_list AND set_bit { $$ = $1 | $3; }
        | set_bit { $$ = $1; }
        ;

opcode: set_cmd set_list {
                out.len = 2;
                out.data[0] = (1L << 30) | ((long)$1 << 27) | $2;
                out.data[1] = 0;
                out.patch[0] = out.patch[1] = 0;
        }
        | DISCONNECT
        {
                out.len = 2;
                out.data[0] = 0x48020000L;
                out.data[1] = 0;
                out.patch[0] = out.patch[1] = 0;
        }
        | INT exp .cond {
                out.len = 2;
                out.data[0] = $3 | 0x98000000L;
                out.data[1] = $2.value;
                out.patch[0] = out.patch[1] = 0;
        }
        | INTFLY exp .cond {
                out.len = 2;
                out.data[0] = $3 | 0x98000000L | COND_INTFLY;
                out.data[1] = $2.value;
                out.patch[0] = out.patch[1] = 0;
        }
        | jump_or_call exp .cond {
                out.len = 2;
                out.data[0] = $1 | $3 | chkreladdr(1, &$2, 2, COND_REL);
                out.patch[0] = 0;
        }
        | jump_or_call REL '(' exp ')' .cond {
                out.len = 2;
                out.data[0] = $1 | $6 | COND_REL;
                out.data[1] = mkreladdr($4.value, 2);
                out.patch[0] = out.patch[1] = 0;
        }
        | MOVE exp ',' .ptr regexp ',' with_or_when phase {
                out.len = 2;
                out.data[0] = ($8 << 24) | $2.value | ($4 << 29) | MOVE_MODE;
                out.data[1] = $5.value;
                out.patch[0] = 0;
                out.patch[1] = patchtype($5.t);
        }
        | MOVE FROM exp ',' with_or_when phase {
                out.len = 2;
                out.data[0] = ($6 << 24) | (1L << 28) | MOVE_MODE;
                out.data[1] = $3.value;
                out.patch[0] = 0;
                out.patch[1] = patchtype($3.t);
        }
        | MOVE MEMORY exp ',' regexp ',' regexp {
                out.len = 3;
                out.data[0] = 0xc0000000L | $3.value;
                out.data[1] = $5.value;
                out.data[2] = $7.value;
                out.patch[0] = 0;
                out.patch[1] = patchtype($5.t);
                out.patch[2] = patchtype($7.t);
        }
        | MOVE regA TO regA             { regmove($2, 2, $4, 0); }      /* do reg to sfbr moves using or 0 */
        | MOVE exp TO regA              { regmove($4, 0, $4, &$2); }
        | MOVE regA '|' exp TO regA     { regmove($2, 2, $6, &$4); }
        | MOVE regA '&' exp TO regA     { regmove($2, 4, $6, &$4); }
        | MOVE regA '+' exp TO regA     { regmove($2, 6, $6, &$4); }
        | MOVE regA '-' exp TO regA     { regmove($2, 6, $6, &$4); }
        | MOVE regA '+' exp TO regA WITH CARRY  {
                regmove($2, 7, $6, &$4);
        }
        | MOVE regA '-' exp TO regA WITH CARRY  {
                $4.value = -$4.value;
                regmove($2, 7, $6, &$4);
        }
        | MOVE regA SHL TO regA         { regmove($2, 1, $5, 0); }
        | MOVE regA SHR TO regA         { regmove($2, 5, $5, 0); }
        | MOVE regA XOR exp TO regA     { regmove($2, 3, $6, &$4); }
        | NOP {
                out.len = 2;
                out.data[0] = 0x80000000L;
                out.data[1] = 0;
                out.patch[0] = out.patch[1] = 0;
        }
        | RESELECT exp ',' exp {
                out.len = 2;
                out.data[0] = 0x40000000L | ((long)$2.value << 16) | (1L << 9) | chkreladdr(1, &$4, 2, IO_REL);
                out.patch[0] = 0;
        }
        | RESELECT exp ',' REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x40000000L | IO_REL
                    | ((long)$2.value << 16) | (1L << 9);
                out.data[1] = mkreladdr($6.value, 2);
                out.patch[0] = out.patch[1] = 0;
        }
        | RESELECT FROM exp ',' exp {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 25) | $3.value | chkreladdr(1, &$5, 2, IO_REL);
                out.patch[0] = 5;
        }
        | RESELECT FROM exp ',' REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 25) | IO_REL | $3.value;
                out.patch[0] = 5;
                out.data[1] = mkreladdr($7.value, 2);
                out.patch[1] = 0;
        }
        | RETURN .cond {
                
                out.len = 2;
                out.data[0] = 0x90000000L | $2;
                out.data[1] = 0;
                out.patch[0] = out.patch[1] = 0;
        }
        | SELECT .atn exp ',' exp {
                out.len = 2;
                out.data[0] =
                    0x40000000L | ((long)$3.value << 16) | (1L << 9) | $2 | chkreladdr(1, &$5, 2, IO_REL);
                out.patch[0] = 0;
        }
        | SELECT .atn exp ',' REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 26)
                    | ((long)$3.value << 16) | (1L << 9) | $2;
                out.data[1] = mkreladdr($7.value, 2);
                out.patch[0] = out.patch[1] = 0;
        }
        | SELECT .atn FROM exp ',' exp {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 25) | $4.value | $2 | chkreladdr(1, &$6, 2, IO_REL);
                out.patch[0] = 5;
        }
        | SELECT .atn FROM exp ',' REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 25) | IO_REL | $4.value | $2;
                out.patch[0] = 5;
                out.data[1] = mkreladdr($8.value, 2);
                out.patch[1] = 0;
        }
        | WAIT DISCONNECT {
                out.len = 2;
                out.data[0] = 0x48000000L;
                out.data[1] = 0;
                out.patch[0] = out.patch[1] = 0;
        }
        | WAIT RESELECT exp {
                out.len = 2;
                out.data[0] = 0x50000000L | chkreladdr(1, &$3, 2, IO_REL);
                out.patch[0] = 0;
        }
        | WAIT RESELECT REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x50000000L | (1L << 26);
                out.data[1] = mkreladdr($5.value, 2);
                out.patch[0] = out.patch[1] = 0;
        }
        | WAIT SELECT exp {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 9) | chkreladdr(1, &$3, 2, IO_REL);
                out.patch[0] = 0;
        }
        | WAIT SELECT REL '(' exp ')' {
                out.len = 2;
                out.data[0] = 0x40000000L | (1L << 26) | (1L << 9);
                out.data[1] = mkreladdr($5.value, 2);
                out.patch[0] = out.patch[1] = 0;
        }
        | DEFW exp {
                out.len = 1;
                out.data[0] = $2.value;
                out.patch[0] = patchtype($2.t);
        }
        ;

.ptr:   PTR { $$ = 1; }
        | { $$ = 0; }
        ;

with_or_when: WITH
        | WHEN
        ;
        
jump_or_call: JUMP       { $$ = 0x80000000L; }
        | CALL           { $$ = 0x88000000L; }
        ;

condsfbr: byteexp { $$ = $1.value | COND_DATA; }
        | byteexp AND MASK byteexp { $$ = ($4.value << 8) | $1.value | COND_DATA; }
        ;

condphase: phase { $$ = ($1 << 24) | COND_PHASE; }

.cond:    ',' IF ATN { $$ = COND_TRUE; }
        | ',' IF condphase { $$ = $3 | COND_TRUE; }
        | ',' IF CARRY { $$ = COND_CARRY | COND_TRUE; }
        | ',' IF condsfbr { $$ = $3 | COND_TRUE; }
        | ',' IF ATN AND condsfbr { $$ = $5 | COND_TRUE; }
        | ',' IF condphase AND condsfbr { $$ = $3 | $5 | COND_TRUE; }
        | ',' WHEN condphase { $$ = $3 | COND_WAIT | COND_TRUE; }
        | ',' WHEN CARRY { $$ = COND_CARRY | COND_WAIT | COND_TRUE; }
        | ',' WHEN condsfbr { $$ = $3 | COND_WAIT | COND_TRUE; }
        | ',' WHEN condphase AND condsfbr { $$ = $3 | $5 | COND_WAIT | COND_TRUE; }
        | ',' IF NOT ATN { $$ = 0; }
        | ',' IF NOT condphase { $$ = $4; }
        | ',' IF NOT CARRY { $$ = COND_CARRY; }
        | ',' IF NOT condsfbr { $$ = $4; }
        | ',' IF NOT ATN OR condsfbr { $$ = $6; }
        | ',' IF NOT condphase OR condsfbr { $$ = $4 | $6; }
        | ',' WHEN NOT condphase { $$ = $4 | COND_WAIT; }
        | ',' WHEN NOT CARRY { $$ = COND_CARRY | COND_WAIT; }
        | ',' WHEN NOT condsfbr { $$ = $4 | COND_WAIT; }
        | ',' WHEN NOT condphase OR condsfbr { $$ = $4 | $6 | COND_WAIT; }
        | { $$ = COND_TRUE; }
        ;

.opcode: opcode
        | { out.len = 0; }
        ;

regA:   reg
        | SFBR { $$ = 8; }
        ;

reg:      SCNTL0        { $$ = 0; }
        | SCNTL1        { $$ = 1; }
        | SCNTL2        { $$ = 2; }
        | SCNTL3        { $$ = 3; }
        | SCID          { $$ = 4; }
        | SXFER         { $$ = 5; }
        | SDID          { $$ = 6; }
        | GPREG         { $$ = 7; }
        | SOCL          { $$ = 9; }
        | SSID          { $$ = 0xa; }
        | SBCL          { $$ = 0xb; }
        | DSTAT         { $$ = 0xc; }
        | SSTAT0        { $$ = 0xd; }
        | SSTAT1        { $$ = 0xe; }
        | SSTAT2        { $$ = 0xf; }
        | DSA0          { $$ = 0x10; }
        | DSA1          { $$ = 0x11; }
        | DSA2          { $$ = 0x12; }
        | DSA3          { $$ = 0x13; }
        | ISTAT         { $$ = 0x14; }
        | CTEST0        { $$ = 0x18; }
        | CTEST1        { $$ = 0x19; }
        | CTEST2        { $$ = 0x1a; }
        | CTEST3        { $$ = 0x1b; }
        | TEMP          { $$ = 0x1c; }
        | DFIFO         { $$ = 0x20; }
        | CTEST4        { $$ = 0x21; }
        | CTEST5        { $$ = 0x22; }
        | CTEST6        { $$ = 0x23; }
        | DBC           { $$ = 0x24; }
        | DCMD          { $$ = 0x27; }
        | DNAD          { $$ = 0x28; }
        | DSP           { $$ = 0x2c; }
        | DSPS          { $$ = 0x30; }
        | SCRATCHA0     { $$ = 0x34; }
        | SCRATCHA1     { $$ = 0x35; }
        | SCRATCHA2     { $$ = 0x36; }
        | SCRATCHA3     { $$ = 0x37; }
        | DMODE         { $$ = 0x38; }
        | DIEN          { $$ = 0x39; }
        | DWT           { $$ = 0x3a; }
        | DCNTL         { $$ = 0x3b; }
        | ADDER         { $$ = 0x3c; }
        | SIEN0         { $$ = 0x40; }
        | SIEN1         { $$ = 0x41; }
        | SIST0         { $$ = 0x42; }
        | SIST1         { $$ = 0x43; }
        | SLPAR         { $$ = 0x44; }
        | MACNTL        { $$ = 0x46; }
        | GPCNTL        { $$ = 0x47; }
        | STIME0        { $$ = 0x48; }
        | STIME1        { $$ = 0x49; }
        | RESPID        { $$ = 0x4a; }
        | STEST0        { $$ = 0x4c; }
        | STEST1        { $$ = 0x4d; }
        | STEST2        { $$ = 0x4e; }
        | STEST3        { $$ = 0x4f; }
        | SIDL          { $$ = 0x50; }
        | SODL          { $$ = 0x54; }
        | SBDL          { $$ = 0x58; }
        | SCRATCHB0     { $$ = 0x5c; }
        | SCRATCHB1     { $$ = 0x5d; }
        | SCRATCHB2     { $$ = 0x5e; }
        | SCRATCHB3     { $$ = 0x5f; }
        | SCRATCHC0     { $$ = 0x60; }
        | SCRATCHC1     { $$ = 0x61; }
        | SCRATCHC2     { $$ = 0x62; }
        | SCRATCHC3     { $$ = 0x63; }
        ;

.atn:   ATN             { $$ = (1 << 24); }
        | /* nothing */ { $$ = 0; }
;

phase:  DATA_OUT        { $$ = 0; }
        | DATA_IN       { $$ = 1; }
        | COMMAND       { $$ = 2; }
        | STATUS        { $$ = 3; }
        | RESERVED_OUT  { $$ = 4; }
        | RESERVED_IN   { $$ = 5; }
        | MESSAGE_OUT   { $$ = 6; }
        | MESSAGE_IN    { $$ = 7; }
;

byteexp: exp
        {
                if (pass2 && ($1.value < 0 || $1.value > 255)) {
                        if (wflag)
                                yywarn("conversion causes truncation");
                        $$.value = $1.value & 0xff;
                }
                else
                        $$.value = $1.value;
        }
        ;

regexp: exp
        | regA { $$.t = Reg; $$.value = $1; }
        ;

exp:    NUM { $$.t = Const; $$.value = $1; }
        | SYMBOL {
                $$.t = $1->t; $$.value = $1->value;
                if (pass2 && $1->t == Unknown)
                {
                        yyerror("Undefined symbol %s", $1->name);
                        $1->t = Error;
                        $1->value = 0;
                        $$.t = Error;
                        $$.value = 0;
                }
        }
        | exp '+' exp { $$ = eval($1, $3, '+'); }
        | exp '-' exp { $$ = eval($1, $3, '-'); }
        | exp '*' exp { $$ = eval($1, $3, '*'); }
        | exp '/' exp { $$ = eval($1, $3, '/'); }
        | '-' exp  %prec NEG { $$ = eval($2, $2, '_'); }
        | '(' exp ')'        { $$ = $2; }
        | '~' exp %prec NEG { $$ = eval($2, $2, '~'); }
        ;
%%

struct {
        char *name;
        int tok;
} toktab[] =
{
        { "when", WHEN },
        { "data_out", DATA_OUT },
        { "data_in", DATA_IN },
        { "msg_out", MESSAGE_OUT },
        { "msg_in", MESSAGE_IN },
        { "cmd", COMMAND },
        { "command", COMMAND },
        { "status", STATUS },
        { "move", MOVE },
        { "select", SELECT },
        { "reselect", RESELECT },
        { "disconnect", DISCONNECT },
        { "wait", WAIT },
        { "set", SET },
        { "clear", CLEAR },
        { "with", WITH },
        { "atn", ATN },
        { "fail", FAIL },
        { "carry", CARRY },
        { "target", TARGET },
        { "ack", ACK },
        { "scntl0", SCNTL0 },
        { "scntl1", SCNTL1 },
        { "scntl2", SCNTL2 },
        { "scntl3", SCNTL3 },
        { "scid", SCID },
        { "sxfer", SXFER },
        { "sdid", SDID },
        { "gpreg", GPREG },
        { "sfbr", SFBR },
        { "socl", SOCL },
        { "ssid", SSID },
        { "sbcl", SBCL },
        { "dstat", DSTAT },
        { "sstat0", SSTAT0 },
        { "sstat1", SSTAT1 },
        { "sstat2", SSTAT2 },
        { "dsa", DSA0 },
        { "dsa0", DSA0 },
        { "dsa1", DSA1 },
        { "dsa2", DSA2 },
        { "dsa3", DSA3 },
        { "istat", ISTAT },
        { "ctest0", CTEST0 },
        { "ctest1", CTEST1 },
        { "ctest2", CTEST2 },
        { "ctest3", CTEST3 },
        { "temp", TEMP },
        { "dfifo", DFIFO },
        { "ctest4", CTEST4 },
        { "ctest5", CTEST5 },
        { "ctest6", CTEST6 },
        { "dbc", DBC },
        { "dcmd", DCMD },
        { "dnad", DNAD },
        { "dsp", DSP },
        { "dsps", DSPS },
        { "scratcha", SCRATCHA0 },
        { "scratcha0", SCRATCHA0 },
        { "scratcha1", SCRATCHA1 },
        { "scratcha2", SCRATCHA2 },
        { "scratcha3", SCRATCHA3 },
        { "dmode", DMODE },
        { "dien", DIEN },
        { "dwt", DWT },
        { "dcntl", DCNTL },
        { "adder", ADDER },
        { "sien0", SIEN0 },
        { "sien1", SIEN1 },
        { "sist0", SIST0 },
        { "sist1", SIST1 },
        { "slpar", SLPAR },
        { "macntl", MACNTL },
        { "gpcntl", GPCNTL },
        { "stime0", STIME0 },
        { "stime1", STIME1 },
        { "respid", RESPID },
        { "stest0", STEST0 },
        { "stest1", STEST1 },
        { "stest2", STEST2 },
        { "stest3", STEST3 },
        { "sidl", SIDL },
        { "sodl", SODL },
        { "sbdl", SBDL },
        { "scratchb", SCRATCHB0 },
        { "scratchb0", SCRATCHB0 },
        { "scratchb1", SCRATCHB1 },
        { "scratchb2", SCRATCHB2 },
        { "scratchb3", SCRATCHB3 },
        { "scratchc", SCRATCHC0 },
        { "scratchc0", SCRATCHC0 },
        { "scratchc1", SCRATCHC1 },
        { "scratchc2", SCRATCHC2 },
        { "scratchc3", SCRATCHC3 },
        { "add", ADD },
        { "addc", ADDC },
        { "and", AND },
        { "or", OR },
        { "xor", XOR },
        { "shl", SHL },
        { "shr", SHR },
        { "jump", JUMP },
        { "call", CALL },
        { "return", RETURN },
        { "int", INT },
        { "intfly", INTFLY },
        { "not", NOT },
        { "absolute", ABSOLUTE },
        { "mask", MASK },
        { "if", IF },
        { "rel", REL },
        { "ptr", PTR },
        { "table", TABLE },
        { "from", FROM },
        { "memory", MEMORY },
        { "to", TO },
        { "nop", NOP },
        { "extern", EXTERN },
        { "defw", DEFW },
};

#define TOKS (sizeof(toktab)/sizeof(toktab[0]))

int lc;
int ll;

void
yyrewind(void)
{
        rewind(in_f);
        ll = lc = 0;
        yyline = 0;
        dot = 0;
}

int
yygetc(void)
{
        if (lc == ll)
        {
        next:
                if (fgets(line, 500, in_f) == 0)
                        return EOF;
                /* do nasty check for #line directives */
                if (strncmp(line, "#line", 5) == 0) {
                        /* #line n "filename" */
                        sscanf(line, "#line %d \"%[^\"]", &yyline, yyfilename);
                        yyline--;
                        goto next;
                }
                yyline++;
                ll = strlen(line);
                lc = 0;
        }
        return line[lc++];
}

void
yyungetc(void)
{
        if (lc <= 0)
                exits("ungetc");
        lc--;
}

int
yylex(void)
{
        char token[100];
        int tl = 0;
        int c;
        while ((c = yygetc()) != EOF && (c == ' ' || c == '\t'))
                ;
        if (c == EOF)
                return 0;
        if (isalpha(c) || c == '_')
        {
                int x;
                do {
                        token[tl++] = c;
                } while ((c = yygetc()) != EOF && (isalnum(c) || c == '_'));
                if (c == EOF)
                        return 0;
                yyungetc();
                token[tl] = 0;
                for (x = 0; x < TOKS; x++)
                        if (strcmp(toktab[x].name, token) == 0)
                                return toktab[x].tok;
                /* must be a symbol */
                yylval.s = findsym(token);
                return SYMBOL;
        }
        else if (isdigit(c))
        {
                /* accept 0x<digits> or 0b<digits> 0<digits> or <digits> */
                int prefix = c == '0';
                unsigned long n = c - '0';
                int base = 10;
                for (;;)
                {
                        c = yygetc();
                        if (c == EOF)
                                return 0;
                        if (prefix)
                        {
                                prefix = 0;
                                if (c == 'x') {
                                        base = 16;
                                        continue;
                                }
                                else if (c == 'b')
                                {
                                        base = 2;
                                        continue;
                                }
                                else
                                        base = 8;
                        }
                        if (isdigit(c))
                                c -= '0';
                        else if (isalpha(c) && base > 10)
                        {
                                if (isupper(c))
                                        c = tolower(c);
                                c = c - 'a' + 10;
                        }
                        else {
                                yyungetc();
                                yylval.n = n;
                                return NUM;
                        }
                        if (c >= base)
                                yyerror("illegal format number");
                        n = n * base + c;
                }
        }
        else if (c == ';') {
                /* skip to end of line */
                while ((c = yygetc()) != EOF && c != '\n')
                        ;
                if (c != EOF)
                        yyungetc();
                return COMMENT;
        }
        return c;
}

void
yyerror(char *s, ...)
{
        va_list ap;

        va_start(ap, s);
        fprintf(stderr, "%s: %d: ", yyfilename, yyline);
        vfprintf(stderr, s, ap);
        if (putc('\n', stderr) < 0)
                exits("io");
        errors++;
        va_end(ap);
}

void
yywarn(char *s, ...)
{
        va_list ap;

        va_start(ap, s);
        fprintf(stderr, "%s: %d: warning: ", yyfilename, yyline);
        vfprintf(stderr, s, ap);
        if (putc('\n', stderr) < 0)
                exits("io");
        warnings++;
        va_end(ap);
}

void
p2error(int line, char *s)
{
        USED(line);
        printf("/*\t%s */\n", s);
}

void
main(int argc, char *argv[])
{
        int a;
        for (a = 1; a < argc; a++)
        {
                if (argv[a][0] == '-')
                        switch (argv[a][1]) {
                        case 'D':
                                /* #defines for cpp */
                                if (ncppopts >= MAXCPPOPTS) {
                                        fprintf(stderr, "too many cpp options\n");
                                        exits("options");
                                }
                                cppopts[ncppopts++] = argv[a];
                                break;
                        default:
                                fprintf(stderr, "unrecognised option %s\n",
                                    argv[a]);
                                exits("options");
                        }
                else
                        break;
        }
        if (a != argc - 1)
        {
                fprintf(stderr, "usage: na [options] file\n");
                exits("options");
        }
        if (access(argv[a], 4) < 0) {
                fprintf(stderr, "can't read %s\n", argv[a]);
                exits("");
        }
        in_f = tmpfile();
        preprocess(argv[a], in_f);
        rewind(in_f);
        strcpy(yyfilename, argv[a]);
        yyparse();
        if (errors)
                exits("pass1");
        pass2 = 1;
        printf("unsigned long na_script[] = {\n");
        yyrewind();
        yyparse();
        printf("};\n");
        printf("\n");
        printf("#define NA_SCRIPT_SIZE %d\n", dot / 4);
        printf("\n");
        fixup();
/*
        assemble();
*/
        exits(errors ? "pass2" : "");
}

void
preprocess(char *in, FILE *out)
{
        Waitmsg *w;
        char **argv;

        if (fork() == 0) {
                /* child */
                dup(fileno(out), 1);
                argv = (char **)malloc(sizeof(char *) * (ncppopts + 5));
                argv[0] = "cpp";
                memcpy(&argv[1], cppopts, sizeof(char *) * ncppopts);
                argv[ncppopts + 1] = "-+";
                argv[ncppopts + 2] = "-N";
                argv[ncppopts + 3] = in;
                argv[ncppopts + 4] = 0;
                exec("/bin/cpp", argv);
                fprintf(stderr, "failed to exec cpp (%R)\n");
                exits("exec");
        }
        w = wait();
        free(w);
}

struct sym *
findsym(char *name)
{
        struct sym *s;
        for (s = symlist; s; s = s->next)
                if (strcmp(name, s->name) == 0)
                        return s;
        s = (struct sym *)malloc(sizeof(*s));
        s->name = strdup(name);
        s->t = Unknown;
        s->set = 0;
        s->next = symlist;
        symlist = s;
        return s;
}

void
setsym(struct sym *s, Type t, long v)
{
        if (pass2) {
                if (t == Unknown || t == Error)
                        yyerror("can't resolve symbol");
                else {
                        s->t = t;
                        s->value = v;
                }
        }
        else {
                if (s->set)
                        yyerror("multiply defined symbol");
                s->set = 1;
                s->t = t;
                s->value = v;
        }
}

int
mk24bitssigned(long *l)
{
        if (*l < 0) {
                if ((*l & 0xff800000L) != 0xff800000L) {
                        *l = 0;
                        return 0;
                }
                else
                        *l = (*l) & 0xffffffL;
        }
        else if (*l > 0xffffffL) {
                *l = 0;
                return 0;
        }
        return 1;
}

static Type addresult[5][5] = {
/*              Const   Addr    Table   Extern  Reg */
/* Const */     Const,  Addr,   Table,  Error,  Reg,
/* Addr */      Addr,   Error,  Error,  Error,  Error,
/* Table */     Table,  Error,  Error,  Error,  Error,
/* Extern */    Error,  Error,  Error,  Error,  Error,
/* Reg */       Reg,    Error,  Error,  Error,  Error,
};

static Type subresult[5][5] = {
/*              Const   Addr    Table   Extern  Reg */
/* Const */     Const,  Error,  Error,  Error,  Error,
/* Addr */      Addr,   Const,  Error,  Error,  Error,
/* Table */     Table,  Error,  Const,  Error,  Error,
/* Extern */    Error,  Error,  Error,  Const,  Error,
/* Reg */       Error,  Error,  Error,  Error,  Error,
};

static Type muldivresult[5][5] = {
/*              Const   Addr    Table   Extern */
/* Const */     Const,  Error,  Error,  Error,  Error,
/* Addr */      Error,  Error,  Error,  Error,  Error,
/* Table */     Error,  Error,  Error,  Error,  Error,
/* Extern */    Error,  Error,  Error,  Error,  Error,
/* Reg */       Error,  Error,  Error,  Error,  Error,
};

static Type negresult[] = {
/* Const */     Const,
/* Addr */      Error,
/* Table */     Error,
/* Extern */    Error,
/* Reg */       Error,
};

int
patchtype(Type t)
{
        switch (t) {
        case Addr:
                return 1;
        case Reg:
                return 2;
        case Extern:
                return 4;
        default:
                return 0;
        }
}

struct expval
eval(struct expval a, struct expval b, char op)
{
        struct expval c;
        
        if (a.t == Unknown || b.t == Unknown) {
                c.t = Unknown;
                c.value = 0;
        }
        else if (a.t == Error || b.t == Error) {
                c.t = Error;
                c.value = 0;
        }
        else {
                switch (op) {
                case '+':
                        c.t = addresult[a.t][b.t];
                        break;
                case '-':
                        c.t = subresult[a.t][b.t];
                        break;
                case '*':
                case '/':
                        c.t = muldivresult[a.t][b.t];
                        break;
                case '_':
                case '~':
                        c.t = negresult[a.t];
                        break;
                default:
                        c.t = Error;
                        break;
                }
                if (c.t == Error) {
                        if (pass2)
                                yyerror("type clash in evaluation");
                        c.value = 0;
                }
                else {
                        switch (op) {
                        case '+':
                                c.value = a.value + b.value;
                                break;
                        case '-':
                                c.value = a.value - b.value;
                                break;
                        case '*':
                                c.value = a.value * b.value;
                                break;
                        case '/':
                                c.value = a.value / b.value;
                                break;
                        case '_':
                                c.value = -a.value;
                                break;
                        case '~':
                                c.value = ~a.value;
                                break;
                        }
                }
        }
        return c;
}

void
regmove(unsigned char src_reg, unsigned char op,
    unsigned char dst_reg, struct expval *imm)
{
        unsigned char func, reg;
        int immdata;
        out.len = 2;
        if (src_reg == 8) {
                func = 5;
                reg = dst_reg;
        }
        else if (dst_reg == 8) {
                func = 6;
                reg = src_reg;
        }
        else {
                if (pass2 && src_reg != dst_reg)
                        yyerror("Registers must be the same");
                func = 7;
                reg = src_reg;
        }
        immdata = imm ? (imm->value & 0xff) : 0;
        out.data[0] = 0x40000000L
            | ((long)func << 27)
            | ((long)op << 24)
            | ((long)reg << 16)
            | ((long)(immdata) << 8);
        out.data[1] = 0;
        out.patch[0] = (imm && imm->t == Extern) ? 3 : 0;
        out.patch[1] = 0;
}

long
mkreladdr(long addr, int len)
{
        long rel;
        rel = addr - (dot + 4 * len);
        mk24bitssigned(&rel);
        return rel;
}

long
chkreladdr(int d, struct expval *e, int len, long relrv)
{
        if (e->t == Addr) {
                out.data[d] = mkreladdr(e->value, len);
                out.patch[d] = 0;
                return relrv;
        } else {
                out.data[d] = e->value;
                out.patch[d] = patchtype(e->t);
                return 0;
        }
}

void
fixup(void)
{
        struct sym *s;
        int p;
        printf("struct na_patch na_patches[] = {\n");
        for (p = 0; p < patches; p++) {
                printf("\t{ 0x%.4x, %d }, /* %.8lx */\n",
                    patch[p].lwoff, patch[p].type, patch[p].lwoff * 4L);
        }
        if (patches == 0) {
                printf("\t{ 0, 0 },\n");
        }
        printf("};\n");
        printf("#define NA_PATCHES %d\n", patches);
        printf("\n");
        if (externs) {
                printf("enum na_external {\n");
                for (p = 0; p < externs; p++) {
                        printf("\tX_%s,\n", externp[p]->name);
                }
                printf("};\n");
        }
        /* dump all labels (symbols of type Addr) as E_<Name> */
        for (s = symlist; s; s = s->next)
                if (s->t == Addr)
                        break;
        if (s) {
                printf("\nenum {\n");
                while (s) {
                        if (s->t == Addr)
                                printf("\tE_%s = %ld,\n", s->name, s->value);
                        s = s->next;
                }
                printf("};\n");
        }
        /* dump all Consts as #define A_<Name> value */
        for (s = symlist; s; s = s->next)
                if (s->t == Const)
                        printf("#define A_%s %ld\n", s->name, s->value);
}