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; } }
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)