📄 tc-tahoe.c
字号:
titP->tit_error = _("Unknown operator"); count = 0; titP->tit_opcode = 0; } else { /* * We found a match! So let's pick up as many operands as the * instruction wants, and even gripe if there are too many. * We expect comma to seperate each operand. * We let instring track the text, while p tracks a part of the * struct tot. */ count = 0; /* no operands seen yet */ instring = p + (*p != '\0'); /* point past the operation code */ /* tip_op() screws with the input_line_pointer, so save it before I jump in */ save_input_line_pointer = input_line_pointer; for (p = twP->args, operandp = titP->tit_operand; !*alloperr && *p; operandp++, p += 2) { /* * Here to parse one operand. Leave instring pointing just * past any one ',' that marks the end of this operand. */ if (!p[1]) as_fatal (_("Compiler bug: ODD number of bytes in arg structure %s."), twP->args); else if (*instring) { for (q = instring; (*q != ',' && *q != '\0'); q++) { if (*q == '\'' && q[1] != '\0') /* Jump quoted characters */ q++; } c = *q; /* * Q points to ',' or '\0' that ends argument. C is that * character. */ *q = '\0'; operandp->top_access = p[0]; operandp->top_width = p[1]; tip_op (instring - 1, operandp); *q = c; /* Restore input text. */ if (*(operandp->top_error)) { alloperr = operandp->top_error; } instring = q + (c ? 1 : 0); /* next operand (if any) */ count++; /* won another argument, may have an operr */ } else alloperr = _("Not enough operands"); } /* Restore the pointer. */ input_line_pointer = save_input_line_pointer; if (!*alloperr) { if (*instring == ' ') instring++; /* Skip whitespace. */ if (*instring) alloperr = _("Too many operands"); } titP->tit_error = alloperr; } } titP->tit_opcode = twP->code; /* The op-code. */ titP->tit_operands = count;} /* tip *//* md_assemble() emit frags for 1 instruction */voidmd_assemble (instruction_string) char *instruction_string; /* A string: assemble 1 instruction. */{ char *p; register struct top *operandP;/* An operand. Scans all operands. */ /* char c_save; fixme: remove this line *//* What used to live after an expression. */ /* struct frag *fragP; fixme: remove this line *//* Fragment of code we just made. */ /* register struct top *end_operandP; fixme: remove this line *//* -> slot just after last operand Limit of the for (each operand). */ register expressionS *expP; /* -> expression values for this operand */ /* These refer to an instruction operand expression. */ segT to_seg; /* Target segment of the address. */ register valueT this_add_number; register symbolS *this_add_symbol; /* +ve (minuend) symbol. */ /* tahoe_opcodeT opcode_as_number; fixme: remove this line *//* The opcode as a number. */ char *opcodeP; /* Where it is in a frag. */ /* char *opmodeP; fixme: remove this line *//* Where opcode type is, in a frag. */ int dispsize; /* From top_dispsize: tahoe_operand_width (in bytes) */ int is_undefined; /* 1 if operand expression's segment not known yet. */ int pc_rel; /* Is this operand pc relative? */ /* Decode the operand. */ tip (&t, instruction_string); /* * Check to see if this operand decode properly. * Notice that we haven't made any frags yet. * If it goofed, then this instruction will wedge in any pass, * and we can safely flush it, without causing interpass symbol phase * errors. That is, without changing label values in different passes. */ if (*t.tit_error) { as_warn (_("Ignoring statement due to \"%s\""), t.tit_error); } else { /* We saw no errors in any operands - try to make frag(s) */ /* Emit op-code. */ /* Remember where it is, in case we want to modify the op-code later. */ opcodeP = frag_more (1); *opcodeP = t.tit_opcode; /* Now do each operand. */ for (operandP = t.tit_operand; operandP < t.tit_operand + t.tit_operands; operandP++) { /* for each operand */ expP = &(operandP->exp_of_operand); if (operandP->top_ndx >= 0) { /* Indexed addressing byte Legality of indexed mode already checked: it is OK */ FRAG_APPEND_1_CHAR (0x40 + operandP->top_ndx); } /* if(top_ndx>=0) */ /* Here to make main operand frag(s). */ this_add_number = expP->X_add_number; this_add_symbol = expP->X_add_symbol; to_seg = operandP->seg_of_operand; know (to_seg == SEG_UNKNOWN || \ to_seg == SEG_ABSOLUTE || \ to_seg == SEG_DATA || \ to_seg == SEG_TEXT || \ to_seg == SEG_BSS); is_undefined = (to_seg == SEG_UNKNOWN); /* Do we know how big this opperand is? */ dispsize = operandP->top_dispsize; pc_rel = 0; /* Deal with the branch possabilities. (Note, this doesn't include jumps.)*/ if (operandP->top_access == 'b') { /* Branches must be expressions. A psuedo branch can also jump to an absolute address. */ if (to_seg == now_seg || is_undefined) { /* If is_undefined, then it might BECOME now_seg by relax time. */ if (dispsize) { /* I know how big the branch is supposed to be (it's a normal branch), so I set up the frag, and let GAS do the rest. */ p = frag_more (dispsize); fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, size_to_fx (dispsize, 1), NULL); } else { /* (to_seg==now_seg || to_seg == SEG_UNKNOWN) && dispsize==0 */ /* If we don't know how big it is, then its a synthetic branch, so we set up a simple relax state. */ switch (operandP->top_width) { case TAHOE_WIDTH_CONDITIONAL_JUMP: /* Simple (conditional) jump. I may have to reverse the condition of opcodeP, and then jump to my destination. I set 1 byte aside for the branch off set, and could need 6 more bytes for the pc_rel jump */ frag_var (rs_machine_dependent, 7, 1, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, is_undefined ? STATE_UNDF : STATE_BYTE), this_add_symbol, this_add_number, opcodeP); break; case TAHOE_WIDTH_ALWAYS_JUMP: /* Simple (unconditional) jump. I may have to convert this to a word branch, or an absolute jump. */ frag_var (rs_machine_dependent, 5, 1, ENCODE_RELAX (STATE_ALWAYS_BRANCH, is_undefined ? STATE_UNDF : STATE_BYTE), this_add_symbol, this_add_number, opcodeP); break; /* The smallest size for the next 2 cases is word. */ case TAHOE_WIDTH_BIG_REV_JUMP: frag_var (rs_machine_dependent, 8, 2, ENCODE_RELAX (STATE_BIG_REV_BRANCH, is_undefined ? STATE_UNDF : STATE_WORD), this_add_symbol, this_add_number, opcodeP); break; case TAHOE_WIDTH_BIG_NON_REV_JUMP: frag_var (rs_machine_dependent, 10, 2, ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, is_undefined ? STATE_UNDF : STATE_WORD), this_add_symbol, this_add_number, opcodeP); break; default: as_fatal (_("Compliler bug: Got a case (%d) I wasn't expecting."), operandP->top_width); } } } else { /* to_seg != now_seg && to_seg != seg_unknown (still in branch) In other words, I'm jumping out of my segment so extend the branches to jumps, and let GAS fix them. */ /* These are "branches" what will always be branches around a jump to the correct addresss in real life. If to_seg is SEG_ABSOLUTE, just encode the branch in, else let GAS fix the address. */ switch (operandP->top_width) { /* The theory: For SEG_ABSOLUTE, then mode is ABSOLUTE_ADDR, jump to that addresss (not pc_rel). For other segs, address is a long word PC rel jump. */ case TAHOE_WIDTH_CONDITIONAL_JUMP: /* b<cond> */ /* To reverse the condition in a TAHOE branch, complement bit 4 */ *opcodeP ^= 0x10; p = frag_more (7); *p++ = 6; *p++ = TAHOE_JMP; *p++ = (operandP->top_mode == TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR : TAHOE_PC_REL_LONG); fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL); /* * Now (eg) BLEQ 1f * JMP foo * 1: */ break; case TAHOE_WIDTH_ALWAYS_JUMP: /* br, just turn it into a jump */ *opcodeP = TAHOE_JMP; p = frag_more (5); *p++ = (operandP->top_mode == TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR : TAHOE_PC_REL_LONG); fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL); /* Now (eg) JMP foo */ break; case TAHOE_WIDTH_BIG_REV_JUMP: p = frag_more (8); *opcodeP ^= 0x10; *p++ = 0; *p++ = 6; *p++ = TAHOE_JMP; *p++ = (operandP->top_mode == TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR : TAHOE_PC_REL_LONG); fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL); /* * Now (eg) ACBx 1f * JMP foo * 1: */ break; case TAHOE_WIDTH_BIG_NON_REV_JUMP: p = frag_more (10); *p++ = 0; *p++ = 2; *p++ = TAHOE_BRB; *p++ = 6; *p++ = TAHOE_JMP; *p++ = (operandP->top_mode == TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR : TAHOE_PC_REL_LONG); fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL); /* * Now (eg) xOBxxx 1f * BRB 2f * 1: JMP @#foo * 2: */ break; case 'b': case 'w': as_warn (_("Real branch displacements must be expressions.")); break; default: as_fatal (_("Complier error: I got an unknown synthetic branch :%c"), operandP->top_width); break; } } } else { /* It ain't a branch operand. */ switch (operandP->top_mode) { /* Auto-foo access, only works for one reg (SP) so the only thing needed is the mode. */ case TAHOE_AUTO_DEC: case TAHOE_AUTO_INC: case TAHOE_AUTO_INC_DEFERRED: FRAG_APPEND_1_CHAR (operandP->top_mode); break; /* Numbered Register only access. Only thing needed is the mode + Register number */ case TAHOE_DIRECT_REG: case TAHOE_REG_DEFERRED: FRAG_APPEND_1_CHAR (operandP->top_mode + operandP->top_reg); break; /* An absolute address. It's size is always 5 bytes. (mode_type + 4 byte address). */ case TAHOE_ABSOLUTE_ADDR: know ((this_add_symbol == NULL)); p = frag_more (5); *p = TAHOE_ABSOLUTE_ADDR; md_number_to_chars (p + 1, this_add_number, 4); break; /* Immediate data. If the size isn't known, then it's an address + and offset, which is 4 bytes big. */ case TAHOE_IMMEDIATE: if (this_add_symbol != NULL) { p = frag_more (5); *p++ = TAHOE_IMMEDIATE_LONGWORD; fix_new (frag_now, p - frag_now->fr_literal, this_add_symbol, this_add_number, FX_32, NULL); } else { /* It's a integer, and I know it's size. */ if ((unsigned) this_add_number < 0x40) { /* Will it fit in a literal? */ FRAG_APPEND_1_CHAR ((byte) this_add_number); } else { p = frag_more (dispsize + 1); switch (dispsize) { case 1: *p++ = TAHOE_IMMEDIATE_BYTE; *p = (byte) this_add_number; break; case 2: *p++ = TAHOE_IMMEDIATE_WORD; md_number_to_chars (p, this_add_number, 2); break; case 4: *p++ = TAHOE_IMMEDIATE_LONGWORD; md_number_to_chars (p, this_add_number, 4); break; } } } break; /* Distance from the PC. If the size isn't known, we have to relax into it. The difference between this and disp(sp) is that this offset is pc_rel, and disp(sp) isn't. Note the drop through code. */ case TAHOE_DISPLACED_RELATIVE: case TAHOE_DISP_REL_DEFERRED: operandP->top_reg = PC_REG; pc_rel = 1; /* Register, plus a displacement mode. Save the register number, and weather its deffered or not, and relax the size if it isn't known. */ case TAHOE_REG_DISP: case TAHOE_REG_DISP_DEFERRED: if (operandP->top_mode == TAHOE_DISP_REL_DEFERRED || operandP->top_mode == TAHOE_REG_DISP_DEFERRED) operandP->top_reg += 0x10; /* deffered mode is always 0x10 higher than it's non-deffered sibling. */ /* Is this a value out of this segment? The first part of this conditional is a cludge to make gas produce the same output as 'as' when there is a lable, in the current segment, displaceing a register. It's strange, and no one in their right mind would do it, but it's easy to cludge. */ if ((dispsize == 0 && !pc_rel) || (to_seg != now_seg && !is_undefined && to_seg != SEG_ABSOLUTE)) dispsize = 4; if (dispsize == 0) { /* * We have a SEG_UNKNOWN symbol, or the size isn't cast. * It might turn out to be in the same segment as * the instruction, permitting relaxation. */ p = frag_var (rs_machine_dependent, 5, 2, ENCODE_RELAX (STATE_PC_RELATIVE, is_undefined ? STATE_UNDF : STATE_BYTE), this_add_symbol, this_add_number, 0); *p = operandP->top_reg; } else { /* Either this is an abs, or a cast. */ p = frag_more (dispsize + 1); switch (dispsize) { case 1: *p = TAHOE_PC_OR_BYTE + operandP->top_reg; break; case 2: *p = TAHOE_PC_OR_WORD + operandP->top_reg; break; case 4: *p = TAHOE_PC_OR_LONG + operandP->top_reg; break; }; fix_new (frag_now, p + 1 - frag_now->fr_literal, this_add_symbol, this_add_number, size_to_fx (dispsize, pc_rel), NULL); } break; default: as_fatal (_("Barf, bad mode %x\n"), operandP->top_mode); } } } /* for(operandP) */ } /* if(!need_pass_2 && !goofed) */} /* tahoe_assemble() *//* We have no need to default values of symbols. */symbolS *md_undefined_symbol (name) char *name;{ return 0;} /* md_undefined_symbol() *//* Round up a section size to the appropriate boundary. */valueTmd_section_align (segment, size) segT segment; valueT size;{ return ((size + 7) & ~7); /* Round all sects to multiple of 8 */} /* md_section_align() *//* Exactly what point is a PC-relative offset relative TO? On the sparc, they're relative to the address of the offset, plus its size. This gets us to the following instruction. (??? Is this right? FIXME-SOON) */longmd_pcrel_from (fixP) fixS *fixP;{ return (((fixP->fx_type == FX_8 || fixP->fx_type == FX_PCREL8) ? 1 : ((fixP->fx_type == FX_16 || fixP->fx_type == FX_PCREL16) ? 2 : ((fixP->fx_type == FX_32 || fixP->fx_type == FX_PCREL32) ? 4 : 0))) + fixP->fx_where + fixP->fx_frag->fr_address);} /* md_pcrel_from() */inttc_is_pcrel (fixP) fixS *fixP;{ /* should never be called */ know (0); return (0);} /* tc_is_pcrel() */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -