home *** CD-ROM | disk | FTP | other *** search
- /* SPIM S20 MIPS simulator.
- Code to build assembly instructions and resolve symbolic labels.
- Copyright (C) 1990 by James Larus (larus@cs.wisc.edu).
-
- SPIM is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 1, or (at your option) any
- later version.
-
- SPIM is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU CC; see the file COPYING. If not, write to James R.
- Larus, Computer Sciences Department, University of Wisconsin--Madison,
- 1210 West Dayton Street, Madison, WI 53706, USA or to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-
- /* $Header: /var/home/cs354/.spim/RCS/inst.c,v 1.4 1992/10/12 11:33:58 cs354 Exp $ */
-
- #include <stdio.h>
- #include "spim.h"
- #include "inst.h"
- #include "mem.h"
- #include "reg.h"
- #include "sym_tbl.h"
- #ifdef WIN32
- #include "y_tab.h"
- #else
- #ifdef MACINTOSH
- #include "y_tab.h"
- #else
- #include "y.tab.h"
- #endif
- #endif
- #include "data.h"
-
- /* Internal functions: */
-
- typedef int (*CompareFunc)();
-
- static instruction *mk_inst(int opcode,int rs,int rt,int rd,int shamt,int offset,int target);
- static void inst_cmp(instruction *inst1, instruction *inst2);
- static int compare_pair_value(const inst_info *p1,const inst_info *p2);
- static void sort_name_table(void);
- static void sort_i_opcode_table(void);
- static void sort_a_opcode_table(void);
- static int print_imm_expr(char *buf,const imm_expr *expr,int base_reg);
- static void i_type_inst_full_word(int opcode,int rt,int rs,
- imm_expr *expr, int value_known,long value);
-
- /* Imported functions: */
-
- imm_expr *branch_offset (int);
- int imm_op_to_op (int);
-
-
- /* Imported variables: */
-
- extern int data_dir;
- extern int text_dir;
-
-
- /* Non-zero means store instructions in kernel, not user, text segment */
-
- int in_kernel = 0;
-
-
- /* Locations for next instruction in user and kernel text segments */
-
- static mem_addr next_text_pc;
-
- static mem_addr next_k_text_pc;
-
- unsigned int last_inst_addr;
-
- #define INST_PC (in_kernel ? next_k_text_pc : next_text_pc)
-
-
- #define BUMP_INST_PC(DELTA) {if (in_kernel) \
- next_k_text_pc += DELTA; \
- else next_text_pc += DELTA;}
-
-
-
- /* Set ADDRESS at which the next instruction is stored. */
-
- void text_begins_at_point (mem_addr addr)
- {
- next_text_pc = addr;
- }
-
-
- void k_text_begins_at_point (mem_addr addr)
- {
- next_k_text_pc = addr;
- }
-
-
- /* Return address for next instruction, in appropriate text segment. */
-
- mem_addr current_text_pc (void)
- {
- return (INST_PC);
- }
-
-
- /* Increment the current text segement PC. */
-
- void increment_text_pc (int delta)
- {
- BUMP_INST_PC (delta);
- }
-
-
- /* If FLAG is non-zero, next instruction goes to kernel text segment,
- otherwise it goes to user segment. */
-
- void
- user_kernel_text_segment (to_kernel)
- int to_kernel;
- {
- in_kernel = to_kernel;
- }
-
-
- /* Store an INSTRUCTION in memory at the next location. */
-
- void store_instruction (instruction *inst)
- {
- if (data_dir)
- {
- store_word (inst_encode (inst));
- free (inst);
- }
- else if (text_dir)
- {
- exception_occurred = 0;
- last_inst_addr = INST_PC;
- SET_MEM_INST (INST_PC, inst);
- if (exception_occurred) {
- sprintf(mess_buff, "Invalid address (0x%08x) for instruction\n",
- INST_PC);
- error (mess_buff);
- }
- else
- BUMP_INST_PC (BYTES_PER_WORD);
- }
- }
-
-
-
- /* Produce an immediate instruction with the OPCODE, RT, RS, and IMM
- fields. NB, because the immediate value may not fit in the field,
- this routine may produce more than one instruction. On the bare
- machine, we resolve symbolic address, but they better produce values
- that fit into instruction's immediate field. */
-
- void i_type_inst (int opcode, int rt, int rs, imm_expr *expr)
- {
- instruction *inst = (instruction *) malloc (sizeof (instruction));
-
- if ( !inst )
- fatal_error("Ran out of memory in i_type_inst().\n");
-
- bzero (inst, sizeof (instruction));
- OPCODE (inst) = opcode;
- RS (inst) = rs;
- RT (inst) = rt;
- EXPR (inst) = expr;
- if (expr->symbol == NULL || SYMBOL_IS_DEFINED (expr->symbol))
- {
- /* Evaluate the instruction's expression. */
- long value = eval_imm_expr (expr);
-
- if (!bare_machine
- && (value & 0xffff0000) != 0
- && !((value & 0xffff0000) == 0xffff0000
- && (opcode == Y_ADDI_OP
- || opcode == Y_ADDIU_OP
- || opcode == Y_SLTI_OP
- || opcode == Y_SLTIU_OP)))
- {
- free (inst);
- i_type_inst_full_word (opcode, rt, rs, expr, 1, value);
- return;
- }
- else
- resolve_a_label (expr->symbol, inst, INST_PC);
- }
- else if (bare_machine || expr->bits != 0)
- /* Don't know expressions value, but only needed upper/lower 16-bits
- anyways. */
- record_inst_uses_symbol (inst, INST_PC, expr->symbol);
- else
- {
- /* Don't know the expressions's value and want all of its bits,
- so assume that it will not produce a small result and generate
- sequence for 32 bit value. */
- free (inst);
-
- i_type_inst_full_word (opcode, rt, rs, expr, 0, 0);
- return;
- }
-
- store_instruction (inst);
- }
-
-
- /* The immediate value for an instruction will (or may) not fit in 16 bits.
- Build the value from its piece with separate instructions. */
-
- static void i_type_inst_full_word (int opcode, int rt, int rs,
- imm_expr *expr, int value_known, long value)
- {
- if (opcode_is_load_store (opcode))
- {
- long offset;
-
- if (expr->symbol != NULL
- && expr->symbol->gp_flag && rs == 0
- && (offset = expr->symbol->addr + expr->offset) >= -32*K
- && offset < 32*K)
- i_type_inst (opcode, rt, REG_GP, make_imm_expr (offset, NULL, 0));
- else if (value_known)
- {
- int low, high;
-
- high = (value >> 16) & 0xffff;
- low = 0xffff & value;
-
- if (high &&
- !(high == 0xffff && (low & 0x8000))) /* load sign-extends */
- {
- if (low & 0x8000)
- high += 1; /* Adjust since load sign-extends */
-
- i_type_inst (Y_LUI_OP, 1, 0, const_imm_expr (high));
- if (rs != 0) /* Base register */
- r_type_inst (Y_ADDU_OP, 1, 1, rs);
- i_type_inst (opcode, rt, 1, const_imm_expr (low));
- }
- else
- i_type_inst (opcode, rt, rs, const_imm_expr (low));
- }
- else
- {
- /* Use $at */
- /* Need to adjust if lower bits are negative */
- i_type_inst (Y_LUI_OP, 1, 0, upper_bits_of_expr (expr));
- if (rs != 0) /* Base register */
- r_type_inst (Y_ADDU_OP, 1, 1, rs);
- i_type_inst (opcode, rt, 1, lower_bits_of_expr (expr));
- }
- }
- else if (opcode_is_branch (opcode))
- {
- /* This only allows branches +/- 32K, which is not correct! */
- i_type_inst (opcode, rt, rs, lower_bits_of_expr (expr));
- }
- else
- /* Computation instruction */
- {
- long offset;
-
- if (expr->symbol != NULL
- && expr->symbol->gp_flag && rs == 0
- && (offset = expr->symbol->addr + expr->offset) >= -32*K
- && offset < 32*K)
- i_type_inst ((opcode == Y_LUI_OP ? Y_ADDIU_OP : opcode),
- rt, REG_GP, make_imm_expr (offset, NULL, 0));
- else
- {
- /* Use $at */
- if ((opcode == Y_ORI_OP
- || opcode == Y_ADDI_OP || opcode == Y_ADDIU_OP
- || opcode == Y_LUI_OP)
- && rs == 0)
- {
- if (value_known && (value & 0xffff) == 0)
- i_type_inst (Y_LUI_OP, rt, 0, upper_bits_of_expr (expr));
- else
- {
- i_type_inst (Y_LUI_OP, 1, 0, upper_bits_of_expr (expr));
- i_type_inst (Y_ORI_OP, rt, 1, lower_bits_of_expr (expr));
- }
- }
- else
- {
- i_type_inst (Y_LUI_OP, 1, 0, upper_bits_of_expr (expr));
- i_type_inst (Y_ORI_OP, 1, 1, lower_bits_of_expr (expr));
- r_type_inst (imm_op_to_op (opcode), rt, rs, 1);
- }
- }
- }
- }
-
-
- /* Return a jump-type instruction with the given OPCODE and TARGET
- fields. NB, even the immediate value may not fit in the field, this
- routine will not produce more than one instruction. */
-
- void j_type_inst (int opcode, imm_expr *target)
- {
- instruction *inst = (instruction *) malloc (sizeof (instruction));
-
- if ( !inst )
- fatal_error("Ran out of memory in j_type_inst().\n");
-
- bzero (inst, sizeof (instruction));
- OPCODE(inst) = opcode;
- target->offset = 0; /* Not PC relative */
- target->pc_relative = 0;
- EXPR (inst) = target;
- if (target->symbol == NULL || SYMBOL_IS_DEFINED (target->symbol))
- resolve_a_label (target->symbol, inst, INST_PC);
- else
- record_inst_uses_symbol (inst, INST_PC, target->symbol);
- store_instruction (inst);
- }
-
-
- /* Return a register-type instruction with the given OPCODE, RD, RS, and RT
- fields. */
-
- instruction *r_type_inst (int opcode, int rd, int rs, int rt)
- {
- instruction *inst = (instruction *) malloc (sizeof (instruction));
-
- if ( !inst )
- fatal_error("Ran out of memory in r_type_inst().\n");
-
- bzero (inst, sizeof (instruction));
- OPCODE(inst) = opcode;
- RS(inst) = rs;
- RT(inst) = rt;
- RD(inst) = rd;
- SHAMT(inst) = 0;
- store_instruction (inst);
- return (inst);
- }
-
-
- /* Return a register-shift instruction with the given OPCODE, RD, RT, and
- SHAMT fields.*/
-
- void r_sh_type_inst (int opcode, int rd, int rt, int shamt)
- {
- instruction *inst = r_type_inst (opcode, rd, 0, rt);
-
- SHAMT(inst) = shamt & 0x1f;
- }
-
-
- /* Return a floating-point compare instruction with the given OPCODE,
- FS, and FT fields.*/
-
- void r_cond_type_inst (int opcode, int rs, int rt)
- {
- instruction *inst = r_type_inst (opcode, 0, rs, rt);
-
- switch (opcode)
- {
- case Y_C_EQ_D_OP:
- case Y_C_EQ_S_OP:
- {
- COND(inst) = COND_EQ;
- break;
- }
-
- case Y_C_LE_D_OP:
- case Y_C_LE_S_OP:
- {
- COND(inst) = COND_IN | COND_LT | COND_EQ;
- break;
- }
-
- case Y_C_LT_D_OP:
- case Y_C_LT_S_OP:
- {
- COND(inst) = COND_IN | COND_LT;
- break;
- }
-
- case Y_C_NGE_D_OP:
- case Y_C_NGE_S_OP:
- {
- COND(inst) = COND_IN | COND_LT | COND_UN;
- break;
- }
-
- case Y_C_NGLE_D_OP:
- case Y_C_NGLE_S_OP:
- {
- COND(inst) = COND_IN | COND_UN;
- break;
- }
-
- case Y_C_NGL_D_OP:
- case Y_C_NGL_S_OP:
- {
- COND(inst) = COND_IN | COND_EQ | COND_UN;
- break;
- }
-
- case Y_C_NGT_D_OP:
- case Y_C_NGT_S_OP:
- {
- COND(inst) = COND_IN | COND_LT | COND_EQ | COND_UN;
- break;
- }
-
- case Y_C_OLE_D_OP:
- case Y_C_OLE_S_OP:
- {
- COND(inst) = COND_LT | COND_EQ;
- break;
- }
-
- case Y_C_SEQ_D_OP:
- case Y_C_SEQ_S_OP:
- {
- COND(inst) = COND_IN | COND_EQ;
- break;
- }
-
- case Y_C_SF_D_OP:
- case Y_C_SF_S_OP:
- {
- COND(inst) = COND_IN;
- break;
- }
-
- case Y_C_F_D_OP:
- case Y_C_F_S_OP:
- {
- COND(inst) = 0;
- break;
- }
-
- case Y_C_UEQ_D_OP:
- case Y_C_UEQ_S_OP:
- {
- COND(inst) = COND_EQ | COND_UN;
- break;
- }
-
- case Y_C_ULE_D_OP:
- case Y_C_ULE_S_OP:
- {
- COND(inst) = COND_LT | COND_EQ | COND_UN;
- break;
- }
-
- case Y_C_UN_D_OP:
- case Y_C_UN_S_OP:
- {
- COND(inst) = COND_UN;
- break;
- }
- }
- }
-
-
-
- /* Maintain a table mapping from opcode to instruction name and
- instruction type.
-
- Table must be sorted before first use since its entries are
- alphabetical on name, not ordered by opcode. */
-
- static int sorted_name_table = 0; /* Non-zero => table sorted */
-
-
- /* Map from opcode -> name/type. */
-
- static inst_info name_tbl [] = {
- #include "y_tab.h"
- #undef OP
- #define OP(NAME, OPCODE, TYPE, R_OPCODE) {NAME, OPCODE, TYPE},
- #include "op.h"
- };
-
-
- /* Compare the VALUE1 field of two INST_INFO entries in the format
- required by qsort. */
-
- static int compare_pair_value (const inst_info *p1, const inst_info *p2)
- {
- if (p1->value1 < p2->value1)
- return (-1);
- else if (p1->value1 > p2->value1)
- return (1);
- else
- return (0);
- }
-
-
- /* Sort the opcode table on their key (the opcode value). */
-
- static void sort_name_table (void)
- {
- void qsort ();
-
- qsort (name_tbl,
- sizeof (name_tbl) / sizeof (inst_info),
- sizeof (inst_info),
- (CompareFunc) compare_pair_value);
- sorted_name_table = 1;
- }
-
-
- /* Print the instruction stored at the memory ADDRESS. */
-
- void print_inst (mem_addr addr)
- {
- instruction *inst;
- char buf [128];
-
- exception_occurred = 0;
- READ_MEM_INST (inst, addr);
-
- if (exception_occurred)
- {
- sprintf(mess_buff,
- "Can't print instruction not in text segment (0x%08x)\n", addr);
- error (mess_buff);
- return;
- }
- print_inst_internal (buf, inst, addr);
- write_output (message_out, buf);
- }
-
-
- int print_inst_internal (char *buf, instruction *inst, mem_addr addr)
- {
- char *bp = buf;
- inst_info *entry;
-
- if (!sorted_name_table)
- sort_name_table ();
-
- sprintf (buf, "[0x%08x]\t", addr);
- buf += strlen (buf);
- if (inst == NULL)
- {
- sprintf (buf, "<none>\n");
- buf += strlen (buf);
- return (buf - bp);
- }
- entry = map_int_to_inst_info (name_tbl,
- sizeof (name_tbl) / sizeof (inst_info),
- OPCODE (inst));
- if (entry == NULL)
- {
- sprintf (buf, "<unknown instruction %d>\n", OPCODE (inst));
- buf += strlen (buf);
- return (buf - bp);
- }
- sprintf (buf, "0x%08x %s", inst_encode (inst), entry->name);
- buf += strlen (buf);
- switch (entry->value2)
- {
- case B0_TYPE_INST:
- sprintf (buf, " %d", SIGN_EX (IMM (inst) << 2));
- buf += strlen (buf);
- break;
-
- case B1_TYPE_INST:
- sprintf (buf, " $%d %d", RS (inst), SIGN_EX (IMM (inst) << 2));
- buf += strlen (buf);
- break;
-
- case I1t_TYPE_INST:
- sprintf (buf, " $%d, %d", RT (inst), IMM (inst));
- buf += strlen (buf);
- break;
-
- case I2_TYPE_INST:
- sprintf (buf, " $%d, $%d, %d", RT (inst), RS (inst), IMM (inst));
- buf += strlen (buf);
- break;
-
- case B2_TYPE_INST:
- sprintf (buf, " $%d, $%d, %d", RS (inst), RT (inst),
- SIGN_EX (IMM (inst) << 2));
- buf += strlen (buf);
- break;
-
- case I2a_TYPE_INST:
- sprintf (buf, " $%d, %d($%d)", RT (inst), IMM (inst), BASE (inst));
- buf += strlen (buf);
- break;
-
- case R1s_TYPE_INST:
- sprintf (buf, " $%d", RS (inst));
- buf += strlen (buf);
- break;
-
- case R1d_TYPE_INST:
- sprintf (buf, " $%d", RD (inst));
- buf += strlen (buf);
- break;
-
- case R2td_TYPE_INST:
- sprintf (buf, " $%d, $%d", RT (inst), RD (inst));
- buf += strlen (buf);
- break;
-
- case R2st_TYPE_INST:
- sprintf (buf, " $%d, $%d", RS (inst), RT (inst));
- buf += strlen (buf);
- break;
-
- case R2ds_TYPE_INST:
- sprintf (buf, " $%d, $%d", RD (inst), RS (inst));
- buf += strlen (buf);
- break;
-
- case R2sh_TYPE_INST:
- sprintf (buf, " $%d, $%d, %d", RD (inst), RT (inst), SHAMT (inst));
- buf += strlen (buf);
- break;
-
- case R3_TYPE_INST:
- sprintf (buf, " $%d, $%d, $%d", RD (inst), RS (inst), RT (inst));
- buf += strlen (buf);
- break;
-
- case R3sh_TYPE_INST:
- sprintf (buf, " $%d, $%d, $%d", RD (inst), RT (inst), RS (inst));
- buf += strlen (buf);
- break;
-
- case FP_I2a_TYPE_INST:
- sprintf (buf, " $f%d, %d($%d)", FT (inst), IMM (inst), BASE (inst));
- buf += strlen (buf);
- break;
-
- case FP_R2ds_TYPE_INST:
- sprintf (buf, " $f%d, $f%d", FD (inst), FS (inst));
- buf += strlen (buf);
- break;
-
- case FP_R2st_TYPE_INST:
- sprintf (buf, " $f%d, $f%d", FS (inst), FT (inst));
- buf += strlen (buf);
- break;
-
- case FP_R3_TYPE_INST:
- sprintf (buf, " $f%d, $f%d, $f%d", FD (inst), FS (inst), FT (inst));
- buf += strlen (buf);
- break;
-
- case FP_MOV_TYPE_INST:
- sprintf (buf, " $f%d, $f%d", FD (inst), FS (inst));
- buf += strlen (buf);
- break;
-
- case J_TYPE_INST:
- sprintf (buf, " 0x%08x", TARGET (inst) << 2);
- buf += strlen (buf);
- break;
-
- case CP_TYPE_INST:
- sprintf (buf, " $%d, $%d", RT (inst), RD (inst));
- buf += strlen (buf);
- break;
-
- case NOARG_TYPE_INST:
- break;
-
- case ASM_DIR:
- case PSEUDO_OP:
- default:
- fatal_error ("Unknown instruction type in print_inst\n");
- }
-
- /*
- if (EXPR (inst) != NULL)
- {
- sprintf (buf, " [");
- buf += strlen (buf);
- if (opcode_is_load_store (OPCODE (inst)))
- buf += print_imm_expr (buf, EXPR (inst), BASE (inst));
- else
- buf += print_imm_expr (buf, EXPR (inst), -1);
- sprintf (buf, "]");
- buf += strlen (buf);
- }
- */
-
- if (COMMENT(inst) != NULL)
- {
- sprintf(buf, "\t# %s", COMMENT(inst));
- buf += strlen(buf);
- }
-
- sprintf (buf, "\n");
- buf += strlen (buf);
- return (buf - bp);
- }
-
-
-
- /* Return non-zero if an INSTRUCTION is a conditional branch. */
-
- int opcode_is_branch (int opcode)
- {
- switch (opcode)
- {
- case Y_BEQ_OP:
- case Y_BEQZ_POP:
- case Y_BGE_POP:
- case Y_BGEU_POP:
- case Y_BGEZ_OP:
- case Y_BGEZAL_OP:
- case Y_BGT_POP:
- case Y_BGTU_POP:
- case Y_BGTZ_OP:
- case Y_BLE_POP:
- case Y_BLEU_POP:
- case Y_BLEZ_OP:
- case Y_BLT_POP:
- case Y_BLTU_POP:
- case Y_BLTZ_OP:
- case Y_BLTZAL_OP:
- case Y_BNE_OP:
- case Y_BNEZ_POP:
- case Y_BC1F_OP:
- case Y_BC1T_OP:
- return (1);
-
- default:
- return (0);
- }
- }
-
-
- /* Return non-zero if an INSTRUCTION is an conditional branch (jump). */
-
- int opcode_is_jump (int opcode)
- {
- switch (opcode)
- {
- case Y_J_OP:
- case Y_JAL_OP:
- return (1);
-
- default:
- return (0);
- }
- }
-
- /* Return non-zero if an INSTRUCTION is a load or store. */
-
- int opcode_is_load_store (int opcode)
- {
- switch (opcode)
- {
- case Y_LB_OP: return (1);
- case Y_LBU_OP: return (1);
- case Y_LH_OP: return (1);
- case Y_LHU_OP: return (1);
- case Y_LW_OP: return (1);
- case Y_LWC0_OP: return (1);
- case Y_LWC1_OP: return (1);
- case Y_LWC2_OP: return (1);
- case Y_LWC3_OP: return (1);
- case Y_LWL_OP: return (1);
- case Y_LWR_OP: return (1);
- case Y_SB_OP: return (1);
- case Y_SH_OP: return (1);
- case Y_SW_OP: return (1);
- case Y_SWC0_OP: return (1);
- case Y_SWC1_OP: return (1);
- case Y_SWC2_OP: return (1);
- case Y_SWC3_OP: return (1);
- case Y_SWL_OP: return (1);
- case Y_SWR_OP: return (1);
- case Y_L_D_POP: return (1);
- case Y_L_S_POP: return (1);
- case Y_S_D_POP: return (1);
- case Y_S_S_POP: return (1);
- default: return (0);
- }
- }
-
-
- static instruction *break_inst = NULL;
-
- #ifdef MACINTOSH
- /* When we re-init the world, we must nullify all malloc'ed pointers, because the
- malloc memory supply gets completely erased. This includes pointers that are
- hard to find, such as break_inst;
- */
-
- void ReinitInst()
- {
- break_inst = NULL;
- }
-
- #endif
-
-
- /* Return non-zero if a breakpoint is set at ADDR. */
-
- int inst_is_breakpoint (mem_addr addr)
- {
- instruction *old_inst;
-
- if (break_inst == NULL)
- break_inst = r_type_inst (Y_BREAK_OP, 1, 0, 0);
-
- READ_MEM_INST (old_inst, addr);
- return (old_inst == break_inst);
- }
-
-
- /* Set a breakpoint at ADDR and return the old instruction. If the
- breakpoint cannot be set, return NULL. */
-
- instruction * set_breakpoint (mem_addr addr)
- {
- instruction *old_inst;
-
- if (break_inst == NULL)
- break_inst = r_type_inst (Y_BREAK_OP, 1, 0, 0);
-
- exception_occurred = 0;
- READ_MEM_INST (old_inst, addr);
- if (old_inst == break_inst)
- return (NULL);
- SET_MEM_INST (addr, break_inst);
- if (exception_occurred)
- return (NULL);
- else
- return (old_inst);
- }
-
-
-
- /* An immediate expression has the form: SYMBOL +/- IOFFSET, where either
- part may be omitted. */
-
- /* Make and return a new immediate expression */
-
- imm_expr *make_imm_expr (int offs, char *sym, int pc_rel)
- {
- imm_expr *expr = (imm_expr *) malloc (sizeof (imm_expr));
-
- if ( !expr )
- fatal_error("Ran out of memory in make_imm_expr().\n");
-
- expr->offset = offs;
- expr->bits = 0;
- expr->pc_relative = pc_rel;
- if (sym != NULL)
- expr->symbol = lookup_label (sym);
- else
- expr->symbol = NULL;
- return (expr);
- }
-
-
- /* Return a shallow copy of the EXPRESSION. */
-
- imm_expr * copy_imm_expr (imm_expr *old_expr)
- {
- imm_expr *expr = (imm_expr *) malloc (sizeof (imm_expr));
-
- if ( !expr )
- fatal_error("Ran out of memory in copy_imm_expr().\n");
-
- bcopy (old_expr, expr, sizeof (imm_expr));
- return (expr);
- }
-
-
- /* Return a shallow copy of an EXPRESSION that only uses the upper
- sixteen bits of the expression's value. */
-
- imm_expr * upper_bits_of_expr (imm_expr *old_expr)
- {
- imm_expr *expr = copy_imm_expr (old_expr);
-
- expr->bits = 1;
- return (expr);
- }
-
-
- /* Return a shallow copy of the EXPRESSION that only uses the lower
- sixteen bits of the expression's value. */
-
- imm_expr *lower_bits_of_expr (imm_expr *old_expr)
- {
- imm_expr *expr = copy_imm_expr (old_expr);
-
- expr->bits = -1;
- return (expr);
- }
-
-
- /* Return an instruction expression for a constant VALUE. */
-
- imm_expr *const_imm_expr (long value)
- {
- return (make_imm_expr (value, NULL, 0));
- }
-
-
- /* Return a shallow copy of the EXPRESSION with the offset field
- incremented by the given amount. */
-
- imm_expr * incr_expr_offset (imm_expr *expr, long value)
- {
- imm_expr *new_expr = copy_imm_expr (expr);
-
- new_expr->offset += value;
- return (new_expr);
- }
-
-
- /* Return the value of the EXPRESSION. */
-
- long eval_imm_expr (imm_expr *expr)
- {
- long value;
-
- if (expr->symbol == NULL)
- value = expr->offset;
- else if (SYMBOL_IS_DEFINED (expr->symbol))
- {
- value = expr->offset + expr->symbol->addr;
- if (expr->symbol->gp_flag) /* Addr is offset from $gp */
- value += gp_midpoint;
- }
- else
- {
- sprintf(mess_buff, "Evaluated undefined symbol: %s\n",
- expr->symbol->name);
- error (mess_buff);
- value = 0;
- }
- if (expr->bits > 0)
- return ((value >> 16) & 0xffff); /* Use upper bits of result */
- else if (expr->bits < 0)
- return (value & 0xffff); /* Use lower bits */
- else
- return (value);
- }
-
-
- /* Print the EXPRESSION. */
-
- static int print_imm_expr (char *buf, const imm_expr *expr, int base_reg)
- {
- char *bp = buf;
-
- if (expr->symbol != NULL)
- sprintf (buf, "%s", expr->symbol->name);
- buf += strlen(buf);
-
- if (expr->pc_relative)
- sprintf (buf, "-0x%08x", -expr->offset);
- else if (expr->offset < -10)
- sprintf (buf, "-%d (-0x%08x)", -expr->offset, -expr->offset);
- else if (expr->offset > 10)
- sprintf (buf, "+%d (0x%08x)", expr->offset, expr->offset);
-
- buf += strlen(buf);
-
- if (base_reg != -1 && expr->symbol != NULL &&
- (expr->offset > 10 || expr->offset < -10))
- {
- if (expr->offset == 0 && base_reg != 0)
- sprintf (buf, "+0");
- if (expr->offset != 0 || base_reg != 0)
- sprintf (buf, "($%d)", base_reg);
- }
- buf += strlen(buf);
-
- return (buf - bp);
- }
-
-
- /* Return non-zero if the EXPRESSION is a constant 0. */
-
- int zero_imm (imm_expr *expr)
- {
- return (expr->offset == 0 && expr->symbol == NULL);
- }
-
-
-
- /* Return an address expression of the form SYMBOL +/- IOFFSET (REGISTER).
- Any of the three parts may be omitted. */
-
- addr_expr * make_addr_expr (long offs, char *sym, int reg_no)
- {
- addr_expr *expr = (addr_expr *) malloc (sizeof (addr_expr));
- label *lab;
-
- if ( !expr )
- fatal_error("Ran out of memory in make_addr_expr().\n");
-
- if (reg_no == 0 && sym != NULL && (lab = lookup_label (sym))->gp_flag)
- {
- expr->reg_no = REG_GP;
- expr->imm = make_imm_expr (offs + lab->addr - gp_midpoint, NULL, 0);
- }
- else
- {
- expr->reg_no = reg_no;
- expr->imm = make_imm_expr (offs, sym, 0);
- }
- return (expr);
- }
-
-
- imm_expr * addr_expr_imm (addr_expr *expr)
- {
- return (expr->imm);
- }
-
-
- int addr_expr_reg (addr_expr *expr)
- {
- return (expr->reg_no);
- }
-
-
-
- /* Map between a SPIM instruction and the binary representation of the
- instruction. */
-
-
- /* Maintain a table mapping from internal opcode (i_opcode) to actual
- opcode (a_opcode). Table must be sorted before first use since its
- entries are alphabetical on name, not ordered by opcode. */
-
- static int sorted_i_opcode_table = 0; /* Non-zero => table sorted */
-
-
- /* Map from internal opcode -> real opcode */
-
- static inst_info i_opcode_tbl [] = {
- #undef OP
- #define OP(NAME, I_OPCODE, TYPE, A_OPCODE) {NAME, I_OPCODE, A_OPCODE},
- #include "op.h"
- };
-
-
- /* Sort the opcode table on their key (the interal opcode value). */
-
- static void sort_i_opcode_table (void)
- {
- void qsort ();
-
- qsort (i_opcode_tbl,
- sizeof (i_opcode_tbl) / sizeof (inst_info),
- sizeof (inst_info),
- (CompareFunc) compare_pair_value);
- sorted_i_opcode_table = 1;
- }
-
-
- #define REGS(R,O) (((R) & 0x1f) << O)
-
-
- long inst_encode (instruction *inst)
- {
- long a_opcode = 0;
- inst_info *entry;
-
- if (inst == NULL)
- return (0);
- if (!sorted_i_opcode_table)
- sort_i_opcode_table ();
- if (!sorted_name_table)
- sort_name_table ();
-
- entry = map_int_to_inst_info (i_opcode_tbl,
- sizeof (i_opcode_tbl) / sizeof (inst_info),
- OPCODE (inst));
- if (entry == NULL)
- return 0;
-
- a_opcode = entry->value2;
- entry = map_int_to_inst_info (name_tbl,
- sizeof (name_tbl) / sizeof (inst_info),
- OPCODE (inst));
-
- switch (entry->value2)
- {
- case B0_TYPE_INST:
- return (a_opcode
- | IOFFSET (inst) & 0xffff);
-
- case B1_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | IOFFSET (inst) & 0xffff);
-
- case I1t_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RT (inst), 16)
- | IMM (inst) & 0xffff);
-
- case I2_TYPE_INST:
- case B2_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RT (inst), 16)
- | IMM (inst) & 0xffff);
-
- case I2a_TYPE_INST:
- return (a_opcode
- | REGS (BASE (inst), 21)
- | REGS (RT (inst), 16)
- | IOFFSET (inst) & 0xffff);
-
- case R1s_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21));
-
- case R1d_TYPE_INST:
- return (a_opcode
- | REGS (RD (inst), 11));
-
- case R2td_TYPE_INST:
- return (a_opcode
- | REGS (RT (inst), 16)
- | REGS (RD (inst), 11));
-
- case R2st_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RT (inst), 16));
-
- case R2ds_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RD (inst), 11));
-
- case R2sh_TYPE_INST:
- return (a_opcode
- | REGS (RT (inst), 16)
- | REGS (RD (inst), 11)
- | REGS (SHAMT (inst), 6));
-
- case R3_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RT (inst), 16)
- | REGS (RD (inst), 11));
-
- case R3sh_TYPE_INST:
- return (a_opcode
- | REGS (RS (inst), 21)
- | REGS (RT (inst), 16)
- | REGS (RD (inst), 11));
-
- case FP_I2a_TYPE_INST:
- return (a_opcode
- | REGS (BASE (inst), 21)
- | REGS (RT (inst), 16)
- | (IOFFSET (inst) & 0xffff));
-
- case FP_R2ds_TYPE_INST:
- return (a_opcode
- | REGS (FS (inst), 11)
- | REGS (FD (inst), 6));
-
- case FP_R2st_TYPE_INST:
- return (a_opcode
- | REGS (FT (inst), 16)
- | REGS (FS (inst), 11));
-
- case FP_R3_TYPE_INST:
- return (a_opcode
- | REGS (FT (inst), 16)
- | REGS (FS (inst), 11)
- | REGS (FD (inst), 6));
-
- case FP_MOV_TYPE_INST:
- return (a_opcode
- | REGS (FS (inst), 11)
- | REGS (FD (inst), 6));
-
- case J_TYPE_INST:
- return (a_opcode
- | TARGET (inst));
-
- case CP_TYPE_INST:
- return (a_opcode
- | REGS (RT (inst), 16)
- | REGS (RD (inst), 11));
-
- case NOARG_TYPE_INST:
- return (a_opcode);
-
- case ASM_DIR:
- case PSEUDO_OP:
- default:
- fatal_error ("Unknown instruction type in inst_encoding\n");
- return (0); /* Not reached */
- }
- }
-
-
- /* Maintain a table mapping from actual opcode to interal opcode.
- Table must be sorted before first use since its entries are
- alphabetical on name, not ordered by opcode. */
-
- static int sorted_a_opcode_table = 0; /* Non-zero => table sorted */
-
-
- /* Map from internal opcode -> real opcode */
-
- static inst_info a_opcode_tbl [] = {
- #undef OP
- #define OP(NAME, I_OPCODE, TYPE, A_OPCODE) {NAME, A_OPCODE, I_OPCODE},
- #include "op.h"
- };
-
-
- /* Sort the opcode table on their key (the interal opcode value). */
-
- static void sort_a_opcode_table (void)
- {
- void qsort ();
-
- qsort (a_opcode_tbl,
- sizeof (a_opcode_tbl) / sizeof (inst_info),
- sizeof (inst_info),
- (CompareFunc) compare_pair_value);
- sorted_a_opcode_table = 1;
- }
-
-
- #define REG(V,O) ((V) >> O) & 0x1f
-
-
- instruction * inst_decode (long value)
- {
- long a_opcode = value & 0xfc000000;
- inst_info *entry;
- long i_opcode;
-
- if (a_opcode == 0) /* SPECIAL */
- a_opcode |= (value & 0x3f);
- else if (a_opcode == 0x04000000) /* BCOND */
- a_opcode |= (value & 0x001f0000);
- else if (a_opcode == 0x40000000) /* COP0 */
- a_opcode |= (value & 0x03e00000) | (value & 0x1f);
- else if (a_opcode == 0x44000000) /* COP1 */
- {
- a_opcode |= (value & 0x03e00000);
- if ((value & 0xff000000) == 0x45000000)
- a_opcode |= (value & 0x00010000); /* BC1f/t */
- else
- a_opcode |= (value & 0x3f);
- }
- else if (a_opcode == 0x48000000 /* COPz */
- || a_opcode == 0x4c000000)
- a_opcode |= (value & 0x03e00000);
-
-
- if (!sorted_a_opcode_table)
- sort_a_opcode_table ();
- if (!sorted_name_table)
- sort_name_table ();
-
- entry = map_int_to_inst_info (a_opcode_tbl,
- sizeof (a_opcode_tbl) / sizeof (inst_info),
- a_opcode);
- if (entry == NULL)
- return (NULL);
- i_opcode = entry->value2;
-
- switch (map_int_to_inst_info (name_tbl,
- sizeof (name_tbl) / sizeof (inst_info),
- i_opcode)->value2)
- {
- case B0_TYPE_INST:
- return (mk_inst (i_opcode, 0, 0, 0, 0, value & 0xffff, 0));
-
- case B1_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), 0, 0, 0,
- value & 0xffff, 0));
-
- case I1t_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16), 0, 0,
- value & 0xffff, 0));
-
- case I2_TYPE_INST:
- case B2_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16), 0, 0,
- value & 0xffff, 0));
-
- case I2a_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16), 0, 0,
- value & 0xffff, 0));
-
- case R1s_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), 0, 0, 0, 0, 0));
-
- case R1d_TYPE_INST:
- return (mk_inst (i_opcode, 0, 0, REG (value, 11), 0, 0, 0));
-
- case R2td_TYPE_INST:
- return (mk_inst (i_opcode, 0, REG (value, 16), REG (value, 11), 0,
- 0, 0));
-
- case R2st_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16), 0, 0,
- 0, 0));
-
- case R2ds_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), 0, REG (value, 11), 0,
- 0, 0));
-
- case R2sh_TYPE_INST:
- return (mk_inst (i_opcode, 0, REG (value, 16), REG (value, 11),
- REG (value, 6), 0, 0));
-
- case R3_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16),
- REG (value, 11), 0, 0, 0));
-
- case R3sh_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16),
- REG (value, 11), 0, 0, 0));
-
- case FP_I2a_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 21), REG (value, 16), 0, 0,
- value & 0xffff, 0));
-
- case FP_R2ds_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 11), 0, REG (value, 6), 0,
- 0, 0));
-
- case FP_R2st_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 11), REG (value, 16), 0, 0,
- value & 0xf, 0));
-
- case FP_R3_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 11), REG (value, 16),
- REG (value, 6), 0, 0, 0));
-
- case FP_MOV_TYPE_INST:
- return (mk_inst (i_opcode, REG (value, 11), 0, REG (value, 6), 0,
- 0, 0));
-
- case J_TYPE_INST:
- return (mk_inst (i_opcode, 0, 0, 0, 0, 0, value & 0x2ffffff));
-
- case CP_TYPE_INST:
- return (mk_inst (i_opcode, 0, REG (value, 16), REG (value, 11), 0,
- 0, 0));
-
- case NOARG_TYPE_INST:
- return (mk_inst (i_opcode, 0, 0, 0, 0, 0, 0));
-
- case ASM_DIR:
- case PSEUDO_OP:
- default:
- fatal_error ("Unknown instruction type in inst_encoding\n");
- return (NULL); /* Not reached */
- }
- }
-
-
- static instruction *mk_inst (int opcode, int rs, int rt, int rd, int shamt,
- int offset, int target)
- {
- instruction *inst = (instruction *) malloc (sizeof (instruction));
-
- if ( !inst )
- fatal_error("Ran out of memory in mk_inst().\n");
-
- bzero (inst, sizeof (inst));
- OPCODE (inst) = opcode;
- RS (inst) = rs;
- RT (inst) = rt;
- RD (inst) = rd;
- SHAMT (inst) = shamt;
- IOFFSET (inst) = offset;
- TARGET (inst) = target;
- EXPR (inst) = NULL;
- return (inst);
- }
-
-
- /* Code to test encode/decode of instructions. */
-
- void test_assembly (instruction *inst)
- {
- instruction *new_inst = inst_decode (inst_encode (inst));
-
- inst_cmp (inst, new_inst);
- free (new_inst);
- }
-
-
- static void inst_cmp (instruction *inst1, instruction *inst2)
- {
- char buf[1024];
-
- if (bcmp (inst1, inst2, sizeof (instruction) - 4))
- {
- printf ("=================== Not Equal ===================\n");
- print_inst_internal (buf, inst1, 0);
- printf ("%s\n", buf);
- print_inst_internal (buf,inst2, 0);
- printf ("%s\n", buf);
- printf ("=================== Not Equal ===================\n");
- }
- }
-