OpenCores
URL https://opencores.org/ocsvn/eco32/eco32/trunk

Subversion Repositories eco32

[/] [eco32/] [trunk/] [binutils/] [as/] [as.c] - Rev 7

Compare with Previous | Blame | View Log

/*
 * as.c -- ECO32 assembler
 */
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
 
#include "../include/a.out.h"
 
 
/**************************************************************/
 
 
#define NUM_REGS	32
#define AUX_REG		1
 
#define LINE_SIZE	200
 
#define TOK_EOL		0
#define TOK_LABEL	1
#define TOK_IDENT	2
#define TOK_STRING	3
#define TOK_NUMBER	4
#define TOK_REGISTER	5
#define TOK_PLUS	6
#define TOK_MINUS	7
#define TOK_STAR	8
#define TOK_SLASH	9
#define TOK_PERCENT	10
#define TOK_LSHIFT	11
#define TOK_RSHIFT	12
#define TOK_LPAREN	13
#define TOK_RPAREN	14
#define TOK_COMMA	15
#define TOK_TILDE	16
#define TOK_AMPER	17
#define TOK_BAR		18
#define TOK_CARET	19
 
#define STATUS_UNKNOWN	0	/* symbol is not yet defined */
#define STATUS_DEFINED	1	/* symbol is defined */
#define STATUS_GLOBREF	2	/* local entry refers to a global one */
 
#define GLOBAL_TABLE	0	/* global symbol table identifier */
#define LOCAL_TABLE	1	/* local symbol table identifier */
 
#define MSB	((unsigned int) 1 << (sizeof(unsigned int) * 8 - 1))
 
 
/**************************************************************/
 
 
#define OP_ADD		0x00
#define OP_ADDI		0x01
#define OP_SUB		0x02
#define OP_SUBI		0x03
 
#define OP_MUL		0x04
#define OP_MULI		0x05
#define OP_MULU		0x06
#define OP_MULUI	0x07
#define OP_DIV		0x08
#define OP_DIVI		0x09
#define OP_DIVU		0x0A
#define OP_DIVUI	0x0B
#define OP_REM		0x0C
#define OP_REMI		0x0D
#define OP_REMU		0x0E
#define OP_REMUI	0x0F
 
#define OP_AND		0x10
#define OP_ANDI		0x11
#define OP_OR		0x12
#define OP_ORI		0x13
#define OP_XOR		0x14
#define OP_XORI		0x15
#define OP_XNOR		0x16
#define OP_XNORI	0x17
 
#define OP_SLL		0x18
#define OP_SLLI		0x19
#define OP_SLR		0x1A
#define OP_SLRI		0x1B
#define OP_SAR		0x1C
#define OP_SARI		0x1D
 
#define OP_LDHI		0x1F
 
#define OP_BEQ		0x20
#define OP_BNE		0x21
#define OP_BLE		0x22
#define OP_BLEU		0x23
#define OP_BLT		0x24
#define OP_BLTU		0x25
#define OP_BGE		0x26
#define OP_BGEU		0x27
#define OP_BGT		0x28
#define OP_BGTU		0x29
 
#define OP_J		0x2A
#define OP_JR		0x2B
#define OP_JAL		0x2C
#define OP_JALR		0x2D
 
#define OP_TRAP		0x2E
#define OP_RFX		0x2F
 
#define OP_LDW		0x30
#define OP_LDH		0x31
#define OP_LDHU		0x32
#define OP_LDB		0x33
#define OP_LDBU		0x34
 
#define OP_STW		0x35
#define OP_STH		0x36
#define OP_STB		0x37
 
#define OP_MVFS		0x38
#define OP_MVTS		0x39
#define OP_TBS		0x3A
#define OP_TBWR		0x3B
#define OP_TBRI		0x3C
#define OP_TBWI		0x3D
 
 
/**************************************************************/
 
 
int debugToken = 0;
int debugCode = 0;
int debugFixup = 0;
 
char codeName[L_tmpnam];
char dataName[L_tmpnam];
char *outName = NULL;
char *inName = NULL;
 
FILE *codeFile = NULL;
FILE *dataFile = NULL;
FILE *outFile = NULL;
FILE *inFile = NULL;
 
char line[LINE_SIZE];
char *lineptr;
int lineno;
 
int token;
int tokenvalNumber;
char tokenvalString[LINE_SIZE];
 
int allowSyn = 1;
int currSeg = SEGMENT_CODE;
unsigned int segPtr[4] = { 0, 0, 0, 0 };
char *segName[4] = { "ABS", "CODE", "DATA", "BSS" };
char *methodName[5] = { "H16", "L16", "R16", "R26", "W32" };
 
 
typedef struct fixup {
  int segment;			/* in which segment */
  unsigned int offset;		/* at which offset */
  int method;			/* what kind of coding method is to be used */
  int value;			/* known part of value */
  int base;			/* segment which this ref is relative to */
				/* valid only when used for relocation */
  struct fixup *next;		/* next fixup */
} Fixup;
 
 
typedef struct symbol {
  char *name;			/* name of symbol */
  int status;			/* status of symbol */
  int segment;			/* the symbol's segment */
  int value;			/* the symbol's value */
  Fixup *fixups;		/* list of locations to fix */
  struct symbol *globref;	/* set if this local refers to a global */
  struct symbol *left;		/* left son in binary search tree */
  struct symbol *right;		/* right son in binary search tree */
  int skip;			/* this symbol is not defined here nor is */
				/* it used here: don't write to object file */
} Symbol;
 
 
/**************************************************************/
 
 
void error(char *fmt, ...) {
  va_list ap;
 
  va_start(ap, fmt);
  fprintf(stderr, "Error: ");
  vfprintf(stderr, fmt, ap);
  fprintf(stderr, "\n");
  va_end(ap);
  if (codeFile != NULL) {
    fclose(codeFile);
    codeFile = NULL;
  }
  if (dataFile != NULL) {
    fclose(dataFile);
    dataFile = NULL;
  }
  if (outFile != NULL) {
    fclose(outFile);
    outFile = NULL;
  }
  if (inFile != NULL) {
    fclose(inFile);
    inFile = NULL;
  }
  if (codeName != NULL) {
    unlink(codeName);
  }
  if (dataName != NULL) {
    unlink(dataName);
  }
  if (outName != NULL) {
    unlink(outName);
  }
  exit(1);
}
 
 
void *allocateMemory(unsigned int size) {
  void *p;
 
  p = malloc(size);
  if (p == NULL) {
    error("out of memory");
  }
  return p;
}
 
 
void freeMemory(void *p) {
  free(p);
}
 
 
/**************************************************************/
 
 
int getNextToken(void) {
  char *p;
  int base;
  int digit;
 
  while (*lineptr == ' ' || *lineptr == '\t') {
    lineptr++;
  }
  if (*lineptr == '\n' || *lineptr == '\0' || *lineptr == ';') {
    return TOK_EOL;
  }
  if (isalpha((int) *lineptr) || *lineptr == '_' || *lineptr == '.') {
    p = tokenvalString;
    while (isalnum((int) *lineptr) || *lineptr == '_' || *lineptr == '.') {
      *p++ = *lineptr++;
    }
    *p = '\0';
    if (*lineptr == ':') {
      lineptr++;
      return TOK_LABEL;
    } else {
      return TOK_IDENT;
    }
  }
  if (isdigit((int) *lineptr)) {
    base = 10;
    tokenvalNumber = 0;
    if (*lineptr == '0') {
      lineptr++;
      if (*lineptr == 'x' || *lineptr == 'X') {
        base = 16;
        lineptr++;
      } else
      if (isdigit((int) *lineptr)) {
        base = 8;
      } else {
        return TOK_NUMBER;
      }
    }
    while (isxdigit((int) *lineptr)) {
      digit = *lineptr++ - '0';
      if (digit >= 'A' - '0') {
        if (digit >= 'a' - '0') {
          digit += '0' - 'a' + 10;
        } else {
          digit += '0' - 'A' + 10;
        }
      }
      if (digit >= base) {
        error("illegal digit value %d in line %d", digit, lineno);
      }
      tokenvalNumber *= base;
      tokenvalNumber += digit;
    }
    return TOK_NUMBER;
  }
  if (*lineptr == '\'') {
    lineptr++;
    if (!isprint((int) *lineptr)) {
      error("cannot quote character 0x%02X in line %d", *lineptr, lineno);
    }
    tokenvalNumber = *lineptr;
    lineptr++;
    if (*lineptr != '\'') {
      error("unbalanced quote in line %d", lineno);
    }
    lineptr++;
    return TOK_NUMBER;
  }
  if (*lineptr == '\"') {
    lineptr++;
    p = tokenvalString;
    while (1) {
      if (*lineptr == '\n' || *lineptr == '\0') {
        error("unterminated string constant in line %d", lineno);
      }
      if (!isprint((int) *lineptr)) {
        error("string contains illegal character 0x%02X in line %d",
              *lineptr, lineno);
      }
      if (*lineptr == '\"') {
        break;
      }
      *p++ = *lineptr++;
    }
    lineptr++;
    *p = '\0';
    return TOK_STRING;
  }
  if (*lineptr == '$') {
    lineptr++;
    if (!isdigit((int) *lineptr)) {
      error("register number expected after '$' in line %d", lineno);
    }
    tokenvalNumber = 0;
    while (isdigit((int) *lineptr)) {
      digit = *lineptr++ - '0';
      tokenvalNumber *= 10;
      tokenvalNumber += digit;
    }
    if (tokenvalNumber < 0 || tokenvalNumber >= NUM_REGS) {
      error("illegal register number %d in line %d", tokenvalNumber, lineno);
    }
    return TOK_REGISTER;
  }
  if (*lineptr == '+') {
    lineptr++;
    return TOK_PLUS;
  }
  if (*lineptr == '-') {
    lineptr++;
    return TOK_MINUS;
  }
  if (*lineptr == '*') {
    lineptr++;
    return TOK_STAR;
  }
  if (*lineptr == '/') {
    lineptr++;
    return TOK_SLASH;
  }
  if (*lineptr == '%') {
    lineptr++;
    return TOK_PERCENT;
  }
  if (*lineptr == '<' && *(lineptr + 1) == '<') {
    lineptr += 2;
    return TOK_LSHIFT;
  }
  if (*lineptr == '>' && *(lineptr + 1) == '>') {
    lineptr += 2;
    return TOK_RSHIFT;
  }
  if (*lineptr == '(') {
    lineptr++;
    return TOK_LPAREN;
  }
  if (*lineptr == ')') {
    lineptr++;
    return TOK_RPAREN;
  }
  if (*lineptr == ',') {
    lineptr++;
    return TOK_COMMA;
  }
  if (*lineptr == '~') {
    lineptr++;
    return TOK_TILDE;
  }
  if (*lineptr == '&') {
    lineptr++;
    return TOK_AMPER;
  }
  if (*lineptr == '|') {
    lineptr++;
    return TOK_BAR;
  }
  if (*lineptr == '^') {
    lineptr++;
    return TOK_CARET;
  }
  error("illegal character 0x%02X in line %d", *lineptr, lineno);
  return 0;
}
 
 
void showToken(void) {
  printf("DEBUG: ");
  switch (token) {
    case TOK_EOL:
      printf("token = TOK_EOL\n");
      break;
    case TOK_LABEL:
      printf("token = TOK_LABEL, value = %s\n", tokenvalString);
      break;
    case TOK_IDENT:
      printf("token = TOK_IDENT, value = %s\n", tokenvalString);
      break;
    case TOK_STRING:
      printf("token = TOK_STRING, value = %s\n", tokenvalString);
      break;
    case TOK_NUMBER:
      printf("token = TOK_NUMBER, value = 0x%x\n", tokenvalNumber);
      break;
    case TOK_REGISTER:
      printf("token = TOK_REGISTER, value = %d\n", tokenvalNumber);
      break;
    case TOK_PLUS:
      printf("token = TOK_PLUS\n");
      break;
    case TOK_MINUS:
      printf("token = TOK_MINUS\n");
      break;
    case TOK_STAR:
      printf("token = TOK_STAR\n");
      break;
    case TOK_SLASH:
      printf("token = TOK_SLASH\n");
      break;
    case TOK_PERCENT:
      printf("token = TOK_PERCENT\n");
      break;
    case TOK_LSHIFT:
      printf("token = TOK_LSHIFT\n");
      break;
    case TOK_RSHIFT:
      printf("token = TOK_RSHIFT\n");
      break;
    case TOK_LPAREN:
      printf("token = TOK_LPAREN\n");
      break;
    case TOK_RPAREN:
      printf("token = TOK_RPAREN\n");
      break;
    case TOK_COMMA:
      printf("token = TOK_COMMA\n");
      break;
    case TOK_TILDE:
      printf("token = TOK_TILDE\n");
      break;
    case TOK_AMPER:
      printf("token = TOK_AMPER\n");
      break;
    case TOK_BAR:
      printf("token = TOK_BAR\n");
      break;
    case TOK_CARET:
      printf("token = TOK_CARET\n");
      break;
    default:
      error("illegal token %d in showToken()", token);
  }
}
 
 
void getToken(void) {
  token = getNextToken();
  if (debugToken) {
    showToken();
  }
}
 
 
static char *tok2str[] = {
  "end-of-line",
  "label",
  "identifier",
  "string",
  "number",
  "register",
  "+",
  "-",
  "*",
  "/",
  "%",
  "<<",
  ">>",
  "(",
  ")",
  ",",
  "~",
  "&",
  "|",
  "^"
};
 
 
void expect(int expected) {
  if (token != expected) {
    error("'%s' expected, got '%s' in line %d",
          tok2str[expected], tok2str[token], lineno);
  }
}
 
 
/**************************************************************/
 
 
Fixup *fixupList = NULL;
 
 
Fixup *newFixup(int segment, unsigned int offset, int method, int value) {
  Fixup *f;
 
  f = allocateMemory(sizeof(Fixup));
  f->segment = segment;
  f->offset = offset;
  f->method = method;
  f->value = value;
  f->base = 0;
  f->next = NULL;
  return f;
}
 
 
void addFixup(Symbol *s,
              int segment, unsigned int offset, int method, int value) {
  Fixup *f;
 
  if (debugFixup) {
    printf("DEBUG: fixup (s:%s, o:%08X, m:%s, v:%08X) added to '%s'\n",
           segName[segment], offset, methodName[method], value, s->name);
  }
  f = newFixup(segment, offset, method, value);
  f->next = s->fixups;
  s->fixups = f;
}
 
 
/**************************************************************/
 
 
Symbol *globalTable = NULL;
Symbol *localTable = NULL;
 
 
Symbol *deref(Symbol *s) {
  if (s->status == STATUS_GLOBREF) {
    return s->globref;
  } else {
    return s;
  }
}
 
 
Symbol *newSymbol(char *name) {
  Symbol *p;
 
  p = allocateMemory(sizeof(Symbol));
  p->name = allocateMemory(strlen(name) + 1);
  strcpy(p->name, name);
  p->status = STATUS_UNKNOWN;
  p->segment = 0;
  p->value = 0;
  p->fixups = NULL;
  p->globref = NULL;
  p->left = NULL;
  p->right = NULL;
  return p;
}
 
 
Symbol *lookupEnter(char *name, int whichTable) {
  Symbol *p, *q, *r;
  int cmp;
 
  if (whichTable == GLOBAL_TABLE) {
    p = globalTable;
  } else {
    p = localTable;
  }
  if (p == NULL) {
    r = newSymbol(name);
    if (whichTable == GLOBAL_TABLE) {
      globalTable = r;
    } else {
      localTable = r;
    }
    return r;
  }
  while (1) {
    q = p;
    cmp = strcmp(name, q->name);
    if (cmp == 0) {
      return q;
    }
    if (cmp < 0) {
      p = q->left;
    } else {
      p = q->right;
    }
    if (p == NULL) {
      r = newSymbol(name);
      if (cmp < 0) {
        q->left = r;
      } else {
        q->right = r;
      }
      return r;
    }
  }
}
 
 
static void linkSymbol(Symbol *s) {
  Fixup *f;
 
  if (s->status == STATUS_UNKNOWN) {
    error("undefined symbol '%s'", s->name);
  }
  if (s->status == STATUS_GLOBREF) {
    if (s->fixups != NULL) {
      error("local fixups detected with global symbol '%s'", s->name);
    }
  } else {
    if (debugFixup) {
      printf("DEBUG: link '%s' (s:%s, v:%08X)\n",
             s->name, segName[s->segment], s->value);
    }
    while (s->fixups != NULL) {
      /* get next fixup record */
      f = s->fixups;
      s->fixups = f->next;
      /* add the symbol's value to the value in the record */
      /* and remember the symbol's segment */
      if (debugFixup) {
        printf("       (s:%s, o:%08X, m:%s, v:%08X --> %08X, b:%s)\n",
               segName[f->segment], f->offset, methodName[f->method],
               f->value, f->value + s->value, segName[s->segment]);
      }
      f->value += s->value;
      f->base = s->segment;
      /* transfer the record to the fixup list */
      f->next = fixupList;
      fixupList = f;
    }
  }
}
 
 
static void linkTree(Symbol *s) {
  if (s == NULL) {
    return;
  }
  linkTree(s->left);
  linkSymbol(s);
  linkTree(s->right);
  freeMemory(s->name);
  freeMemory(s);
}
 
 
void linkLocals(void) {
  linkTree(localTable);
  localTable = NULL;
  fseek(codeFile, 0, SEEK_END);
  fseek(dataFile, 0, SEEK_END);
}
 
 
/**************************************************************/
 
 
void emitByte(unsigned int byte) {
  byte &= 0x000000FF;
  if (debugCode) {
    printf("DEBUG: byte @ segment = %s, offset = %08X",
           segName[currSeg], segPtr[currSeg]);
    printf(", value = %02X\n", byte);
  }
  switch (currSeg) {
    case SEGMENT_ABS:
      error("illegal segment in emitByte()");
      break;
    case SEGMENT_CODE:
      fputc(byte, codeFile);
      break;
    case SEGMENT_DATA:
      fputc(byte, dataFile);
      break;
    case SEGMENT_BSS:
      break;
  }
  segPtr[currSeg] += 1;
}
 
 
void emitHalf(unsigned int half) {
  half &= 0x0000FFFF;
  if (debugCode) {
    printf("DEBUG: half @ segment = %s, offset = %08X",
           segName[currSeg], segPtr[currSeg]);
    printf(", value = %02X%02X\n",
           (half >> 8) & 0xFF, half & 0xFF);
  }
  switch (currSeg) {
    case SEGMENT_ABS:
      error("illegal segment in emitHalf()");
      break;
    case SEGMENT_CODE:
      fputc((half >> 8) & 0xFF, codeFile);
      fputc(half & 0xFF, codeFile);
      break;
    case SEGMENT_DATA:
      fputc((half >> 8) & 0xFF, dataFile);
      fputc(half & 0xFF, dataFile);
      break;
    case SEGMENT_BSS:
      break;
  }
  segPtr[currSeg] += 2;
}
 
 
void emitWord(unsigned int word) {
  if (debugCode) {
    printf("DEBUG: word @ segment = %s, offset = %08X",
           segName[currSeg], segPtr[currSeg]);
    printf(", value = %02X%02X%02X%02X\n",
           (word >> 24) & 0xFF, (word >> 16) & 0xFF,
           (word >> 8) & 0xFF, word & 0xFF);
  }
  switch (currSeg) {
    case SEGMENT_ABS:
      error("illegal segment in emitWord()");
      break;
    case SEGMENT_CODE:
      fputc((word >> 24) & 0xFF, codeFile);
      fputc((word >> 16) & 0xFF, codeFile);
      fputc((word >> 8) & 0xFF, codeFile);
      fputc(word & 0xFF, codeFile);
      break;
    case SEGMENT_DATA:
      fputc((word >> 24) & 0xFF, dataFile);
      fputc((word >> 16) & 0xFF, dataFile);
      fputc((word >> 8) & 0xFF, dataFile);
      fputc(word & 0xFF, dataFile);
      break;
    case SEGMENT_BSS:
      break;
  }
  segPtr[currSeg] += 4;
}
 
 
/**************************************************************/
 
 
typedef struct {
  int con;
  Symbol *sym;
} Value;
 
 
Value parseExpression(void);
 
 
Value parsePrimaryExpression(void) {
  Value v;
  Symbol *s;
 
  if (token == TOK_NUMBER) {
    v.con = tokenvalNumber;
    v.sym = NULL;
    getToken();
  } else
  if (token == TOK_IDENT) {
    s = deref(lookupEnter(tokenvalString, LOCAL_TABLE));
    if (s->status == STATUS_DEFINED && s->segment == SEGMENT_ABS) {
      v.con = s->value;
      v.sym = NULL;
    } else {
      v.con = 0;
      v.sym = s;
    }
    getToken();
  } else
  if (token == TOK_LPAREN) {
    getToken();
    v = parseExpression();
    expect(TOK_RPAREN);
    getToken();
  } else {
    error("illegal primary expression, line %d", lineno);
  }
  return v;
}
 
 
Value parseUnaryExpression(void) {
  Value v;
 
  if (token == TOK_PLUS) {
    getToken();
    v = parseUnaryExpression();
  } else
  if (token == TOK_MINUS) {
    getToken();
    v = parseUnaryExpression();
    if (v.sym != NULL) {
      error("cannot negate symbol '%s' in line %d", v.sym->name, lineno);
    }
    v.con = -v.con;
  } else
  if (token == TOK_TILDE) {
    getToken();
    v = parseUnaryExpression();
    if (v.sym != NULL) {
      error("cannot complement symbol '%s' in line %d", v.sym->name, lineno);
    }
    v.con = ~v.con;
  } else {
    v = parsePrimaryExpression();
  }
  return v;
}
 
 
Value parseMultiplicativeExpression(void) {
  Value v1, v2;
 
  v1 = parseUnaryExpression();
  while (token == TOK_STAR || token == TOK_SLASH || token == TOK_PERCENT) {
    if (token == TOK_STAR) {
      getToken();
      v2 = parseUnaryExpression();
      if (v1.sym != NULL || v2.sym != NULL) {
        error("multiplication of symbols not supported, line %d", lineno);
      }
      v1.con *= v2.con;
    } else
    if (token == TOK_SLASH) {
      getToken();
      v2 = parseUnaryExpression();
      if (v1.sym != NULL || v2.sym != NULL) {
        error("division of symbols not supported, line %d", lineno);
      }
      if (v2.con == 0) {
        error("division by zero, line %d", lineno);
      }
      v1.con /= v2.con;
    } else
    if (token == TOK_PERCENT) {
      getToken();
      v2 = parseUnaryExpression();
      if (v1.sym != NULL || v2.sym != NULL) {
        error("division of symbols not supported, line %d", lineno);
      }
      if (v2.con == 0) {
        error("division by zero, line %d", lineno);
      }
      v1.con %= v2.con;
    }
  }
  return v1;
}
 
 
Value parseAdditiveExpression(void) {
  Value v1, v2;
 
  v1 = parseMultiplicativeExpression();
  while (token == TOK_PLUS || token == TOK_MINUS) {
    if (token == TOK_PLUS) {
      getToken();
      v2 = parseMultiplicativeExpression();
      if (v1.sym != NULL && v2.sym != NULL) {
        error("addition of symbols not supported, line %d", lineno);
      }
      if (v2.sym != NULL) {
        v1.sym = v2.sym;
      }
      v1.con += v2.con;
    } else
    if (token == TOK_MINUS) {
      getToken();
      v2 = parseMultiplicativeExpression();
      if (v2.sym != NULL) {
        error("subtraction of symbols not supported, line %d", lineno);
      }
      v1.con -= v2.con;
    }
  }
  return v1;
}
 
 
Value parseShiftExpression(void) {
  Value v1, v2;
 
  v1 = parseAdditiveExpression();
  while (token == TOK_LSHIFT || token == TOK_RSHIFT) {
    if (token == TOK_LSHIFT) {
      getToken();
      v2 = parseAdditiveExpression();
      if (v1.sym != NULL || v2.sym != NULL) {
        error("shifting of symbols not supported, line %d", lineno);
      }
      v1.con <<= v2.con;
    } else
    if (token == TOK_RSHIFT) {
      getToken();
      v2 = parseAdditiveExpression();
      if (v1.sym != NULL || v2.sym != NULL) {
        error("shifting of symbols not supported, line %d", lineno);
      }
      v1.con >>= v2.con;
    }
  }
  return v1;
}
 
 
Value parseAndExpression(void) {
  Value v1, v2;
 
  v1 = parseShiftExpression();
  while (token == TOK_AMPER) {
    getToken();
    v2 = parseShiftExpression();
    if (v2.sym != NULL) {
      error("bitwise 'and' of symbols not supported, line %d", lineno);
    }
    v1.con &= v2.con;
  }
  return v1;
}
 
 
Value parseExclusiveOrExpression(void) {
  Value v1, v2;
 
  v1 = parseAndExpression();
  while (token == TOK_CARET) {
    getToken();
    v2 = parseAndExpression();
    if (v2.sym != NULL) {
      error("bitwise 'xor' of symbols not supported, line %d", lineno);
    }
    v1.con ^= v2.con;
  }
  return v1;
}
 
 
Value parseInclusiveOrExpression(void) {
  Value v1, v2;
 
  v1 = parseExclusiveOrExpression();
  while (token == TOK_BAR) {
    getToken();
    v2 = parseExclusiveOrExpression();
    if (v2.sym != NULL) {
      error("bitwise 'or' of symbols not supported, line %d", lineno);
    }
    v1.con |= v2.con;
  }
  return v1;
}
 
 
Value parseExpression(void) {
  Value v;
 
  v = parseInclusiveOrExpression();
  return v;
}
 
 
/**************************************************************/
 
 
void dotSyn(unsigned int code) {
  allowSyn = 1;
}
 
 
void dotNosyn(unsigned int code) {
  allowSyn = 0;
}
 
 
void dotCode(unsigned int code) {
  currSeg = SEGMENT_CODE;
}
 
 
void dotData(unsigned int code) {
  currSeg = SEGMENT_DATA;
}
 
 
void dotBss(unsigned int code) {
  currSeg = SEGMENT_BSS;
}
 
 
void dotExport(unsigned int code) {
  Symbol *global;
  Symbol *local;
  Fixup *f;
 
  while (1) {
    expect(TOK_IDENT);
    global = lookupEnter(tokenvalString, GLOBAL_TABLE);
    if (global->status != STATUS_UNKNOWN) {
      error("exported symbol '%s' multiply defined in line %d",
            global->name, lineno);
    }
    local = lookupEnter(tokenvalString, LOCAL_TABLE);
    if (local->status == STATUS_GLOBREF) {
      error("exported symbol '%s' multiply exported in line %d",
            local->name, lineno);
    }
    global->status = local->status;
    global->segment = local->segment;
    global->value = local->value;
    while (local->fixups != NULL) {
      f = local->fixups;
      local->fixups = f->next;
      f->next = global->fixups;
      global->fixups = f;
    }
    local->status = STATUS_GLOBREF;
    local->globref = global;
    getToken();
    if (token != TOK_COMMA) {
      break;
    }
    getToken();
  }
}
 
 
void dotImport(unsigned int code) {
  Symbol *global;
  Symbol *local;
  Fixup *f;
 
  while (1) {
    expect(TOK_IDENT);
    global = lookupEnter(tokenvalString, GLOBAL_TABLE);
    local = lookupEnter(tokenvalString, LOCAL_TABLE);
    if (local->status != STATUS_UNKNOWN) {
      error("imported symbol '%s' multiply defined in line %d",
            local->name, lineno);
    }
    while (local->fixups != NULL) {
      f = local->fixups;
      local->fixups = f->next;
      f->next = global->fixups;
      global->fixups = f;
    }
    local->status = STATUS_GLOBREF;
    local->globref = global;
    getToken();
    if (token != TOK_COMMA) {
      break;
    }
    getToken();
  }
}
 
 
int countBits(unsigned int x) {
  int n;
 
  n = 0;
  while (x != 0) {
    x &= x - 1;
    n++;
  }
  return n;
}
 
 
void dotAlign(unsigned int code) {
  Value v;
  unsigned int mask;
 
  v = parseExpression();
  if (v.sym != NULL) {
    error("absolute expression expected in line %d", lineno);
  }
  if (countBits(v.con) != 1) {
    error("argument must be a power of 2 in line %d", lineno);
  }
  mask = v.con - 1;
  while ((segPtr[currSeg] & mask) != 0) {
    emitByte(0);
  }
}
 
 
void dotSpace(unsigned int code) {
  Value v;
  int i;
 
  v = parseExpression();
  if (v.sym != NULL) {
    error("absolute expression expected in line %d", lineno);
  }
  for (i = 0; i < v.con; i++) {
    emitByte(0);
  }
}
 
 
void dotLocate(unsigned int code) {
  Value v;
 
  v = parseExpression();
  if (v.sym != NULL) {
    error("absolute expression expected in line %d", lineno);
  }
  while (segPtr[currSeg] != v.con) {
    emitByte(0);
  }
}
 
 
void dotByte(unsigned int code) {
  Value v;
  char *p;
 
  while (1) {
    if (token == TOK_STRING) {
      p = tokenvalString;
      while (*p != '\0') {
        emitByte(*p);
        p++;
      }
      getToken();
    } else {
      v = parseExpression();
      if (v.sym != NULL) {
        error("absolute expression expected in line %d", lineno);
      }
      emitByte(v.con);
    }
    if (token != TOK_COMMA) {
      break;
    }
    getToken();
  }
}
 
 
void dotHalf(unsigned int code) {
  Value v;
 
  while (1) {
    v = parseExpression();
    if (v.sym != NULL) {
      error("absolute expression expected in line %d", lineno);
    }
    emitHalf(v.con);
    if (token != TOK_COMMA) {
      break;
    }
    getToken();
  }
}
 
 
void dotWord(unsigned int code) {
  Value v;
 
  while (1) {
    v = parseExpression();
    if (v.sym == NULL) {
      emitWord(v.con);
    } else {
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_W32, v.con);
      emitWord(0);
    }
    if (token != TOK_COMMA) {
      break;
    }
    getToken();
  }
}
 
 
void dotSet(unsigned int code) {
  Value v;
  Symbol *symbol;
 
  expect(TOK_IDENT);
  symbol = deref(lookupEnter(tokenvalString, LOCAL_TABLE));
  if (symbol->status != STATUS_UNKNOWN) {
    error("symbol '%s' multiply defined in line %d",
          symbol->name, lineno);
  }
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (v.sym == NULL) {
    symbol->status = STATUS_DEFINED;
    symbol->segment = SEGMENT_ABS;
    symbol->value = v.con;
  } else {
    error("illegal type of symbol '%s' in expression, line %d",
          v.sym->name, lineno);
  }
}
 
 
void formatN(unsigned int code) {
  Value v;
  unsigned int immed;
 
  /* opcode with no operands */
  if (token != TOK_EOL) {
    /* in exceptional cases (trap) there may be one constant operand */
    v = parseExpression();
    if (v.sym != NULL) {
      error("operand must be a constant, line %d", lineno);
    }
    immed = v.con;
  } else {
    immed = 0;
  }
  emitWord(code << 26 | (immed & 0x03FFFFFF));
}
 
 
void formatRH(unsigned int code) {
  int reg;
  Value v;
 
  /* opcode with one register and a half operand */
  expect(TOK_REGISTER);
  reg = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (v.sym == NULL) {
    emitHalf(code << 10 | reg);
    emitHalf(v.con);
  } else {
    addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
    emitHalf(code << 10 | reg);
    emitHalf(0);
  }
}
 
 
void formatRHH(unsigned int code) {
  int reg;
  Value v;
 
  /* opcode with one register and a half operand */
  /* ATTENTION: high order 16 bits encoded in instruction */
  expect(TOK_REGISTER);
  reg = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (v.sym == NULL) {
    emitHalf(code << 10 | reg);
    emitHalf(v.con >> 16);
  } else {
    addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_H16, v.con);
    emitHalf(code << 10 | reg);
    emitHalf(0);
  }
}
 
 
void formatRRH(unsigned int code) {
  int dst, src;
  Value v;
 
  /* opcode with two registers and a half operand */
  expect(TOK_REGISTER);
  dst = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (allowSyn) {
    if (v.sym == NULL) {
      if ((v.con & 0xFFFF0000) == 0) {
        /* code: op dst,src,con */
        emitHalf(code << 10 | src << 5 | dst);
        emitHalf(v.con);
      } else {
        /* code: ldhi $1,con; or $1,$1,con; add $1,$1,src; op dst,$1,0 */
        emitHalf(OP_LDHI << 10 | AUX_REG);
        emitHalf(v.con >> 16);
        emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
        emitHalf(v.con);
        emitHalf(OP_ADD << 10 | AUX_REG << 5 | src);
        emitHalf(AUX_REG << 11);
        emitHalf(code << 10 | AUX_REG << 5 | dst);
        emitHalf(0);
      }
    } else {
      /* code: ldhi $1,con; or $1,$1,con; add $1,$1,src; op dst,$1,0 */
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_H16, v.con);
      emitHalf(OP_LDHI << 10 | AUX_REG);
      emitHalf(0);
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
      emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
      emitHalf(0);
      emitHalf(OP_ADD << 10 | AUX_REG << 5 | src);
      emitHalf(AUX_REG << 11);
      emitHalf(code << 10 | AUX_REG << 5 | dst);
      emitHalf(0);
    }
  } else {
    if (v.sym == NULL) {
      emitHalf(code << 10 | src << 5 | dst);
      emitHalf(v.con);
    } else {
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
      emitHalf(code << 10 | src << 5 | dst);
      emitHalf(0);
    }
  }
}
 
 
void formatRRS(unsigned int code) {
  int dst, src;
  Value v;
 
  /* opcode with two registers and a signed half operand */
  expect(TOK_REGISTER);
  dst = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (allowSyn) {
    if (v.sym == NULL) {
      if ((v.con & 0xFFFF8000) == 0x00000000 ||
          (v.con & 0xFFFF8000) == 0xFFFF8000) {
        /* code: op dst,src,con */
        emitHalf(code << 10 | src << 5 | dst);
        emitHalf(v.con);
      } else {
        /* code: ldhi $1,con; or $1,$1,con; add $1,$1,src; op dst,$1,0 */
        emitHalf(OP_LDHI << 10 | AUX_REG);
        emitHalf(v.con >> 16);
        emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
        emitHalf(v.con);
        emitHalf(OP_ADD << 10 | AUX_REG << 5 | src);
        emitHalf(AUX_REG << 11);
        emitHalf(code << 10 | AUX_REG << 5 | dst);
        emitHalf(0);
      }
    } else {
      /* code: ldhi $1,con; or $1,$1,con; add $1,$1,src; op dst,$1,0 */
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_H16, v.con);
      emitHalf(OP_LDHI << 10 | AUX_REG);
      emitHalf(0);
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
      emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
      emitHalf(0);
      emitHalf(OP_ADD << 10 | AUX_REG << 5 | src);
      emitHalf(AUX_REG << 11);
      emitHalf(code << 10 | AUX_REG << 5 | dst);
      emitHalf(0);
    }
  } else {
    if (v.sym == NULL) {
      emitHalf(code << 10 | src << 5 | dst);
      emitHalf(v.con);
    } else {
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
      emitHalf(code << 10 | src << 5 | dst);
      emitHalf(0);
    }
  }
}
 
 
void formatRRR(unsigned int code) {
  int dst, src1, src2;
 
  /* opcode with three register operands */
  expect(TOK_REGISTER);
  dst = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src1 = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src2 = tokenvalNumber;
  getToken();
  emitHalf(code << 10 | src1 << 5 | src2);
  emitHalf(dst << 11);
}
 
 
void formatRRX(unsigned int code) {
  int dst, src1, src2;
  Value v;
 
  /* opcode with three register operands
     or two registers and a half operand */
  expect(TOK_REGISTER);
  dst = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src1 = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  if (token == TOK_REGISTER) {
    src2 = tokenvalNumber;
    getToken();
    emitHalf(code << 10 | src1 << 5 | src2);
    emitHalf(dst << 11);
  } else {
    v = parseExpression();
    if (allowSyn) {
      if (v.sym == NULL) {
        if ((v.con & 0xFFFF0000) == 0) {
          /* code: op dst,src,con */
          emitHalf((code + 1) << 10 | src1 << 5 | dst);
          emitHalf(v.con);
        } else {
          if ((v.con & 0x0000FFFF) == 0) {
            /* code: ldhi $1,con; op dst,src,$1 */
            emitHalf(OP_LDHI << 10 | AUX_REG);
            emitHalf(v.con >> 16);
            emitHalf(code << 10 | src1 << 5 | AUX_REG);
            emitHalf(dst << 11);
          } else {
            /* code: ldhi $1,con; or $1,$1,con; op dst,src,$1 */
            emitHalf(OP_LDHI << 10 | AUX_REG);
            emitHalf(v.con >> 16);
            emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
            emitHalf(v.con);
            emitHalf(code << 10 | src1 << 5 | AUX_REG);
            emitHalf(dst << 11);
          }
        }
      } else {
        /* code: ldhi $1,con; or $1,$1,con; op dst,src,$1 */
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_H16, v.con);
        emitHalf(OP_LDHI << 10 | AUX_REG);
        emitHalf(0);
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
        emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
        emitHalf(0);
        emitHalf(code << 10 | src1 << 5 | AUX_REG);
        emitHalf(dst << 11);
      }
    } else {
      if (v.sym == NULL) {
        emitHalf((code + 1) << 10 | src1 << 5 | dst);
        emitHalf(v.con);
      } else {
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
        emitHalf((code + 1) << 10 | src1 << 5 | dst);
        emitHalf(0);
      }
    }
  }
}
 
 
void formatRRY(unsigned int code) {
  int dst, src1, src2;
  Value v;
 
  /* opcode with three register operands
     or two registers and a signed half operand */
  expect(TOK_REGISTER);
  dst = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src1 = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  if (token == TOK_REGISTER) {
    src2 = tokenvalNumber;
    getToken();
    emitHalf(code << 10 | src1 << 5 | src2);
    emitHalf(dst << 11);
  } else {
    v = parseExpression();
    if (allowSyn) {
      if (v.sym == NULL) {
        if ((v.con & 0xFFFF8000) == 0x00000000 ||
            (v.con & 0xFFFF8000) == 0xFFFF8000) {
          /* code: op dst,src,con */
          emitHalf((code + 1) << 10 | src1 << 5 | dst);
          emitHalf(v.con);
        } else {
          if ((v.con & 0x0000FFFF) == 0) {
            /* code: ldhi $1,con; op dst,src,$1 */
            emitHalf(OP_LDHI << 10 | AUX_REG);
            emitHalf(v.con >> 16);
            emitHalf(code << 10 | src1 << 5 | AUX_REG);
            emitHalf(dst << 11);
          } else {
            /* code: ldhi $1,con; or $1,$1,con; op dst,src,$1 */
            emitHalf(OP_LDHI << 10 | AUX_REG);
            emitHalf(v.con >> 16);
            emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
            emitHalf(v.con);
            emitHalf(code << 10 | src1 << 5 | AUX_REG);
            emitHalf(dst << 11);
          }
        }
      } else {
        /* code: ldhi $1,con; or $1,$1,con; op dst,src,$1 */
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_H16, v.con);
        emitHalf(OP_LDHI << 10 | AUX_REG);
        emitHalf(0);
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
        emitHalf((OP_OR + 1) << 10 | AUX_REG << 5 | AUX_REG);
        emitHalf(0);
        emitHalf(code << 10 | src1 << 5 | AUX_REG);
        emitHalf(dst << 11);
      }
    } else {
      if (v.sym == NULL) {
        emitHalf((code + 1) << 10 | src1 << 5 | dst);
        emitHalf(v.con);
      } else {
        addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_L16, v.con);
        emitHalf((code + 1) << 10 | src1 << 5 | dst);
        emitHalf(0);
      }
    }
  }
}
 
 
void formatRRB(unsigned int code) {
  int src1, src2;
  Value v;
  unsigned int immed;
 
  /* opcode with two registers and a 16 bit signed offset operand */
  expect(TOK_REGISTER);
  src1 = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  expect(TOK_REGISTER);
  src2 = tokenvalNumber;
  getToken();
  expect(TOK_COMMA);
  getToken();
  v = parseExpression();
  if (v.sym == NULL) {
    immed = (v.con - ((signed) segPtr[currSeg] + 4)) / 4;
  } else {
    addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_R16, v.con);
    immed = 0;
  }
  emitHalf(code << 10 | src1 << 5 | src2);
  emitHalf(immed);
}
 
 
void formatJ(unsigned int code) {
  Value v;
  unsigned int immed;
  int target;
 
  /* opcode with no registers and a 26 bit signed offset operand or
     opcode with a single register */
  if (token == TOK_REGISTER) {
    target = tokenvalNumber;
    getToken();
    emitWord((code + 1) << 26 | target << 21);
  } else {
    v = parseExpression();
    if (v.sym == NULL) {
      immed = (v.con - ((signed) segPtr[currSeg] + 4)) / 4;
    } else {
      addFixup(v.sym, currSeg, segPtr[currSeg], METHOD_R26, v.con);
      immed = 0;
    }
    emitWord(code << 26 | (immed & 0x03FFFFFF));
  }
}
 
 
void formatJR(unsigned int code) {
  int target;
 
  /* opcode with one register operand */
  expect(TOK_REGISTER);
  target = tokenvalNumber;
  getToken();
  emitWord(code << 26 | target << 21);
}
 
 
typedef struct instr {
  char *name;
  void (*func)(unsigned int code);
  unsigned int code;
} Instr;
 
 
Instr instrTable[] = {
 
  /* pseudo instructions */
  { ".syn",    dotSyn,    0 },
  { ".nosyn",  dotNosyn,  0 },
  { ".code",   dotCode,   0 },
  { ".data",   dotData,   0 },
  { ".bss",    dotBss,    0 },
  { ".export", dotExport, 0 },
  { ".import", dotImport, 0 },
  { ".align",  dotAlign,  0 },
  { ".space",  dotSpace,  0 },
  { ".locate", dotLocate, 0 },
  { ".byte",   dotByte,   0 },
  { ".half",   dotHalf,   0 },
  { ".word",   dotWord,   0 },
  { ".set",    dotSet,    0 },
 
  /* arithmetical instructions */
  { "add",     formatRRY, OP_ADD  },
  { "sub",     formatRRY, OP_SUB  },
 
  { "mul",     formatRRY, OP_MUL  },
  { "mulu",    formatRRX, OP_MULU },
  { "div",     formatRRY, OP_DIV  },
  { "divu",    formatRRX, OP_DIVU },
  { "rem",     formatRRY, OP_REM  },
  { "remu",    formatRRX, OP_REMU },
 
  /* logical instructions */
  { "and",     formatRRX, OP_AND  },
  { "or",      formatRRX, OP_OR   },
  { "xor",     formatRRX, OP_XOR  },
  { "xnor",    formatRRX, OP_XNOR },
 
  /* shift instructions */
  { "sll",     formatRRX, OP_SLL  },
  { "slr",     formatRRX, OP_SLR  },
  { "sar",     formatRRX, OP_SAR  },
 
  /* load immediate instructions */
  { "ldhi",    formatRHH, OP_LDHI },
 
  /* branch instructions */
  { "beq",     formatRRB, OP_BEQ  },
  { "bne",     formatRRB, OP_BNE  },
  { "ble",     formatRRB, OP_BLE  },
  { "bleu",    formatRRB, OP_BLEU },
  { "blt",     formatRRB, OP_BLT  },
  { "bltu",    formatRRB, OP_BLTU },
  { "bge",     formatRRB, OP_BGE  },
  { "bgeu",    formatRRB, OP_BGEU },
  { "bgt",     formatRRB, OP_BGT  },
  { "bgtu",    formatRRB, OP_BGTU },
 
  /* jump, call & return instructions */
  { "j",       formatJ,   OP_J    },
  { "jr",      formatJR,  OP_JR   },
  { "jal",     formatJ,   OP_JAL  },
  { "jalr",    formatJR,  OP_JALR },
 
  /* interrupt related instructions */
  { "trap",    formatN,   OP_TRAP },
  { "rfx",     formatN,   OP_RFX  },
 
  /* load instructions */
  { "ldw",     formatRRS, OP_LDW  },
  { "ldh",     formatRRS, OP_LDH  },
  { "ldhu",    formatRRS, OP_LDHU },
  { "ldb",     formatRRS, OP_LDB  },
  { "ldbu",    formatRRS, OP_LDBU },
 
  /* store instructions */
  { "stw",     formatRRS, OP_STW  },
  { "sth",     formatRRS, OP_STH  },
  { "stb",     formatRRS, OP_STB  },
 
  /* processor control instructions */
  { "mvfs",    formatRH,  OP_MVFS },
  { "mvts",    formatRH,  OP_MVTS },
  { "tbs",     formatN,   OP_TBS  },
  { "tbwr",    formatN,   OP_TBWR },
  { "tbri",    formatN,   OP_TBRI },
  { "tbwi",    formatN,   OP_TBWI }
 
};
 
 
static int cmpInstr(const void *instr1, const void *instr2) {
  return strcmp(((Instr *) instr1)->name, ((Instr *) instr2)->name);
}
 
 
void sortInstrTable(void) {
  qsort(instrTable, sizeof(instrTable)/sizeof(instrTable[0]),
        sizeof(instrTable[0]), cmpInstr);
}
 
 
Instr *lookupInstr(char *name) {
  int lo, hi, tst;
  int res;
 
  lo = 0;
  hi = sizeof(instrTable) / sizeof(instrTable[0]) - 1;
  while (lo <= hi) {
    tst = (lo + hi) / 2;
    res = strcmp(instrTable[tst].name, name);
    if (res == 0) {
      return &instrTable[tst];
    }
    if (res < 0) {
      lo = tst + 1;
    } else {
      hi = tst - 1;
    }
  }
  return NULL;
}
 
 
/**************************************************************/
 
 
void roundupSegments(void) {
  while (segPtr[SEGMENT_CODE] & 3) {
    fputc(0, codeFile);
    segPtr[SEGMENT_CODE] += 1;
  }
  while (segPtr[SEGMENT_DATA] & 3) {
    fputc(0, dataFile);
    segPtr[SEGMENT_DATA] += 1;
  }
  while (segPtr[SEGMENT_BSS] & 3) {
    segPtr[SEGMENT_BSS] += 1;
  }
}
 
 
void asmModule(void) {
  Symbol *label;
  Instr *instr;
 
  allowSyn = 1;
  currSeg = SEGMENT_CODE;
  lineno = 0;
  while (fgets(line, LINE_SIZE, inFile) != NULL) {
    lineno++;
    lineptr = line;
    getToken();
    while (token == TOK_LABEL) {
      label = deref(lookupEnter(tokenvalString, LOCAL_TABLE));
      if (label->status != STATUS_UNKNOWN) {
        error("label '%s' multiply defined in line %d",
              label->name, lineno);
      }
      label->status = STATUS_DEFINED;
      label->segment = currSeg;
      label->value = segPtr[currSeg];
      getToken();
    }
    if (token == TOK_IDENT) {
      instr = lookupInstr(tokenvalString);
      if (instr == NULL) {
        error("unknown instruction '%s' in line %d",
              tokenvalString, lineno);
      }
      getToken();
      (*instr->func)(instr->code);
    }
    if (token != TOK_EOL) {
      error("garbage in line %d", lineno);
    }
  }
  roundupSegments();
}
 
 
/**************************************************************/
 
 
unsigned int read4FromEco(unsigned char *p) {
  return (unsigned int) p[0] << 24 |
         (unsigned int) p[1] << 16 |
         (unsigned int) p[2] <<  8 |
         (unsigned int) p[3] <<  0;
}
 
 
void write4ToEco(unsigned char *p, unsigned int data) {
  p[0] = data >> 24;
  p[1] = data >> 16;
  p[2] = data >>  8;
  p[3] = data >>  0;
}
 
 
void conv4FromEcoToNative(unsigned char *p) {
  unsigned int data;
 
  data = read4FromEco(p);
  * (unsigned int *) p = data;
}
 
 
void conv4FromNativeToEco(unsigned char *p) {
  unsigned int data;
 
  data = * (unsigned int *) p;
  write4ToEco(p, data);
}
 
 
/**************************************************************/
 
 
static ExecHeader execHeader;
static int numSymbols;
static int crelSize;
static int drelSize;
static int symtblSize;
static int stringSize;
 
 
static void walkTree(Symbol *s, void (*fp)(Symbol *sp)) {
  if (s == NULL) {
    return;
  }
  walkTree(s->left, fp);
  (*fp)(s);
  walkTree(s->right, fp);
}
 
 
void writeDummyHeader(void) {
  fwrite(&execHeader, sizeof(ExecHeader), 1, outFile);
}
 
 
void writeRealHeader(void) {
  rewind(outFile);
  execHeader.magic = EXEC_MAGIC;
  execHeader.csize = segPtr[SEGMENT_CODE];
  execHeader.dsize = segPtr[SEGMENT_DATA];
  execHeader.bsize = segPtr[SEGMENT_BSS];
  execHeader.crsize = crelSize;
  execHeader.drsize = drelSize;
  execHeader.symsize = symtblSize;
  execHeader.strsize = stringSize;
  conv4FromNativeToEco((unsigned char *) &execHeader.magic);
  conv4FromNativeToEco((unsigned char *) &execHeader.csize);
  conv4FromNativeToEco((unsigned char *) &execHeader.dsize);
  conv4FromNativeToEco((unsigned char *) &execHeader.bsize);
  conv4FromNativeToEco((unsigned char *) &execHeader.crsize);
  conv4FromNativeToEco((unsigned char *) &execHeader.drsize);
  conv4FromNativeToEco((unsigned char *) &execHeader.symsize);
  conv4FromNativeToEco((unsigned char *) &execHeader.strsize);
  fwrite(&execHeader, sizeof(ExecHeader), 1, outFile);
}
 
 
void writeCode(void) {
  int data;
 
  rewind(codeFile);
  while (1) {
    data = fgetc(codeFile);
    if (data == EOF) {
      break;
    }
    fputc(data, outFile);
  }
}
 
 
void writeData(void) {
  int data;
 
  rewind(dataFile);
  while (1) {
    data = fgetc(dataFile);
    if (data == EOF) {
      break;
    }
    fputc(data, outFile);
  }
}
 
 
void transferFixupsForSymbol(Symbol *s) {
  Fixup *f;
 
  if (s->status != STATUS_UNKNOWN && s->status != STATUS_DEFINED) {
    /* this should never happen */
    error("global symbol is neither unknown nor defined");
  }
  if (s->status == STATUS_UNKNOWN && s->fixups == NULL) {
    /* this symbol is neither defined here nor referenced here: skip */
    s->skip = 1;
    return;
  }
  s->skip = 0;
  while (s->fixups != NULL) {
    /* get next fixup record */
    f = s->fixups;
    s->fixups = f->next;
    /* use the 'base' component to store the current symbol number */
    f->base = MSB | numSymbols;
    /* transfer the record to the fixup list */
    f->next = fixupList;
    fixupList = f;
  }
  numSymbols++;
}
 
 
void transferFixups(void) {
  numSymbols = 0;
  walkTree(globalTable, transferFixupsForSymbol);
}
 
 
void writeCodeRelocs(void) {
  Fixup *f;
  RelocRecord relRec;
 
  crelSize = 0;
  f = fixupList;
  while (f != NULL) {
    if (f->segment != SEGMENT_CODE && f->segment != SEGMENT_DATA) {
      /* this should never happan */
      error("fixup found in a segment other than code or data");
    }
    if (f->segment == SEGMENT_CODE) {
      relRec.offset = f->offset;
      relRec.method = f->method;
      relRec.value = f->value;
      relRec.base = f->base;
      conv4FromNativeToEco((unsigned char *) &relRec.offset);
      conv4FromNativeToEco((unsigned char *) &relRec.method);
      conv4FromNativeToEco((unsigned char *) &relRec.value);
      conv4FromNativeToEco((unsigned char *) &relRec.base);
      fwrite(&relRec, sizeof(RelocRecord), 1, outFile);
      crelSize += sizeof(RelocRecord);
    }
    f = f->next;
  }
}
 
 
void writeDataRelocs(void) {
  Fixup *f;
  RelocRecord relRec;
 
  drelSize = 0;
  f = fixupList;
  while (f != NULL) {
    if (f->segment != SEGMENT_CODE && f->segment != SEGMENT_DATA) {
      /* this should never happan */
      error("fixup found in a segment other than code or data");
    }
    if (f->segment == SEGMENT_DATA) {
      relRec.offset = f->offset;
      relRec.method = f->method;
      relRec.value = f->value;
      relRec.base = f->base;
      conv4FromNativeToEco((unsigned char *) &relRec.offset);
      conv4FromNativeToEco((unsigned char *) &relRec.method);
      conv4FromNativeToEco((unsigned char *) &relRec.value);
      conv4FromNativeToEco((unsigned char *) &relRec.base);
      fwrite(&relRec, sizeof(RelocRecord), 1, outFile);
      drelSize += sizeof(RelocRecord);
    }
    f = f->next;
  }
}
 
 
void writeSymbol(Symbol *s) {
  SymbolRecord symRec;
 
  if (s->skip) {
    /* this symbol is neither defined here nor referenced here: skip */
    return;
  }
  symRec.name = stringSize;
  if (s->status == STATUS_UNKNOWN) {
    symRec.type = MSB;
    symRec.value = 0;
  } else {
    symRec.type = s->segment;
    symRec.value = s->value;
  }
  conv4FromNativeToEco((unsigned char *) &symRec.name);
  conv4FromNativeToEco((unsigned char *) &symRec.type);
  conv4FromNativeToEco((unsigned char *) &symRec.value);
  fwrite(&symRec, sizeof(SymbolRecord), 1, outFile);
  symtblSize += sizeof(SymbolRecord);
  stringSize += strlen(s->name) + 1;
}
 
 
void writeSymbols(void) {
  symtblSize = 0;
  stringSize = 0;
  walkTree(globalTable, writeSymbol);
}
 
 
void writeString(Symbol *s) {
  if (s->skip) {
    /* this symbol is neither defined here nor referenced here: skip */
    return;
  }
  fputs(s->name, outFile);
  fputc('\0', outFile);
}
 
 
void writeStrings(void) {
  walkTree(globalTable, writeString);
}
 
 
/**************************************************************/
 
 
void usage(char *myself) {
  fprintf(stderr, "Usage: %s\n", myself);
  fprintf(stderr, "         [-o objfile]     set object file name\n");
  fprintf(stderr, "         file             source file name\n");
  fprintf(stderr, "         [files...]       additional source files\n");
  exit(1);
}
 
 
int main(int argc, char *argv[]) {
  int i;
  char *argp;
 
  sortInstrTable();
  tmpnam(codeName);
  tmpnam(dataName);
  outName = "a.out";
  for (i = 1; i < argc; i++) {
    argp = argv[i];
    if (*argp != '-') {
      break;
    }
    argp++;
    switch (*argp) {
      case 'o':
        if (i == argc - 1) {
          usage(argv[0]);
        }
        outName = argv[++i];
        break;
      default:
        usage(argv[0]);
    }
  }
  if (i == argc) {
    usage(argv[0]);
  }
  codeFile = fopen(codeName, "w+b");
  if (codeFile == NULL) {
    error("cannot create temporary code file '%s'", codeName);
  }
  dataFile = fopen(dataName, "w+b");
  if (dataFile == NULL) {
    error("cannot create temporary data file '%s'", dataName);
  }
  outFile = fopen(outName, "wb");
  if (outFile == NULL) {
    error("cannot open output file '%s'", outName);
  }
  do {
    inName = argv[i];
    if (*inName == '-') {
      usage(argv[0]);
    }
    inFile = fopen(inName, "rt");
    if (inFile == NULL) {
      error("cannot open input file '%s'", inName);
    }
    fprintf(stderr, "Assembling module '%s'...\n", inName);
    asmModule();
    if (inFile != NULL) {
      fclose(inFile);
      inFile = NULL;
    }
    linkLocals();
  } while (++i < argc);
  writeDummyHeader();
  writeCode();
  writeData();
  transferFixups();
  writeCodeRelocs();
  writeDataRelocs();
  writeSymbols();
  writeStrings();
  writeRealHeader();
  if (codeFile != NULL) {
    fclose(codeFile);
    codeFile = NULL;
  }
  if (dataFile != NULL) {
    fclose(dataFile);
    dataFile = NULL;
  }
  if (outFile != NULL) {
    fclose(outFile);
    outFile = NULL;
  }
  if (codeName != NULL) {
    unlink(codeName);
  }
  if (dataName != NULL) {
    unlink(dataName);
  }
  return 0;
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.