| /* Instruction printing code for the DLX Microprocessor |
| Copyright (C) 2002-2024 Free Software Foundation, Inc. |
| Contributed by Kuang Hwa Lin. Written by Kuang Hwa Lin, 03/2002. |
| |
| This file is part of the GNU opcodes library. |
| |
| This library 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 3, or (at your option) |
| any later version. |
| |
| It 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 this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "disassemble.h" |
| #include "opcode/dlx.h" |
| |
| #define R_ERROR 0x1 |
| #define R_TYPE 0x2 |
| #define ILD_TYPE 0x3 |
| #define IST_TYPE 0x4 |
| #define IAL_TYPE 0x5 |
| #define IBR_TYPE 0x6 |
| #define IJ_TYPE 0x7 |
| #define IJR_TYPE 0x8 |
| #define NIL 0x9 |
| |
| #define OPC(x) ((x >> 26) & 0x3F) |
| #define FUNC(x) (x & 0x7FF) |
| |
| unsigned char opc, rs1, rs2, rd; |
| unsigned long imm26, imm16, func, current_insn_addr; |
| |
| /* Print one instruction from MEMADDR on INFO->STREAM. |
| Return the size of the instruction (always 4 on dlx). */ |
| |
| static unsigned char |
| dlx_get_opcode (unsigned long opcode) |
| { |
| return (unsigned char) ((opcode >> 26) & 0x3F); |
| } |
| |
| static unsigned char |
| dlx_get_rs1 (unsigned long opcode) |
| { |
| return (unsigned char) ((opcode >> 21) & 0x1F); |
| } |
| |
| static unsigned char |
| dlx_get_rs2 (unsigned long opcode) |
| { |
| return (unsigned char) ((opcode >> 16) & 0x1F); |
| } |
| |
| static unsigned char |
| dlx_get_rdR (unsigned long opcode) |
| { |
| return (unsigned char) ((opcode >> 11) & 0x1F); |
| } |
| |
| static unsigned long |
| dlx_get_func (unsigned long opcode) |
| { |
| return (unsigned char) (opcode & 0x7FF); |
| } |
| |
| static unsigned long |
| dlx_get_imm16 (unsigned long opcode) |
| { |
| return (unsigned long) (opcode & 0xFFFF); |
| } |
| |
| static unsigned long |
| dlx_get_imm26 (unsigned long opcode) |
| { |
| return (unsigned long) (opcode & 0x03FFFFFF); |
| } |
| |
| /* Fill the opcode to the max length. */ |
| |
| static void |
| operand_deliminator (struct disassemble_info *info, char *ptr) |
| { |
| int difft = 8 - (int) strlen (ptr); |
| |
| while (difft > 0) |
| { |
| (*info->fprintf_func) (info->stream, "%c", ' '); |
| difft -= 1; |
| } |
| } |
| |
| /* Process the R-type opcode. */ |
| |
| static unsigned char |
| dlx_r_type (struct disassemble_info *info) |
| { |
| unsigned char r_opc[] = { OPC(ALUOP) }; /* Fix ME */ |
| int r_opc_num = (sizeof r_opc) / (sizeof (char)); |
| struct _r_opcode |
| { |
| unsigned long func; |
| char *name; |
| } |
| dlx_r_opcode[] = |
| { |
| { NOPF, "nop" }, /* NOP */ |
| { ADDF, "add" }, /* Add */ |
| { ADDUF, "addu" }, /* Add Unsigned */ |
| { SUBF, "sub" }, /* SUB */ |
| { SUBUF, "subu" }, /* Sub Unsigned */ |
| { MULTF, "mult" }, /* MULTIPLY */ |
| { MULTUF, "multu" }, /* MULTIPLY Unsigned */ |
| { DIVF, "div" }, /* DIVIDE */ |
| { DIVUF, "divu" }, /* DIVIDE Unsigned */ |
| { ANDF, "and" }, /* AND */ |
| { ORF, "or" }, /* OR */ |
| { XORF, "xor" }, /* Exclusive OR */ |
| { SLLF, "sll" }, /* SHIFT LEFT LOGICAL */ |
| { SRAF, "sra" }, /* SHIFT RIGHT ARITHMETIC */ |
| { SRLF, "srl" }, /* SHIFT RIGHT LOGICAL */ |
| { SEQF, "seq" }, /* Set if equal */ |
| { SNEF, "sne" }, /* Set if not equal */ |
| { SLTF, "slt" }, /* Set if less */ |
| { SGTF, "sgt" }, /* Set if greater */ |
| { SLEF, "sle" }, /* Set if less or equal */ |
| { SGEF, "sge" }, /* Set if greater or equal */ |
| { SEQUF, "sequ" }, /* Set if equal */ |
| { SNEUF, "sneu" }, /* Set if not equal */ |
| { SLTUF, "sltu" }, /* Set if less */ |
| { SGTUF, "sgtu" }, /* Set if greater */ |
| { SLEUF, "sleu" }, /* Set if less or equal */ |
| { SGEUF, "sgeu" }, /* Set if greater or equal */ |
| { MVTSF, "mvts" }, /* Move to special register */ |
| { MVFSF, "mvfs" }, /* Move from special register */ |
| { BSWAPF, "bswap" }, /* Byte swap ?? */ |
| { LUTF, "lut" } /* ????????? ?? */ |
| }; |
| int dlx_r_opcode_num = (sizeof dlx_r_opcode) / (sizeof dlx_r_opcode[0]); |
| int idx; |
| |
| for (idx = 0; idx < r_opc_num; idx++) |
| { |
| if (r_opc[idx] != opc) |
| continue; |
| else |
| break; |
| } |
| |
| if (idx == r_opc_num) |
| return NIL; |
| |
| for (idx = 0 ; idx < dlx_r_opcode_num; idx++) |
| if (dlx_r_opcode[idx].func == func) |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_r_opcode[idx].name); |
| |
| if (func != NOPF) |
| { |
| /* This is not a nop. */ |
| operand_deliminator (info, dlx_r_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d,", (int)rd); |
| (*info->fprintf_func) (info->stream, "r%d", (int)rs1); |
| if (func != MVTSF && func != MVFSF) |
| (*info->fprintf_func) (info->stream, ",r%d", (int)rs2); |
| } |
| return (unsigned char) R_TYPE; |
| } |
| |
| return (unsigned char) R_ERROR; |
| } |
| |
| /* Process the memory read opcode. */ |
| |
| static unsigned char |
| dlx_load_type (struct disassemble_info* info) |
| { |
| struct _load_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_load_opcode[] = |
| { |
| { OPC(LHIOP), "lhi" }, /* Load HI to register. */ |
| { OPC(LBOP), "lb" }, /* load byte sign extended. */ |
| { OPC(LBUOP), "lbu" }, /* load byte unsigned. */ |
| { OPC(LSBUOP),"ldstbu"}, /* load store byte unsigned. */ |
| { OPC(LHOP), "lh" }, /* load halfword sign extended. */ |
| { OPC(LHUOP), "lhu" }, /* load halfword unsigned. */ |
| { OPC(LSHUOP),"ldsthu"}, /* load store halfword unsigned. */ |
| { OPC(LWOP), "lw" }, /* load word. */ |
| { OPC(LSWOP), "ldstw" } /* load store word. */ |
| }; |
| int dlx_load_opcode_num = |
| (sizeof dlx_load_opcode) / (sizeof dlx_load_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_load_opcode_num; idx++) |
| if (dlx_load_opcode[idx].opcode == opc) |
| { |
| if (opc == OPC (LHIOP)) |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_load_opcode[idx].name); |
| operand_deliminator (info, dlx_load_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d,", (int)rs2); |
| (*info->fprintf_func) (info->stream, "0x%04x", (int)imm16); |
| } |
| else |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_load_opcode[idx].name); |
| operand_deliminator (info, dlx_load_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d,", (int)rs2); |
| (*info->fprintf_func) (info->stream, "0x%04x[r%d]", (int)imm16, (int)rs1); |
| } |
| |
| return (unsigned char) ILD_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| /* Process the memory store opcode. */ |
| |
| static unsigned char |
| dlx_store_type (struct disassemble_info* info) |
| { |
| struct _store_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_store_opcode[] = |
| { |
| { OPC(SBOP), "sb" }, /* Store byte. */ |
| { OPC(SHOP), "sh" }, /* Store halfword. */ |
| { OPC(SWOP), "sw" }, /* Store word. */ |
| }; |
| int dlx_store_opcode_num = |
| (sizeof dlx_store_opcode) / (sizeof dlx_store_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_store_opcode_num; idx++) |
| if (dlx_store_opcode[idx].opcode == opc) |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_store_opcode[idx].name); |
| operand_deliminator (info, dlx_store_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "0x%04x[r%d],", (int)imm16, (int)rs1); |
| (*info->fprintf_func) (info->stream, "r%d", (int)rs2); |
| return (unsigned char) IST_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| /* Process the Arithmetic and Logical I-TYPE opcode. */ |
| |
| static unsigned char |
| dlx_aluI_type (struct disassemble_info* info) |
| { |
| struct _aluI_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_aluI_opcode[] = |
| { |
| { OPC(ADDIOP), "addi" }, /* Store byte. */ |
| { OPC(ADDUIOP), "addui" }, /* Store halfword. */ |
| { OPC(SUBIOP), "subi" }, /* Store word. */ |
| { OPC(SUBUIOP), "subui" }, /* Store word. */ |
| { OPC(ANDIOP), "andi" }, /* Store word. */ |
| { OPC(ORIOP), "ori" }, /* Store word. */ |
| { OPC(XORIOP), "xori" }, /* Store word. */ |
| { OPC(SLLIOP), "slli" }, /* Store word. */ |
| { OPC(SRAIOP), "srai" }, /* Store word. */ |
| { OPC(SRLIOP), "srli" }, /* Store word. */ |
| { OPC(SEQIOP), "seqi" }, /* Store word. */ |
| { OPC(SNEIOP), "snei" }, /* Store word. */ |
| { OPC(SLTIOP), "slti" }, /* Store word. */ |
| { OPC(SGTIOP), "sgti" }, /* Store word. */ |
| { OPC(SLEIOP), "slei" }, /* Store word. */ |
| { OPC(SGEIOP), "sgei" }, /* Store word. */ |
| { OPC(SEQUIOP), "sequi" }, /* Store word. */ |
| { OPC(SNEUIOP), "sneui" }, /* Store word. */ |
| { OPC(SLTUIOP), "sltui" }, /* Store word. */ |
| { OPC(SGTUIOP), "sgtui" }, /* Store word. */ |
| { OPC(SLEUIOP), "sleui" }, /* Store word. */ |
| { OPC(SGEUIOP), "sgeui" }, /* Store word. */ |
| #if 0 |
| { OPC(MVTSOP), "mvts" }, /* Store word. */ |
| { OPC(MVFSOP), "mvfs" }, /* Store word. */ |
| #endif |
| }; |
| int dlx_aluI_opcode_num = |
| (sizeof dlx_aluI_opcode) / (sizeof dlx_aluI_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_aluI_opcode_num; idx++) |
| if (dlx_aluI_opcode[idx].opcode == opc) |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_aluI_opcode[idx].name); |
| operand_deliminator (info, dlx_aluI_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d,", (int)rs2); |
| (*info->fprintf_func) (info->stream, "r%d,", (int)rs1); |
| (*info->fprintf_func) (info->stream, "0x%04x", (int)imm16); |
| |
| return (unsigned char) IAL_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| /* Process the branch instruction. */ |
| |
| static unsigned char |
| dlx_br_type (struct disassemble_info* info) |
| { |
| struct _br_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_br_opcode[] = |
| { |
| { OPC(BEQOP), "beqz" }, /* Store byte. */ |
| { OPC(BNEOP), "bnez" } /* Store halfword. */ |
| }; |
| int dlx_br_opcode_num = |
| (sizeof dlx_br_opcode) / (sizeof dlx_br_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_br_opcode_num; idx++) |
| if (dlx_br_opcode[idx].opcode == opc) |
| { |
| if (imm16 & 0x00008000) |
| imm16 |= 0xFFFF0000; |
| |
| imm16 += (current_insn_addr + 4); |
| (*info->fprintf_func) (info->stream, "%s", dlx_br_opcode[idx].name); |
| operand_deliminator (info, dlx_br_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d,", (int) rs1); |
| (*info->fprintf_func) (info->stream, "0x%08x", (int) imm16); |
| |
| return (unsigned char) IBR_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| /* Process the jump instruction. */ |
| |
| static unsigned char |
| dlx_jmp_type (struct disassemble_info* info) |
| { |
| struct _jmp_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_jmp_opcode[] = |
| { |
| { OPC(JOP), "j" }, /* Store byte. */ |
| { OPC(JALOP), "jal" }, /* Store halfword. */ |
| { OPC(BREAKOP), "break" }, /* Store halfword. */ |
| { OPC(TRAPOP), "trap" }, /* Store halfword. */ |
| { OPC(RFEOP), "rfe" } /* Store halfword. */ |
| }; |
| int dlx_jmp_opcode_num = |
| (sizeof dlx_jmp_opcode) / (sizeof dlx_jmp_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_jmp_opcode_num; idx++) |
| if (dlx_jmp_opcode[idx].opcode == opc) |
| { |
| if (imm26 & 0x02000000) |
| imm26 |= 0xFC000000; |
| |
| imm26 += (current_insn_addr + 4); |
| |
| (*info->fprintf_func) (info->stream, "%s", dlx_jmp_opcode[idx].name); |
| operand_deliminator (info, dlx_jmp_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "0x%08x", (int)imm26); |
| |
| return (unsigned char) IJ_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| /* Process the jump register instruction. */ |
| |
| static unsigned char |
| dlx_jr_type (struct disassemble_info* info) |
| { |
| struct _jr_opcode |
| { |
| unsigned long opcode; |
| char *name; |
| } |
| dlx_jr_opcode[] = |
| { |
| { OPC(JROP), "jr" }, /* Store byte. */ |
| { OPC(JALROP), "jalr" } /* Store halfword. */ |
| }; |
| int dlx_jr_opcode_num = |
| (sizeof dlx_jr_opcode) / (sizeof dlx_jr_opcode[0]); |
| int idx; |
| |
| for (idx = 0 ; idx < dlx_jr_opcode_num; idx++) |
| if (dlx_jr_opcode[idx].opcode == opc) |
| { |
| (*info->fprintf_func) (info->stream, "%s", dlx_jr_opcode[idx].name); |
| operand_deliminator (info, dlx_jr_opcode[idx].name); |
| (*info->fprintf_func) (info->stream, "r%d", (int)rs1); |
| return (unsigned char) IJR_TYPE; |
| } |
| |
| return (unsigned char) NIL; |
| } |
| |
| typedef unsigned char (* dlx_insn) (struct disassemble_info *); |
| |
| /* This is the main DLX insn handling routine. */ |
| |
| int |
| print_insn_dlx (bfd_vma memaddr, struct disassemble_info* info) |
| { |
| bfd_byte buffer[4]; |
| int insn_idx; |
| unsigned long insn_word; |
| dlx_insn dlx_insn_type[] = |
| { |
| dlx_r_type, |
| dlx_load_type, |
| dlx_store_type, |
| dlx_aluI_type, |
| dlx_br_type, |
| dlx_jmp_type, |
| dlx_jr_type, |
| (dlx_insn) NULL |
| }; |
| int dlx_insn_type_num = ((sizeof dlx_insn_type) / (sizeof (dlx_insn))) - 1; |
| int status = |
| (*info->read_memory_func) (memaddr, (bfd_byte *) &buffer[0], 4, info); |
| |
| if (status != 0) |
| { |
| (*info->memory_error_func) (status, memaddr, info); |
| return -1; |
| } |
| |
| /* Now decode the insn */ |
| insn_word = bfd_getb32 (buffer); |
| opc = dlx_get_opcode (insn_word); |
| rs1 = dlx_get_rs1 (insn_word); |
| rs2 = dlx_get_rs2 (insn_word); |
| rd = dlx_get_rdR (insn_word); |
| func = dlx_get_func (insn_word); |
| imm16= dlx_get_imm16 (insn_word); |
| imm26= dlx_get_imm26 (insn_word); |
| |
| #if 0 |
| printf ("print_insn_big_dlx: opc = 0x%02x\n" |
| " rs1 = 0x%02x\n" |
| " rs2 = 0x%02x\n" |
| " rd = 0x%02x\n" |
| " func = 0x%08x\n" |
| " imm16 = 0x%08x\n" |
| " imm26 = 0x%08x\n", |
| opc, rs1, rs2, rd, func, imm16, imm26); |
| #endif |
| |
| /* Scan through all the insn type and print the insn out. */ |
| current_insn_addr = (unsigned long) memaddr; |
| |
| for (insn_idx = 0; dlx_insn_type[insn_idx] != 0x0; insn_idx++) |
| switch ((dlx_insn_type[insn_idx]) (info)) |
| { |
| /* Found the correct opcode */ |
| case R_TYPE: |
| case ILD_TYPE: |
| case IST_TYPE: |
| case IAL_TYPE: |
| case IBR_TYPE: |
| case IJ_TYPE: |
| case IJR_TYPE: |
| return 4; |
| |
| /* Wrong insn type check next one. */ |
| default: |
| case NIL: |
| continue; |
| |
| /* All rest of the return code are not recongnized, treat it as error */ |
| /* we should never get here, I hope! */ |
| case R_ERROR: |
| return -1; |
| } |
| |
| if (insn_idx == dlx_insn_type_num) |
| /* Well, does not recoganize this opcode. */ |
| (*info->fprintf_func) (info->stream, "<%s>", "Unrecognized Opcode"); |
| |
| return 4; |
| } |