Пример #1
0
int parse_instruction_NAME(struct _asm_context *asm_context, char *instr)
{
char token[TOKENLEN];
int token_type;
char instr_case[TOKENLEN];
int n;

  lower_copy(instr_case, instr);

  //token_type=get_token(asm_context, token, TOKENLEN);
  //pushback(asm_context, token, token_type);

  print_error_unknown_instr(instr, asm_context);

  return -1;
}
Пример #2
0
int parse_instruction_mips(struct _asm_context *asm_context, char *instr)
{
struct _operand operands[3];
int operand_count = 0;
char token[TOKENLEN];
int token_type;
char instr_case[TOKENLEN];
int paren_flag;
int num,n,r;
int opcode;
#if 0
int n,cond,s=0;
int opcode=0;
#endif

  lower_copy(instr_case, instr);
  memset(operands, 0, sizeof(operands));

//printf("%s %s\n", instr_case, instr);

  while(1)
  {
    token_type = tokens_get(asm_context, token, TOKENLEN);
    if (token_type == TOKEN_EOL) { break; }
    //printf("token=%s token_type=%d\n", token, token_type);

    if (operand_count == 0 && IS_TOKEN(token,'.'))
    {
      strcat(instr_case, ".");
      strcat(instr, ".");
      token_type = tokens_get(asm_context, token, TOKENLEN);
      strcat(instr, token);
      n = 0;
      while(token[n] != 0) { token[n]=tolower(token[n]); n++; }
      strcat(instr_case, token);
      continue;
    }

    do
    {
      paren_flag = 0;

      if (IS_TOKEN(token,'('))
      {
        token_type = tokens_get(asm_context, token, TOKENLEN);
        paren_flag = 1;
      }

      num = get_register_mips(token, 't');
      if (num != -1)
      {
        operands[operand_count].value = num;
        operands[operand_count].type = OPERAND_TREG;
        if (paren_flag == 0) { break; }
      }
        else
      if (paren_flag == 0)
      {
        num = get_register_mips(token, 'f');
        if (num != -1)
        {
          operands[operand_count].value = num;
          operands[operand_count].type = OPERAND_FREG;
          break;
        }
      }

      if (paren_flag == 1)
      {
        token_type = tokens_get(asm_context, token, TOKENLEN);
        if (IS_NOT_TOKEN(token,')'))
        {
          print_error_unexp(token, asm_context);
          return -1;
        }

        operands[operand_count].reg2 = operands[operand_count].value;
        operands[operand_count].value = 0;
        operands[operand_count].type = OPERAND_IMMEDIATE_RS;;

        break;
      }

      operands[operand_count].type = OPERAND_IMMEDIATE;

      if (asm_context->pass == 1)
      {
        eat_operand(asm_context);
        break;
      }

      tokens_push(asm_context, token, token_type);
      if (eval_expression(asm_context, &num) != 0)
      {
        print_error_unexp(token, asm_context);
        return -1;
      }

      operands[operand_count].value = num;

      token_type = tokens_get(asm_context, token, TOKENLEN);
      if (IS_TOKEN(token,'('))
      {
        token_type = tokens_get(asm_context, token, TOKENLEN);
        num = get_register_mips(token, 't');
        if (num == -1)
        {
          print_error_unexp(token, asm_context);
          return -1;
        }

        operands[operand_count].reg2 = num;
        operands[operand_count].type = OPERAND_IMMEDIATE_RS;;

        token_type = tokens_get(asm_context, token, TOKENLEN);

        if (IS_NOT_TOKEN(token,')'))
        {
          print_error_unexp(token, asm_context);
          return -1;
        }
      }
        else
      {
        tokens_push(asm_context, token, token_type);
      }

    } while(0);

    operand_count++;

    token_type = tokens_get(asm_context, token, TOKENLEN);
    if (token_type == TOKEN_EOL) { break; }
    if (IS_NOT_TOKEN(token,','))
    {
      print_error_unexp(token, asm_context);
      return -1;
    }

    if (operand_count == 3)
    {
      print_error_unexp(token, asm_context);
      return -1;
    }
  }

  if (asm_context->pass == 1)
  {
    add_bin32(asm_context, 0, IS_OPCODE);
    return 4;
  }

  // Check pseudo-instructions
  if (strcmp(instr_case, "move") == 0 && operand_count == 2)
  {
    strcpy(instr_case, "add");
    operands[operand_count].value = 0;
    operands[operand_count].type = OPERAND_TREG;;
    operand_count++;
  }
    else
#if 0
  if (strcmp(instr_case, "li") == 0 && operand_count == 2)
  {
    strcpy(instr_case, "addi");
    memcpy(&operands[operand_count], &operands[operand_count-1], sizeof(struct _operand));
    operands[operand_count-1].value = 0;
    operands[operand_count-1].reg2 = 0;
    operands[operand_count-1].type = OPERAND_TREG;;
    operand_count++;
  }
    else
#endif
  if (strcmp(instr_case, "nop") == 0 && operand_count == 0)
  {
    strcpy(instr_case, "add");
    operand_count = 3;
  }
    else
  if (strcmp(instr_case, "neg") == 0 && operand_count == 1)
  {
    strcpy(instr_case, "subu");
    memcpy(&operands[1], &operands[0], sizeof(struct _operand));
    operand_count = 3;
  }

  // R-Type Instruction [ op 6, rs 5, rt 5, rd 5, sa 5, function 6 ]
  n = 0;
  while(mips_r_table[n].instr != NULL)
  {
    if (strcmp(instr_case, mips_r_table[n].instr) == 0)
    {
      char shift_table[] = { 0, 11, 21, 16, 6 };
      if (mips_r_table[n].operand_count != operand_count)
      {
        print_error_illegal_operands(instr, asm_context);
        return -1;
      }

      opcode = mips_r_table[n].function;

      for (r = 0; r < operand_count; r++)
      {
        if (operands[r].type != OPERAND_TREG)
        {
//printf("%s %s %s\n", instr_case, mips_r_table[n].instr, instr);
          printf("Error: '%s' expects registers at %s:%d\n", instr, asm_context->filename, asm_context->line);
          return -1;
        }
//printf("%s  %d<<%d\n", instr, operands[r].value, shift_table[(int)mips_r_table[n].operand[r]]);
        opcode |= operands[r].value << shift_table[(int)mips_r_table[n].operand[r]];
      }

      add_bin32(asm_context, opcode, IS_OPCODE);
      return 4;
    }
    n++;
  }

  // J-Type Instruction [ op 6, target 26 ]
  if (strcmp(instr_case, "ja") == 0 || strcmp(instr_case, "jal") == 0)
  {
    // FIXME - what to do with this
    //unsigned int upper = (address + 4) & 0xf0000000;
    if (operand_count != 1)
    {
      print_error_illegal_operands(instr, asm_context);
      return -1;
    }

    if (operands[0].type != OPERAND_IMMEDIATE)
    {
      printf("Error: Expecting address for '%s' at %s:%d\n", instr, asm_context->filename, asm_context->line);
      return -1;
    }

    if (instr_case[2] == 'l')  { opcode = 2 << 26; }
    else { opcode = 3 << 26; }

    add_bin32(asm_context, opcode | operands[0].value >> 2, IS_OPCODE);

    return 4;
  }

  // Coprocessor Instruction [ op 6, format 5, ft 5, fs 5, fd 5, funct 6 ]
  n = 0;
  while(mips_cop_table[n].instr != NULL)
  {
    if (strcmp(instr_case, mips_cop_table[n].instr) == 0)
    {
      char shift_table[] = { 0, 5, 11, 16 };
      if (mips_cop_table[n].operand_count != operand_count)
      {
        print_error_illegal_operands(instr, asm_context);
        return -1;
      }

      opcode = (0x11 << 26) | (mips_cop_table[n].format << 21) | mips_cop_table[n].function;

      for (r = 0; r < operand_count; r++)
      {
        if (operands[r].type != OPERAND_FREG)
        {
          printf("Error: '%s' expects registers at %s:%d\n", instr, asm_context->filename, asm_context->line);
          return -1;
        }
        opcode |= operands[r].value << shift_table[(int)mips_cop_table[n].operand[r]];
      }

      add_bin32(asm_context, opcode, IS_OPCODE);
      return 4;
    }
    n++;
  }

  // I-Type?  [ op 6, rs 5, rt 5, imm 16 ]
  n = 0;
  while(mips_i_table[n].instr != NULL)
  {
    if (strcmp(instr_case, mips_i_table[n].instr) == 0)
    {
      char shift_table[] = { 0, 0, 21, 16 };
      if (mips_i_table[n].operand_count != operand_count)
      {
        print_error_opcount(instr, asm_context);
        return -1;
      }

      opcode = mips_i_table[n].function << 26;

      for (r = 0; r < mips_i_table[n].operand_count; r++)
      {
        if ((mips_i_table[n].operand[r] == MIPS_OP_RT ||
            mips_i_table[n].operand[r] == MIPS_OP_RS) &&
            operands[r].type == OPERAND_TREG)
        {
          opcode |= operands[r].value << shift_table[(int)mips_i_table[n].operand[r]];
        }
          else
        if (mips_i_table[n].operand[r] == MIPS_OP_LABEL)
        {
          // FIXME - Calculate address
          //if (operands[r].value > 65535 || operands[r].value < -32768)
          //{
          //  print_error("Constant larger than 16 bit.", asm_context);
          //  return -1;
          //}
          opcode |= operands[r].value;
        }
          else
        if (mips_i_table[n].operand[r] == MIPS_OP_IMMEDIATE)
        {
          if (operands[r].value > 65535 || operands[r].value < -32768)
          {
            print_error("Constant larger than 16 bit.", asm_context);
            return -1;
          }
          opcode |= operands[r].value;
        }
          else
        if (mips_i_table[n].operand[r] == MIPS_OP_IMMEDIATE_RS)
        {
          if (operands[r].value > 65535 || operands[r].value < -32768)
          {
            print_error("Constant larger than 16 bit.", asm_context);
            return -1;
          }
          opcode |= operands[r].value;
          opcode |= operands[r].reg2 << 21;
        }
          else
        if (mips_i_table[n].operand[r] == MIPS_OP_RT_IS_0)
        {
          // Derr
        }
          else
        if (mips_i_table[n].operand[r] == MIPS_OP_RT_IS_1)
        {
          opcode |= 1 << 16;
        }
          else
        {
          print_error_illegal_operands(instr, asm_context);
          return -1;
        }
        opcode |= operands[r].value << shift_table[(int)mips_i_table[n].operand[r]];
      }

      add_bin32(asm_context, opcode, IS_OPCODE);
      return 4;
    }
    n++;
  }

  print_error_unknown_instr(instr, asm_context);

  return -1;
}
Пример #3
0
int parse_instruction_6800(struct _asm_context *asm_context, char *instr)
{
  char token[TOKENLEN];
  int token_type;
  char instr_case[TOKENLEN];
  int operand_type;
  int operand_value;
  int address_size = 0;
  int opcode = -1;
  int n;

  lower_copy(instr_case, instr);

  do
  {
    token_type = tokens_get(asm_context, token, TOKENLEN);

    if (token_type == TOKEN_EOL || token_type == TOKEN_EOF)
    {
      operand_type = OPERAND_NONE;
      break;
    }

    if (token_type == TOKEN_POUND)
    {
      operand_type = OPERAND_NUMBER;
      if (eval_expression(asm_context, &operand_value) != 0)
      {
        if (asm_context->pass == 2)
        {
          print_error_illegal_expression(instr, asm_context);
          return -1;
        }

        eat_operand(asm_context);
        operand_value = 0xffff;
      }
    }
      else
    {
      operand_type = OPERAND_ADDRESS;
      tokens_push(asm_context, token, token_type);
      if (eval_expression(asm_context, &operand_value) != 0)
      {
        if (asm_context->pass == 2)
        {
          print_error_illegal_expression(instr, asm_context);
          return -1;
        }

        eat_operand(asm_context);
        operand_value = 0xffff;
      }

      token_type = tokens_get(asm_context, token, TOKENLEN);
      if (token_type == TOKEN_EOL || token_type == TOKEN_EOF) { break; }
      if (IS_NOT_TOKEN(token, ','))
      {
        print_error_unexp(token, asm_context);
        return -1;
      }

      if (expect_token_s(asm_context, "x") != 0) { return -1; }
      operand_type = OPERAND_ADDRESS_COMMA_X;
    }
  } while(0);

  for (n = 0; n < 256; n++)
  {
    if (table_6800[n].instr == NULL) { continue; }

    if (strcmp(instr_case, table_6800[n].instr) == 0)
    {
      if (table_6800[n].operand_type == M6800_OP_NONE &&
          operand_type == OPERAND_NONE)
      {
        add_bin8(asm_context, n, IS_OPCODE);
        return 1;
      }
        else
      if (table_6800[n].operand_type == M6800_OP_REL_OFFSET &&
          operand_type == OPERAND_ADDRESS)
      {
        int offset = operand_value - (asm_context->address + 2);
        if (asm_context->pass != 1)
        {
          if (offset < -128 || offset > 127)
          {
            print_error_range("Offset", -128, 127, asm_context);
            return -1;
          }
        }
        add_bin8(asm_context, n, IS_OPCODE);
        add_bin8(asm_context, offset & 0xff, IS_OPCODE);

        return 1;
      }
        else
      if (table_6800[n].operand_type == M6800_OP_IMM16 &&
          operand_type == OPERAND_NUMBER)
      {
        add_bin8(asm_context, n, IS_OPCODE);
        add_bin8(asm_context, operand_value >> 8, IS_OPCODE);
        add_bin8(asm_context, operand_value & 0xff, IS_OPCODE);
        return 3;
      }
        else
      if (table_6800[n].operand_type == M6800_OP_IMM8 &&
          operand_type == OPERAND_NUMBER)
      {
        if (asm_context->pass != 1)
        {
          if (operand_value < -128 || operand_value > 255)
          {
            print_error_range("Offset", -128, 255, asm_context);
            return -1;
          }
        }
        add_bin8(asm_context, n, IS_OPCODE);
        add_bin8(asm_context, operand_value & 0xff, IS_OPCODE);
        return 2;
      }
        else
      if (table_6800[n].operand_type == M6800_OP_NN_X &&
          operand_type == OPERAND_ADDRESS_COMMA_X)
      {
        int offset = operand_value-(asm_context->address + 2);
        if (asm_context->pass != 1)
        {
          if (offset < -128 || offset > 127)
          {
            print_error_range("Offset", -128, 127, asm_context);
            return -1;
          }
        }
        add_bin8(asm_context, n, IS_OPCODE);
        add_bin8(asm_context, offset & 0xff, IS_OPCODE);
        return 2;
      }
        else
      if (table_6800[n].operand_type == M6800_OP_DIR_PAGE_8 &&
          operand_type == OPERAND_ADDRESS)
      {
        if (asm_context->pass == 1)
        {
          if (address_size == 0 && (operand_value >= 0 && operand_value <= 255))
          {
            address_size = 1;
            opcode = n;
          }
        }

        if (memory_read_m(&asm_context->memory, asm_context->address) == 1)
        {
          add_bin8(asm_context, n, IS_OPCODE);
          add_bin8(asm_context, operand_value, IS_OPCODE);
          return 2;
        }
      }
        else
      if (table_6800[n].operand_type == M6800_OP_ABSOLUTE_16 &&
          operand_type == OPERAND_ADDRESS)
      {
        if (asm_context->pass == 1)
        {
          if (address_size == 0)
          {
            address_size = 2;
            opcode=n;
          }
        }

        if (memory_read_m(&asm_context->memory, asm_context->address)==2)
        {
          add_bin8(asm_context, n, IS_OPCODE);
          add_bin8(asm_context, operand_value >> 8, IS_OPCODE);
          add_bin8(asm_context, operand_value & 0xff, IS_OPCODE);
          return 2;
        }
      }
Пример #4
0
int parse_instruction_65xx(struct _asm_context *asm_context, char *instr)
{
  char token[TOKENLEN];
  char instr_case[TOKENLEN];
  char temp[80];
  int token_type;
  int opcode;
  int mode;
  int num;
  int index;
  int i;

  // make lower case
  lower_copy(instr_case, instr);

  // get instruction index
  index = -1;

  for(i = 0; i < 56; i++)
  {
    if(strcmp(instr_case, opcodes_65xx[i].name) == 0)
    {
      index = i;
      break;
    }
  }

  // no instruction found
  if(index == -1)
  {
    print_error_unexp(token, asm_context);
    return -1;
  }

  // default
  mode = MODE_IMPLIED;

  // begin parsing
  token_type=get_token(asm_context, token, TOKENLEN);
  if (token_type==TOKEN_EOL) { goto skip; }

  if(IS_TOKEN(token, '#'))
  {
    mode = MODE_IMMEDIATE;
    if(eval_expression(asm_context, &num) != 0)
    {
      if(asm_context->pass == 1)
      {
        eat_operand(asm_context);
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
    }

    num = (unsigned char)num;

    if(num < 0 || num > 0xFF)
      print_error_unexp(token, asm_context);
  }
    else
  if(IS_TOKEN(token, '('))
  {
    mode = MODE_INDIRECT;
    if(eval_expression(asm_context, &num) != 0)
    {
      if(asm_context->pass == 1)
      {
        eat_operand(asm_context);
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
    }

    token_type = get_token(asm_context, token, TOKENLEN);
    if (token_type==TOKEN_EOL) { goto skip; }
    if(IS_TOKEN(token, ','))
    {
      if(num < 0 || num > 0xFF)
      {
        print_error("X-Indexed Indirect value out of range", asm_context);
        return -1;
      }
      mode = MODE_X_INDEXED_INDIRECT;
      token_type = get_token(asm_context, token, TOKENLEN);
      if (token_type==TOKEN_EOL) { goto skip; }
      if(IS_NOT_TOKEN(token, 'x') && IS_NOT_TOKEN(token, 'X'))
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
      token_type = get_token(asm_context, token, TOKENLEN);
      if (token_type==TOKEN_EOL) { goto skip; }
      if(IS_NOT_TOKEN(token, ')'))
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
    }
      else
    if(IS_TOKEN(token, ')'))
    {
      mode = MODE_INDIRECT;
      token_type = get_token(asm_context, token, TOKENLEN);
      if (token_type==TOKEN_EOL) { goto skip; }
      if(IS_TOKEN(token, ','))
      {
        if(num < 0 || num > 0xFF)
        {
          print_error("Indirect Y-Indexed value out of range", asm_context);
          return -1;
        }
        mode = MODE_INDIRECT_Y_INDEXED;
        token_type = get_token(asm_context, token, TOKENLEN);
        if (token_type==TOKEN_EOL) { goto skip; }
        if(IS_NOT_TOKEN(token, 'y') && IS_NOT_TOKEN(token, 'Y'))
        {
          print_error_unexp(token, asm_context);
          return -1;
        }
        token_type = get_token(asm_context, token, TOKENLEN);
      }
    }
  }  
    else
  {
    pushback(asm_context, token, token_type);

    if(eval_expression(asm_context, &num) != 0)
    {
      if(asm_context->pass == 1)
      {
        eat_operand(asm_context);
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
    }

    // try zero page
    if((num >= 0x01 && num <= 0xFF)
        && opcodes_65xx[index].opcode[MODE_ZEROPAGE] != 0xFF)
    {
      mode = MODE_ZEROPAGE;
    }
      else
    {
      mode = MODE_ABSOLUTE;
    }

    token_type = get_token(asm_context, token, TOKENLEN);
    if (token_type==TOKEN_EOL) { goto skip; }

    if(IS_TOKEN(token, ','))
    {
      token_type = get_token(asm_context, token, TOKENLEN);
      if (token_type==TOKEN_EOL) { goto skip; }

      if(IS_TOKEN(token, 'x') || IS_TOKEN(token, 'X'))
      {
        if((num >= 0x00 && num <= 0xFF)
            && opcodes_65xx[index].opcode[MODE_ZEROPAGE_X_INDEXED] != 0xFF)
        {
          mode = MODE_ZEROPAGE_X_INDEXED;
        }
          else
        {
          mode = MODE_ABSOLUTE_X_INDEXED;
        }
      }
        else
      if(IS_TOKEN(token, 'y') || IS_TOKEN(token, 'Y'))
      {
        if((num >= 0x00 && num <= 0xFF)
            && opcodes_65xx[index].opcode[MODE_ZEROPAGE_Y_INDEXED] != 0xFF)
        {
          mode = MODE_ZEROPAGE_Y_INDEXED;
        }
          else
        {
          mode = MODE_ABSOLUTE_Y_INDEXED;
        }
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }
    }

    if(num < 0 || num > 0xFFFF)
    {
      print_error("Address out of range", asm_context);
      return -1;
    }
  }

skip:
  // branches are in table positions 3 - 10
  if(index >= 3 && index <= 10)
  {
    if(mode == MODE_IMMEDIATE)
    {
      mode = MODE_RELATIVE;
      num = (unsigned char)num;
    }
    else
    {
      mode = MODE_RELATIVE;
      if(asm_context->pass == 2)
      {
        // calculate branch offset, need to add 2 to current
        // address, since thats where the program counter would be
        num -= (asm_context->address + 2);
        if(num < -128 || num > 127)
        {
          print_error("Branch out of range", asm_context);
          return -1;
        }
        num = (unsigned char)num;
      }
    }
  }

  // see if theres an opcode for this instruction and mode
  opcode = opcodes_65xx[index].opcode[mode];

  if(asm_context->pass == 2 && opcode == 0xFF)
  {
    sprintf(temp, "No instruction found for addressing mode %d", mode);
    print_error(temp, asm_context);
    return -1;
  }

  // warn if indirect JMP bug will happen
  if((opcode == 0x6C) && ((num & 0xFF) == 0xFF))
  {
    print_error("Warning: Indirect JMP to upper page boundary (6502 bug)", asm_context);
  }

//printf("address=%04x, num=%04x\n", asm_context->address, num);

  // write opcode
//printf("%04x, %02x\n", asm_context->address, opcode & 0xFF);
  add_bin8(asm_context, opcode & 0xFF, IS_OPCODE);

  // write low byte first, if any
  if(mode_bytes[mode] > 1)
  {
//printf("%04x, %02x\n", asm_context->address, num & 0xFF);
    add_bin8(asm_context, num & 0xFF, IS_OPCODE);
  }

  // then high byte, if any
  if(mode_bytes[mode] > 2)
  {
//printf("%04x, %02x\n", asm_context->address, (num >> 8) & 0xFF);
    add_bin8(asm_context, (num >> 8) & 0xFF, IS_OPCODE);
  }
Пример #5
0
int parse_instruction_thumb(struct _asm_context *asm_context, char *instr)
{
char token[TOKENLEN];
int token_type;
char instr_case[TOKENLEN];
struct _operand operands[3];
int operand_count=0;
int matched=0;
int num;
int n;

  lower_copy(instr_case, instr);

  memset(&operands, 0, sizeof(operands));
  while(1)
  {
    token_type=tokens_get(asm_context, token, TOKENLEN);
    if (token_type==TOKEN_EOL || token_type==TOKEN_EOF)
    {
      break;
    }

    if (operand_count>=3)
    {
      print_error_opcount(instr, asm_context);
      return -1;
    }

    if ((num=get_register_thumb(token))!=-1)
    {
      operands[operand_count].type=OPERAND_REGISTER;
      operands[operand_count].value=num;
      token_type=tokens_get(asm_context, token, TOKENLEN);
      if (IS_TOKEN(token,'!'))
      {
        operands[operand_count].type=OPERAND_REGISTER_INC;
      }
        else
      {
        tokens_push(asm_context, token, token_type);
      }
    }
      else
    if ((num=get_h_register_thumb(token))!=-1)
    {
      operands[operand_count].type=OPERAND_H_REGISTER;
      operands[operand_count].value=num;
    }
      else
    if (token_type==TOKEN_POUND)
    {
      if (eval_expression(asm_context, &num)!=0)
      {
        if (asm_context->pass==1)
        {
          eat_operand(asm_context);
        }
          else
        {
          print_error_illegal_expression(instr, asm_context);
          return -1;
        }
      }

      operands[operand_count].type=OPERAND_NUMBER;
      operands[operand_count].value=num;
    }
      else
    if (IS_TOKEN(token,'['))
    {
      token_type=tokens_get(asm_context, token, TOKENLEN);

      if (strcasecmp(token,"pc")==0 || strcasecmp(token,"r15")==0)
      {
        operands[operand_count].type=OPERAND_PC_AND_REG_IN_BRACKETS;
      }
        else
      if (strcasecmp(token,"sp")==0 || strcasecmp(token,"r13")==0)
      {
        operands[operand_count].type=OPERAND_SP_AND_REG_IN_BRACKETS;
      }
        else
      if ((num=get_register_thumb(token))!=-1)
      {
        operands[operand_count].type=OPERAND_TWO_REG_IN_BRACKETS;
        operands[operand_count].value=num;
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }

      if (expect_token_s(asm_context,",")!=0) { return -1; }

      token_type=tokens_get(asm_context, token, TOKENLEN);

      if ((num=get_register_thumb(token))!=-1)
      {
        operands[operand_count].second_value=num;
        if (operands[operand_count].type==OPERAND_PC_AND_NUM_IN_BRACKETS)
        {
          operands[operand_count].type=OPERAND_PC_AND_REG_IN_BRACKETS;
        }
      }
        else
      if (token_type==TOKEN_POUND)
      {
        if (eval_expression(asm_context, &num)!=0)
        {
          if (asm_context->pass==1)
          {
            eat_operand(asm_context);
          }
            else
          {
            print_error_illegal_expression(instr, asm_context);
            return -1;
          }
        }

        operands[operand_count].second_value=num;
        operands[operand_count].type++;
      }
        else
      {
        print_error_unexp(token, asm_context);
        return -1;
      }

      if (expect_token_s(asm_context,"]")!=0) { return -1; }
    }
      else
    if (IS_TOKEN(token,'{'))
    {
      operands[operand_count].type=OPERAND_REGISTER_LIST;
      if (read_register_list(asm_context, &operands[operand_count])==-1)
      {
        return -1;
      }
    }
      else
    {
      tokens_push(asm_context, token, token_type);

      if (eval_expression(asm_context, &num)!=0)
      {
        if (asm_context->pass==1)
        {
          eat_operand(asm_context);
        }
          else
        {
          print_error_illegal_expression(instr, asm_context);
          return -1;
        }
      }

      operands[operand_count].value=num;
      operands[operand_count].type=OPERAND_ADDRESS;
    }

    operand_count++;
    token_type=tokens_get(asm_context, token, TOKENLEN);
    if (token_type==TOKEN_EOL) break;
    if (IS_NOT_TOKEN(token,',') || operand_count==3)
    {
      print_error_unexp(token, asm_context);
      return -1;
    }
  }

  n=0;
  while(table_thumb[n].instr!=NULL)
  {
    if (strcmp(table_thumb[n].instr,instr_case)==0)
    {
      matched=1;

      switch(table_thumb[n].type)
      {
        case OP_SHIFT:
          if (operand_count==3 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REGISTER &&
              operands[2].type==OPERAND_NUMBER)
          {
            if (check_reg_lower(asm_context, operands[0].value)==-1) { return -1; }
            if (check_reg_lower(asm_context, operands[1].value)==-1) { return -1; }
            if (check_range(asm_context, "Offset", operands[2].value, 0, 31)==-1) { return -1; }
#if 0
            if (operands[2].value<0 || operands[2].value>31)
            {
              print_error_range("Offset", 0, 31, asm_context);
              return -1;
            }
#endif
            add_bin16(asm_context, table_thumb[n].opcode|(operands[2].value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_ADD_SUB:
          if (operand_count==3 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REGISTER &&
              operands[2].type==OPERAND_REGISTER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[2].value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
            else
          if (operand_count==3 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REGISTER &&
              operands[2].type==OPERAND_NUMBER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(1<<10)|(operands[2].value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_IMM:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_NUMBER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<8)|(operands[1].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_ALU:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REGISTER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_HI:
          if (operand_count==2)
          {
            if (operands[0].type==OPERAND_H_REGISTER &&
                operands[1].type==OPERAND_REGISTER)
            {
              add_bin16(asm_context, table_thumb[n].opcode|(1<<7)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
              return 2;
            }
              else
            if (operands[0].type==OPERAND_REGISTER &&
                operands[1].type==OPERAND_H_REGISTER)
            {
              add_bin16(asm_context, table_thumb[n].opcode|(1<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
              return 2;
            }
              else
            if (operands[0].type==OPERAND_H_REGISTER &&
                operands[1].type==OPERAND_H_REGISTER)
            {
              add_bin16(asm_context, table_thumb[n].opcode|(1<<7)|(1<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
              return 2;
            }
          }
          break;
        case OP_HI_BX:
          if (operand_count==1 &&
              operands[0].type==OPERAND_REGISTER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<3), IS_OPCODE);
            return 2;
          }
            else
          if (operand_count==1 &&
              operands[0].type==OPERAND_H_REGISTER)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(1<<6)|(operands[0].value<<3), IS_OPCODE);
            return 2;
          }
          break;
        case OP_PC_RELATIVE_LOAD:
          // REVIEW - Docs say this is a 10 bit, 4 byte aligned number
          // and it seems unsigned.  Why isn't this signed?
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_PC_AND_NUM_IN_BRACKETS)
          {
            if (check_range(asm_context, "Offset", operands[1].second_value, 0, 1020)==-1) { return -1; }
            if (is_4_byte_aligned(asm_context, operands[1].second_value)==-1) { return -1; }
#if 0
            if ((operands[1].second_value&0x3)!=0)
            {
              print_error("Offset not 4 byte aligned", asm_context);
              return -1;
            }
#endif
            add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<8)|(operands[1].second_value>>2), IS_OPCODE);
            return 2;
          }
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_ADDRESS)
          {
            // REVIEW: This looks so odd.  Docs say: The value of the PC will
            // be 4 bytes greater than the address of this instruction, but bit
            // 1 of the PC is forced to 0 to ensure it is word aligned.
            if (is_4_byte_aligned(asm_context, operands[1].value)==-1) { return -1; }
#if 0
            if ((operands[1].value&0x3)!=0)
            {
              print_error("Offset not 4 byte aligned", asm_context);
              return -1;
            }
#endif
            int offset=operands[1].value-((asm_context->address+4)&0xfffffffc);
            if (asm_context->pass==1) { offset=0; }
            if (check_range(asm_context, "Offset", offset, 0, 1020)==-1) { return -1; }
#if 0
            if (offset<0 || offset>1020)
            {
              print_error_range("Offset", 0, 1020, asm_context);
              return -1;
            }
#endif
            add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<8)|(offset>>2), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_TWO_REG_IN_BRACKETS)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[1].second_value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE_SIGN_EXT_HALF_WORD:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_TWO_REG_IN_BRACKETS)
          {
            add_bin16(asm_context, table_thumb[n].opcode|(operands[1].second_value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE_IMM_OFFSET_WORD:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REG_AND_NUM_IN_BRACKETS)
          {
            int offset=operands[1].second_value;
            if (check_range(asm_context, "Offset", offset, 0, 124)==-1) { return -1; }
            if (is_4_byte_aligned(asm_context, offset)==-1) { return -1; }
#if 0
            if ((offset&0x3)!=0)
            {
              print_error("Offset not 4 byte aligned", asm_context);
              return -1;
            }
#endif
            offset=offset>>2;
            add_bin16(asm_context, table_thumb[n].opcode|(offset<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE_IMM_OFFSET:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REG_AND_NUM_IN_BRACKETS)
          {
            if (check_range(asm_context, "Offset", operands[1].second_value, 0, 31)==-1) { return -1; }
            add_bin16(asm_context, table_thumb[n].opcode|(operands[1].second_value<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE_IMM_OFFSET_HALF_WORD:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_REG_AND_NUM_IN_BRACKETS)
          {
            int offset=operands[1].second_value;
            if (check_range(asm_context, "Offset", offset, 0, 60)==-1) { return -1; }
            if (is_2_byte_aligned(asm_context, offset)==-1) { return -1; }
#if 0
            if ((offset&0x1)!=0)
            {
              print_error("Offset not 2 byte aligned", asm_context);
              return -1;
            }
#endif
            offset=offset>>1;
            add_bin16(asm_context, table_thumb[n].opcode|(offset<<6)|(operands[1].value<<3)|(operands[0].value), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_STORE_SP_RELATIVE:
          if (operand_count==2 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_SP_AND_NUM_IN_BRACKETS)
          {
            if (check_range(asm_context, "Offset", operands[1].value, 0, 1020)==-1) { return -1; }
            if (is_4_byte_aligned(asm_context, operands[1].value)==-1) { return -1; }
            add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<8)|(operands[1].second_value>>2), IS_OPCODE);
            return 2;
          }
          break;
        case OP_LOAD_ADDRESS:
          if (operand_count==3 &&
              operands[0].type==OPERAND_REGISTER &&
              operands[1].type==OPERAND_H_REGISTER &&
              operands[2].type==OPERAND_NUMBER)
          {
            if (check_range(asm_context, "Offset", operands[2].value, 0, 1020)==-1) { return -1; }
            if (is_4_byte_aligned(asm_context, operands[2].value)==-1) { return -1; }
            if (operands[1].value==7)
            {
              add_bin16(asm_context, table_thumb[n].opcode|(operands[0].value<<8)|(operands[2].value>>2), IS_OPCODE);
              return 2;
            }
              else
            if (operands[1].value==5)