diff options
Diffstat (limited to 'usr/src/lib/libdisasm/sparc/dis_sparc_fmt.c')
| -rw-r--r-- | usr/src/lib/libdisasm/sparc/dis_sparc_fmt.c | 2422 |
1 files changed, 2422 insertions, 0 deletions
diff --git a/usr/src/lib/libdisasm/sparc/dis_sparc_fmt.c b/usr/src/lib/libdisasm/sparc/dis_sparc_fmt.c new file mode 100644 index 0000000000..b49c381d96 --- /dev/null +++ b/usr/src/lib/libdisasm/sparc/dis_sparc_fmt.c @@ -0,0 +1,2422 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2007 Jason King. All rights reserved. + * Use is subject to license terms. + */ + + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/byteorder.h> +#include <stdarg.h> + +#if !defined(DIS_STANDALONE) +#include <stdio.h> +#endif /* DIS_STANDALONE */ + +#include "libdisasm.h" +#include "libdisasm_impl.h" +#include "dis_sparc.h" +#include "dis_sparc_fmt.h" + +extern char *strncpy(char *, const char *, size_t); +extern size_t strlen(const char *); +extern int strcmp(const char *, const char *); +extern int strncmp(const char *, const char *, size_t); +extern size_t strlcat(char *, const char *, size_t); +extern size_t strlcpy(char *, const char *, size_t); +extern int snprintf(char *, size_t, const char *, ...); +extern int vsnprintf(char *, size_t, const char *, va_list); + +/* + * This file has the functions that do all the dirty work of outputting the + * disassembled instruction + * + * All the non-static functions follow the format_fcn (in dis_sparc.h): + * Input: + * disassembler handle/context + * instruction to disassemble + * instruction definition pointer (inst_t *) + * index in the table of the instruction + * Return: + * 0 Success + * !0 Invalid instruction + * + * Generally, instructions found in the same table use the same output format + * or have a few minor differences (which are described in the 'flags' field + * of the instruction definition. In some cases, certain instructions differ + * radically enough from those in the same table, that their own format + * function is used. + * + * Typically each table has a unique format function defined in this file. In + * some cases (such as branches) a common one for all the tables is used. + * + * When adding support for new instructions, it is largely a judgement call + * as to when a new format function is defined. + */ + +/* The various instruction formats of a sparc instruction */ + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format1 { + uint32_t op:2; + uint32_t disp30:30; +} format1_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format1 { + uint32_t disp30:30; + uint32_t op:2; +} format1_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format2 { + uint32_t op:2; + uint32_t rd:5; + uint32_t op2:3; + uint32_t imm22:22; +} format2_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format2 { + uint32_t imm22:22; + uint32_t op2:3; + uint32_t rd:5; + uint32_t op:2; +} format2_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format2a { + uint32_t op:2; + uint32_t a:1; + uint32_t cond:4; + uint32_t op2:3; + uint32_t disp22:22; +} format2a_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format2a { + uint32_t disp22:22; + uint32_t op2:3; + uint32_t cond:4; + uint32_t a:1; + uint32_t op:2; +} format2a_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format2b { + uint32_t op:2; + uint32_t a:1; + uint32_t cond:4; + uint32_t op2:3; + uint32_t cc:2; + uint32_t p:1; + uint32_t disp19:19; +} format2b_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format2b { + uint32_t disp19:19; + uint32_t p:1; + uint32_t cc:2; + uint32_t op2:3; + uint32_t cond:4; + uint32_t a:1; + uint32_t op:2; +} format2b_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format2c { + uint32_t op:2; + uint32_t a:1; + uint32_t cond:4; + uint32_t op2:3; + uint32_t d16hi:2; + uint32_t p:1; + uint32_t rs1:5; + uint32_t d16lo:14; +} format2c_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format2c { + uint32_t d16lo:14; + uint32_t rs1:5; + uint32_t p:1; + uint32_t d16hi:2; + uint32_t op2:3; + uint32_t cond:4; + uint32_t a:1; + uint32_t op:2; +} format2c_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format3 { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t asi:8; + uint32_t rs2:5; +} format3_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format3 { + uint32_t rs2:5; + uint32_t asi:8; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} format3_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format3a { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t simm13:13; +} format3a_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format3a { + uint32_t simm13:13; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} format3a_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format3b { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t x:1; + uint32_t undef:6; + uint32_t shcnt:6; +} format3b_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format3b { + uint32_t shcnt:6; + uint32_t undef:6; + uint32_t x:1; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} format3b_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format3c { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t cc2:1; + uint32_t cond:4; + uint32_t i:1; + uint32_t cc:2; + uint32_t simm11:11; +} format3c_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format3c { + uint32_t simm11:11; + uint32_t cc:2; + uint32_t i:1; + uint32_t cond:4; + uint32_t cc2:1; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} format3c_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct format3d { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t rcond:3; + uint32_t simm10:10; +} format3d_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct format3d { + uint32_t simm10:10; + uint32_t rcond:3; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} format3d_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formatcp { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t opc:9; + uint32_t rs2:5; +} formatcp_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formatcp { + uint32_t rs2:5; + uint32_t opc:9; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} formatcp_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formattcc { + uint32_t op:2; + uint32_t undef:1; + uint32_t cond:4; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t cc:2; + uint32_t undef2:3; + uint32_t immtrap:8; +} formattcc_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formattcc { + uint32_t immtrap:8; + uint32_t undef2:3; + uint32_t cc:2; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t cond:4; + uint32_t undef:1; + uint32_t op:2; +} formattcc_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formattcc2 { + uint32_t op:2; + uint32_t undef:1; + uint32_t cond:4; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t cc:2; + uint32_t undef2:6; + uint32_t rs2:5; +} formattcc2_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formattcc2 { + uint32_t rs2:5; + uint32_t undef2:6; + uint32_t cc:2; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t cond:4; + uint32_t undef:1; + uint32_t op:2; +} formattcc2_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formatmbr { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t i:1; + uint32_t undef:6; + uint32_t cmask:3; + uint32_t mmask:4; +} formatmbr_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formatmbr { + uint32_t mmask:4; + uint32_t cmask:3; + uint32_t undef:6; + uint32_t i:1; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} formatmbr_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formatfcmp { + uint32_t op:2; + uint32_t undef:3; + uint32_t cc:2; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t opf:9; + uint32_t rs2:5; +} formatfcmp_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formatfcmp { + uint32_t rs2:5; + uint32_t opf:9; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t cc:2; + uint32_t undef:3; + uint32_t op:2; +} formatfcmp_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formatfmov { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t undef:1; + uint32_t cond:4; + uint32_t cc:3; + uint32_t opf:6; + uint32_t rs2:5; +} formatfmov_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formatfmov { + uint32_t rs2:5; + uint32_t opf:6; + uint32_t cc:3; + uint32_t cond:4; + uint32_t undef:1; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} formatfmov_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +#if defined(_BIT_FIELDS_HTOL) +typedef struct formatfused { + uint32_t op:2; + uint32_t rd:5; + uint32_t op3:6; + uint32_t rs1:5; + uint32_t rs3:5; + uint32_t op5:4; + uint32_t rs2:5; +} formatfused_t; +#elif defined(_BIT_FIELDS_LTOH) +typedef struct formatfused { + uint32_t rs2:5; + uint32_t op5:4; + uint32_t rs3:5; + uint32_t rs1:5; + uint32_t op3:6; + uint32_t rd:5; + uint32_t op:2; +} formatfused_t; +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + +typedef union ifmt { + uint32_t i; + format1_t f1; + format2_t f2; + format2a_t f2a; + format2b_t f2b; + format2c_t f2c; + format3_t f3; + format3a_t f3a; + format3b_t f3b; + format3c_t f3c; + format3d_t f3d; + formatcp_t fcp; + formattcc_t ftcc; + formattcc2_t ftcc2; + formatfcmp_t fcmp; + formatmbr_t fmb; + formatfmov_t fmv; + formatfused_t fused; +} ifmt_t; + +/* integer register names */ +static const char *reg_names[32] = { + "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", + "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7" +}; + +/* floating point register names */ +static const char *freg_names[32] = { + "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", + "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", + "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", + "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31" +}; + +/* double precision register names */ +static const char *fdreg_names[32] = { + "%d0", "%d32", "%d2", "%d34", "%d4", "%d36", "%d6", "%d38", + "%d8", "%d40", "%d10", "%d42", "%d12", "%d44", "%d14", "%d46", + "%d16", "%d48", "%d18", "%d50", "%d20", "%d52", "%d22", "%d54", + "%d24", "%d56", "%d26", "%d58", "%d28", "%d60", "%d30", "%d62" +}; + +static const char *compat_fdreg_names[32] = { + "%f0", "%f32", "%f2", "%f34", "%f4", "%f36", "%f6", "%f38", + "%f8", "%f40", "%f10", "%f42", "%f12", "%f44", "%f14", "%f46", + "%f16", "%f48", "%f18", "%f50", "%f20", "%f52", "%f22", "%f54", + "%f24", "%f56", "%f26", "%f58", "%f28", "%f60", "%f30", "%f62" +}; + + +static const char *fqreg_names[32] = { + "%q0", "%q32", "%f2", "%f3", "%f4", "%q4", "%q36", "%f6", + "%f7", "%q8", "%q40", "%f10", "%f11", "%q12", "%q44", "%f14", + "%f15", "%q16", "%q48", "%f18", "%f19", "%q20", "%q52", "%f22", + "%f23", "%q24", "%q56", "%f26", "%f27", "%q28", "%q60", "%f30", +}; + + +/* coprocessor register names -- sparcv8 only */ +static const char *cpreg_names[32] = { + "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", + "%c8", "%c9", "%c10", "%c11", "%c12", "%c13", "%c14", "%c15", + "%c16", "%c17", "%c18", "%c19", "%c20", "%c21", "%c22", "%c23", + "%c24", "%c25", "%c26", "%c27", "%c28", "%c29", "%c30", "%c31", +}; + +/* floating point condition code names */ +static const char *fcc_names[4] = { + "%fcc0", "%fcc1", "%fcc2", "%fcc3" +}; + +/* condition code names */ +static const char *icc_names[4] = { + "%icc", NULL, "%xcc", NULL +}; + +/* bitmask values for membar */ +static const char *membar_mmask[4] = { + "#LoadLoad", "#StoreLoad", "#LoadStore", "#StoreStore" +}; + +static const char *membar_cmask[3] = { + "#Lookaside", "#MemIssue", "#Sync" +}; + +/* v8 ancillary state register names */ +static const char *asr_names[32] = { + "%y", "%asr1", "%asr2", "%asr3", + "%asr4", "%asr5", "%asr6", "%asr7", + "%asr8", "%asr9", "%asr10", "%asr11", + "%asr12", "%asr13", "%asr14", "%asr15", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; +static const uint32_t asr_rdmask = 0x0000ffffL; +static const uint32_t asr_wrmask = 0x0000ffffL; + +static const char *v9_asr_names[32] = { + "%y", NULL, "%ccr", "%asi", + "%tick", "%pc", "%fprs", NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "%pcr", "%pic", NULL, "%gsr", + "%softint_set", "%softint_clr", "%softint", "%tick_cmpr", + "%stick", "%stick_cmpr", NULL, NULL, + NULL, NULL, NULL, NULL +}; +/* + * on v9, only certain registers are valid for read or writing + * these are bitmasks corresponding to which registers are valid in which + * case + */ +static const uint32_t v9_asr_rdmask = 0x03cb007d; +static const uint32_t v9_asr_wrmask = 0x02fb004d; + +/* privledged register names on v9 */ +/* TODO: compat - NULL to %priv_nn */ +static const char *v9_privreg_names[32] = { + "%tpc", "%tnpc", "%tstate", "%tt", + "%tick", "%tba", "%pstate", "%tl", + "%pil", "%cwp", "%cansave", "%canrestore", + "%cleanwin", "%otherwin", "%wstate", "%fq", + "%gl", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "%ver" +}; + +static const uint32_t v9_pr_rdmask = 0x80017fff; +static const uint32_t v9_pr_wrmask = 0x00017fef; + +static const char *prefetch_str[32] = { + "#n_reads", "#one_read", + "#n_writes", "#one_write", + "#page", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, "#unified", NULL, NULL, + "#n_reads_strong", "#one_read_strong", + "#n_writes_strong", "#one_write_strong", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static void prt_field(const char *, uint32_t, int); + +static const char *get_regname(dis_handle_t *, int, uint32_t); +static int32_t sign_extend(int32_t, int32_t); + +static void prt_name(dis_handle_t *, const char *, int); + +#define IMM_SIGNED 0x01 /* Is immediate value signed */ +#define IMM_ADDR 0x02 /* Is immediate value part of an address */ +static void prt_imm(dis_handle_t *, uint32_t, int); + +static void prt_asi(dis_handle_t *, uint32_t); +static void prt_address(dis_handle_t *, uint32_t, int); +static void prt_aluargs(dis_handle_t *, uint32_t, uint32_t); +static void bprintf(dis_handle_t *, const char *, ...); + +/* + * print out val (which is 'bitlen' bits long) in binary + */ +#if defined(DIS_STANDALONE) +/* ARGSUSED */ +void +prt_binary(uint32_t val, int bitlen) +{ + +} + +#else + +void +prt_binary(uint32_t val, int bitlen) +{ + int i; + + for (i = bitlen - 1; i >= 0; --i) { + (void) fprintf(stderr, ((val & (1L << i)) != 0) ? "1" : "0"); + + if (i % 4 == 0 && i != 0) + (void) fprintf(stderr, " "); + } +} +#endif /* DIS_STANDALONE */ + + +/* + * print out a call instruction + * format: call address <name> + */ +/* ARGSUSED1 */ +int +fmt_call(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + + int32_t disp; + size_t curlen; + + int octal = ((dhp->dh_flags & DIS_OCTAL) != 0); + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f1.op, 2); + prt_field("disp30", f->f1.disp30, 30); + } + + disp = sign_extend(f->f1.disp30, 30) * 4; + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + + bprintf(dhp, (octal != 0) ? "%s0%-11lo" : "%s0x%-10lx", + (disp < 0) ? "-" : "+", + (disp < 0) ? (-disp) : disp); + + (void) strlcat(dhp->dh_buf, " <", dhp->dh_buflen); + + curlen = strlen(dhp->dh_buf); + dhp->dh_lookup(dhp->dh_data, dhp->dh_addr + (int64_t)disp, + dhp->dh_buf + curlen, dhp->dh_buflen - curlen - 1, NULL, + NULL); + (void) strlcat(dhp->dh_buf, ">", dhp->dh_buflen); + + + return (0); +} + +int +fmt_sethi(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f2.op, 2); + prt_field("op2", f->f2.op2, 3); + prt_field("rd", f->f2.rd, 5); + prt_field("imm22", f->f2.imm22, 22); + } + + if (idx == 0) { + /* unimp / illtrap */ + prt_name(dhp, inp->in_data.in_def.in_name, 1); + prt_imm(dhp, f->f2.imm22, 0); + return (0); + } + + if (f->f2.imm22 == 0 && f->f2.rd == 0) { + prt_name(dhp, "nop", 0); + return (0); + } + + /* ?? Should we return -1 if rd == 0 && disp != 0 */ + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + + bprintf(dhp, + ((dhp->dh_flags & DIS_OCTAL) != 0) ? + "%%hi(0%lo), %s" : "%%hi(0x%lx), %s", + f->f2.imm22 << 10, + reg_names[f->f2.rd]); + + return (0); +} + +/* ARGSUSED3 */ +int +fmt_branch(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + const char *name = inp->in_data.in_def.in_name; + const char *r = NULL; + const char *annul = ""; + const char *pred = ""; + + char buf[15]; + + ifmt_t *f = (ifmt_t *)&instr; + + size_t curlen; + int32_t disp; + uint32_t flags = inp->in_data.in_def.in_flags; + int octal = ((dhp->dh_flags & DIS_OCTAL) != 0); + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f2.op, 2); + prt_field("op2", f->f2.op2, 3); + + switch (FLG_DISP_VAL(flags)) { + case DISP22: + prt_field("cond", f->f2a.cond, 4); + prt_field("a", f->f2a.a, 1); + prt_field("disp22", f->f2a.disp22, 22); + break; + + case DISP19: + prt_field("cond", f->f2a.cond, 4); + prt_field("a", f->f2a.a, 1); + prt_field("p", f->f2b.p, 1); + prt_field("cc", f->f2b.cc, 2); + prt_field("disp19", f->f2b.disp19, 19); + break; + + case DISP16: + prt_field("bit 28", ((instr & (1L << 28)) >> 28), 1); + prt_field("rcond", f->f2c.cond, 3); + prt_field("p", f->f2c.p, 1); + prt_field("rs1", f->f2c.rs1, 5); + prt_field("d16hi", f->f2c.d16hi, 2); + prt_field("d16lo", f->f2c.d16lo, 14); + break; + } + } + + if (f->f2b.op2 == 0x01 && idx == 0x00 && f->f2b.p == 1 && + f->f2b.cc == 0x02 && ((dhp->dh_debug & DIS_DEBUG_SYN_ALL) != 0)) { + name = "iprefetch"; + flags = FLG_RS1(REG_NONE)|FLG_DISP(DISP19); + } + + switch (FLG_DISP_VAL(flags)) { + case DISP22: + disp = sign_extend(f->f2a.disp22, 22); + break; + + case DISP19: + disp = sign_extend(f->f2b.disp19, 19); + break; + + case DISP16: + disp = sign_extend((f->f2c.d16hi << 14)|f->f2c.d16lo, 16); + break; + + } + + disp *= 4; + + if ((FLG_RS1_VAL(flags) == REG_ICC) || (FLG_RS1_VAL(flags) == REG_FCC)) + r = get_regname(dhp, FLG_RS1_VAL(flags), f->f2b.cc); + else + r = get_regname(dhp, FLG_RS1_VAL(flags), f->f2c.rs1); + + if (r == NULL) + return (-1); + + if (f->f2a.a == 1) + annul = ",a"; + + if ((flags & FLG_PRED) != 0) { + if (f->f2b.p == 0) { + pred = ",pn"; + } else { + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) != 0) + pred = ",pt"; + } + } + + (void) snprintf(buf, sizeof (buf), "%s%s%s", name, annul, pred); + prt_name(dhp, buf, 1); + + + switch (FLG_DISP_VAL(flags)) { + case DISP22: + bprintf(dhp, + (octal != 0) ? "%s0%-11lo <" : "%s0x%-10lx <", + (disp < 0) ? "-" : "+", + (disp < 0) ? (-disp) : disp); + break; + + case DISP19: + bprintf(dhp, + (octal != 0) ? "%s, %s0%-5lo <" : "%s, %s0x%-04lx <", + r, + (disp < 0) ? "-" : "+", + (disp < 0) ? (-disp) : disp); + break; + + case DISP16: + bprintf(dhp, + (octal != 0) ? "%s, %s0%-6lo <" : "%s, %s0x%-5lx <", + r, + (disp < 0) ? "-" : "+", + (disp < 0) ? (-disp) : disp); + break; + } + + curlen = strlen(dhp->dh_buf); + dhp->dh_lookup(dhp->dh_data, dhp->dh_addr + (int64_t)disp, + dhp->dh_buf + curlen, dhp->dh_buflen - curlen - 1, NULL, NULL); + + (void) strlcat(dhp->dh_buf, ">", dhp->dh_buflen); + + return (0); +} + + + +/* + * print out the compare and swap instructions (casa/casxa) + * format: casa/casxa [%rs1] imm_asi, %rs2, %rd + * casa/casxa [%rs1] %asi, %rs2, %rd + * + * If DIS_DEBUG_SYN_ALL is set, synthetic instructions are emitted + * when an immediate ASI value is given as follows: + * + * casa [%rs1]#ASI_P, %rs2, %rd -> cas [%rs1], %rs2, %rd + * casa [%rs1]#ASI_P_L, %rs2, %rd -> casl [%rs1], %rs2, %rd + * casxa [%rs1]#ASI_P, %rs2, %rd -> casx [%rs1], %rs2, %rd + * casxa [%rs1]#ASI_P_L, %rs2, %rd -> casxl [%rs1], %rs2, %rd + */ +static int +fmt_cas(dis_handle_t *dhp, uint32_t instr, const char *name) +{ + ifmt_t *f = (ifmt_t *)&instr; + int noasi = 0; + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) != 0) { + if (f->f3.op3 == 0x3c && f->f3.i == 0) { + if (f->f3.asi == 0x80) { + noasi = 1; + name = "cas"; + } + + if (f->f3.asi == 0x88) { + noasi = 1; + name = "casl"; + } + } + + if (f->f3.op3 == 0x3e && f->f3.i == 0) { + if (f->f3.asi == 0x80) { + noasi = 1; + name = "casx"; + } + + if (f->f3.asi == 0x88) { + noasi = 1; + name = "casxl"; + } + } + } + + prt_name(dhp, name, 1); + + bprintf(dhp, "[%s]", reg_names[f->f3.rs1]); + + if (noasi == 0) { + (void) strlcat(dhp->dh_buf, " ", dhp->dh_buflen); + prt_asi(dhp, instr); + } + + bprintf(dhp, ", %s, %s", reg_names[f->f3.rs2], reg_names[f->f3.rd]); + + return (0); +} + +/* + * format a load/store instruction + * format: ldXX [%rs1 + %rs2], %rd load, i==0 + * ldXX [%rs1 +/- nn], %rd load, i==1 + * ldXX [%rs1 + %rs2] #XX, %rd load w/ imm_asi, i==0 + * ldXX [%rs1 +/- nn] %asi, %rd load from asi[%asi], i==1 + * + * stXX %rd, [%rs1 + %rs2] store, i==0 + * stXX %rd, [%rs1 +/- nn] store, i==1 + * stXX %rd, [%rs1 + %rs1] #XX store to imm_asi, i==0 + * stXX %rd, [%rs1 +/-nn] %asi store to asi[%asi], i==1 + * + * The register sets used for %rd are set in the instructions flags field + * The asi variants are used if FLG_ASI is set in the instructions flags field + * + * If DIS_DEBUG_SYNTH_ALL or DIS_DEBUG_COMPAT are set, + * When %rs1, %rs2 or nn are 0, they are not printed, i.e. + * [ %rs1 + 0x0 ], %rd -> [%rs1], %rd for example + * + * The following synthetic instructions are also implemented: + * + * stb %g0, [addr] -> clrb [addr] DIS_DEBUG_SYNTH_ALL + * sth %g0, [addr] -> crlh [addr] DIS_DEBUG_SYNTH_ALL + * stw %g0, [addr] -> clr [addr] DIS_DEBUG_SYNTH_ALL|DIS_DEBUG_COMPAT + * stx %g0, [addr] -> clrx [addr] DIS_DEBUG_SYNTH_ALL + * + * If DIS_DEBUG_COMPAT is set, the following substitutions also take place + * lduw -> ld + * ldtw -> ld + * stuw -> st + * sttw -> st + */ +int +fmt_ls(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + const char *regstr = NULL; + + const char *iname = inp->in_data.in_def.in_name; + uint32_t flags = inp->in_data.in_def.in_flags; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3.op, 2); + prt_field("op3", f->f3.op3, 6); + prt_field("rs1", f->f3.rs1, 5); + prt_field("i", f->f3.i, 1); + if (f->f3.i != 0) { + prt_field("simm13", f->f3a.simm13, 13); + } else { + if ((flags & FLG_ASI) != 0) + prt_field("imm_asi", f->f3.asi, 8); + prt_field("rs2", f->f3.rs2, 5); + } + prt_field("rd", f->f3.rd, 5); + } + + if (idx == 0x2d || idx == 0x3d) { + /* prefetch / prefetcha */ + + prt_name(dhp, iname, 1); + + prt_address(dhp, instr, 0); + + if (idx == 0x3d) { + (void) strlcat(dhp->dh_buf, " ", dhp->dh_buflen); + prt_asi(dhp, instr); + } + + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + + /* fcn field is the same as rd */ + if (prefetch_str[f->f3.rd] != NULL) + (void) strlcat(dhp->dh_buf, prefetch_str[f->f3.rd], + dhp->dh_buflen); + else + prt_imm(dhp, f->f3.rd, 0); + + return (0); + } + + /* casa / casxa */ + if (idx == 0x3c || idx == 0x3e) + return (fmt_cas(dhp, instr, iname)); + + /* synthetic instructions & special cases */ + switch (idx) { + case 0x00: + /* ld */ + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + iname = "lduw"; + break; + + case 0x03: + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + iname = "ldtw"; + break; + + case 0x04: + /* stw */ + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + iname = "stuw"; + + if ((dhp->dh_flags & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (f->f3.rd == 0) { + iname = "clr"; + flags = FLG_RD(REG_NONE); + } + break; + + case 0x05: + /* stb */ + if ((dhp->dh_flags & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (f->f3.rd == 0) { + iname = "clrb"; + flags = FLG_RD(REG_NONE); + } + break; + + case 0x06: + /* sth */ + if ((dhp->dh_flags & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (f->f3.rd == 0) { + iname = "clrh"; + flags = FLG_RD(REG_NONE); + } + break; + + case 0x07: + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + iname = "sttw"; + break; + + case 0x0e: + /* stx */ + + if ((dhp->dh_flags & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (f->f3.rd == 0) { + iname = "clrx"; + flags = FLG_RD(REG_NONE); + } + break; + + case 0x13: + /* ldtwa */ + if (((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) && + ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) != 0)) + iname = "ldtwa"; + break; + + case 0x17: + /* sttwa */ + if (((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) && + ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) != 0)) + iname = "sttwa"; + break; + + case 0x21: + case 0x25: + /* + * on sparcv8 it merely says that rd != 1 should generate an + * exception, on v9, it is illegal + */ + if ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) == 0) + break; + + iname = (idx == 0x21) ? "ldx" : "stx"; + + if (f->f3.rd > 1) + return (-1); + + break; + + case 0x31: + /* stda */ + switch (f->f3.asi) { + case 0xc0: + case 0xc1: + case 0xc8: + case 0xc9: + case 0xc2: + case 0xc3: + case 0xca: + case 0xcb: + case 0xc4: + case 0xc5: + case 0xcc: + case 0xcd: + /* + * store partial floating point, only valid w/ + * vis + * + * Somewhat confusingly, it uses the same op + * code as 'stda' -- store double to alternate + * space. It is distinguised by specific + * imm_asi values (as seen above), and + * has a slightly different output syntax + */ + + if ((dhp->dh_flags & DIS_SPARC_V9_SGI) == 0) + break; + if (f->f3.i != 0) + break; + prt_name(dhp, iname, 1); + bprintf(dhp, "%s, %s, [%s] ", + get_regname(dhp, REG_FPD, f->f3.rd), + get_regname(dhp, REG_FPD, f->f3.rs2), + get_regname(dhp, REG_FPD, f->f3.rs1)); + prt_asi(dhp, instr); + return (0); + + default: + break; + } + + } + + regstr = get_regname(dhp, FLG_RD_VAL(flags), f->f3.rd); + + prt_name(dhp, iname, 1); + + if ((flags & FLG_STORE) != 0) { + if (regstr[0] != '\0') { + (void) strlcat(dhp->dh_buf, regstr, dhp->dh_buflen); + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + } + + prt_address(dhp, instr, 0); + if ((flags & FLG_ASI) != 0) { + (void) strlcat(dhp->dh_buf, " ", dhp->dh_buflen); + prt_asi(dhp, instr); + } + } else { + prt_address(dhp, instr, 0); + if ((flags & FLG_ASI) != 0) { + (void) strlcat(dhp->dh_buf, " ", dhp->dh_buflen); + prt_asi(dhp, instr); + } + + if (regstr[0] != '\0') { + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + (void) strlcat(dhp->dh_buf, regstr, dhp->dh_buflen); + } + } + + return (0); +} + +static int +fmt_cpop(dis_handle_t *dhp, uint32_t instr, const inst_t *inp) +{ + ifmt_t *f = (ifmt_t *)&instr; + int flags = FLG_P1(REG_CP)|FLG_P2(REG_CP)|FLG_NOIMM|FLG_P3(REG_CP); + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->fcp.op, 2); + prt_field("op3", f->fcp.op3, 6); + prt_field("opc", f->fcp.opc, 9); + prt_field("rs1", f->fcp.rs1, 5); + prt_field("rs2", f->fcp.rs2, 5); + prt_field("rd", f->fcp.rd, 5); + } + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + prt_imm(dhp, f->fcp.opc, 0); + + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + (void) prt_aluargs(dhp, instr, flags); + + return (0); +} + +static int +dis_fmt_rdwr(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + const char *psr_str = "%psr"; + const char *wim_str = "%wim"; + const char *tbr_str = "%tbr"; + + const char *name = inp->in_data.in_def.in_name; + const char *regstr = NULL; + + ifmt_t *f = (ifmt_t *)&instr; + + int rd = (idx < 0x30); + int v9 = (dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)); + int ridx = f->f3.rs1; + int i, first; + int pr_rs1 = 1; + int pr_rs2 = 1; + + int use_mask = 1; + uint32_t mask; + + if (rd == 0) + ridx = f->f3.rd; + + switch (idx) { + case 0x28: + /* rd */ + + /* stbar */ + if ((f->f3.rd == 0) && (f->f3.rs1 == 15) && (f->f3.i == 0)) { + prt_name(dhp, "stbar", 0); + return (0); + } + + /* membar */ + if ((v9 != 0) && (f->f3.rd == 0) && (f->f3.rs1 == 15) && + (f->f3.i == 1) && ((f->i & (1L << 12)) == 0)) { + + prt_name(dhp, "membar", + ((f->fmb.cmask != 0) || (f->fmb.mmask != 0))); + + first = 0; + + for (i = 0; i < 4; ++i) { + if ((f->fmb.cmask & (1L << i)) != 0) { + bprintf(dhp, "%s%s", + (first != 0) ? "|" : "", + membar_cmask[i]); + first = 1; + } + } + + for (i = 0; i < 5; ++i) { + if ((f->fmb.mmask & (1L << i)) != 0) { + bprintf(dhp, "%s%s", + (first != 0) ? "|" : "", + membar_mmask[i]); + first = 1; + } + } + + return (0); + } + + if (v9 != 0) { + regstr = v9_asr_names[ridx]; + mask = v9_asr_rdmask; + } else { + regstr = asr_names[ridx]; + mask = asr_rdmask; + } + break; + + case 0x29: + /* + * NOTE: due to an overlay entry, this only gets executed when + * disassembling v8 instructions + */ + regstr = psr_str; + use_mask = 0; + break; + + case 0x2a: + if (v9 != 0) { + regstr = v9_privreg_names[ridx]; + mask = v9_pr_rdmask; + } else { + regstr = wim_str; + use_mask = 0; + } + break; + + case 0x2b: + if (v9 != 0) { + /* flushw */ + prt_name(dhp, name, 0); + return (0); + } + + regstr = tbr_str; + use_mask = 0; + break; + + case 0x30: + if (v9 != 0) { + regstr = v9_asr_names[ridx]; + mask = v9_asr_wrmask; + } else { + regstr = asr_names[ridx]; + mask = asr_wrmask; + } + + /* synth: mov */ + if ((dhp->dh_debug & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (f->f3.rs1 == 0) { + name = "mov"; + pr_rs1 = 0; + } + + if ((f->f3.i == 0 && f->f3.rs2 == 0) || + (f->f3.i == 1 && f->f3a.simm13 == 0)) { + name = "mov"; + pr_rs2 = 0; + } + + if (pr_rs1 == 0) + pr_rs2 = 1; + + break; + + case 0x31: + /* + * NOTE: due to the presence of an overlay entry for another + * table, this case only happens when doing v8 instructions + * only + */ + regstr = psr_str; + use_mask = 0; + break; + + case 0x32: + if (v9 != 0) { + regstr = v9_privreg_names[ridx]; + mask = v9_pr_wrmask; + } else { + regstr = wim_str; + use_mask = 0; + } + break; + + case 0x33: + /* NOTE: due to an overlay entry, this is v8 only */ + regstr = tbr_str; + use_mask = 0; + break; + } + + if (regstr == NULL) + return (-1); + + if (use_mask != 0 && ((1L << ridx) & mask) == 0) + return (-1); + + prt_name(dhp, name, 1); + + if (rd != 0) { + bprintf(dhp, "%s, %s", regstr, reg_names[f->f3.rd]); + } else { + if (pr_rs1 == 1) + bprintf(dhp, "%s, ", reg_names[f->f3.rs1]); + + if (pr_rs2 != 0) { + if (f->f3.i == 1) + prt_imm(dhp, sign_extend(f->f3a.simm13, 13), + IMM_SIGNED); + else + (void) strlcat(dhp->dh_buf, + reg_names[f->f3.rs2], dhp->dh_buflen); + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + } + + (void) strlcat(dhp->dh_buf, regstr, dhp->dh_buflen); + } + + return (0); +} + +/* ARGSUSED3 */ +int +fmt_trap(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + + int v9 = ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) != 0); + int p_rs1, p_t; + + if (f->ftcc.undef != 0) + return (-1); + + if (icc_names[f->ftcc.cc] == NULL) + return (-1); + + if (f->ftcc.i == 1 && f->ftcc.undef2 != 0) + return (-1); + + if (f->ftcc2.i == 0 && f->ftcc2.undef2 != 0) + return (-1); + + p_rs1 = ((f->ftcc.rs1 != 0) || + ((dhp->dh_debug & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) == 0)); + + if (f->ftcc.i == 0) { + p_t = (f->f3.rs2 != 0 || p_rs1 == 0); + + bprintf(dhp, "%-9s %s%s%s%s%s", inp->in_data.in_def.in_name, + (v9 != 0) ? icc_names[f->ftcc2.cc] : "", + (v9 != 0) ? ", " : "", + (p_rs1 != 0) ? reg_names[f->ftcc2.rs1] : "", + (p_rs1 != 0) ? " + " : "", + (p_t != 0) ? reg_names[f->f3.rs2] : ""); + } else { + bprintf(dhp, "%-9s %s%s%s%s0x%x", inp->in_data.in_def.in_name, + (v9 != 0) ? icc_names[f->ftcc2.cc] : "", + (v9 != 0) ? ", " : "", + (p_rs1 != 0) ? reg_names[f->ftcc2.rs1] : "", + (p_rs1 != 0) ? " + " : "", + f->ftcc.immtrap); + } + + return (0); +} + +static int +prt_shift(dis_handle_t *dhp, uint32_t instr, const inst_t *inp) +{ + char name[5]; + uint32_t cnt; + + ifmt_t *f = (ifmt_t *)&instr; + int octal = ((dhp->dh_flags & DIS_OCTAL) != 0); + + name[0] = '\0'; + (void) strlcat(name, inp->in_data.in_def.in_name, sizeof (name)); + + if (f->f3b.i == 1) + cnt = f->f3.rs2; + + if (f->f3b.x == 1 && ((dhp->dh_flags & DIS_SPARC_V8) == 0)) { + cnt = f->f3b.shcnt; + (void) strlcat(name, "x", sizeof (name)); + } + + prt_name(dhp, name, 1); + + if (f->f3b.i == 1) + bprintf(dhp, (octal != 0) ? "%s, 0%lo, %s" : "%s, 0x%lx, %s", + reg_names[f->f3.rs1], cnt, reg_names[f->f3.rd]); + else + bprintf(dhp, "%s, %s, %s", reg_names[f->f3.rs1], + reg_names[f->f3.rs2], reg_names[f->f3.rd]); + + return (0); +} + +/* ARGSUSED3 */ +static int +prt_jmpl(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + const char *name = inp->in_data.in_def.in_name; + ifmt_t *f = (ifmt_t *)&instr; + + if (f->f3.rd == 15 && ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0)) + name = "call"; + + if (f->f3.rd == 0) { + if (f->f3.i == 1 && f->f3a.simm13 == 8) { + if (f->f3.rs1 == 15) { + prt_name(dhp, "retl", 0); + return (0); + } + + if (f->f3.rs1 == 31) { + prt_name(dhp, "ret", 0); + return (0); + } + } + + name = "jmp"; + } + + prt_name(dhp, name, 1); + prt_address(dhp, instr, 1); + + if (f->f3.rd == 0) + return (0); + + if (f->f3.rd == 15 && ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0)) + return (0); + + bprintf(dhp, ", %s", reg_names[f->f3.rd]); + + return (0); +} + +int +fmt_alu(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + + const char *name = inp->in_data.in_def.in_name; + int flags = inp->in_data.in_def.in_flags; + int arg = 0; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3.op, 2); + prt_field("op3", f->f3.op3, 6); + prt_field("rs1", f->f3.rs1, 5); + + switch (idx) { + /* TODO: more formats */ + + default: + if (f->f3.i == 0) + prt_field("rs2", f->f3.rs2, 5); + else + prt_field("simm13", f->f3a.simm13, 13); + + prt_field("rd", f->f3.rd, 5); + } + + } + + switch (idx) { + case 0x00: + /* add */ + + if ((dhp->dh_debug & DIS_DEBUG_SYN_ALL) == 0) + break; + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 == 1) { + name = "inc"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM; + break; + } + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 != 1) { + name = "inc"; + flags = FLG_P1(REG_NONE); + break; + } + break; + + case 0x02: + /* or */ + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + if ((dhp->dh_debug & DIS_DEBUG_SYN_ALL) != 0) { + if (f->f3.rs1 == f->f3.rd) { + name = "bset"; + flags = FLG_P1(REG_NONE); + break; + } + } + + if (((f->f3.i == 0 && f->f3.rs2 == 0) || + (f->f3.i == 1 && f->f3a.simm13 == 0)) && + (f->f3.rs1 == 0)) { + name = "clr"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM; + break; + } + + if (f->f3.rs1 == 0) { + name = "mov"; + flags = FLG_P1(REG_NONE); + break; + } + break; + + case 0x04: + /* sub */ + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + if (f->f3.rs1 == 0 && f->f3.i == 0 && f->f3.rs2 == f->f3.rd) { + name = "neg"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE); + break; + } + + if (f->f3.rs1 == 0 && f->f3.i == 0 && f->f3.rs2 != f->f3.rd) { + name = "neg"; + flags = FLG_P1(REG_NONE); + break; + } + + if ((dhp->dh_debug & DIS_DEBUG_SYN_ALL) == 0) + break; + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 == 1) { + name = "dec"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM; + break; + } + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 != 1) { + name = "dec"; + flags = FLG_P1(REG_NONE); + break; + } + break; + + case 0x07: + /* xnor */ + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + /* + * xnor -> not when you have: + * xnor %rs1, 0x0 or %g0, %rd + */ + if ((f->f3.i == 0 && f->f3.rs2 != 0) || + (f->f3.i == 1 && f->f3a.simm13 != 0)) + break; + + name = "not"; + + if (f->f3.rs1 == f->f3.rd) + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM| + FLG_P3(REG_INT); + else + flags = FLG_P1(REG_INT)|FLG_P2(REG_NONE)|FLG_NOIMM| + FLG_P3(REG_INT); + + break; + + case 0x10: + /* addcc */ + + if ((dhp->dh_debug & DIS_DEBUG_SYN_ALL) == 0) + break; + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 == 1) { + name = "inccc"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM; + break; + } + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 != 1) { + name = "inccc"; + flags = FLG_P1(REG_NONE); + break; + } + break; + + case 0x11: + /* andcc */ + + if (f->f3.rd != 0) + break; + + if ((dhp->dh_debug & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) + == 0) + break; + + if (((dhp->dh_debug & DIS_DEBUG_COMPAT) != 0) && + ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) == 0)) + break; + + name = "btst"; + flags = FLG_P1(REG_NONE); + f->f3.rd = f->f3.rs1; + break; + + case 0x12: + /* orcc */ + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + if (f->f3.rs1 == 0 && f->f3.rd == 0 && f->f3.i == 0) { + name = "tst"; + flags = FLG_P1(REG_NONE)|FLG_P3(REG_NONE); + break; + } + + if (f->f3.rs2 == 0 && f->f3.rd == 0 && f->f3.i == 0) { + name = "tst"; + flags = FLG_P2(REG_NONE)|FLG_P3(REG_NONE); + break; + } + + break; + + case 0x14: + /* subcc */ + + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + if (f->f3.rd == 0) { + name = "cmp"; + flags = FLG_P3(REG_NONE); + break; + } + + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) != 0) + break; + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 == 1) { + name = "deccc"; + flags = FLG_P1(REG_NONE)|FLG_P2(REG_NONE)|FLG_NOIMM; + break; + } + + if (f->f3.rs1 == f->f3.rd && f->f3.i == 1 && + f->f3a.simm13 != 1) { + name = "deccc"; + flags = FLG_P1(REG_NONE); + break; + } + + break; + + case 0x25: + case 0x26: + case 0x27: + return (prt_shift(dhp, instr, inp)); + + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + return (dis_fmt_rdwr(dhp, instr, inp, idx)); + + case 0x36: + case 0x37: + /* NOTE: overlayed on v9 */ + if ((dhp->dh_flags & DIS_SPARC_V8) != 0) + return (fmt_cpop(dhp, instr, inp)); + break; + + case 0x38: + /* jmpl */ + return (prt_jmpl(dhp, instr, inp, idx)); + + case 0x39: + /* rett / return */ + prt_name(dhp, name, 1); + prt_address(dhp, instr, 1); + return (0); + + case 0x3b: + /* flush */ + prt_name(dhp, name, 1); + prt_address(dhp, instr, 0); + return (0); + + case 0x3c: + case 0x3d: + /* save / restore */ + if ((dhp->dh_debug & (DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT)) + == 0) + break; + + if (f->f3.rs1 != 0 || f->f3.rs2 != 0 || f->f3.rd != 0) + break; + + if (f->f3.i != 0 && ((dhp->dh_debug & DIS_DEBUG_COMPAT) != 0)) + break; + + prt_name(dhp, name, 0); + return (0); + } + + if (FLG_P1_VAL(flags) != REG_NONE || FLG_P2_VAL(flags) != REG_NONE || + FLG_P3_VAL(flags) != REG_NONE) + arg = 1; + + prt_name(dhp, name, (arg != 0)); + prt_aluargs(dhp, instr, flags); + + return (0); +} + +/* ARGSUSED1 */ +int +fmt_regwin(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + prt_name(dhp, inp->in_data.in_def.in_name, 0); + return (0); +} + +/* ARGSUSED1 */ +int +fmt_trap_ret(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + prt_name(dhp, inp->in_data.in_def.in_name, 0); + return (0); +} + +/* ARGSUSED3 */ +int +fmt_movcc(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + const char **regs = NULL; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3c.op, 2); + prt_field("op3", f->f3c.op3, 6); + prt_field("cond", f->f3c.cond, 4); + prt_field("cc2", f->f3c.cc2, 1); + prt_field("cc", f->f3c.cc, 2); + prt_field("i", f->f3c.i, 1); + + if (f->f3c.i == 0) + prt_field("rs2", f->f3.rs2, 5); + else + prt_field("simm11", f->f3c.simm11, 11); + + prt_field("rd", f->f3.rd, 5); + } + + if (f->f3c.cc2 == 0) { + regs = fcc_names; + } else { + regs = icc_names; + if (regs[f->f3c.cc] == NULL) + return (-1); + } + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + + bprintf(dhp, "%s, ", regs[f->f3c.cc]); + + if (f->f3c.i == 1) + prt_imm(dhp, sign_extend(f->f3c.simm11, 11), IMM_SIGNED); + else + (void) strlcat(dhp->dh_buf, reg_names[f->f3.rs2], + dhp->dh_buflen); + + bprintf(dhp, ", %s", reg_names[f->f3.rd]); + + return (0); +} + +/* ARGSUSED3 */ +int +fmt_movr(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + + bprintf(dhp, "%s, ", reg_names[f->f3d.rs1]); + + if (f->f3d.i == 1) + prt_imm(dhp, sign_extend(f->f3d.simm10, 10), IMM_SIGNED); + else + (void) strlcat(dhp->dh_buf, reg_names[f->f3.rs2], + dhp->dh_buflen); + + bprintf(dhp, ", %s", reg_names[f->f3.rd]); + + return (0); +} + +/* ARGSUSED3 */ +int +fmt_fpop1(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + int flags = inp->in_data.in_def.in_flags; + + flags |= FLG_NOIMM; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3.op, 2); + prt_field("op3", f->f3.op3, 6); + prt_field("opf", f->fcmp.opf, 9); + prt_field("rs1", f->f3.rs1, 5); + prt_field("rs2", f->f3.rs2, 5); + prt_field("rd", f->f3.rd, 5); + } + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + prt_aluargs(dhp, instr, flags); + + return (0); +} + +int +fmt_fpop2(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + static const char *condstr_icc[16] = { + "n", "e", "le", "l", "leu", "lu", "neg", "vs", + "a", "nz", "g", "ge", "gu", "geu", "pos", "vc" + }; + + static const char *condstr_fcc[16] = { + "n", "nz", "lg", "ul", "l", "ug", "g", "u", + "a", "e", "ue", "ge", "uge", "le", "ule", "o" + }; + + ifmt_t *f = (ifmt_t *)&instr; + const char *ccstr = ""; + char name[15]; + + int flags = inp->in_data.in_def.in_flags; + int is_cmp = (idx == 0x51 || idx == 0x52 || idx == 0x53 || + idx == 0x55 || idx == 0x56 || idx == 0x57); + int is_fmov = (idx & 0x3f); + int is_v9 = ((dhp->dh_flags & (DIS_SPARC_V9|DIS_SPARC_V9_SGI)) != 0); + int is_compat = ((dhp->dh_debug & DIS_DEBUG_COMPAT) != 0); + + int p_cc = 0; + + is_fmov = (is_fmov == 0x1 || is_fmov == 0x2 || is_fmov == 0x3); + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3.op, 2); + prt_field("op3", f->f3.op3, 6); + prt_field("opf", f->fcmp.opf, 9); + + switch (idx & 0x3f) { + case 0x51: + case 0x52: + case 0x53: + case 0x55: + case 0x56: + case 0x57: + prt_field("cc", f->fcmp.cc, 2); + prt_field("rs1", f->f3.rs1, 5); + prt_field("rs2", f->f3.rs2, 5); + break; + + case 0x01: + case 0x02: + case 0x03: + prt_field("opf_low", f->fmv.opf, 6); + prt_field("cond", f->fmv.cond, 4); + prt_field("opf_cc", f->fmv.cc, 3); + prt_field("rs2", f->fmv.rs2, 5); + break; + + default: + prt_field("rs1", f->f3.rs1, 5); + prt_field("rs2", f->f3.rs2, 5); + prt_field("rd", f->f3.rd, 5); + } + } + + name[0] = '\0'; + (void) strlcat(name, inp->in_data.in_def.in_name, sizeof (name)); + + if (is_fmov != 0) { + (void) strlcat(name, + (f->fmv.cc < 4) ? condstr_fcc[f->fmv.cond] + : condstr_icc[f->fmv.cond], + sizeof (name)); + } + + prt_name(dhp, name, 1); + + if (is_cmp != 0) + ccstr = fcc_names[f->fcmp.cc]; + + if (is_fmov != 0) + ccstr = (f->fmv.cc < 4) ? fcc_names[f->fmv.cc & 0x3] + : icc_names[f->fmv.cc & 0x3]; + + if (ccstr == NULL) + return (-1); + + p_cc = (is_compat == 0 || is_v9 != 0 || + (is_cmp != 0 && f->fcmp.cc != 0) || + (is_fmov != 0 && f->fmv.cc != 0)); + + if (p_cc != 0) + bprintf(dhp, "%s, ", ccstr); + + prt_aluargs(dhp, instr, flags); + + return (0); +} + +int +fmt_vis(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + int flags = inp->in_data.in_def.in_flags; + + if ((dhp->dh_debug & DIS_DEBUG_PRTFMT) != 0) { + prt_field("op", f->f3.op, 2); + prt_field("op3", f->f3.op3, 6); + prt_field("opf", f->fcmp.opf, 9); + + if (idx == 0x081) { + prt_field("mode", instr & 02L, 2); + } else { + prt_field("rs1", f->f3.rs1, 5); + prt_field("rs2", f->f3.rs2, 5); + prt_field("rd", f->f3.rd, 5); + } + } + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + + if (idx == 0x081) { + /* siam */ + bprintf(dhp, "%d", instr & 0x7L); + return (0); + } + + prt_aluargs(dhp, instr, flags); + + return (0); +} + +/* ARGSUSED3 */ +int +fmt_fused(dis_handle_t *dhp, uint32_t instr, const inst_t *inp, int idx) +{ + ifmt_t *f = (ifmt_t *)&instr; + int flags = inp->in_data.in_def.in_flags; + + prt_name(dhp, inp->in_data.in_def.in_name, 1); + bprintf(dhp, "%s, %s, %s, %s", + get_regname(dhp, FLG_P1_VAL(flags), f->fused.rs1), + get_regname(dhp, FLG_P1_VAL(flags), f->fused.rs2), + get_regname(dhp, FLG_P1_VAL(flags), f->fused.rs3), + get_regname(dhp, FLG_P1_VAL(flags), f->fused.rd)); + + return (0); +} +/* + * put name into the output buffer + * if add_space !=0, append a space after it + */ +static void +prt_name(dis_handle_t *dhp, const char *name, int add_space) +{ + bprintf(dhp, (add_space == 0) ? "%s" : "%-9s ", name); +} + +/* + * For debugging, print out a field of the instruction + * field is the name of the field + * val is the value of the field + * len is the length of the field (in bits) + */ +#if defined(DIS_STANDALONE) +/* ARGSUSED */ +static void +prt_field(const char *field, uint32_t val, int len) +{ + +} + +#else +static void +prt_field(const char *field, uint32_t val, int len) +{ + (void) fprintf(stderr, "DISASM: %8s = 0x%-8ulx (", field, val); + prt_binary(val, len); + (void) fprintf(stderr, ")\n"); +} +#endif /* DIS_STANDALONE */ + +/* + * sign extend a val (that is 'bits' bits in length) to a 32-bit signed + * integer + */ +static int32_t +sign_extend(int32_t val, int32_t bits) +{ + if ((val & (1L << (bits - 1))) == 0) + return (val); + + return ((-1L << bits) | val); +} + +/* + * print out an immediate (i.e. constant) value + * val is the value + * format indicates if it is: + * 0 Unsigned + * IMM_SIGNED A signed value (prepend +/- to the value) + * IMM_ADDR Part of an address expression (prepend +/- but with a space + * between the sign and the value for things like [%i1 + 0x55] + */ +static void +prt_imm(dis_handle_t *dhp, uint32_t val, int format) +{ + const char *fmtstr = NULL; + int32_t sv = (int32_t)val; + int octal = dhp->dh_flags & DIS_OCTAL; + + switch (format) { + case IMM_ADDR: + if (sv < 0) { + sv = -sv; + fmtstr = (octal != 0) ? "- 0%lo" : "- 0x%lx"; + } else { + fmtstr = (octal != 0) ? "+ 0%lo" : "+ 0x%lx"; + } + break; + + case IMM_SIGNED: + if (sv < 0) { + sv = -sv; + fmtstr = (octal != 0) ? "-0%lo" : "-0x%lx"; + break; + } + /* fall through */ + + default: + fmtstr = (octal != 0) ? "0%lo" : "0x%lx"; + } + + bprintf(dhp, fmtstr, sv); +} + +/* + * return the symbolic name of a register + * regset is one of the REG_* values indicating which type of register it is + * such as integer, floating point, etc. + * idx is the numeric value of the register + * + * If regset is REG_NONE, an empty, but non-NULL string is returned + * NULL may be returned if the index indicates an invalid register value + * such as with the %icc/%xcc sets + */ +static const char * +get_regname(dis_handle_t *dhp, int regset, uint32_t idx) +{ + const char *regname = NULL; + + switch (regset) { + case REG_INT: + regname = reg_names[idx]; + break; + + case REG_FP: + regname = freg_names[idx]; + break; + + case REG_FPD: + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + regname = fdreg_names[idx]; + else + regname = compat_fdreg_names[idx]; + + break; + + case REG_FPQ: + if ((dhp->dh_debug & DIS_DEBUG_COMPAT) == 0) + regname = fqreg_names[idx]; + else + regname = freg_names[idx]; + + break; + + case REG_CP: + regname = cpreg_names[idx]; + break; + + case REG_ICC: + regname = icc_names[idx]; + break; + + case REG_FCC: + regname = fcc_names[idx]; + break; + + case REG_FSR: + regname = "%fsr"; + break; + + case REG_CSR: + regname = "%csr"; + break; + + case REG_CQ: + regname = "%cq"; + break; + + case REG_NONE: + regname = ""; + break; + } + + return (regname); +} + +/* + * output the asi value from the instruction + * + * TODO: investigate if this should perhaps have a mask -- are undefined ASI + * values for an instruction still disassembled?? + */ +static void +prt_asi(dis_handle_t *dhp, uint32_t instr) +{ + ifmt_t *f = (ifmt_t *)&instr; + int octal = ((dhp->dh_flags & DIS_OCTAL) != 0); + + if (f->f3.i != 0) + bprintf(dhp, "%%asi"); + else + bprintf(dhp, (octal != 0) ? "0%03o" : "0x%02x", f->f3.asi); + +} + +/* + * put an address expression into the output buffer + * + * instr is the instruction to use + * if nobrackets != 0, [] are not added around the instruction + * + * Currently this option is set when printing out the address portion + * of a jmpl instruction, but otherwise 0 for load/stores + * + * If no debug flags are set, the full expression is output, even when + * %g0 or 0x0 appears in the address + * + * If DIS_DEBUG_SYN_ALL or DIS_DEBUG_COMPAT are set, when %g0 or 0x0 + * appear in the address, they are not output. If the wierd (and probably + * shouldn't happen) address of [%g0 + %g0] or [%g0 + 0x0] is encountered, + * [%g0] is output + */ +static void +prt_address(dis_handle_t *dhp, uint32_t instr, int nobrackets) +{ + ifmt_t *f = (ifmt_t *)&instr; + int32_t simm13; + int octal = ((dhp->dh_flags & DIS_OCTAL) != 0); + int p1 = ((dhp->dh_debug & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) == 0); + int p2 = ((dhp->dh_debug & (DIS_DEBUG_COMPAT|DIS_DEBUG_SYN_ALL)) == 0); + + if (f->f3a.i == 0) { + p1 |= ((f->f3a.rs1 != 0) || f->f3.rs2 == 0); + p2 |= (f->f3.rs2 != 0); + + bprintf(dhp, "%s%s%s%s%s", + (nobrackets == 0) ? "[" : "", + (p1 != 0) ? reg_names[f->f3a.rs1] : "", + (p1 != 0 && p2 != 0) ? " + " : "", + (p2 != 0) ? reg_names[f->f3.rs2] : "", + (nobrackets == 0) ? "]" : ""); + } else { + const char *sign; + + simm13 = sign_extend(f->f3a.simm13, 13); + sign = (simm13 < 0) ? "-" : "+"; + + p1 |= (f->f3a.rs1 != 0); + p2 |= (p1 == 0 || simm13 != 0); + + if (p1 == 0 && simm13 == 0) + p2 = 1; + + if (p1 == 0 && simm13 >= 0) + sign = ""; + + if (p2 != 0) + bprintf(dhp, + (octal != 0) ? "%s%s%s%s%s0%lo%s" : + "%s%s%s%s%s0x%lx%s", + (nobrackets == 0) ? "[" : "", + (p1 != 0) ? reg_names[f->f3a.rs1] : "", + (p1 != 0) ? " " : "", + sign, + (p1 != 0) ? " " : "", + (simm13 < 0) ? -(simm13) : simm13, + (nobrackets == 0) ? "]" : ""); + else + bprintf(dhp, "%s%s%s", + (nobrackets == 0) ? "[" : "", + reg_names[f->f3a.rs1], + (nobrackets == 0) ? "]" : ""); + } +} + +/* + * print out the arguments to an alu operation (add, sub, etc.) + * conatined in 'instr' + * + * alu instructions have the following format: + * %rs1, %rs2, %rd (i == 0) + * %rs1, 0xnnn, %rd (i == 1) + * ^ ^ ^ + * | | | + * p1 p2 p3 + * + * flags indicates the register set to use for each position (p1, p2, p3) + * as well as if immediate values (i == 1) are allowed + * + * if flags indicates a specific position has REG_NONE set as it's register + * set, it is omitted from the output. This is primarly used for certain + * floating point operations + */ +static void +prt_aluargs(dis_handle_t *dhp, uint32_t instr, uint32_t flags) +{ + ifmt_t *f = (ifmt_t *)&instr; + const char *r1, *r2, *r3; + int p1, p2, p3; + + r1 = get_regname(dhp, FLG_P1_VAL(flags), f->f3.rs1); + r2 = get_regname(dhp, FLG_P2_VAL(flags), f->f3.rs2); + r3 = get_regname(dhp, FLG_P3_VAL(flags), f->f3.rd); + + p1 = (FLG_P1_VAL(flags) != REG_NONE); + p2 = (((flags & FLG_NOIMM) == 0) || (FLG_P2_VAL(flags) != REG_NONE)); + p3 = (FLG_RD_VAL(flags) != REG_NONE); + + if (r1 == NULL || r1[0] == '\0') + p1 = 0; + + if (f->f3a.i == 0 && (r2 == NULL || r2[0] == '\0')) + p2 = 0; + + if (r3 == NULL || r3[0] == '\0') + p3 = 0; + + if (p1 != 0) { + (void) strlcat(dhp->dh_buf, r1, dhp->dh_buflen); + if (p2 != 0 || p3 != 0) + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + } + + if (p2 != 0) { + if (f->f3.i == 0 || ((flags & FLG_NOIMM) != 0)) + (void) strlcat(dhp->dh_buf, r2, dhp->dh_buflen); + else + prt_imm(dhp, sign_extend(f->f3a.simm13, 13), + IMM_SIGNED); + + if (p3 != 0) + (void) strlcat(dhp->dh_buf, ", ", dhp->dh_buflen); + } + + if (p3 != 0) + (void) strlcat(dhp->dh_buf, r3, dhp->dh_buflen); +} + +/* + * just a handy function that takes care of managing the buffer length + * w/ printf + */ + +/* + * PRINTF LIKE 1 + */ +static void +bprintf(dis_handle_t *dhp, const char *fmt, ...) +{ + size_t curlen; + va_list ap; + + curlen = strlen(dhp->dh_buf); + + va_start(ap, fmt); + (void) vsnprintf(dhp->dh_buf + curlen, dhp->dh_buflen - curlen, fmt, + ap); + va_end(ap); +} |
