static CORE_ADDR sparc_analyze_control_transfer (struct gdbarch *arch, CORE_ADDR pc, CORE_ADDR *npc) { unsigned long insn = sparc_fetch_instruction (pc); int conditional_p = X_COND (insn) & 0x7; int branch_p = 0; long offset = 0; /* Must be signed for sign-extend. */ if (X_OP (insn) == 0 && X_OP2 (insn) == 3 && (insn & 0x1000000) == 0) { /* Branch on Integer Register with Prediction (BPr). */ branch_p = 1; conditional_p = 1; } else if (X_OP (insn) == 0 && X_OP2 (insn) == 6) { /* Branch on Floating-Point Condition Codes (FBfcc). */ branch_p = 1; offset = 4 * X_DISP22 (insn); } else if (X_OP (insn) == 0 && X_OP2 (insn) == 5) { /* Branch on Floating-Point Condition Codes with Prediction (FBPfcc). */ branch_p = 1; offset = 4 * X_DISP19 (insn); } else if (X_OP (insn) == 0 && X_OP2 (insn) == 2) { /* Branch on Integer Condition Codes (Bicc). */ branch_p = 1; offset = 4 * X_DISP22 (insn); } else if (X_OP (insn) == 0 && X_OP2 (insn) == 1) { /* Branch on Integer Condition Codes with Prediction (BPcc). */ branch_p = 1; offset = 4 * X_DISP19 (insn); } else if (X_OP (insn) == 2 && X_OP3 (insn) == 0x3a) { /* Trap instruction (TRAP). */ return gdbarch_tdep (arch)->step_trap (insn); } /* FIXME: Handle DONE and RETRY instructions. */ if (branch_p) { if (conditional_p) { /* For conditional branches, return nPC + 4 iff the annul bit is 1. */ return (X_A (insn) ? *npc + 4 : 0); } else { /* For unconditional branches, return the target if its specified condition is "always" and return nPC + 4 if the condition is "never". If the annul bit is 1, set *NPC to zero. */ if (X_COND (insn) == 0x0) pc = *npc, offset = 4; if (X_A (insn)) *npc = 0; gdb_assert (offset != 0); return pc + offset; } } return 0; }
int print_insn_sparc (bfd_vma memaddr, disassemble_info *info) { FILE *stream = info->stream; bfd_byte buffer[4]; unsigned long insn; sparc_opcode_hash *op; /* Nonzero of opcode table has been initialized. */ static int opcodes_initialized = 0; /* bfd mach number of last call. */ static unsigned long current_mach = 0; bfd_vma (*getword) (const void *); if (!opcodes_initialized || info->mach != current_mach) { int i; current_arch_mask = compute_arch_mask (info->mach); if (!opcodes_initialized) sorted_opcodes = xmalloc (sparc_num_opcodes * sizeof (sparc_opcode *)); /* Reset the sorted table so we can resort it. */ for (i = 0; i < sparc_num_opcodes; ++i) sorted_opcodes[i] = &sparc_opcodes[i]; qsort ((char *) sorted_opcodes, sparc_num_opcodes, sizeof (sorted_opcodes[0]), compare_opcodes); build_hash_table (sorted_opcodes, opcode_hash_table, sparc_num_opcodes); current_mach = info->mach; opcodes_initialized = 1; } { int status = (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info); if (status != 0) { (*info->memory_error_func) (status, memaddr, info); return -1; } } /* On SPARClite variants such as DANlite (sparc86x), instructions are always big-endian even when the machine is in little-endian mode. */ if (info->endian == BFD_ENDIAN_BIG || info->mach == bfd_mach_sparc_sparclite) getword = bfd_getb32; else getword = bfd_getl32; insn = getword (buffer); info->insn_info_valid = 1; /* We do return this info. */ info->insn_type = dis_nonbranch; /* Assume non branch insn. */ info->branch_delay_insns = 0; /* Assume no delay. */ info->target = 0; /* Assume no target known. */ for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next) { const sparc_opcode *opcode = op->opcode; /* If the insn isn't supported by the current architecture, skip it. */ if (! (opcode->architecture & current_arch_mask)) continue; if ((opcode->match & insn) == opcode->match && (opcode->lose & insn) == 0) { /* Nonzero means that we have found an instruction which has the effect of adding or or'ing the imm13 field to rs1. */ int imm_added_to_rs1 = 0; int imm_ored_to_rs1 = 0; /* Nonzero means that we have found a plus sign in the args field of the opcode table. */ int found_plus = 0; /* Nonzero means we have an annulled branch. */ int is_annulled = 0; /* Do we have an `add' or `or' instruction combining an immediate with rs1? */ if (opcode->match == 0x80102000) /* or */ imm_ored_to_rs1 = 1; if (opcode->match == 0x80002000) /* add */ imm_added_to_rs1 = 1; if (X_RS1 (insn) != X_RD (insn) && strchr (opcode->args, 'r') != 0) /* Can't do simple format if source and dest are different. */ continue; if (X_RS2 (insn) != X_RD (insn) && strchr (opcode->args, 'O') != 0) /* Can't do simple format if source and dest are different. */ continue; (*info->fprintf_func) (stream, "%s", opcode->name); { const char *s; if (opcode->args[0] != ',') (*info->fprintf_func) (stream, " "); for (s = opcode->args; *s != '\0'; ++s) { while (*s == ',') { (*info->fprintf_func) (stream, ","); ++s; switch (*s) { case 'a': (*info->fprintf_func) (stream, "a"); is_annulled = 1; ++s; continue; case 'N': (*info->fprintf_func) (stream, "pn"); ++s; continue; case 'T': (*info->fprintf_func) (stream, "pt"); ++s; continue; default: break; } } (*info->fprintf_func) (stream, " "); switch (*s) { case '+': found_plus = 1; /* Fall through. */ default: (*info->fprintf_func) (stream, "%c", *s); break; case '#': (*info->fprintf_func) (stream, "0"); break; #define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n]) case '1': case 'r': reg (X_RS1 (insn)); break; case '2': case 'O': reg (X_RS2 (insn)); break; case 'd': reg (X_RD (insn)); break; #undef reg #define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n]) #define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)]) case 'e': freg (X_RS1 (insn)); break; case 'v': /* Double/even. */ case 'V': /* Quad/multiple of 4. */ fregx (X_RS1 (insn)); break; case 'f': freg (X_RS2 (insn)); break; case 'B': /* Double/even. */ case 'R': /* Quad/multiple of 4. */ fregx (X_RS2 (insn)); break; case '4': freg (X_RS3 (insn)); break; case '5': /* Double/even. */ fregx (X_RS3 (insn)); break; case 'g': freg (X_RD (insn)); break; case 'H': /* Double/even. */ case 'J': /* Quad/multiple of 4. */ case '}': /* Double/even. */ fregx (X_RD (insn)); break; #undef freg #undef fregx #define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n)) case 'b': creg (X_RS1 (insn)); break; case 'c': creg (X_RS2 (insn)); break; case 'D': creg (X_RD (insn)); break; #undef creg case 'h': (*info->fprintf_func) (stream, "%%hi(%#x)", ((unsigned) 0xFFFFFFFF & ((int) X_IMM22 (insn) << 10))); break; case 'i': /* 13 bit immediate. */ case 'I': /* 11 bit immediate. */ case 'j': /* 10 bit immediate. */ { int imm; if (*s == 'i') imm = X_SIMM (insn, 13); else if (*s == 'I') imm = X_SIMM (insn, 11); else imm = X_SIMM (insn, 10); /* Check to see whether we have a 1+i, and take note of that fact. Note: because of the way we sort the table, we will be matching 1+i rather than i+1, so it is OK to assume that i is after +, not before it. */ if (found_plus) imm_added_to_rs1 = 1; if (imm <= 9) (*info->fprintf_func) (stream, "%d", imm); else (*info->fprintf_func) (stream, "%#x", imm); } break; case ')': /* 5 bit unsigned immediate from RS3. */ (info->fprintf_func) (stream, "%#x", (unsigned int) X_RS3 (insn)); break; case 'X': /* 5 bit unsigned immediate. */ case 'Y': /* 6 bit unsigned immediate. */ { int imm = X_IMM (insn, *s == 'X' ? 5 : 6); if (imm <= 9) (info->fprintf_func) (stream, "%d", imm); else (info->fprintf_func) (stream, "%#x", (unsigned) imm); } break; case '3': (info->fprintf_func) (stream, "%ld", X_IMM (insn, 3)); break; case 'K': { int mask = X_MEMBAR (insn); int bit = 0x40, printed_one = 0; const char *name; if (mask == 0) (info->fprintf_func) (stream, "0"); else while (bit) { if (mask & bit) { if (printed_one) (info->fprintf_func) (stream, "|"); name = sparc_decode_membar (bit); (info->fprintf_func) (stream, "%s", name); printed_one = 1; } bit >>= 1; } break; } case '=': info->target = memaddr + SEX (X_DISP10 (insn), 10) * 4; (*info->print_address_func) (info->target, info); break; case 'k': info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4; (*info->print_address_func) (info->target, info); break; case 'G': info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4; (*info->print_address_func) (info->target, info); break; case '6': case '7': case '8': case '9': (*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0'); break; case 'z': (*info->fprintf_func) (stream, "%%icc"); break; case 'Z': (*info->fprintf_func) (stream, "%%xcc"); break; case 'E': (*info->fprintf_func) (stream, "%%ccr"); break; case 's': (*info->fprintf_func) (stream, "%%fprs"); break; case '{': (*info->fprintf_func) (stream, "%%mcdper"); break; case 'o': (*info->fprintf_func) (stream, "%%asi"); break; case 'W': (*info->fprintf_func) (stream, "%%tick"); break; case 'P': (*info->fprintf_func) (stream, "%%pc"); break; case '?': if (X_RS1 (insn) == 31) (*info->fprintf_func) (stream, "%%ver"); else if (X_RS1 (insn) == 23) (*info->fprintf_func) (stream, "%%pmcdper"); else if ((unsigned) X_RS1 (insn) < 17) (*info->fprintf_func) (stream, "%%%s", v9_priv_reg_names[X_RS1 (insn)]); else (*info->fprintf_func) (stream, "%%reserved"); break; case '!': if (X_RD (insn) == 31) (*info->fprintf_func) (stream, "%%ver"); else if (X_RD (insn) == 23) (*info->fprintf_func) (stream, "%%pmcdper"); else if ((unsigned) X_RD (insn) < 17) (*info->fprintf_func) (stream, "%%%s", v9_priv_reg_names[X_RD (insn)]); else (*info->fprintf_func) (stream, "%%reserved"); break; case '$': if ((unsigned) X_RS1 (insn) < 32) (*info->fprintf_func) (stream, "%%%s", v9_hpriv_reg_names[X_RS1 (insn)]); else (*info->fprintf_func) (stream, "%%reserved"); break; case '%': if ((unsigned) X_RD (insn) < 32) (*info->fprintf_func) (stream, "%%%s", v9_hpriv_reg_names[X_RD (insn)]); else (*info->fprintf_func) (stream, "%%reserved"); break; case '/': if (X_RS1 (insn) < 16 || X_RS1 (insn) > 28) (*info->fprintf_func) (stream, "%%reserved"); else (*info->fprintf_func) (stream, "%%%s", v9a_asr_reg_names[X_RS1 (insn)-16]); break; case '_': if (X_RD (insn) < 16 || X_RD (insn) > 28) (*info->fprintf_func) (stream, "%%reserved"); else (*info->fprintf_func) (stream, "%%%s", v9a_asr_reg_names[X_RD (insn)-16]); break; case '*': { const char *name = sparc_decode_prefetch (X_RD (insn)); if (name) (*info->fprintf_func) (stream, "%s", name); else (*info->fprintf_func) (stream, "%ld", X_RD (insn)); break; } case 'M': (*info->fprintf_func) (stream, "%%asr%ld", X_RS1 (insn)); break; case 'm': (*info->fprintf_func) (stream, "%%asr%ld", X_RD (insn)); break; case 'L': info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4; (*info->print_address_func) (info->target, info); break; case 'n': (*info->fprintf_func) (stream, "%#x", SEX (X_DISP22 (insn), 22)); break; case 'l': info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4; (*info->print_address_func) (info->target, info); break; case 'A': { const char *name = sparc_decode_asi (X_ASI (insn)); if (name) (*info->fprintf_func) (stream, "%s", name); else (*info->fprintf_func) (stream, "(%ld)", X_ASI (insn)); break; } case 'C': (*info->fprintf_func) (stream, "%%csr"); break; case 'F': (*info->fprintf_func) (stream, "%%fsr"); break; case '(': (*info->fprintf_func) (stream, "%%efsr"); break; case 'p': (*info->fprintf_func) (stream, "%%psr"); break; case 'q': (*info->fprintf_func) (stream, "%%fq"); break; case 'Q': (*info->fprintf_func) (stream, "%%cq"); break; case 't': (*info->fprintf_func) (stream, "%%tbr"); break; case 'w': (*info->fprintf_func) (stream, "%%wim"); break; case 'x': (*info->fprintf_func) (stream, "%ld", ((X_LDST_I (insn) << 8) + X_ASI (insn))); break; case 'y': (*info->fprintf_func) (stream, "%%y"); break; case 'u': case 'U': { int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn); const char *name = sparc_decode_sparclet_cpreg (val); if (name) (*info->fprintf_func) (stream, "%s", name); else (*info->fprintf_func) (stream, "%%cpreg(%d)", val); break; } } } } /* If we are adding or or'ing something to rs1, then check to see whether the previous instruction was a sethi to the same register as in the sethi. If so, attempt to print the result of the add or or (in this context add and or do the same thing) and its symbolic value. */ if (imm_ored_to_rs1 || imm_added_to_rs1) { unsigned long prev_insn; int errcode; if (memaddr >= 4) errcode = (*info->read_memory_func) (memaddr - 4, buffer, sizeof (buffer), info); else errcode = 1; prev_insn = getword (buffer); if (errcode == 0) { /* If it is a delayed branch, we need to look at the instruction before the delayed branch. This handles sequences such as: sethi %o1, %hi(_foo), %o1 call _printf or %o1, %lo(_foo), %o1 */ if (is_delayed_branch (prev_insn)) { if (memaddr >= 8) errcode = (*info->read_memory_func) (memaddr - 8, buffer, sizeof (buffer), info); else errcode = 1; prev_insn = getword (buffer); } } /* If there was a problem reading memory, then assume the previous instruction was not sethi. */ if (errcode == 0) { /* Is it sethi to the same register? */ if ((prev_insn & 0xc1c00000) == 0x01000000 && X_RD (prev_insn) == X_RS1 (insn)) { (*info->fprintf_func) (stream, "\t! "); info->target = ((unsigned) 0xFFFFFFFF & ((int) X_IMM22 (prev_insn) << 10)); if (imm_added_to_rs1) info->target += X_SIMM (insn, 13); else info->target |= X_SIMM (insn, 13); (*info->print_address_func) (info->target, info); info->insn_type = dis_dref; info->data_size = 4; /* FIXME!!! */ } } } if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR)) { /* FIXME -- check is_annulled flag. */ (void) is_annulled; if (opcode->flags & F_UNBR) info->insn_type = dis_branch; if (opcode->flags & F_CONDBR) info->insn_type = dis_condbranch; if (opcode->flags & F_JSR) info->insn_type = dis_jsr; if (opcode->flags & F_DELAYED) info->branch_delay_insns = 1; } return sizeof (buffer); } }