ICUasm

From Hamsterworks Wiki!

Jump to: navigation, search

Here's a very quick and dirty assembler for a one bit 'cpu'. It is written assuming that the ROM is 16 bits wide, with the high nibble holding opcode.

Input

; Comment
        EQU     const   248 ; Constant

        LD      4       ; Another comment
        ORC     label2
        JMP     10
label:  NOPO    0
label2:
        AND     label
        OR      const

Output

[root@fedora13 ICUasm]# ./ICUasm test.asm
1004
6004
c00a
0000
3003
50f8

If you use the -l option it gives a listing of source and opcodes:

[root@fedora13 ICUasm]# ./ICUasm -l test.asm
Addr Opc  Source
---- ---- ------
   0              ; Comment
   0              EQU     const   234 ; Constant test
   0
   0 1004         LD      4       ; Another comment
   1 6004         ORC     label2
   2 C00A         JMP     10
   3 0000 label:  NOPO    0
   4      label2:
   4 3003         AND     label
   5 50EA         OR      const

Source code

NOTE! This is filled with bugs and buffer overruns - use at own risk! It is also has very very poor run time properties - it is O(N^2) due to simple linear searching for stuff. But it works for most useful size code

The way the "EQU" pseudo-instruction works is very crufty!

/******************************
* ICUasm.c : Compiler for a small 1-bit CPU, the Motorola MC14500B Industrial Control Unit
*
* Author: Mike Field <hamster@snap.net.nz>
*
* This is very, very poor coding, has buffer overruns and many errors
* But it works after a fashion. Supplied as-is, where-is.
*
* v0.1  Initial release
********************************/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>

/*******
* Longer OPCODEs must come before the shorter ones
* that may also match
* so it matches "ANDC" before seeing "AND".
*******/
struct Instruction {
  char *text;
  char opcode;
  char hasOperand;
} instr[] = {
  {"NOPO", 0,1},
  {"LDC",  2,1},
  {"LD",   1,1},
  {"ANDC", 4,1},
  {"AND",  3,1},
  {"ORC",  6,1},
  {"OR",   5,1},
  {"XOR",  7,1},
  {"STOC", 9,1},
  {"STO",  8,1},
  {"IEN", 10,1},
  {"OEN", 11,1},
  {"JMP", 12,1},
  {"RTN", 13,1},
  {"SKZ", 14,1},
  {"NOPF",15,1}
};

#define INSTR_COUNT (sizeof(instr)/sizeof(struct Instruction))
struct Line {
  struct Line *next;
  unsigned lineNum;
  char     text[128];
  unsigned addr;
  unsigned opcode;
  unsigned operand;
  char     isLabel;
  char     isConstant;
  char     hasLabel;
  char     isInstr;
  unsigned constantStart;
  unsigned constantLen;
  unsigned opLabelStart;
  unsigned opLabelLen;
};
struct Line *firstLine, *lastLine;

int errored = 0;
int listing = 1;
/***************************************************************/
int isWhitespace(char c)
{
  return (c == ' ') || (c == '\t');
}
/***************************************************************/
int isFirstLabelChar(char c)
{
  if(c >= 'a' && c <= 'z') return 1;
  if(c >= 'A' && c <= 'Z') return 1;
  if(c >= '0' && c <= '9') return 1;
  if(c == '_') return 1;
  return 0;
}
/***************************************************************/
int isLabelChar(char c)
{
  if(c >= 'a' && c <= 'z') return 1;
  if(c >= 'A' && c <= 'Z') return 1;
  if(c >= '0' && c <= '9') return 1;
  if(c == '_') return 1;
  return 0;
}
/***************************************************************/
unsigned labelLookup(char *str, unsigned len, unsigned *dest)
{
  struct Line *current;
  current = firstLine;
  while(current != NULL)
  {
    if(current->isLabel)
    {
      int i;
      for(i = 0; i < len; i++)
      {
        if(str[i] != current->text[i])
          break;
      }
      if(i == len && current->text[i] == ':')
      {
        if(dest != NULL)
          *dest = current->addr;
        return 1;
      }
    }
    else if(current->isConstant)
    {
      int i;
      char *check;
      check = current->text+current->constantStart;
      for(i = 0; i < len; i++)
      {
        if(str[i] != check[i])
          break;
      }

      if(i == len && (isWhitespace(check[i]) || check[i] == '\0'))
      {
        if(dest != NULL)
          *dest = current->operand;
        return 1;
      }
    }
    current = current->next;
  }
  return 0;
}
/***************************************************************/
int readaline(FILE *f)
{
  struct Line *l;
  int j,i, c;
  static int lineNum = 0;

  /*************************
   * Allocate memory for  new line
   */
  l = malloc(sizeof(struct Line));
  if(l == NULL)
  {
    fprintf(stderr,"Out of memory\n");
    errored = 1;
    return 0;
  }
  memset(l,0,sizeof(struct Line));


  /*************************
   * Read the contents of the line
   */
  l->lineNum = ++lineNum;
  i = 0;
  for(;;)
  {
    c = getc(f);
    if(c == '\n' || c == EOF) break;
    if(c == '\r') continue;  /* Skip returns */
    if(c == '\t') /* Expand tabs */
    {
      if(i < 127) l->text[i] = ' ';
      i++;

      while((i&7) != 0) {
        if(i < 127)
          l->text[i] = ' ';
        i++;
      }
    }
    else {
      if(i < 127) l->text[i] = c;
      i++;
    }
  }
  l->text[i] = '\0'; /* Terminate it */

  /*************************
   * End of file?
   */
  if(c==EOF && i == 0) {
    free(l);
    return 0;
  }

  /*************************
   * Link it onto the list
   */
  if(lastLine == NULL)
  {
     firstLine = l;
     lastLine = l;
  }
  else
  {
    lastLine->next = l;
    lastLine = l;
  }

  /*************************
   * Now try to parse it
   */
  i=0;
  if(!isWhitespace(l->text[i]))
  {
    if(l->text[i] == ';') return 1;   /* Ignore comments */
    if(l->text[i] == '\0') return 1;   /* Ignore comments */

    /* Labels need to be on the hard left */
    if(!isFirstLabelChar(l->text[i]))
    {
      fprintf(stderr,"%i: Invalid label\n",l->lineNum);
      errored = 1;
      return 1;
    }

    while(isLabelChar(l->text[i])) {
        i++;
    }
    if(labelLookup(l->text,i, NULL))
    {
      fprintf(stderr,"%i: Label defined twice\n",l->lineNum);
      errored = 1;
    }

    if(l->text[i] != ':')
    {
      fprintf(stderr,"%i: Expecting colon following label\n",l->lineNum);
      errored = 1;
      return 1;
    }
    l->isLabel = 1;
    i++;  /* Skip the colon */
  }

  /* Skip up to the first instruction */
  while(isWhitespace(l->text[i]))
     i++;

  /**********************
  * Check if we are at end of line or at comment
  */
  if(l->text[i] == '\0' || l->text[i] == ';')
    return 1;

  /****************
   * Find the opcode
   */
  for(j = 0; j < INSTR_COUNT; j++)
  {
    if(strncmp(l->text+i, instr[j].text, strlen(instr[j].text))==0)
    {
      l->opcode  = instr[j].opcode;
      l->isInstr = 1;
      i += strlen(instr[j].text);
      break;
    }
  }
  if(j == INSTR_COUNT)
  {
    if(l->text[i] == 'E' && l->text[i+1] == 'Q' && l->text[i+2] == 'U' && isWhitespace(l->text[i+3]))
    {
      i += 3;
      while(isWhitespace(l->text[i]))
 i++;

      if(!isFirstLabelChar(l->text[i]))
      {
        fprintf(stderr,"%i: Expecting name for equate\n",l->lineNum);
        errored = 1;
        return 1;
      }

      l->constantStart = i;
      l->constantLen = 0;
      while(isLabelChar(l->text[i]))
      {
        l->constantLen++;
        i++;
      }

      if(!isWhitespace(l->text[i]))
      {
        fprintf(stderr,"%i: Expecting value for equate\n",l->lineNum);
        errored = 1;
        return 1;
      }

      if(labelLookup(l->text+l->constantStart, l->constantLen, NULL))
      {
        fprintf(stderr,"%i: Label defined twice\n",l->lineNum);
        errored = 1;
      }
      l->isConstant = 1;
    }
    else
    {
      fprintf(stderr,"%i: Expecting instruction or equate\n",l->lineNum);
      errored = 1;
      return 1;
    }
  }

  if(!isWhitespace(l->text[i]) && l->text[i] != '\0')
  {
    fprintf(stderr,"%i: Garbled instruction\n",l->lineNum);
    errored = 1;
    return 1;
  }

  /* Skip up to the operand */
  while(isWhitespace(l->text[i]))
     i++;
  if(l->text[i] == ';' || l->text[i] == '\0')
  {
    fprintf(stderr,"%i: Expected operand\n",l->lineNum);
    errored = 1;
    return 1;
  }
  /***************************
  * Now process the operand
  */
  if(l->text[i] == '0')
  {
    l->operand = 0;
    i++;
  }
  else if(l->text[i] >= '1' && l->text[i] <= '9')
  {
     while(l->text[i] >= '0' && l->text[i] <= '9')
     {
       l->operand = l->operand * 10 + (l->text[i]-'0');
       i++;
     }
  }
  else if(isFirstLabelChar(l->text[i]) && l->isConstant == 0)
  {
    l->opLabelStart = i;
    l->opLabelLen = 0;
    while(isLabelChar(l->text[i]))
    {
      l->opLabelLen++;
      i++;
    }
  }
  else
  {
    fprintf(stderr,"%i unsupported operand\n",l->lineNum);
    errored = 1;
    return 1;
  }

  while(isWhitespace(l->text[i]))
     i++;
  if(l->text[i] != '\0' && l->text[i] != ';')
  {
    fprintf(stderr,"%i: Junk on end of line\n",l->lineNum);
    errored = 1;
    return 1;
  }
  return 1;
}
/***************************************************************/
static void assignAddresses(void)
{
  struct Line *current;
  unsigned nextAddress;
  nextAddress = 0;

  current = firstLine;
  while(current != NULL)
  {
    /* If an address has been assigned in the source,
       then use that address */
    if(current->addr == 0)
    {
      current->addr = nextAddress;
      /* Only increment address if this really is an intruction! */
      if(current->isInstr)
        nextAddress++;
    }
    else
      nextAddress = current->addr+1;
    current = current->next;
  }
}
/***************************************************************/
static void resolveLabels(void)
{
  struct Line *current;
  unsigned nextAddress;
  nextAddress = 0;

  current = firstLine;
  while(current != NULL)
  {
    if(current->opLabelLen != 0)
    {
      if(!labelLookup(current->text+current->opLabelStart,current->opLabelLen,&current->operand))
      {
     fprintf(stderr,"%i: Unable to resolve label\n",current->lineNum);
     errored = 1;
      }
    }
    current = current->next;
  }
}
/***************************************************************/
static void printObjectCode(void)
{
  int i;
  unsigned maxAddr = 0;
  struct Line *current;
  /* This is very dumb code due to time taken grows O(n^2), but
     it works. Would be better to sort the instructions first */

  /* Find the highest address */
  current = firstLine;
  while(current != NULL)
  {
    if(current->isInstr && current->addr >= maxAddr)
      maxAddr = current->addr+1;
    current = current->next;
  }
  if(listing) {
     printf("Addr Opc  Source\n");
     printf("---- ---- ------\n");
  }
  /* Print out all instructions from 0 to maxAddr */
  for(i = 0; i < maxAddr; i++)
  {
    current = firstLine;
    while(current != NULL)
    {
      if(listing)
      {
        if(current->isInstr && current->addr == i)
        {
          printf("%4X %04X %s\n", current->addr, (current->opcode<<12) + current->operand, current->text);
          current = current->next;
          break;
        }
        else if(current->addr == i)
          printf("%4X      %s\n", current->addr, current->text);
      }
      else
      {
        if(current->isInstr && current->addr == i)
        {
          printf("%04x\n", (current->opcode<<12) + current->operand);
          current = current->next;
          break;
        }
      }

      current = current->next;
    }
    while(current != NULL)
    {
      if(current->isInstr && current->addr == i) {
        fprintf(stderr,"warning: address %u is assigned twice\n",current->addr);
      }
      current = current->next;
    }
  }
}
/***************************************************************/
int main(int argc, char *argv[])
{
  FILE *f;

  if(argc == 3 && strcmp(argv[1],"-l") == 0)
  {
    listing = 1;
    f = fopen(argv[2],"r");
  }
  else if(argc == 2)
  {
    listing = 0;
    f = fopen(argv[1],"r");
  }
  else
  {
    fprintf(stderr,"Usage: %s [-l] filename\n",argv[0]);
    return 0;
  }


  if(f==NULL)
  {
    fprintf(stderr,"Unable to open file\n");
    return 0;
  }

  /* Read all lines into memory */
  while(readaline(f))
 ;

  if(!errored) assignAddresses();

  if(!errored) resolveLabels();

  if(!errored) printObjectCode();
  return 0;
}

Personal tools