/* ** cpu.c x86 cpu-description file ** (c) in 2005-2006,2011,2015-2017 by Frank Wille */ #include "vasm.h" mnemonic mnemonics[] = { #include "opcodes.h" }; int mnemonic_cnt = sizeof(mnemonics)/sizeof(mnemonics[0]); char *cpu_copyright = "vasm x86 cpu backend 0.7 (c) 2005-2006,2011,2015-2017 Frank Wille"; char *cpuname = "x86"; int bitsperbyte = 8; int bytespertaddr = 4; /* cpu options */ uint32_t cpu_type = CPUAny; static long cpudebug = 0; static regsym x86_regsyms[] = { #include "registers.h" }; /* opcode-prefixes recognized during parse_instruction() */ static int prefix_cnt; static unsigned char prefix[MAX_PREFIXES]; /* opcodes */ static unsigned char OC_ADDR_PREFIX; static unsigned char OC_DATA_PREFIX; static unsigned char OC_WAIT_PREFIX; static unsigned char OC_LOCK_PREFIX; static unsigned char OC_CSEG_PREFIX; static unsigned char OC_DSEG_PREFIX; static unsigned char OC_ESEG_PREFIX; static unsigned char OC_FSEG_PREFIX; static unsigned char OC_GSEG_PREFIX; static unsigned char OC_SSEG_PREFIX; static unsigned char OC_REPE_PREFIX; static unsigned char OC_REPNE_PREFIX; static unsigned char OC_POP_SREG2; static unsigned char OC_MOV_ACC_DISP; static unsigned char OC_JMP_DISP; /* opcode suffixes */ static char *b_str = "b"; static char *w_str = "w"; static char *l_str = "l"; /*static char *s_str = "s";*/ static char *q_str = "q"; /*static char *x_str = "x";*/ /* assembler mode: 16, 32 or 64 bit */ enum codetype { CODE_32BIT, CODE_16BIT, CODE_64BIT }; static enum codetype mode_flag = CODE_32BIT; /* 32 bit is default */ /* operand types for printing */ static const char *operand_type_str[] = { "Reg8","Reg16","Reg32","Reg64","Imm8","Imm8S","Imm16","Imm32","Imm32S", "Imm64","Imm1","Disp8","Disp16","Disp32","Disp32S","Disp64", "BaseIndex","ShiftCntReg","IOPortReg","CtrlReg","DebugReg", "TestReg","Acc","SegReg2","SegReg3","MMXReg","XMMReg", "FloatReg","FloatAcc","EsSeg","JmpAbs","InvMem" }; /* scale factors log2(scale) -> original scale factor */ static const int scale_factor_tab[4] = { 1, 2, 4, 8 }; operand *new_operand(void) { return mycalloc(sizeof(operand)); } int x86_data_operand(int n) /* return data operand type for these number of bits */ { if (n&OPSZ_FLOAT) return OPSZ_BITS(n)>32?Float64:Float32; if (OPSZ_BITS(n)<=8) return Data8; if (OPSZ_BITS(n)<=16) return Data16; if (OPSZ_BITS(n)<=32) return Data32; if (OPSZ_BITS(n)<=64) return Data64; cpu_error(20,n); /* data objects with n bits size are not supported */ return 0; } static void print_operand_type(int ot,char c) { int i,b; for (i=0,b=0; i<32; i++,ot>>=1) { if (ot & 1) { if (b) putchar(c); printf("%s",operand_type_str[i]); b = 1; } } } static void print_operands(instruction *ip,taddr pc) { mnemonic *mnemo = &mnemonics[ip->code]; int i; if (pc == -1) printf(" \"%s\".\"%s\"\n",mnemo->name, ip->qualifiers[0] ? ip->qualifiers[0] : emptystr); else printf(" %08lx: \"%s\".\"%s\"\n",(unsigned long)pc,mnemo->name, ip->qualifiers[0] ? ip->qualifiers[0] : emptystr); for (i=0; iop[i] == NULL) break; printf("operand %d: ",i+1); print_operand_type(ip->op[i]->type,' '); putchar('\n'); } } static int prefix_index(unsigned char oc) { int i = -1; if (oc == OC_WAIT_PREFIX) { i = WAIT_PREFIX; } else if (oc==OC_LOCK_PREFIX || oc==OC_REPE_PREFIX || oc==OC_REPNE_PREFIX) { i = LOCKREP_PREFIX; } else if (oc == OC_ADDR_PREFIX) { i = ADDR_PREFIX; } else if (oc == OC_DATA_PREFIX) { i = DATA_PREFIX; } else if (oc==OC_CSEG_PREFIX || oc==OC_DSEG_PREFIX || oc==OC_ESEG_PREFIX || oc==OC_FSEG_PREFIX || oc==OC_GSEG_PREFIX || oc==OC_SSEG_PREFIX) { i = SEG_PREFIX; } else ierror(0); return i; } #if 0 /* @@@ not now */ static int add_prefix(unsigned char oc) { int i = prefix_index(oc); int a = prefix[i] == 0; prefix[i] = oc; prefix_cnt += a; return a; } #endif static int add_ip_prefix(instruction *ip,unsigned char oc) { int i = prefix_index(oc); int a = ip->ext.prefix[i] == 0; ip->ext.prefix[i] = oc; ip->ext.num_prefixes += a; return a; } static int add_seg_prefix(instruction *ip,int segrn) { switch (segrn) { case ESEG_REGNUM: return add_ip_prefix(ip,OC_ESEG_PREFIX); case CSEG_REGNUM: return add_ip_prefix(ip,OC_CSEG_PREFIX); case SSEG_REGNUM: return add_ip_prefix(ip,OC_SSEG_PREFIX); case DSEG_REGNUM: return add_ip_prefix(ip,OC_DSEG_PREFIX); case FSEG_REGNUM: return add_ip_prefix(ip,OC_FSEG_PREFIX); case GSEG_REGNUM: return add_ip_prefix(ip,OC_GSEG_PREFIX); default: ierror(0); } return 0; } static char suffix_from_reg(instruction *ip) { mnemonic *mnemo = &mnemonics[ip->code]; int i,ot,io; /* i/o instruction? */ io = (mnemo->operand_type[0] & IOPortReg) || (mnemo->operand_type[1] & IOPortReg); /* determine size qualifier from last register operand */ for (i=MAX_OPERANDS-2; i>=0; i--) { if (ip->op[i] == NULL) continue; ot = ip->op[i]->parsed_type; if ((ot & Reg) && !(io && (ot & IOPortReg))) { if (ot & Reg8) ip->qualifiers[0] = b_str; else if (ot & Reg16) ip->qualifiers[0] = w_str; else if (ot & Reg32) ip->qualifiers[0] = l_str; else if (ot & Reg64) ip->qualifiers[0] = q_str; else ierror(0); break; } } return ip->qualifiers[0] ? tolower((unsigned char)ip->qualifiers[0][0]) : '\0'; } static void chk_byte_reg(instruction *ip) { int i,ot; for (i=0; iop[i] == NULL) break; ot = ip->op[i]->type; if (ot & (AnyReg|FloatReg|FloatAcc)) { if (ot & Reg8) continue; /* 8-bit register is just perfect */ if ((ot & WordReg) && ip->op[i]->basereg->reg_num < 4) { if (mode_flag!=CODE_64BIT /*@@@ || (ot & IOPortReg)*/) { regsym *oldreg = ip->op[i]->basereg; if (ot & Reg16) ip->op[i]->basereg -= AX_INDEX - AL_INDEX; else ip->op[i]->basereg -= EAX_INDEX - AL_INDEX; /* warning: using register x instead of y due to suffix */ cpu_error(9,ip->op[i]->basereg->reg_name,oldreg->reg_name, ip->qualifiers[0][0]); ip->op[i]->type &= ~Reg; ip->op[i]->type |= Reg8; ip->op[i]->parsed_type &= ~Reg; ip->op[i]->parsed_type |= Reg8; } continue; } /* any other register is not allowed */ cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } } } static void chk_word_reg(instruction *ip) { mnemonic *mnemo = &mnemonics[ip->code]; int i,ot; for (i=0; iop[i] == NULL) break; ot = ip->op[i]->type; if (ot & (AnyReg|FloatReg|FloatAcc)) { /* allow 8-bit registers only when exclusively required */ if ((ot & Reg8) && (mnemo->operand_type[i] & (Reg16|Reg32|Acc))) { cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } else if ((ot & Reg32) && (mnemo->operand_type[i] & (Reg16|Acc))) { regsym *oldreg = ip->op[i]->basereg; if (mode_flag == CODE_64BIT) { cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } ip->op[i]->basereg -= EAX_INDEX - AX_INDEX; /* warning: using register x instead of y due to suffix */ cpu_error(9,ip->op[i]->basereg->reg_name,oldreg->reg_name, ip->qualifiers[0][0]); ip->op[i]->type &= ~Reg; ip->op[i]->type |= Reg16; ip->op[i]->parsed_type &= ~Reg; ip->op[i]->parsed_type |= Reg16; } } } } static void chk_long_reg(instruction *ip) { mnemonic *mnemo = &mnemonics[ip->code]; int i,ot; for (i=0; iop[i] == NULL) break; ot = ip->op[i]->type; if (ot & (AnyReg|FloatReg|FloatAcc)) { /* allow 8-bit registers only when exclusively required */ if ((ot & Reg8) && (mnemo->operand_type[i] & (Reg16|Reg32|Acc))) { cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } else if ((ot & Reg16) && (mnemo->operand_type[i] & (Reg32|Acc))) { regsym *oldreg = ip->op[i]->basereg; if (mode_flag == CODE_64BIT) { cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } ip->op[i]->basereg += EAX_INDEX - AX_INDEX; /* warning: using register x instead of y due to suffix */ cpu_error(9,ip->op[i]->basereg->reg_name,oldreg->reg_name, ip->qualifiers[0][0]); ip->op[i]->type &= ~Reg; ip->op[i]->type |= Reg32; ip->op[i]->parsed_type &= ~Reg; ip->op[i]->parsed_type |= Reg32; } } } } static void chk_quad_reg(instruction *ip) { mnemonic *mnemo = &mnemonics[ip->code]; int i,ot; for (i=0; iop[i] == NULL) break; ot = ip->op[i]->type; if (ot & (AnyReg|FloatReg|FloatAcc)) { /* allow 8-bit registers only when exclusively required */ if ((ot & Reg8) && (mnemo->operand_type[i] & (Reg16|Reg32|Acc))) { cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } else if ((ot & (Reg16|Reg32)) && (mnemo->operand_type[i] & (Reg32|Acc))) { /* @@@ Reg32? */ cpu_error(10,ip->op[i]->basereg->reg_name,ip->qualifiers[0][0]); suffix_from_reg(ip); break; } } } } static int assign_suffix(instruction *ip) { mnemonic *mnemo = &mnemonics[ip->code]; char suffix = ip->qualifiers[0] ? tolower((unsigned char)ip->qualifiers[0][0]) : '\0'; if (suffix == '\0' && (mnemo->ext.opcode_modifier & (b_Illegal|w_Illegal|l_Illegal|s_Illegal|x_Illegal|q_Illegal)) == (b_Illegal|w_Illegal|l_Illegal|s_Illegal|x_Illegal|q_Illegal)) { return 1; /* no suffix needed */ } if (mnemo->ext.opcode_modifier & (Size16|Size32|Size64)) { if (mnemo->ext.opcode_modifier & Size16) ip->qualifiers[0] = w_str; else if (mnemo->ext.opcode_modifier & Size64) ip->qualifiers[0] = q_str; else ip->qualifiers[0] = l_str; } else if (ip->ext.flags & HAS_REG_OPER) { if (suffix == '\0') { /* a missing suffix can be determined by the last register operand */ suffix = suffix_from_reg(ip); } /* check if register operands match the given suffix */ else if (suffix == 'b') chk_byte_reg(ip); else if (suffix == 'w') chk_word_reg(ip); else if (suffix == 'l') chk_long_reg(ip); else if (suffix == 'q') chk_quad_reg(ip); else if (!(mnemo->ext.opcode_modifier & IgnoreSize)) { suffix_from_reg(ip); cpu_error(11,suffix); /* illegal suffix */ return 0; } } else if (suffix=='\0' && (mnemo->ext.opcode_modifier & DefaultSize)) { /* not needed for i386 */ } if (suffix=='\0' && (mnemo->ext.opcode_modifier & W)) { /* need suffix to determine size of instruction! */ cpu_error(12); /* instruction has no suffix and no regs */ ip->qualifiers[0] = b_str; return 0; } return 1; } static int process_suffix(instruction *ip,int final) { char suffix = ip->qualifiers[0] ? tolower((unsigned char)ip->qualifiers[0][0]) : '\0'; if (suffix != 'b') { /* we have to select word or dword operation mode */ mnemonic *mnemo = &mnemonics[ip->code]; if (cpudebug & 64) { printf("%s(%c%c): process_suffix(%dbit): %c\n",mnemo->name, (mnemo->ext.opcode_modifier & W) ? 'W' : ' ', (mnemo->ext.opcode_modifier & ShortForm) ? 'S' : ' ', mode_flag==CODE_64BIT?64:(mode_flag==CODE_32BIT?32:16), suffix ? suffix : ' '); } if (mnemo->ext.opcode_modifier & W) { if (mnemo->ext.opcode_modifier & ShortForm) ip->ext.base_opcode |= 8; else ip->ext.base_opcode |= 1; } if (suffix!='q' && !(mnemo->ext.opcode_modifier & IgnoreSize)) { if ((suffix=='w' && mode_flag==CODE_32BIT) || (suffix=='l' && mode_flag==CODE_16BIT) || (mode_flag==CODE_64BIT && (mnemo->ext.opcode_modifier & JmpByte))) { /* we need a prefix to select the correct size */ unsigned char code = OC_DATA_PREFIX; if (mnemo->ext.opcode_modifier & JmpByte) code = OC_ADDR_PREFIX; /* jcxz, loop */ if (cpudebug & 64) printf("\tadding prefix %02x\n",(unsigned)code); if (!add_ip_prefix(ip,code)) { if (!final) cpu_error(2); /* same type of prefix used twice */ return 0; } } } /* size floating point instruction */ if (suffix == 'l') { if (mnemo->ext.opcode_modifier & FloatMF) ip->ext.base_opcode ^= 4; } } return 1; } static uint32_t suffix_flag(instruction *ip) /* return the No_?Suf flag for the instruction's suffix */ { uint32_t chksuffix; switch (ip->qualifiers[0] ? tolower((unsigned char)ip->qualifiers[0][0]) : '\0') { case 'b': chksuffix = b_Illegal; break; case 'w': chksuffix = w_Illegal; break; case 'l': chksuffix = l_Illegal; break; case 's': chksuffix = s_Illegal; break; case 'q': chksuffix = q_Illegal; break; case 'x': chksuffix = x_Illegal; break; case '\0': chksuffix = 0; break; default: ierror(0); break; } return chksuffix; } static int find_next_mnemonic(instruction *ip) /* finds a mnemonic with the same name, which fits the given operand types and suffix */ { char *inst_name = mnemonics[ip->code].name; int code = ip->code + 1; mnemonic *mnemo = &mnemonics[code]; uint32_t chksuffix = suffix_flag(ip); if ((cpudebug & 32) && !(cpudebug & 4)) { printf("Finding next matching instruction for (opcode=0x%x):", ip->ext.base_opcode); print_operands(ip,-1); } while (!strcmp(inst_name,mnemo->name)) { int i,given,allowed,overlap,new_types[MAX_OPERANDS]; for (i=0; ioperand_type[i]) { if (ip->op[i]) { given = ip->op[i]->parsed_type; overlap = allowed & given; if ((overlap & ~JmpAbs) && (given & (BaseIndex|JmpAbs)) == (overlap & (BaseIndex|JmpAbs))) { new_types[i] = overlap; } else break; } else break; } else { if (ip->op[i]) break; } } if (i == MAX_OPERANDS) { /* all operands match, check suffix and CPU type */ if (cpudebug & 32) { printf("\toperands match, suffixOK=%d, cpuOK=%d\n", (mnemo->ext.opcode_modifier & chksuffix) == 0, (mnemo->ext.available & cpu_type) == mnemo->ext.available); } if (!(mnemo->ext.opcode_modifier & chksuffix) && (mnemo->ext.available & cpu_type)==mnemo->ext.available) { for (i=0; iop[i]) { if (cpudebug & 32) { printf("\tnew op %d: ",i+1); print_operand_type(ip->op[i]->parsed_type,'|'); printf(" & "); print_operand_type(new_types[i],'|'); printf(" = "); print_operand_type(ip->op[i]->parsed_type&new_types[i],'|'); printf("\n"); } ip->op[i]->type = ip->op[i]->parsed_type & new_types[i]; } } ip->code = code; ip->ext.base_opcode = mnemo->ext.base_opcode; assign_suffix(ip); if (cpudebug & 32) printf("\tNew opcode=0x%x!\n",ip->ext.base_opcode); return 1; } } /* try next entry from mnemonics table */ mnemo++; code++; } if (cpudebug & 32) printf("\tNo matching instruction found!\n"); return 0; } static int fix_imm_size(instruction *ip,operand *op,int final) /* apply suffix to immediate operand */ { char suffix = ip->qualifiers[0] ? tolower((unsigned char)ip->qualifiers[0][0]) : '\0'; int ot = op->type; if ((ot & (Imm8|Imm8S|Imm16|Imm32|Imm32S)) && ot!=Imm8 && ot!=Imm8S && ot!=Imm16 && ot!=Imm32 && ot!=Imm32S && ot!=Imm64) { switch (suffix) { case 'b': ot &= Imm8|Imm8S; break; case 'w': ot &= Imm16; break; case 'l': ot &= Imm32; break; case 'q': ot &= Imm64|Imm32S; break; case '\0': if ((mode_flag==CODE_16BIT) ^ (ip->ext.prefix[DATA_PREFIX]!=0)) ot &= Imm16; else ot &= Imm32|Imm32S; break; default: ierror(0); break; } if (ot!=Imm8 && ot!=Imm8S && ot!=Imm16 && ot!=Imm32 && ot!=Imm32S && ot!=Imm64) { if (final) cpu_error(24); /* can't determine imm. op. size w/o suffix */ return 0; } } return ot; } static void optimize_imm(instruction *ip,operand *op,section *sec, taddr pc,int final) /* determine smallest type which can hold the immediate mode */ { taddr val; int ot,nt; if (!eval_expr(op->value,&val,sec,pc)) { /* reloc or unknown symbols have always full size until they are known */ ot = Imm32; /* @@@ fixme? gas also allows Imm8-references */ } else { /* set valid operand types for this number */ ot = Imm64; if (val>=-0x80000000LL && val<=0xffffffffLL) { ot |= Imm32; if (val<=0x7fffffffLL) { ot |= Imm32S; if (val>=-0x8000 && val<=0xffff) { ot |= Imm16; if (val>=-0x80 && val<=0xff) { ot |= Imm8; if (val<=0x7f) ot |= Imm8S; } } } } } nt = ot & fix_imm_size(ip,op,final); while (!nt) { /* instruction doesn't support required modes, look for another one */ int oldtype = op->type; op->type = ot; if (!find_next_mnemonic(ip)) { op->type = oldtype; break; } nt = op->type & fix_imm_size(ip,op,final); } if (nt) op->type = nt; else if (final) cpu_error(23); /* instruction doesn't support these operand sizes */ } static void optimize_jump(instruction *ip,operand *op,section *sec, taddr pc,int final) { mnemonic *mnemo = &mnemonics[ip->code]; int mod = mnemo->ext.opcode_modifier; int label_in_sec; symbol *base; taddr val,diff; if (!eval_expr(op->value,&val,sec,pc)) { if (find_base(op->value,&base,sec,pc) != BASE_OK) { if (final) general_error(38); /* illegal relocation */ return; } label_in_sec = LOCREF(base) && (base->sec==sec); } else label_in_sec = 0; if (mod & JmpByte) { op->type = Disp8; if (final && label_in_sec) { diff = val - (pc + ip->ext.last_size); if (diff<-0x80 || diff>0x7f) cpu_error(22,diff); /* jump destination out of range */ } } else if (mod & JmpDword) { if (mode_flag == CODE_16BIT) op->type &= ~(Disp32|Disp32S); else op->type &= ~Disp16; if (final && label_in_sec) { diff = val - (pc + ip->ext.last_size); if (mode_flag == CODE_16BIT) { if (diff<-0x8000 || diff>0x7fff) cpu_error(22,diff); /* jump destination out of range */ } else { if (diff<-0x80000000LL || diff>0x7fffffffLL) cpu_error(22,diff); /* jump destination out of range */ } } } else { /* Jump */ op->type = (mode_flag == CODE_16BIT) ? Disp16 : Disp32; if (label_in_sec && ip->ext.last_size>=0) { /* check if it fits in 8-bit */ diff = val - (pc + ip->ext.last_size); if (diff>=-0x80 && diff<=0x7f && (ip->ext.flags&OPTFAILED)!=OPTFAILED) { op->type = Disp8; return; } else if (final) { if (mode_flag == CODE_16BIT) { if (diff<-0x8000 || diff>0x7fff) cpu_error(22,diff); /* jump dest. out of range */ } else { if (diff<-0x80000000LL || diff>0x7fffffffLL) cpu_error(22,diff); /* jump dest. out of range */ } } } if (ip->ext.base_opcode == OC_JMP_DISP) ip->ext.base_opcode = 0xe9; /* jmp */ else ip->ext.base_opcode += 0xf10; /* j */ } } static void optimize_disp(operand *op,section *sec,taddr pc,int final) { taddr val; if (!eval_expr(op->value,&val,sec,pc)) { /* reloc or unknown symbols have always full size until they are known */ if (mode_flag == CODE_16BIT) { op->type &= ~(Disp8|Disp32|Disp32S|Disp64); if (final && !(op->type & Disp16)) cpu_error(21,16); /* need at least n bits for a relocatable symbol */ } else { /* CODE_32BIT */ op->type &= ~(Disp8|Disp16); if (final && !(op->type & (Disp32|Disp32S|Disp64))) cpu_error(21,32); /* need at least n bits for a relocatable symbol */ } } else { /* try to use the smallest type for absolute displacements */ if (mode_flag == CODE_16BIT) { op->type &= ~(Disp32|Disp32S|Disp64); if (final && (val<-0x8000 || val>0x7fff)) cpu_error(25,16); /* doesn't fit into 16 bits */ if (val>=-0x80 && val<=0x7f && (op->type & Disp8)) op->type &= ~Disp16; else op->type &= ~Disp8; } else { /* CODE_32BIT */ op->type &= ~Disp16; /* no 16bit displacements possible */ if (val>=-0x80000000LL && val<=0xffffffffLL && (op->type & Disp32)) { op->type &= ~Disp64; if (val<=0x7fffffffLL && (op->type & Disp32S)) op->type &= ~Disp32; if (val>=-0x80 && val<=0x7f && (op->type & Disp8)) op->type &= ~(Disp32|Disp32S); else op->type &= ~Disp8; } else op->type &= ~(Disp8|Disp16|Disp32|Disp32S); } } } static int mode_from_disp_size(int type) { return (type & Disp8) ? 1 : (type & (Disp16 | Disp32 | Disp32S)) ? 2 : 0; } static int make_modrm(instruction *ip,mnemonic *mnemo,int final) { int defaultseg = -1; int i,reg_operands,mem_operands; /* count number of register and memory operands */ for (i=0,reg_operands=0,mem_operands=0; iop[i]) { if (ip->op[i]->type & AnyReg) reg_operands++; else if (ip->op[i]->type & AnyMem) mem_operands++; } } ip->ext.flags |= MODRM_BYTE; if (final && (cpudebug & 8)) { printf("make_modrm(): %s: %d reg operands, %d mem operands\n", mnemo->name,reg_operands,mem_operands); } if (reg_operands == 2) { i = (ip->op[0]->type & AnyReg) ? 0 : 1; /* first register operand */ ip->ext.rm.mode = 3; if (!(mnemo->operand_type[i+1] & AnyMem)) { ip->ext.rm.reg = ip->op[i+1]->basereg->reg_num; ip->ext.rm.regmem = ip->op[i]->basereg->reg_num; } else { ip->ext.rm.reg = ip->op[i]->basereg->reg_num; ip->ext.rm.regmem = ip->op[i+1]->basereg->reg_num; } } else { if (mem_operands) { operand *memop = ip->op[(ip->op[0]->type & AnyMem) ? 0 : ((ip->op[1]->type & AnyMem) ? 1 : 2)]; if (final && (cpudebug & 8)) { if (memop->basereg) { printf("\tbase=%%%s(",memop->basereg->reg_name); print_operand_type(memop->basereg->reg_type,'|'); putchar(')'); } else printf("\tbase=none"); if (memop->indexreg) { printf(" index=%%%s(",memop->indexreg->reg_name); print_operand_type(memop->indexreg->reg_type,'|'); putchar(')'); } else printf(" index=none"); printf(" scale=%d\n",scale_factor_tab[memop->log2_scale]); } defaultseg = DSEG_REGNUM; if (memop->basereg == NULL) { ip->ext.rm.mode = 0; if (final && memop->value==NULL) memop->value = number_expr(0); if (memop->indexreg == NULL) { /* no base and no index */ if ((mode_flag==CODE_16BIT) ^ (ip->ext.prefix[ADDR_PREFIX]!=0) && mode_flag!=CODE_64BIT) { ip->ext.rm.regmem = NO_BASEREG16; memop->type &= ~Disp; memop->type |= Disp16; } else if (mode_flag!=CODE_64BIT || ip->ext.prefix[ADDR_PREFIX]) { ip->ext.rm.regmem = NO_BASEREG; memop->type &= ~Disp; memop->type |= Disp32; } else { ip->ext.rm.regmem = TWO_BYTE_ESCAPE; ip->ext.sib.base = NO_BASEREG; ip->ext.sib.index = NO_INDEXREG; memop->type &= ~Disp; memop->type |= Disp32S; ip->ext.flags |= SIB_BYTE; } } else { /* no base, only index */ ip->ext.rm.regmem = TWO_BYTE_ESCAPE; ip->ext.sib.base = NO_BASEREG; ip->ext.sib.index = memop->indexreg->reg_num; ip->ext.sib.scale = memop->log2_scale; memop->type &= ~Disp; if (mode_flag != CODE_64BIT) memop->type |= Disp32; else memop->type |= Disp32S; ip->ext.flags |= SIB_BYTE; } } else if (memop->basereg->reg_type == BaseIndex) { /* RIP base register for 64-bit mode */ ip->ext.rm.regmem = NO_BASEREG; memop->type &= ~Disp; memop->type |= Disp32S; memop->flags |= OPER_PCREL; } else if (memop->basereg->reg_type & Reg16) { /* 16-bit mode base register */ switch (memop->basereg->reg_num) { case 3: /* %bx */ if (memop->indexreg) /* (%bx,%si) or (%bx,%di) */ ip->ext.rm.regmem = memop->indexreg->reg_num - 6; else ip->ext.rm.regmem = 7; break; case 5: /* %bp */ defaultseg = SSEG_REGNUM; if (memop->indexreg) { /* (%bp,%si) or (%bp,%di) */ ip->ext.rm.regmem = memop->indexreg->reg_num - 4; } else { ip->ext.rm.regmem = 6; if (!(memop->type & Disp)) { memop->type |= Disp8; /* fake 0(%bp) */ if (final) memop->value = number_expr(0); } } break; default: /* (%si) or (%di) */ ip->ext.rm.regmem = memop->basereg->reg_num - 2; break; } ip->ext.rm.mode = mode_from_disp_size(memop->type); } else { /* 32/64 bit mode base register */ if (mode_flag==CODE_64BIT && (memop->type & Disp)) memop->type = (memop->type&Disp8) ? Disp8|Disp32S : Disp32S; ip->ext.rm.regmem = memop->basereg->reg_num; ip->ext.sib.base = memop->basereg->reg_num; if (memop->basereg->reg_num == EBP_REGNUM) { defaultseg = SSEG_REGNUM; if (memop->value == NULL) { memop->type |= Disp8; /* fake 8-bit zero-displacement */ if (final) memop->value = number_expr(0); } } else if (memop->basereg->reg_num == ESP_REGNUM) { defaultseg = SSEG_REGNUM; } ip->ext.sib.scale = memop->log2_scale; if (memop->indexreg == NULL) { ip->ext.sib.index = NO_INDEXREG; } else { ip->ext.sib.index = memop->indexreg->reg_num; ip->ext.rm.regmem = TWO_BYTE_ESCAPE; ip->ext.flags |= SIB_BYTE; } ip->ext.rm.mode = mode_from_disp_size(memop->type); if (ip->ext.rm.regmem == TWO_BYTE_ESCAPE) ip->ext.flags |= SIB_BYTE; } } if (reg_operands) { operand *regop = ip->op[(ip->op[0]->type & AnyReg) ? 0 : ((ip->op[1]->type & AnyReg) ? 1 : 2)]; if (final && (cpudebug & 8)) { printf("\tregister=%%%s(",regop->basereg->reg_name); print_operand_type(regop->basereg->reg_type,'|'); printf(")\n"); } if (mnemo->ext.extension_opcode != NO_EXTOPCODE) { ip->ext.rm.regmem = regop->basereg->reg_num; } else { ip->ext.rm.reg = regop->basereg->reg_num; } if (!mem_operands) ip->ext.rm.mode = 3; } /* insert extension opcode when available */ if (mnemo->ext.extension_opcode != NO_EXTOPCODE) ip->ext.rm.reg = mnemo->ext.extension_opcode; } if (final && (cpudebug & 8)) { printf("\tMod/RM: mode=%d reg=%d regmem=%d\n", (int)ip->ext.rm.mode,(int)ip->ext.rm.reg,(int)ip->ext.rm.regmem); if (ip->ext.flags & SIB_BYTE) { printf("\tSIB: scale=%d index=%d base=%d\n", (int)ip->ext.sib.scale,(int)ip->ext.sib.index,(int)ip->ext.sib.base); } } return defaultseg; } static int process_operands(instruction *ip,int final) { mnemonic *mnemo = &mnemonics[ip->code]; int defaultseg = -1; int mod = mnemo->ext.opcode_modifier; int i; regsym *seg; if ((mod & FakeLastReg) && ip->op[0]) { /* convert "imul %imm,%reg" into "imul %imm,%reg,%reg" and convert "clr %reg" into "xor %reg,%reg" */ int regoper = (ip->op[0]->type & Reg) ? 0 : 1; ip->op[regoper+1] = ip->op[regoper]; } if ((mod & ShortForm) && ip->op[0]) { /* copy register number to base-opcode in short-format */ int regoper = (ip->op[0]->type & (Reg|FloatReg)) ? 0 : 1; ip->ext.base_opcode |= ip->op[regoper]->basereg->reg_num; if ((mod & Deprecated) && final) { if (ip->op[1]) { /* translating to fxxxx %reg,%reg */ cpu_error(16,mnemo->name,ip->op[1]->basereg->reg_name, ip->op[0]->basereg->reg_name); } else { /* translating to fxxxx %reg */ cpu_error(17,mnemo->name,ip->op[0]->basereg->reg_name); } } } else if (mod & M) { defaultseg = make_modrm(ip,mnemo,final); } else if (mod & (Seg2ShortForm|Seg3ShortForm)) { if (mnemo->ext.base_opcode==OC_POP_SREG2 && ip->op[0]->basereg && final) { if (ip->op[0]->basereg->reg_num == CSEG_REGNUM) { cpu_error(15,ip->op[0]->basereg->reg_name); /* cannot pop %cs */ return 0; } } ip->ext.base_opcode |= ip->op[0]->basereg->reg_num << 3; } else if (mnemo->ext.base_opcode == OC_MOV_ACC_DISP) { defaultseg = DSEG_REGNUM; } else if (mod & StrInst) { defaultseg = DSEG_REGNUM; } /* if a segment was explicitely specified and differs from the instruction's default we need to select it by another opcode prefix */ for (i=0; iop[i] != NULL) { if (seg = ip->op[i]->segoverride) { if (seg->reg_num != defaultseg) { if (!add_seg_prefix(ip,seg->reg_num)) { if (!final) cpu_error(2); /* same type of prefix used twice */ return 0; } } } } } return 1; } static int get_disp_bits(int t) { if (t & Disp8) return 8; if (t & Disp16) return 16; if (t & (Disp32|Disp32S)) return 32; return 64; } static int get_imm_bits(int t) { if (t & (Imm8|Imm8S)) return 8; if (t & Imm16) return 16; if (t & (Imm32|Imm32S)) return 32; return 64; } static size_t get_opcode_size(instruction *ip) { uint32_t c = ip->ext.base_opcode; size_t size = 1; if (c > 0xff) size++; if (c > 0xffff) size++; return size; } static size_t imm_size(int t) { size_t size = 0; if (t & Imm) { if (t & (Imm8|Imm8S)) size++; else if (t & Imm16) size += 2; else if (t & Imm64) size += 8; else size += 4; } return size; } static size_t disp_size(int t) { size_t size = 0; if (t & Disp) { if (t & Disp8) size++; else if (t & Disp16) size += 2; else if (t & Disp64) size += 8; else size += 4; } return size; } static size_t finalize_instruction(instruction *ip,section *sec, taddr pc,int final) /* execute optimizations, calculate instruction opcode, opcode-extension, modrm- and sib-bytes, and return its size in bytes */ { mnemonic *mnemo = &mnemonics[ip->code]; size_t size; int i; for (i=0; iop[i]) { /* optimizations */ if (ip->op[i]->type & Imm) { /* use smallest immediate size */ optimize_imm(ip,ip->op[i],sec,pc,final); } else if (ip->op[i]->type & Disp) { if (mnemo->ext.opcode_modifier & (Jmp|JmpByte|JmpDword)) { /* check jump-distances and try to use 8-bit form */ optimize_jump(ip,ip->op[i],sec,pc,final); } else { /* use the smallest type for absolute displacements */ optimize_disp(ip->op[i],sec,pc,final); } } if (final && ip->op[i]->scalefactor!=NULL) { /* have to evaluate scale factor now */ taddr sf; if (!eval_expr(ip->op[i]->scalefactor,&sf,sec,pc)) { cpu_error(18); /* absolute scale factor required */ ip->op[i]->log2_scale = 0; } else { switch (sf) { case 1: sf = 0; break; case 2: sf = 1; break; case 4: sf = 2; break; case 8: sf = 3; break; default: cpu_error(19); /* illegal scale factor */ break; } ip->op[i]->log2_scale = (int)sf; } } else ip->op[i]->log2_scale = 0; } } /* process operation size, add prefixes when required */ process_suffix(ip,final); /* set base opcode, process operands and generate modrm/sib if present */ process_operands(ip,final); /* determine size of instruction */ size = get_opcode_size(ip); size += ip->ext.num_prefixes; if (ip->ext.flags & MODRM_BYTE) size++; if (ip->ext.flags & SIB_BYTE) size++; for (i=0; iop[i]) { size += imm_size(ip->op[i]->type); size += disp_size(ip->op[i]->type); } else break; } if (cpudebug & 16) printf("%08lx: (%u) %s",(unsigned long)pc,(unsigned)size,mnemo->name); return size; } static unsigned char make_byte233(unsigned char b76,unsigned char b543, unsigned char b210) /* make a byte from three bit-fields: bit7-6, bit5-3 and bit2-0 */ { return ((b76 & 3)<<6) | ((b543 & 7)<<3) | (b210&7); } static unsigned char *write_taddr(unsigned char *d,taddr val,size_t bits) { switch (bits) { case 8: *d++ = val & 0xff; break; case 16: case 32: case 64: d = setval(0,d,bits>>3,val); break; default: ierror(0); break; } return d; } static unsigned char *output_prefixes(unsigned char *d,instruction *ip) { unsigned char p; int i; for (i=0; iext.prefix[i]) *d++ = p; } return d; } static unsigned char *output_opcodes(unsigned char *d,instruction *ip) { uint32_t oc = (uint32_t)ip->ext.base_opcode; if (oc > 0xffff) *d++ = (oc >> 16) & 0xff; if (oc > 0xff) *d++ = (oc >> 8) & 0xff; *d++ = oc & 0xff; if (ip->ext.flags & MODRM_BYTE) *d++ = make_byte233(ip->ext.rm.mode,ip->ext.rm.reg,ip->ext.rm.regmem); if (ip->ext.flags & SIB_BYTE) *d++ = make_byte233(ip->ext.sib.scale,ip->ext.sib.index,ip->ext.sib.base); return d; } static unsigned char *output_disp(dblock *db,unsigned char *d, instruction *ip,section *sec,taddr pc) { int i; size_t bits; operand *op; taddr val; for (i=0; iop[i]) { if (op->type & Disp) { bits = get_disp_bits(op->type); if (!eval_expr(op->value,&val,sec,pc)) { mnemonic *mnemo = &mnemonics[ip->code]; symbol *base; if (find_base(op->value,&base,sec,pc) == BASE_OK) { if ((mnemo->ext.opcode_modifier & (Jmp|JmpByte|JmpDword)) || (op->flags & OPER_PCREL)) { /* handle pc-relative displacement for jumps */ if (!is_pc_reloc(base,sec)) { val = val - (pc + (d-(unsigned char *)db->data) + (bits>>3)); } else { val -= bits>>3; add_extnreloc(&db->relocs,base,val,REL_PC,0,bits, (int)(d-(unsigned char *)db->data)); } } else { /* reloc for a normal absolute displacement */ add_extnreloc(&db->relocs,base,val,REL_ABS,0,bits, (int)(d-(unsigned char *)db->data)); } } else general_error(38); /* illegal relocation */ } d = write_taddr(d,val,bits); } } } return d; } static unsigned char *output_imm(dblock *db,unsigned char *d, instruction *ip,section *sec,taddr pc) { int i,ot; size_t bits; operand *op; taddr val; for (i=0; iop[i]) { if (ot = (op->type & Imm)) { bits = get_imm_bits(ot); #if 0 /* done in optimize_imm() */ if (bits>8 && ((ot & Imm16) && ((ot & Imm32) || (ot & Imm32S)))) { /* 16 or 32 bit immediate depends on current data size */ if ((mode_flag==CODE_16BIT) ^ (ip->ext.prefix[DATA_PREFIX]!=0)) bits = 16; else bits = 32; } #endif if (!eval_expr(op->value,&val,sec,pc)) { symbol *base; if (find_base(op->value,&base,sec,pc) == BASE_OK) { add_extnreloc(&db->relocs,base,val,REL_ABS,0,bits, (int)(d-(unsigned char *)db->data)); } else general_error(38); /* illegal relocation */ } d = write_taddr(d,val,bits); } } } return d; } static instruction *copy_instruction(instruction *ip) /* copy an instruction and its operands */ { static instruction newip; static operand newop[MAX_OPERANDS]; int i; newip.code = ip->code; newip.qualifiers[0] = ip->qualifiers[0]; for (i=0; iop[i] != NULL) { newip.op[i] = &newop[i]; *newip.op[i] = *ip->op[i]; } else newip.op[i] = NULL; } memcpy(&newip.ext,&ip->ext,sizeof(instruction_ext)); return &newip; } char *parse_cpu_special(char *start) /* parse cpu-specific directives; return pointer to end of cpu-specific text */ { char *name=start,*s=start; if (ISIDSTART(*s)) { s++; while (ISIDCHAR(*s)) s++; if (s-name==7 && !strncmp(name,".code16",7)) { mode_flag = CODE_16BIT; return s; } else if (s-name==7 && !strncmp(name,".code32",7)) { mode_flag = CODE_32BIT; return s; } else if (s-name==7 && !strncmp(name,".code64",7)) { mode_flag = CODE_64BIT; return s; } } return start; } char *parse_instruction(char *s,int *inst_len,char **ext,int *ext_len, int *ext_cnt) /* parse instruction and save extension locations */ { hashdata data; char *inst = s; int len; /* reset opcode prefixes */ memset(prefix,0,sizeof(prefix)); prefix_cnt = 0; if (cpudebug & 1) printf("parse_instruction: \"%s\"\n",s); for (;;) { while (*s && !isspace((unsigned char)*s)) s++; len = s - inst; if (find_namelen(mnemohash,inst,len,&data)) { #if 0 /*@@@ need a way to support prefixes at the same line with vasm */ mnemonic *mnemo = &mnemonics[data.idx]; if (mnemo->ext.opcode_modifier & IsPrefix) { /* matched a prefix instruction, remember it and look for more */ s = skip(s); if (ISIDSTART(*s)) { /* real opcode or another prefix follows, save current prefix */ if (!(mnemo->ext.available & cpu_type)) cpu_error(0); /* instruction not supported on selected arch. */ /* do not allow addr16 in 16-bit and addr32 in 32-bit mode */ if (((mnemo->ext.opcode_modifier&Size16) && mode_flag==CODE_16BIT) || ((mnemo->ext.opcode_modifier&Size32) && mode_flag==CODE_32BIT)) { cpu_error(7,mnemo->name); /* redundant prefix ignored */ } else { /* enter new prefix */ if (cpudebug & 1) { printf("\tadd prefix %02x for \"%s\"\n", (unsigned)mnemo->ext.base_opcode,mnemo->name); } if (!add_prefix(mnemo->ext.base_opcode)) cpu_error(2); /* same type of prefix used twice */ } inst = s; continue; /* parse next opcode or prefix */ } } #endif } else { /* no direct match, so look if we can strip a suffix from it */ if (len >= 2) { char x = *(s-1); if (x=='b' || x=='w' || x=='l' || x=='s' || x=='q' || x=='x') { /* a potential suffix found, save it */ int cnt = *ext_cnt; ext[cnt] = s-1; ext_len[cnt] = 1; *ext_cnt = ++cnt; --len; } } } break; } *inst_len = len; if (cpudebug & 1) { printf("\tinst=\"%.*s\" suffix=\"%.*s\"\n",len,inst, *ext_cnt?ext_len[0]:0, *ext_cnt?ext[0]:emptystr); } return s; } int set_default_qualifiers(char **q,int *q_len) /* fill in pointers to default qualifiers, return number of qualifiers */ { /* FIXME: no default size for x86 instructions? */ return 0; } static regsym *parse_reg(char **pp) /* parse a register name, return pointer to regsym when successful */ { char *p,*start; regsym *r; p = skip(*pp); if (*p++ == '%') { start = p; if (ISIDSTART(*p)) { p++; while (ISIDCHAR(*p)) p++; /* special case float-registers: %st(n) */ if (p-start==2 && (!strncmp(start,"st(",3) || !strncmp(start,"ST(",3))) { if (isdigit((unsigned char)*(p+1)) && *(p+2)==')') p += 3; } if (r = find_regsym_nc(start,p-start)) { if ((r->reg_flags & (RegRex64|RegRex)) && mode_flag!=CODE_64BIT) return NULL; *pp = p; return r; } } } return NULL; } int parse_operand(char *p,int len,operand *op,int requirements) /* Parses operands, reads expressions and assigns relocation types */ { char *start = p; int need_mem_oper = 0; int given_type,overlap; if (cpudebug & 2) { printf("parse_operand (reqs=%08lx): \"%.*s\"\n", (unsigned long)requirements,len,p); } /* @@@ This does no longer work, since save_symbols()/restore_symbols(). given_type = op->parsed_type; */ given_type = 0; /* ... so parse the operand every time again */ if (given_type == 0) { p = skip(p); if (*p == '%') { /* check for a segment override first */ char *savedp = p; regsym *sreg = parse_reg(&p); p = skip(p); if (sreg!=NULL && (sreg->reg_type & (SegReg2|SegReg3)) && *p++==':') { op->segoverride = sreg; need_mem_oper = 1; /* only memory operands may follow */ p = skip(p); } else { /* no segment override - forget all we did */ p = savedp; } } if (*p == '*') { /* indicates absolute jumps */ p = skip(++p); given_type = JmpAbs; } else given_type = 0; if (*p == '%') { /* single register operand */ if (need_mem_oper) { cpu_error(14); /* memory operand expected */ return PO_CORRUPT; } if (op->basereg = parse_reg(&p)) { op->flags |= OPER_REG; given_type |= op->basereg->reg_type & ~BaseIndex; } else { cpu_error(8); /* unknown register specified */ return PO_CORRUPT; } } else if (*p == '$') { /* immediate operand */ if (need_mem_oper) { cpu_error(14); /* memory operand expected */ return PO_CORRUPT; } if (given_type & JmpAbs) { cpu_error(3); /* immediate operand illegal with absolute jump */ return PO_CORRUPT; } p++; op->value = parse_expr(&p); given_type = Imm; /* exact size is not available at this stage */ } else { if (*p != '(') { /* read displacement (or data) */ given_type |= Disp; /* exact size is not available at this stage */ if ((requirements & FloatData) == FloatData) { op->value = parse_expr_float(&p); given_type |= FloatData; } else op->value = parse_expr(&p); p = skip(p); } if (*p == '(') { /* BaseIndex mode: read base, index, scale */ p = skip(++p); if (*p != ',') { if (!(op->basereg = parse_reg(&p))) { cpu_error(4); /* base register expected */ return PO_CORRUPT; } p = skip(p); } if (*p == ',') { p = skip(++p); if (!(op->indexreg = parse_reg(&p))) { cpu_error(5); /* scale factor without index register */ return PO_CORRUPT; } p = skip(p); if (*p == ',') { p = skip(++p); op->scalefactor = parse_expr(&p); p = skip(p); } } if (*p++ != ')') { cpu_error(6); /* missing ')' in baseindex addressing mode */ return PO_CORRUPT; } given_type |= BaseIndex; } } p = skip(p); if (p - start < len) cpu_error(1); /* trailing garbage in operand */ op->parsed_type = given_type; /* remember type for next try */ } overlap = given_type & requirements; if (cpudebug & 2) { printf("\ttype given: %08lx overlap with reqs: %08lx - ", (unsigned long)given_type,(unsigned long)overlap); } if ((overlap & ~JmpAbs) && (given_type & (BaseIndex|JmpAbs)) == (overlap & (BaseIndex|JmpAbs))) { /* ok, parsed operand type matches requirements */ if (cpudebug & 2) printf("MATCHED!\n"); op->type = overlap; /* @@@ check register types??? */ /* If given types r0 and r1 are registers they must be of the same type unless the expected operand type register overlap is null. Note that Acc in a template matches every size of reg. */ return PO_MATCH; } if (cpudebug & 2) printf("failed\n"); return PO_NOMATCH; } void init_instruction_ext(instruction_ext *ixp) { int i; memset(ixp,0,sizeof(instruction_ext)); if (prefix_cnt) { ixp->num_prefixes = prefix_cnt; for (i=0; iprefix[i] = prefix[i]; } ixp->last_size = -1; } size_t instruction_size(instruction *realip,section *sec,taddr pc) /* Calculate the size of the current instruction; must be identical to the data created by eval_instruction. */ { mnemonic *mnemo = &mnemonics[realip->code]; int i,diff; size_t size; /* assign the suffix, which was unknown during operand evaluation */ if (!(realip->ext.flags & SUFFIX_CHECKED)) { if (cpudebug & 4) { printf("instruction_size():"); print_operands(realip,pc); } /* set base opcode from mnemonic */ realip->ext.base_opcode = mnemo->ext.base_opcode; for (i=0; iop[i]) { if (realip->op[i]->flags & OPER_REG) realip->ext.flags |= HAS_REG_OPER; } else break; } /* try to assign a suffix */ assign_suffix(realip); realip->ext.flags |= SUFFIX_CHECKED; /* and check if instruction supports suffix and matches selected cpu */ if ((mnemo->ext.opcode_modifier & suffix_flag(realip)) || (mnemo->ext.available & cpu_type)!=mnemo->ext.available) { if (!find_next_mnemonic(realip)) cpu_error(0); /* instruction not supported on selected arch. */ } } /* work on a copy of the current instruction and finalize it */ size = finalize_instruction(copy_instruction(realip),sec,pc,0); if (realip->ext.last_size>=0 && (diff=realip->ext.last_size-(int)size)!=0) { if (diff > 0) { if (cpudebug & 16) printf(" (%d bytes gained)\n",diff); realip->ext.flags |= POSOPT; } else { if (cpudebug & 16) printf(" (%d bytes lost)\n",-diff); realip->ext.flags |= NEGOPT; } } else { if (cpudebug & 16) printf("\n"); } realip->ext.last_size = size; return size; } dblock *eval_instruction(instruction *ip,section *sec,taddr pc) /* Convert an instruction into a DATA atom including relocations, if necessary. */ { dblock *db = new_dblock(); unsigned char *d; db->size = finalize_instruction(ip,sec,pc,1); db->data = mymalloc(db->size); if (cpudebug & 16) printf("\n"); if (cpudebug & 4) { printf("eval_instruction():"); print_operands(ip,pc); } d = output_prefixes(db->data,ip); d = output_opcodes(d,ip); d = output_disp(db,d,ip,sec,pc); d = output_imm(db,d,ip,sec,pc); return db; } dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc) /* Create a dblock (with relocs, if necessary) for size bits of data. */ { dblock *db = new_dblock(); taddr val; tfloat flt; db->size = bitsize >> 3; db->data = mymalloc(db->size); if (type_of_expr(op->value) == FLT) { if (!eval_expr_float(op->value,&flt)) general_error(60); /* cannot evaluate floating point */ switch (bitsize) { case 32: conv2ieee32(0,db->data,flt); break; case 64: conv2ieee64(0,db->data,flt); break; default: cpu_error(20,bitsize); /* illegal bitsize */ break; } } else { if (!eval_expr(op->value,&val,sec,pc)) { symbol *base; int btype; btype = find_base(op->value,&base,sec,pc); if (base) add_extnreloc(&db->relocs,base,val, btype==BASE_PCREL?REL_PC:REL_ABS, 0,bitsize,0); else if (btype != BASE_NONE) general_error(38); /* illegal relocation */ } write_taddr(db->data,val,bitsize); } return db; } int init_cpu() { int i; regsym *r; if (!(cpu_type & CPU64)) cpu_type |= CPUNo64; for (i=0; ireg_name!=NULL; r++) add_regsym(r); return 1; } int cpu_args(char *p) { if (!strncmp(p,"-cpudebug=",7)) { if (!sscanf(p+10,"%li",&cpudebug)) return 0; } else if (!strncmp(p,"-m",2)) { p += 2; if (!strcmp(p,"8086")) cpu_type = CPU086; else if (!strcmp(p,"i186")) cpu_type = CPU086|CPU186; else if (!strcmp(p,"i286")) cpu_type = CPU086|CPU186|CPU286; else if (!strcmp(p,"i386")) cpu_type = CPU086|CPU186|CPU286|CPU386; else if (!strcmp(p,"i486")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486; else if (!strcmp(p,"i586")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPUMMX; else if (!strcmp(p,"i686")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUMMX|CPUSSE; else if (!strcmp(p,"pentium")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPUMMX; else if (!strcmp(p,"pentiumpro")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUMMX|CPUSSE; else if (!strcmp(p,"pentium4")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUP4|CPUMMX|CPUSSE|CPUSSE2; else if (!strcmp(p,"k6")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPUK6|CPUMMX|CPU3dnow; else if (!strcmp(p,"athlon")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUK6|CPUAthlon|CPUMMX|CPU3dnow; else if (!strcmp(p,"sledgehammer")) cpu_type = CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUK6|CPUAthlon|CPUSledgehammer|CPUMMX|CPU3dnow|CPUSSE|CPUSSE2; else if (!strcmp(p,"64")) { /* 64 bits architecture */ cpu_type = CPUAny|CPU64; bytespertaddr = 8; } else return 0; } return 1; }