#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.26 2025/03/22 10:37:19 hans Exp $");
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
#include <ddb/db_interface.h>
#include <ddb/db_output.h>
#include <ddb/db_command.h>
#include <vax/vax/db_disasm.h>
#ifdef VMS_MODE
#define DEFERRED '@'
#define LITERAL '#'
#else
#define DEFERRED '*'
#define LITERAL '$'
#endif
#ifdef BROKEN_DB_REGS
const struct {
const char *name;
void *valuep;
} my_db_regs[16] = {
{ "r0", NULL },
{ "r1", NULL },
{ "r2", NULL },
{ "r3", NULL },
{ "r4", NULL },
{ "r5", NULL },
{ "r6", NULL },
{ "r7", NULL },
{ "r8", NULL },
{ "r9", NULL },
{ "r10", NULL },
{ "r11", NULL },
{ "ap", NULL },
{ "fp", NULL },
{ "sp", NULL },
{ "pc", NULL },
};
#else
#define my_db_regs db_regs
#endif
typedef struct {
char dasm[256];
char *curp;
char *ppc;
int opc;
const char *argp;
int itype;
int atype;
int off;
int addr;
} inst_buffer;
#define ITYPE_INVALID -1
#define ITYPE_UNSPEC 0
#define ITYPE_BRANCH 1
#define ITYPE_CALL 2
static inline int get_byte(inst_buffer * ib);
static inline int get_word(inst_buffer * ib);
static inline int get_long(inst_buffer * ib);
static int get_opcode(inst_buffer * ib);
static int get_operands(inst_buffer * ib);
static int get_operand(inst_buffer * ib, int size);
static inline void add_char(inst_buffer * ib, char c);
static inline void add_str(inst_buffer * ib, const char *s);
static void add_int(inst_buffer * ib, int i);
static void add_xint(inst_buffer * ib, int i);
static void add_sym(inst_buffer * ib, int i);
static void add_off(inst_buffer * ib, int i);
#define err_print printf
db_addr_t
db_disasm(db_addr_t loc, bool altfmt)
{
db_expr_t diff;
db_sym_t sym;
const char *symname;
inst_buffer ib;
if (!db_validate_address(loc)) {
db_printf("location 0x%lx inaccessible\n", loc);
db_error(NULL);
}
memset(&ib, 0, sizeof(ib));
ib.ppc = (void *) loc;
ib.curp = ib.dasm;
if (!altfmt) {
diff = INT_MAX;
symname = NULL;
sym = db_search_symbol(loc, DB_STGY_PROC, &diff);
db_symbol_values(sym, &symname, 0);
if (symname && !diff) {
db_printf("function \"%s()\", entry-mask 0x%x\n",
symname, (unsigned short) get_word(&ib));
return ((u_int) ib.ppc);
}
}
get_opcode(&ib);
get_operands(&ib);
db_printf("%s\n", ib.dasm);
return ((u_int) ib.ppc);
}
int
get_opcode(inst_buffer *ib)
{
ib->opc = get_byte(ib);
if (ib->opc >> 2 == 0x3F) {
ib->opc = ib->opc << 8;
ib->opc += get_byte(ib);
}
switch (ib->opc) {
case 0xFA:
case 0xFB:
case 0xFC:
ib->itype = ITYPE_CALL;
break;
case 0x16:
case 0x17:
ib->itype = ITYPE_BRANCH;
break;
default:
ib->itype = ITYPE_UNSPEC;
}
if (ib->opc < 0 || ib->opc > 0xFF) {
add_str(ib, "invalid or two-byte opcode ");
add_xint(ib, ib->opc);
ib->itype = ITYPE_INVALID;
} else {
add_str(ib, vax_inst[ib->opc].mnemonic);
add_char(ib, '\t');
}
return (ib->opc);
}
int
get_operands(inst_buffer *ib)
{
int aa = 0;
int size;
if (ib->opc < 0 || ib->opc > 0xFF) {
ib->argp = NULL;
return (-1);
}
ib->argp = vax_inst[ib->opc].argdesc;
if (ib->argp == NULL)
return 0;
while (*ib->argp) {
switch (*ib->argp) {
case 'b':
switch (*(++ib->argp)) {
case 'b':
ib->off = (signed char) get_byte(ib);
break;
case 'w':
ib->off = (short) get_word(ib);
break;
case 'l':
ib->off = get_long(ib);
break;
default:
err_print("XXX error\n");
}
ib->addr = (u_int) ib->ppc + ib->off;
add_off(ib, ib->addr);
break;
case 'a':
aa = 1;
default:
switch (*(++ib->argp)) {
case 'b':
size = SIZE_BYTE;
break;
case 'w':
size = SIZE_WORD;
break;
case 'l':
case 'f':
size = SIZE_LONG;
break;
case 'q':
case 'd':
case 'g':
size = SIZE_QWORD;
break;
case 'o':
case 'h':
size = SIZE_OWORD;
break;
default:
err_print("invalid op-type %X (%c) found.\n",
*ib->argp, *ib->argp);
size = 0;
}
if (aa) {
ib->addr = get_operand(ib, size);
add_sym(ib, ib->addr);
} else {
ib->addr = get_operand(ib, size);
add_off(ib, ib->addr);
}
}
if (!*ib->argp || !*++ib->argp)
break;
if (*ib->argp++ == ',') {
add_char(ib, ',');
add_char(ib, ' ');
} else {
err_print("XXX error\n");
add_char(ib, '\0');
return (-1);
}
}
add_char(ib, '\0');
return (0);
}
int
get_operand(inst_buffer *ib, int size)
{
int c = get_byte(ib);
int mode = c >> 4;
int reg = c & 0x0F;
int lit = c & 0x3F;
int tmp = 0;
char buf[16];
switch (mode) {
case 0:
case 1:
case 2:
case 3:
add_char(ib, LITERAL);
add_int(ib, lit);
tmp = lit;
break;
case 4:
snprintf(buf, sizeof(buf), "[%s]", my_db_regs[reg].name);
get_operand(ib, 0);
add_str(ib, buf);
break;
case 5:
add_str(ib, my_db_regs[reg].name);
break;
case 6:
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
break;
case 7:
add_char(ib, '-');
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
if (reg == 0x0F) {
err_print("autodecrement not allowd for PC.\n");
}
break;
case 9:
add_char(ib, DEFERRED);
if (reg == 0x0F) {
tmp = get_long(ib);
add_off(ib, tmp);
break;
}
case 8:
if (reg == 0x0F) {
switch (size) {
case SIZE_BYTE:
tmp = (signed char) get_byte(ib);
break;
case SIZE_WORD:
tmp = (signed short) get_word(ib);
break;
case SIZE_LONG:
tmp = get_long(ib);
break;
default:
err_print("illegal op-type %d\n", size);
tmp = -1;
}
if (mode == 8)
add_char(ib, LITERAL);
add_int(ib, tmp);
break;
}
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
add_char(ib, '+');
break;
case 11:
add_char(ib, DEFERRED);
case 10:
tmp = (signed char) get_byte(ib);
if (reg == 0x0F) {
add_off(ib, (u_int) ib->ppc + tmp);
break;
}
add_int(ib, tmp);
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
break;
case 13:
add_char(ib, DEFERRED);
case 12:
tmp = (signed short) get_word(ib);
if (reg == 0x0F) {
add_off(ib, (u_int) ib->ppc + tmp);
break;
}
add_int(ib, tmp);
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
break;
case 15:
add_char(ib, DEFERRED);
case 14:
tmp = get_long(ib);
if (reg == 0x0F) {
add_off(ib, (u_int) ib->ppc + tmp);
break;
}
add_int(ib, tmp);
add_char(ib, '(');
add_str(ib, my_db_regs[reg].name);
add_char(ib, ')');
break;
default:
err_print("can\'t evaluate operand (%02X).\n", lit);
break;
}
return (0);
}
int
get_byte(inst_buffer *ib)
{
return ((unsigned char) *(ib->ppc++));
}
int
get_word(inst_buffer *ib)
{
int tmp = *(uint16_t *)ib->ppc;
ib->ppc += 2;
return tmp;
}
int
get_long(inst_buffer *ib)
{
int tmp = *(int *)ib->ppc;
ib->ppc += 4;
return (tmp);
}
void
add_char(inst_buffer *ib, char c)
{
*ib->curp++ = c;
}
void
add_str(inst_buffer *ib, const char *s)
{
while ((*ib->curp++ = *s++));
--ib->curp;
}
void
add_int(inst_buffer *ib, int i)
{
char buf[32];
if (i < 100 && i > -100)
snprintf(buf, sizeof(buf), "%d", i);
else
snprintf(buf, sizeof(buf), "0x%x", i);
add_str(ib, buf);
}
void
add_xint(inst_buffer *ib, int val)
{
char buf[32];
snprintf(buf, sizeof(buf), "0x%x", val);
add_str(ib, buf);
}
void
add_sym(inst_buffer *ib, int loc)
{
db_expr_t diff;
db_sym_t sym;
const char *symname;
if (!loc)
return;
diff = INT_MAX;
symname = NULL;
sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
db_symbol_values(sym, &symname, 0);
if (symname && !diff) {
add_str(ib, symname);
} else
add_xint(ib, loc);
}
void
add_off(inst_buffer *ib, int loc)
{
db_expr_t diff;
db_sym_t sym;
const char *symname;
if (!loc)
return;
diff = INT_MAX;
symname = NULL;
sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
db_symbol_values(sym, &symname, 0);
if (symname) {
add_str(ib, symname);
if (diff) {
add_char(ib, '+');
add_xint(ib, diff);
}
} else
add_xint(ib, loc);
}