librz/arch/vax: add new LGPL DEC VAX-11 disassembler and analysis plugin
Add a new LGPL-3.0 VAX-11 architecture plugin that replaces the removed binutils-derived GPL one. It is written from scratch from the documented VAX operand-specifier encoding and does not reuse any GPL code.
This commit is contained in:
parent
423104df4a
commit
a2454fec15
10 changed files with 2119 additions and 5 deletions
196
librz/arch/isa/vax/vax.h
Normal file
196
librz/arch/isa/vax/vax.h
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file vax.h
|
||||
* Clean-room VAX-11 disassembler core.
|
||||
*
|
||||
* This decoder is written from the publicly documented VAX instruction
|
||||
* encoding (1-or-2 byte opcode followed by uniform operand specifiers) and
|
||||
* does not derive from any GPL-licensed VAX disassembler.
|
||||
*/
|
||||
|
||||
#ifndef RZ_VAX_H
|
||||
#define RZ_VAX_H
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util/rz_strbuf.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Maximum number of explicit operands a single VAX instruction can carry. */
|
||||
#define VAX_MAX_OPS 6
|
||||
/** Safe upper bound used by the analysis layer for non-CASE instructions. */
|
||||
#define VAX_MAX_OP_SIZE 56
|
||||
|
||||
/** General-purpose register indices (PC-relative addressing keys off PC). */
|
||||
enum {
|
||||
VAX_REG_AP = 12, ///< argument pointer
|
||||
VAX_REG_FP = 13, ///< frame pointer
|
||||
VAX_REG_SP = 14, ///< stack pointer
|
||||
VAX_REG_PC = 15, ///< program counter
|
||||
};
|
||||
|
||||
/**
|
||||
* Named opcodes used by the control-flow and data classification logic.
|
||||
* Only the opcodes that are referred to by name elsewhere are listed; the
|
||||
* disassembler itself indexes a full 256-entry table and does not need them.
|
||||
* The four float opcodes mark the bounds of the contiguous F_ and D_ ranges.
|
||||
*/
|
||||
typedef enum {
|
||||
VAX_OP_HALT = 0x00,
|
||||
VAX_OP_NOP = 0x01,
|
||||
VAX_OP_REI = 0x02,
|
||||
VAX_OP_BPT = 0x03,
|
||||
VAX_OP_RET = 0x04,
|
||||
VAX_OP_RSB = 0x05,
|
||||
VAX_OP_LDPCTX = 0x06,
|
||||
VAX_OP_SVPCTX = 0x07,
|
||||
VAX_OP_PROBER = 0x0c,
|
||||
VAX_OP_PROBEW = 0x0d,
|
||||
VAX_OP_BSBB = 0x10,
|
||||
VAX_OP_BRB = 0x11,
|
||||
VAX_OP_BNEQ = 0x12,
|
||||
VAX_OP_BEQL = 0x13,
|
||||
VAX_OP_BGTR = 0x14,
|
||||
VAX_OP_BLEQ = 0x15,
|
||||
VAX_OP_JSB = 0x16,
|
||||
VAX_OP_JMP = 0x17,
|
||||
VAX_OP_BGEQ = 0x18,
|
||||
VAX_OP_BLSS = 0x19,
|
||||
VAX_OP_BGTRU = 0x1a,
|
||||
VAX_OP_BLEQU = 0x1b,
|
||||
VAX_OP_BVC = 0x1c,
|
||||
VAX_OP_BVS = 0x1d,
|
||||
VAX_OP_BGEQU = 0x1e,
|
||||
VAX_OP_BLSSU = 0x1f,
|
||||
VAX_OP_BSBW = 0x30,
|
||||
VAX_OP_BRW = 0x31,
|
||||
VAX_OP_ACBW = 0x3d,
|
||||
VAX_OP_PUSHAW = 0x3f,
|
||||
VAX_OP_ADDF2 = 0x40, ///< first F_floating opcode
|
||||
VAX_OP_ACBF = 0x4f,
|
||||
VAX_OP_CVTFD = 0x56, ///< last F_floating opcode
|
||||
VAX_OP_ADDD2 = 0x60, ///< first D_floating opcode
|
||||
VAX_OP_ACBD = 0x6f,
|
||||
VAX_OP_CVTDF = 0x76, ///< last D_floating opcode
|
||||
VAX_OP_PUSHAQ = 0x7f,
|
||||
VAX_OP_CASEB = 0x8f,
|
||||
VAX_OP_ACBB = 0x9d,
|
||||
VAX_OP_PUSHAB = 0x9f,
|
||||
VAX_OP_CASEW = 0xaf,
|
||||
VAX_OP_CHMK = 0xbc,
|
||||
VAX_OP_CHME = 0xbd,
|
||||
VAX_OP_CHMS = 0xbe,
|
||||
VAX_OP_CHMU = 0xbf,
|
||||
VAX_OP_CASEL = 0xcf,
|
||||
VAX_OP_MTPR = 0xda,
|
||||
VAX_OP_MFPR = 0xdb,
|
||||
VAX_OP_PUSHL = 0xdd,
|
||||
VAX_OP_PUSHAL = 0xdf,
|
||||
VAX_OP_BBS = 0xe0,
|
||||
VAX_OP_BBC = 0xe1,
|
||||
VAX_OP_BBSS = 0xe2,
|
||||
VAX_OP_BBCS = 0xe3,
|
||||
VAX_OP_BBSC = 0xe4,
|
||||
VAX_OP_BBCC = 0xe5,
|
||||
VAX_OP_BBSSI = 0xe6,
|
||||
VAX_OP_BBCCI = 0xe7,
|
||||
VAX_OP_BLBS = 0xe8,
|
||||
VAX_OP_BLBC = 0xe9,
|
||||
VAX_OP_ACBL = 0xf1,
|
||||
VAX_OP_AOBLSS = 0xf2,
|
||||
VAX_OP_AOBLEQ = 0xf3,
|
||||
VAX_OP_SOBGEQ = 0xf4,
|
||||
VAX_OP_SOBGTR = 0xf5,
|
||||
VAX_OP_CALLG = 0xfa,
|
||||
VAX_OP_CALLS = 0xfb,
|
||||
VAX_OP_XFC = 0xfc,
|
||||
} VaxOpcode;
|
||||
|
||||
/** Operand access class as defined by the instruction template. */
|
||||
typedef enum {
|
||||
VAX_AC_NONE = 0,
|
||||
VAX_AC_R, ///< read
|
||||
VAX_AC_W, ///< write
|
||||
VAX_AC_M, ///< modify (read + write)
|
||||
VAX_AC_A, ///< address
|
||||
VAX_AC_B, ///< branch displacement (implicit, PC relative)
|
||||
VAX_AC_V, ///< variable bit-field base
|
||||
} VaxAccess;
|
||||
|
||||
/** Operand data type; determines immediate size and autoincrement stride. */
|
||||
typedef enum {
|
||||
VAX_DT_NONE = 0,
|
||||
VAX_DT_B, ///< byte (1)
|
||||
VAX_DT_W, ///< word (2)
|
||||
VAX_DT_L, ///< longword (4)
|
||||
VAX_DT_Q, ///< quadword (8)
|
||||
VAX_DT_O, ///< octaword (16)
|
||||
VAX_DT_F, ///< F_floating (4)
|
||||
VAX_DT_D, ///< D_floating (8)
|
||||
VAX_DT_G, ///< G_floating (8)
|
||||
VAX_DT_H, ///< H_floating (16)
|
||||
} VaxDataType;
|
||||
|
||||
/** Decoded addressing mode of a single operand. */
|
||||
typedef enum {
|
||||
VAX_AM_INVALID = 0,
|
||||
VAX_AM_LITERAL, ///< short literal $const (0..63)
|
||||
VAX_AM_REG, ///< Rn
|
||||
VAX_AM_REGDEF, ///< (Rn)
|
||||
VAX_AM_AUTODEC, ///< -(Rn)
|
||||
VAX_AM_AUTOINC, ///< (Rn)+
|
||||
VAX_AM_AUTOINCDEF, ///< *(Rn)+
|
||||
VAX_AM_BYTEDISP, ///< d(Rn) with byte displacement
|
||||
VAX_AM_BYTEDISPDEF, ///< *d(Rn) with byte displacement
|
||||
VAX_AM_WORDDISP, ///< d(Rn) with word displacement
|
||||
VAX_AM_WORDDISPDEF, ///< *d(Rn) with word displacement
|
||||
VAX_AM_LONGDISP, ///< d(Rn) with longword displacement
|
||||
VAX_AM_LONGDISPDEF, ///< *d(Rn) with longword displacement
|
||||
VAX_AM_IMMEDIATE, ///< $imm (PC autoincrement)
|
||||
VAX_AM_ABSOLUTE, ///< *0xaddr (PC autoincrement deferred)
|
||||
VAX_AM_BYTEREL, ///< PC byte relative
|
||||
VAX_AM_BYTERELDEF, ///< PC byte relative deferred
|
||||
VAX_AM_WORDREL, ///< PC word relative
|
||||
VAX_AM_WORDRELDEF, ///< PC word relative deferred
|
||||
VAX_AM_LONGREL, ///< PC longword relative
|
||||
VAX_AM_LONGRELDEF, ///< PC longword relative deferred
|
||||
VAX_AM_BRANCH, ///< implicit branch displacement target
|
||||
} VaxAddrMode;
|
||||
|
||||
/** One fully decoded operand. */
|
||||
typedef struct {
|
||||
VaxAddrMode mode;
|
||||
VaxAccess access;
|
||||
VaxDataType dt;
|
||||
ut8 reg; ///< primary register (for register/displacement/deferred modes)
|
||||
bool indexed; ///< true when an index prefix [Rx] applies
|
||||
ut8 index_reg; ///< Rx for the index prefix
|
||||
st64 disp; ///< sign-extended displacement / short literal value
|
||||
ut64 imm; ///< raw immediate bit pattern (sized by dt)
|
||||
ut64 target; ///< resolved absolute address for PC-relative / absolute / branch
|
||||
bool has_target; ///< whether \p target carries a meaningful value
|
||||
} VaxOperand;
|
||||
|
||||
/** A decoded VAX instruction. */
|
||||
typedef struct {
|
||||
ut16 opcode; ///< 1-byte opcode, or 0xFDxx / 0xFExx / 0xFFxx for escaped opcodes
|
||||
const char *name; ///< canonical mnemonic, NULL when invalid
|
||||
int size; ///< total instruction length in bytes
|
||||
int n_ops; ///< number of decoded operands
|
||||
VaxOperand ops[VAX_MAX_OPS];
|
||||
} VaxInst;
|
||||
|
||||
RZ_API int rz_vax_decode(RZ_NONNULL VaxInst *inst, RZ_NONNULL const ut8 *buf, int len, ut64 addr);
|
||||
RZ_API void rz_vax_format(RZ_NONNULL const VaxInst *inst, RZ_NONNULL RzStrBuf *sb);
|
||||
RZ_API int rz_vax_dt_size(VaxDataType dt);
|
||||
RZ_API const char *rz_vax_reg_name(int reg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RZ_VAX_H */
|
||||
716
librz/arch/isa/vax/vax_dis.c
Normal file
716
librz/arch/isa/vax/vax_dis.c
Normal file
|
|
@ -0,0 +1,716 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file vax_dis.c
|
||||
* Clean-room VAX-11 instruction decoder and formatter.
|
||||
*
|
||||
* The opcode tables and operand templates below were assembled from the
|
||||
* publicly documented VAX architecture (the uniform "operand specifier"
|
||||
* encoding and the standard one/two byte opcode map). No GPL-licensed VAX
|
||||
* disassembler was consulted while writing this file.
|
||||
*
|
||||
* Operand templates are encoded as a flat string of (access, data-type)
|
||||
* character pairs:
|
||||
* access: r=read w=write m=modify a=address b=branch v=bit-field-base
|
||||
* type: b=byte w=word l=long q=quad o=octa f/d/g/h=floating
|
||||
*/
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
#include "vax/vax.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *ops;
|
||||
} VaxOpInfo;
|
||||
|
||||
static const char *const vax_reg_names[16] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "ap", "fp", "sp", "pc"
|
||||
};
|
||||
|
||||
/* Single-byte opcode map. NULL entries are reserved/undefined. */
|
||||
static const VaxOpInfo vax_op_table[256] = {
|
||||
[0x00] = { "halt", "" },
|
||||
[0x01] = { "nop", "" },
|
||||
[0x02] = { "rei", "" },
|
||||
[0x03] = { "bpt", "" },
|
||||
[0x04] = { "ret", "" },
|
||||
[0x05] = { "rsb", "" },
|
||||
[0x06] = { "ldpctx", "" },
|
||||
[0x07] = { "svpctx", "" },
|
||||
[0x08] = { "cvtps", "rwabrwab" },
|
||||
[0x09] = { "cvtsp", "rwabrwab" },
|
||||
[0x0a] = { "index", "rlrlrlrlrlwl" },
|
||||
[0x0b] = { "crc", "abrlrwab" },
|
||||
[0x0c] = { "prober", "rbrwab" },
|
||||
[0x0d] = { "probew", "rbrwab" },
|
||||
[0x0e] = { "insque", "abab" },
|
||||
[0x0f] = { "remque", "abwl" },
|
||||
[0x10] = { "bsbb", "bb" },
|
||||
[0x11] = { "brb", "bb" },
|
||||
[0x12] = { "bneq", "bb" },
|
||||
[0x13] = { "beql", "bb" },
|
||||
[0x14] = { "bgtr", "bb" },
|
||||
[0x15] = { "bleq", "bb" },
|
||||
[0x16] = { "jsb", "ab" },
|
||||
[0x17] = { "jmp", "ab" },
|
||||
[0x18] = { "bgeq", "bb" },
|
||||
[0x19] = { "blss", "bb" },
|
||||
[0x1a] = { "bgtru", "bb" },
|
||||
[0x1b] = { "blequ", "bb" },
|
||||
[0x1c] = { "bvc", "bb" },
|
||||
[0x1d] = { "bvs", "bb" },
|
||||
[0x1e] = { "bgequ", "bb" },
|
||||
[0x1f] = { "blssu", "bb" },
|
||||
[0x20] = { "addp4", "rwabrwab" },
|
||||
[0x21] = { "addp6", "rwabrwabrwab" },
|
||||
[0x22] = { "subp4", "rwabrwab" },
|
||||
[0x23] = { "subp6", "rwabrwabrwab" },
|
||||
[0x24] = { "cvtpt", "rwababrwab" },
|
||||
[0x25] = { "mulp", "rwabrwabrwab" },
|
||||
[0x26] = { "cvttp", "rwababrwab" },
|
||||
[0x27] = { "divp", "rwabrwabrwab" },
|
||||
[0x28] = { "movc3", "rwabab" },
|
||||
[0x29] = { "cmpc3", "rwabab" },
|
||||
[0x2a] = { "scanc", "rwababrb" },
|
||||
[0x2b] = { "spanc", "rwababrb" },
|
||||
[0x2c] = { "movc5", "rwabrbrwab" },
|
||||
[0x2d] = { "cmpc5", "rwabrbrwab" },
|
||||
[0x2e] = { "movtc", "rwabrbabrwab" },
|
||||
[0x2f] = { "movtuc", "rwabrbabrwab" },
|
||||
[0x30] = { "bsbw", "bw" },
|
||||
[0x31] = { "brw", "bw" },
|
||||
[0x32] = { "cvtwl", "rwwl" },
|
||||
[0x33] = { "cvtwb", "rwwb" },
|
||||
[0x34] = { "movp", "rwabab" },
|
||||
[0x35] = { "cmpp3", "rwabab" },
|
||||
[0x36] = { "cvtpl", "rwabwl" },
|
||||
[0x37] = { "cmpp4", "rwabrwab" },
|
||||
[0x38] = { "editpc", "rwababab" },
|
||||
[0x39] = { "matchc", "rwabrwab" },
|
||||
[0x3a] = { "locc", "rbrwab" },
|
||||
[0x3b] = { "skpc", "rbrwab" },
|
||||
[0x3c] = { "movzwl", "rwwl" },
|
||||
[0x3d] = { "acbw", "rwrwmwbw" },
|
||||
[0x3e] = { "movaw", "awwl" },
|
||||
[0x3f] = { "pushaw", "aw" },
|
||||
[0x40] = { "addf2", "rfmf" },
|
||||
[0x41] = { "addf3", "rfrfwf" },
|
||||
[0x42] = { "subf2", "rfmf" },
|
||||
[0x43] = { "subf3", "rfrfwf" },
|
||||
[0x44] = { "mulf2", "rfmf" },
|
||||
[0x45] = { "mulf3", "rfrfwf" },
|
||||
[0x46] = { "divf2", "rfmf" },
|
||||
[0x47] = { "divf3", "rfrfwf" },
|
||||
[0x48] = { "cvtfb", "rfwb" },
|
||||
[0x49] = { "cvtfw", "rfww" },
|
||||
[0x4a] = { "cvtfl", "rfwl" },
|
||||
[0x4b] = { "cvtrfl", "rfwl" },
|
||||
[0x4c] = { "cvtbf", "rbwf" },
|
||||
[0x4d] = { "cvtwf", "rwwf" },
|
||||
[0x4e] = { "cvtlf", "rlwf" },
|
||||
[0x4f] = { "acbf", "rfrfmfbw" },
|
||||
[0x50] = { "movf", "rfwf" },
|
||||
[0x51] = { "cmpf", "rfrf" },
|
||||
[0x52] = { "mnegf", "rfwf" },
|
||||
[0x53] = { "tstf", "rf" },
|
||||
[0x54] = { "emodf", "rfrbrfwlwf" },
|
||||
[0x55] = { "polyf", "rfrwab" },
|
||||
[0x56] = { "cvtfd", "rfwd" },
|
||||
[0x58] = { "adawi", "rwmw" },
|
||||
[0x5c] = { "insqhi", "abaq" },
|
||||
[0x5d] = { "insqti", "abaq" },
|
||||
[0x5e] = { "remqhi", "aqwl" },
|
||||
[0x5f] = { "remqti", "aqwl" },
|
||||
[0x60] = { "addd2", "rdmd" },
|
||||
[0x61] = { "addd3", "rdrdwd" },
|
||||
[0x62] = { "subd2", "rdmd" },
|
||||
[0x63] = { "subd3", "rdrdwd" },
|
||||
[0x64] = { "muld2", "rdmd" },
|
||||
[0x65] = { "muld3", "rdrdwd" },
|
||||
[0x66] = { "divd2", "rdmd" },
|
||||
[0x67] = { "divd3", "rdrdwd" },
|
||||
[0x68] = { "cvtdb", "rdwb" },
|
||||
[0x69] = { "cvtdw", "rdww" },
|
||||
[0x6a] = { "cvtdl", "rdwl" },
|
||||
[0x6b] = { "cvtrdl", "rdwl" },
|
||||
[0x6c] = { "cvtbd", "rbwd" },
|
||||
[0x6d] = { "cvtwd", "rwwd" },
|
||||
[0x6e] = { "cvtld", "rlwd" },
|
||||
[0x6f] = { "acbd", "rdrdmdbw" },
|
||||
[0x70] = { "movd", "rdwd" },
|
||||
[0x71] = { "cmpd", "rdrd" },
|
||||
[0x72] = { "mnegd", "rdwd" },
|
||||
[0x73] = { "tstd", "rd" },
|
||||
[0x74] = { "emodd", "rdrbrdwlwd" },
|
||||
[0x75] = { "polyd", "rdrwab" },
|
||||
[0x76] = { "cvtdf", "rdwf" },
|
||||
[0x78] = { "ashl", "rbrlwl" },
|
||||
[0x79] = { "ashq", "rbrqwq" },
|
||||
[0x7a] = { "emul", "rlrlrlwq" },
|
||||
[0x7b] = { "ediv", "rlrqwlwl" },
|
||||
[0x7c] = { "clrq", "wq" },
|
||||
[0x7d] = { "movq", "rqwq" },
|
||||
[0x7e] = { "movaq", "aqwl" },
|
||||
[0x7f] = { "pushaq", "aq" },
|
||||
[0x80] = { "addb2", "rbmb" },
|
||||
[0x81] = { "addb3", "rbrbwb" },
|
||||
[0x82] = { "subb2", "rbmb" },
|
||||
[0x83] = { "subb3", "rbrbwb" },
|
||||
[0x84] = { "mulb2", "rbmb" },
|
||||
[0x85] = { "mulb3", "rbrbwb" },
|
||||
[0x86] = { "divb2", "rbmb" },
|
||||
[0x87] = { "divb3", "rbrbwb" },
|
||||
[0x88] = { "bisb2", "rbmb" },
|
||||
[0x89] = { "bisb3", "rbrbwb" },
|
||||
[0x8a] = { "bicb2", "rbmb" },
|
||||
[0x8b] = { "bicb3", "rbrbwb" },
|
||||
[0x8c] = { "xorb2", "rbmb" },
|
||||
[0x8d] = { "xorb3", "rbrbwb" },
|
||||
[0x8e] = { "mnegb", "rbwb" },
|
||||
[0x8f] = { "caseb", "rbrbrb" },
|
||||
[0x90] = { "movb", "rbwb" },
|
||||
[0x91] = { "cmpb", "rbrb" },
|
||||
[0x92] = { "mcomb", "rbwb" },
|
||||
[0x93] = { "bitb", "rbrb" },
|
||||
[0x94] = { "clrb", "wb" },
|
||||
[0x95] = { "tstb", "rb" },
|
||||
[0x96] = { "incb", "mb" },
|
||||
[0x97] = { "decb", "mb" },
|
||||
[0x98] = { "cvtbl", "rbwl" },
|
||||
[0x99] = { "cvtbw", "rbww" },
|
||||
[0x9a] = { "movzbl", "rbwl" },
|
||||
[0x9b] = { "movzbw", "rbww" },
|
||||
[0x9c] = { "rotl", "rbrlwl" },
|
||||
[0x9d] = { "acbb", "rbrbmbbw" },
|
||||
[0x9e] = { "movab", "abwl" },
|
||||
[0x9f] = { "pushab", "ab" },
|
||||
[0xa0] = { "addw2", "rwmw" },
|
||||
[0xa1] = { "addw3", "rwrwww" },
|
||||
[0xa2] = { "subw2", "rwmw" },
|
||||
[0xa3] = { "subw3", "rwrwww" },
|
||||
[0xa4] = { "mulw2", "rwmw" },
|
||||
[0xa5] = { "mulw3", "rwrwww" },
|
||||
[0xa6] = { "divw2", "rwmw" },
|
||||
[0xa7] = { "divw3", "rwrwww" },
|
||||
[0xa8] = { "bisw2", "rwmw" },
|
||||
[0xa9] = { "bisw3", "rwrwww" },
|
||||
[0xaa] = { "bicw2", "rwmw" },
|
||||
[0xab] = { "bicw3", "rwrwww" },
|
||||
[0xac] = { "xorw2", "rwmw" },
|
||||
[0xad] = { "xorw3", "rwrwww" },
|
||||
[0xae] = { "mnegw", "rwww" },
|
||||
[0xaf] = { "casew", "rwrwrw" },
|
||||
[0xb0] = { "movw", "rwww" },
|
||||
[0xb1] = { "cmpw", "rwrw" },
|
||||
[0xb2] = { "mcomw", "rwww" },
|
||||
[0xb3] = { "bitw", "rwrw" },
|
||||
[0xb4] = { "clrw", "ww" },
|
||||
[0xb5] = { "tstw", "rw" },
|
||||
[0xb6] = { "incw", "mw" },
|
||||
[0xb7] = { "decw", "mw" },
|
||||
[0xb8] = { "bispsw", "rw" },
|
||||
[0xb9] = { "bicpsw", "rw" },
|
||||
[0xba] = { "popr", "rw" },
|
||||
[0xbb] = { "pushr", "rw" },
|
||||
[0xbc] = { "chmk", "rw" },
|
||||
[0xbd] = { "chme", "rw" },
|
||||
[0xbe] = { "chms", "rw" },
|
||||
[0xbf] = { "chmu", "rw" },
|
||||
[0xc0] = { "addl2", "rlml" },
|
||||
[0xc1] = { "addl3", "rlrlwl" },
|
||||
[0xc2] = { "subl2", "rlml" },
|
||||
[0xc3] = { "subl3", "rlrlwl" },
|
||||
[0xc4] = { "mull2", "rlml" },
|
||||
[0xc5] = { "mull3", "rlrlwl" },
|
||||
[0xc6] = { "divl2", "rlml" },
|
||||
[0xc7] = { "divl3", "rlrlwl" },
|
||||
[0xc8] = { "bisl2", "rlml" },
|
||||
[0xc9] = { "bisl3", "rlrlwl" },
|
||||
[0xca] = { "bicl2", "rlml" },
|
||||
[0xcb] = { "bicl3", "rlrlwl" },
|
||||
[0xcc] = { "xorl2", "rlml" },
|
||||
[0xcd] = { "xorl3", "rlrlwl" },
|
||||
[0xce] = { "mnegl", "rlwl" },
|
||||
[0xcf] = { "casel", "rlrlrl" },
|
||||
[0xd0] = { "movl", "rlwl" },
|
||||
[0xd1] = { "cmpl", "rlrl" },
|
||||
[0xd2] = { "mcoml", "rlwl" },
|
||||
[0xd3] = { "bitl", "rlrl" },
|
||||
[0xd4] = { "clrl", "wl" },
|
||||
[0xd5] = { "tstl", "rl" },
|
||||
[0xd6] = { "incl", "ml" },
|
||||
[0xd7] = { "decl", "ml" },
|
||||
[0xd8] = { "adwc", "rlml" },
|
||||
[0xd9] = { "sbwc", "rlml" },
|
||||
[0xda] = { "mtpr", "rlrl" },
|
||||
[0xdb] = { "mfpr", "rlwl" },
|
||||
[0xdc] = { "movpsl", "wl" },
|
||||
[0xdd] = { "pushl", "rl" },
|
||||
[0xde] = { "moval", "alwl" },
|
||||
[0xdf] = { "pushal", "al" },
|
||||
[0xe0] = { "bbs", "rlvbbb" },
|
||||
[0xe1] = { "bbc", "rlvbbb" },
|
||||
[0xe2] = { "bbss", "rlvbbb" },
|
||||
[0xe3] = { "bbcs", "rlvbbb" },
|
||||
[0xe4] = { "bbsc", "rlvbbb" },
|
||||
[0xe5] = { "bbcc", "rlvbbb" },
|
||||
[0xe6] = { "bbssi", "rlvbbb" },
|
||||
[0xe7] = { "bbcci", "rlvbbb" },
|
||||
[0xe8] = { "blbs", "rlbb" },
|
||||
[0xe9] = { "blbc", "rlbb" },
|
||||
[0xea] = { "ffs", "rlrbvbwl" },
|
||||
[0xeb] = { "ffc", "rlrbvbwl" },
|
||||
[0xec] = { "cmpv", "rlrbvbrl" },
|
||||
[0xed] = { "cmpzv", "rlrbvbrl" },
|
||||
[0xee] = { "extv", "rlrbvbwl" },
|
||||
[0xef] = { "extzv", "rlrbvbwl" },
|
||||
[0xf0] = { "insv", "rlrlrbvb" },
|
||||
[0xf1] = { "acbl", "rlrlmlbw" },
|
||||
[0xf2] = { "aoblss", "rlmlbb" },
|
||||
[0xf3] = { "aobleq", "rlmlbb" },
|
||||
[0xf4] = { "sobgeq", "mlbb" },
|
||||
[0xf5] = { "sobgtr", "mlbb" },
|
||||
[0xf6] = { "cvtlb", "rlwb" },
|
||||
[0xf7] = { "cvtlw", "rlww" },
|
||||
[0xf8] = { "ashp", "rbrwabrbrwab" },
|
||||
[0xf9] = { "cvtlp", "rlrwab" },
|
||||
[0xfa] = { "callg", "abab" },
|
||||
[0xfb] = { "calls", "rlab" },
|
||||
[0xfc] = { "xfc", "" },
|
||||
};
|
||||
|
||||
/* Two-byte opcodes escaped with the 0xFD prefix (G_ and H_ floating). */
|
||||
static const VaxOpInfo vax_op_fd_table[256] = {
|
||||
[0x40] = { "addg2", "rgmg" },
|
||||
[0x41] = { "addg3", "rgrgwg" },
|
||||
[0x42] = { "subg2", "rgmg" },
|
||||
[0x43] = { "subg3", "rgrgwg" },
|
||||
[0x44] = { "mulg2", "rgmg" },
|
||||
[0x45] = { "mulg3", "rgrgwg" },
|
||||
[0x46] = { "divg2", "rgmg" },
|
||||
[0x47] = { "divg3", "rgrgwg" },
|
||||
[0x48] = { "cvtgb", "rgwb" },
|
||||
[0x49] = { "cvtgw", "rgww" },
|
||||
[0x4a] = { "cvtgl", "rgwl" },
|
||||
[0x4b] = { "cvtrgl", "rgwl" },
|
||||
[0x4c] = { "cvtbg", "rbwg" },
|
||||
[0x4d] = { "cvtwg", "rwwg" },
|
||||
[0x4e] = { "cvtlg", "rlwg" },
|
||||
[0x4f] = { "acbg", "rgrgmgbw" },
|
||||
[0x50] = { "movg", "rgwg" },
|
||||
[0x51] = { "cmpg", "rgrg" },
|
||||
[0x52] = { "mnegg", "rgwg" },
|
||||
[0x53] = { "tstg", "rg" },
|
||||
[0x54] = { "emodg", "rgrwrgwlwg" },
|
||||
[0x55] = { "polyg", "rgrwab" },
|
||||
[0x56] = { "cvtgh", "rgwh" },
|
||||
[0x60] = { "addh2", "rhmh" },
|
||||
[0x61] = { "addh3", "rhrhwh" },
|
||||
[0x62] = { "subh2", "rhmh" },
|
||||
[0x63] = { "subh3", "rhrhwh" },
|
||||
[0x64] = { "mulh2", "rhmh" },
|
||||
[0x65] = { "mulh3", "rhrhwh" },
|
||||
[0x66] = { "divh2", "rhmh" },
|
||||
[0x67] = { "divh3", "rhrhwh" },
|
||||
[0x68] = { "cvthb", "rhwb" },
|
||||
[0x69] = { "cvthw", "rhww" },
|
||||
[0x6a] = { "cvthl", "rhwl" },
|
||||
[0x6b] = { "cvtrhl", "rhwl" },
|
||||
[0x6c] = { "cvtbh", "rbwh" },
|
||||
[0x6d] = { "cvtwh", "rwwh" },
|
||||
[0x6e] = { "cvtlh", "rlwh" },
|
||||
[0x6f] = { "acbh", "rhrhmhbw" },
|
||||
[0x70] = { "movh", "rhwh" },
|
||||
[0x71] = { "cmph", "rhrh" },
|
||||
[0x72] = { "mnegh", "rhwh" },
|
||||
[0x73] = { "tsth", "rh" },
|
||||
[0x74] = { "emodh", "rhrwrhwlwh" },
|
||||
[0x75] = { "polyh", "rhrwab" },
|
||||
[0x76] = { "cvthg", "rhwg" },
|
||||
[0x7c] = { "clro", "wo" },
|
||||
[0x7d] = { "movo", "rowo" },
|
||||
[0x7e] = { "movao", "aowl" },
|
||||
[0x7f] = { "pushao", "ao" },
|
||||
[0x98] = { "cvtfh", "rfwh" },
|
||||
[0x99] = { "cvtfg", "rfwg" },
|
||||
[0xf6] = { "cvthf", "rhwf" },
|
||||
[0xf7] = { "cvthd", "rhwd" },
|
||||
};
|
||||
|
||||
/** \brief Size in bytes of a value of the given VAX data type. */
|
||||
RZ_API int rz_vax_dt_size(VaxDataType dt) {
|
||||
switch (dt) {
|
||||
case VAX_DT_B: return 1;
|
||||
case VAX_DT_W: return 2;
|
||||
case VAX_DT_L: return 4;
|
||||
case VAX_DT_Q: return 8;
|
||||
case VAX_DT_O: return 16;
|
||||
case VAX_DT_F: return 4;
|
||||
case VAX_DT_D: return 8;
|
||||
case VAX_DT_G: return 8;
|
||||
case VAX_DT_H: return 16;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Translate an operand-template access character (r/w/m/a/b/v) into a VaxAccess.
|
||||
static VaxAccess vax_acc_from_char(char c) {
|
||||
switch (c) {
|
||||
case 'r': return VAX_AC_R;
|
||||
case 'w': return VAX_AC_W;
|
||||
case 'm': return VAX_AC_M;
|
||||
case 'a': return VAX_AC_A;
|
||||
case 'b': return VAX_AC_B;
|
||||
case 'v': return VAX_AC_V;
|
||||
default: return VAX_AC_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Translate an operand-template data-type character (b/w/l/q/o/f/d/g/h) into a VaxDataType.
|
||||
static VaxDataType vax_dt_from_char(char c) {
|
||||
switch (c) {
|
||||
case 'b': return VAX_DT_B;
|
||||
case 'w': return VAX_DT_W;
|
||||
case 'l': return VAX_DT_L;
|
||||
case 'q': return VAX_DT_Q;
|
||||
case 'o': return VAX_DT_O;
|
||||
case 'f': return VAX_DT_F;
|
||||
case 'd': return VAX_DT_D;
|
||||
case 'g': return VAX_DT_G;
|
||||
case 'h': return VAX_DT_H;
|
||||
default: return VAX_DT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Read n little-endian bytes at *pos (bounds-checked), advancing the cursor.
|
||||
static bool vax_read_le(const ut8 *buf, int len, int *pos, int n, ut64 *out) {
|
||||
if (n <= 0 || *pos + n > len) {
|
||||
return false;
|
||||
}
|
||||
ut64 v = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
v |= (ut64)buf[*pos + i] << (8 * i);
|
||||
}
|
||||
*pos += n;
|
||||
*out = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sign-extend the low n bytes of v to a 64-bit signed value.
|
||||
static st64 vax_sext(ut64 v, int n) {
|
||||
int bits = n * 8;
|
||||
if (bits >= 64) {
|
||||
return (st64)v;
|
||||
}
|
||||
ut64 m = (ut64)1 << (bits - 1);
|
||||
return (st64)((v ^ m) - m);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a single operand specifier (and any index prefix) at \p pos.
|
||||
* \param o receives the decoded operand
|
||||
* \param buf instruction bytes
|
||||
* \param len number of valid bytes in \p buf
|
||||
* \param pos in/out cursor into \p buf, advanced past the operand
|
||||
* \param addr instruction address, used to resolve PC-relative targets
|
||||
* \param acc access class from the opcode template
|
||||
* \param dt data type from the opcode template (sets immediate width)
|
||||
* \return true on success, false on truncation or an invalid specifier
|
||||
*/
|
||||
static bool vax_decode_operand(VaxOperand *o, const ut8 *buf, int len, int *pos, ut64 addr, VaxAccess acc, VaxDataType dt) {
|
||||
o->access = acc;
|
||||
o->dt = dt;
|
||||
if (*pos >= len) {
|
||||
return false;
|
||||
}
|
||||
ut8 spec = buf[(*pos)++];
|
||||
ut8 m = spec >> 4;
|
||||
ut8 r = spec & 0xf;
|
||||
if (m == 4) {
|
||||
/* index prefix [Rx]; the base specifier follows */
|
||||
o->indexed = true;
|
||||
o->index_reg = r;
|
||||
if (*pos >= len) {
|
||||
return false;
|
||||
}
|
||||
spec = buf[(*pos)++];
|
||||
m = spec >> 4;
|
||||
r = spec & 0xf;
|
||||
}
|
||||
o->reg = r;
|
||||
ut64 v = 0;
|
||||
switch (m) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
o->mode = VAX_AM_LITERAL;
|
||||
o->disp = spec & 0x3f;
|
||||
o->reg = 0;
|
||||
break;
|
||||
case 5:
|
||||
o->mode = VAX_AM_REG;
|
||||
break;
|
||||
case 6:
|
||||
o->mode = VAX_AM_REGDEF;
|
||||
break;
|
||||
case 7:
|
||||
o->mode = VAX_AM_AUTODEC;
|
||||
break;
|
||||
case 8:
|
||||
if (r == VAX_REG_PC) {
|
||||
int sz = rz_vax_dt_size(dt);
|
||||
if (sz < 1) {
|
||||
sz = 4;
|
||||
}
|
||||
if (!vax_read_le(buf, len, pos, sz, &v)) {
|
||||
return false;
|
||||
}
|
||||
o->mode = VAX_AM_IMMEDIATE;
|
||||
o->imm = v;
|
||||
o->disp = vax_sext(v, sz);
|
||||
} else {
|
||||
o->mode = VAX_AM_AUTOINC;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (r == VAX_REG_PC) {
|
||||
if (!vax_read_le(buf, len, pos, 4, &v)) {
|
||||
return false;
|
||||
}
|
||||
o->mode = VAX_AM_ABSOLUTE;
|
||||
o->target = v;
|
||||
o->has_target = true;
|
||||
} else {
|
||||
o->mode = VAX_AM_AUTOINCDEF;
|
||||
}
|
||||
break;
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
if (!vax_read_le(buf, len, pos, 1, &v)) {
|
||||
return false;
|
||||
}
|
||||
o->disp = vax_sext(v, 1);
|
||||
if (r == VAX_REG_PC) {
|
||||
o->mode = (m == 0xa) ? VAX_AM_BYTEREL : VAX_AM_BYTERELDEF;
|
||||
o->target = addr + (ut64)*pos + o->disp;
|
||||
o->has_target = true;
|
||||
} else {
|
||||
o->mode = (m == 0xa) ? VAX_AM_BYTEDISP : VAX_AM_BYTEDISPDEF;
|
||||
}
|
||||
break;
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
if (!vax_read_le(buf, len, pos, 2, &v)) {
|
||||
return false;
|
||||
}
|
||||
o->disp = vax_sext(v, 2);
|
||||
if (r == VAX_REG_PC) {
|
||||
o->mode = (m == 0xc) ? VAX_AM_WORDREL : VAX_AM_WORDRELDEF;
|
||||
o->target = addr + (ut64)*pos + o->disp;
|
||||
o->has_target = true;
|
||||
} else {
|
||||
o->mode = (m == 0xc) ? VAX_AM_WORDDISP : VAX_AM_WORDDISPDEF;
|
||||
}
|
||||
break;
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
if (!vax_read_le(buf, len, pos, 4, &v)) {
|
||||
return false;
|
||||
}
|
||||
o->disp = vax_sext(v, 4);
|
||||
if (r == VAX_REG_PC) {
|
||||
o->mode = (m == 0xe) ? VAX_AM_LONGREL : VAX_AM_LONGRELDEF;
|
||||
o->target = addr + (ut64)*pos + o->disp;
|
||||
o->has_target = true;
|
||||
} else {
|
||||
o->mode = (m == 0xe) ? VAX_AM_LONGDISP : VAX_AM_LONGDISPDEF;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
o->mode = VAX_AM_INVALID;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decode a single VAX instruction.
|
||||
* \param inst output, zero-initialized by the function
|
||||
* \param buf instruction bytes
|
||||
* \param len number of valid bytes in \p buf
|
||||
* \param addr virtual address of the instruction (for PC-relative resolution)
|
||||
* \return the instruction length in bytes, or a value <= 0 on failure
|
||||
*
|
||||
* Handles the 0xFD escape page, immediate sizing, index prefixes, PC-relative
|
||||
* target resolution and consumption of the inline CASE displacement table.
|
||||
*/
|
||||
RZ_API int rz_vax_decode(RZ_NONNULL VaxInst *inst, RZ_NONNULL const ut8 *buf, int len, ut64 addr) {
|
||||
rz_return_val_if_fail(inst && buf, -1);
|
||||
memset(inst, 0, sizeof(*inst));
|
||||
if (len < 1) {
|
||||
return -1;
|
||||
}
|
||||
int pos = 0;
|
||||
ut8 b0 = buf[pos++];
|
||||
const VaxOpInfo *info;
|
||||
if (b0 == 0xfd) {
|
||||
if (pos >= len) {
|
||||
inst->size = pos;
|
||||
return pos;
|
||||
}
|
||||
ut8 b1 = buf[pos++];
|
||||
inst->opcode = 0xfd00 | b1;
|
||||
info = &vax_op_fd_table[b1];
|
||||
} else {
|
||||
inst->opcode = b0;
|
||||
info = &vax_op_table[b0];
|
||||
}
|
||||
if (!info->name) {
|
||||
inst->name = NULL;
|
||||
inst->size = pos;
|
||||
return pos;
|
||||
}
|
||||
inst->name = info->name;
|
||||
const char *p = info->ops;
|
||||
int nop = 0;
|
||||
while (p && p[0] && p[1] && nop < VAX_MAX_OPS) {
|
||||
VaxAccess acc = vax_acc_from_char(p[0]);
|
||||
VaxDataType dt = vax_dt_from_char(p[1]);
|
||||
p += 2;
|
||||
VaxOperand *o = &inst->ops[nop];
|
||||
if (acc == VAX_AC_B) {
|
||||
/* implicit branch displacement (not an operand specifier) */
|
||||
int sz = (dt == VAX_DT_W) ? 2 : 1;
|
||||
ut64 v = 0;
|
||||
if (!vax_read_le(buf, len, &pos, sz, &v)) {
|
||||
break;
|
||||
}
|
||||
o->access = acc;
|
||||
o->dt = dt;
|
||||
o->mode = VAX_AM_BRANCH;
|
||||
o->disp = vax_sext(v, sz);
|
||||
o->target = addr + (ut64)pos + o->disp;
|
||||
o->has_target = true;
|
||||
nop++;
|
||||
continue;
|
||||
}
|
||||
if (!vax_decode_operand(o, buf, len, &pos, addr, acc, dt)) {
|
||||
break;
|
||||
}
|
||||
nop++;
|
||||
}
|
||||
inst->n_ops = nop;
|
||||
/* CASEB/CASEW/CASEL embed a table of (limit + 1) word displacements. */
|
||||
if ((b0 == 0x8f || b0 == 0xaf || b0 == 0xcf) && nop == 3) {
|
||||
st64 limit = -1;
|
||||
const VaxOperand *lim = &inst->ops[2];
|
||||
if (lim->mode == VAX_AM_LITERAL) {
|
||||
limit = lim->disp;
|
||||
} else if (lim->mode == VAX_AM_IMMEDIATE) {
|
||||
limit = (st64)lim->imm;
|
||||
}
|
||||
if (limit >= 0 && limit < 512) {
|
||||
for (st64 i = 0; i <= limit; i++) {
|
||||
ut64 v = 0;
|
||||
if (!vax_read_le(buf, len, &pos, 2, &v)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inst->size = pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Append a signed displacement to sb in 0x-prefixed hex.
|
||||
static void vax_format_disp(RzStrBuf *sb, st64 disp) {
|
||||
if (disp < 0) {
|
||||
rz_strbuf_appendf(sb, "-0x%" PFMT64x, (ut64)(-disp));
|
||||
} else {
|
||||
rz_strbuf_appendf(sb, "0x%" PFMT64x, (ut64)disp);
|
||||
}
|
||||
}
|
||||
|
||||
// Append a single decoded operand to sb in conventional VAX syntax.
|
||||
static void vax_format_operand(RzStrBuf *sb, const VaxOperand *o) {
|
||||
const char *reg = vax_reg_names[o->reg & 0xf];
|
||||
switch (o->mode) {
|
||||
case VAX_AM_LITERAL:
|
||||
rz_strbuf_appendf(sb, "$0x%" PFMT64x, (ut64)(o->disp & 0x3f));
|
||||
break;
|
||||
case VAX_AM_IMMEDIATE:
|
||||
rz_strbuf_appendf(sb, "$0x%" PFMT64x, o->imm);
|
||||
break;
|
||||
case VAX_AM_ABSOLUTE:
|
||||
rz_strbuf_appendf(sb, "*0x%" PFMT64x, o->target);
|
||||
break;
|
||||
case VAX_AM_REG:
|
||||
rz_strbuf_append(sb, reg);
|
||||
break;
|
||||
case VAX_AM_REGDEF:
|
||||
rz_strbuf_appendf(sb, "(%s)", reg);
|
||||
break;
|
||||
case VAX_AM_AUTODEC:
|
||||
rz_strbuf_appendf(sb, "-(%s)", reg);
|
||||
break;
|
||||
case VAX_AM_AUTOINC:
|
||||
rz_strbuf_appendf(sb, "(%s)+", reg);
|
||||
break;
|
||||
case VAX_AM_AUTOINCDEF:
|
||||
rz_strbuf_appendf(sb, "*(%s)+", reg);
|
||||
break;
|
||||
case VAX_AM_BYTEDISP:
|
||||
case VAX_AM_WORDDISP:
|
||||
case VAX_AM_LONGDISP:
|
||||
vax_format_disp(sb, o->disp);
|
||||
rz_strbuf_appendf(sb, "(%s)", reg);
|
||||
break;
|
||||
case VAX_AM_BYTEDISPDEF:
|
||||
case VAX_AM_WORDDISPDEF:
|
||||
case VAX_AM_LONGDISPDEF:
|
||||
rz_strbuf_append(sb, "*");
|
||||
vax_format_disp(sb, o->disp);
|
||||
rz_strbuf_appendf(sb, "(%s)", reg);
|
||||
break;
|
||||
case VAX_AM_BYTEREL:
|
||||
case VAX_AM_WORDREL:
|
||||
case VAX_AM_LONGREL:
|
||||
case VAX_AM_BRANCH:
|
||||
rz_strbuf_appendf(sb, "0x%" PFMT64x, o->target);
|
||||
break;
|
||||
case VAX_AM_BYTERELDEF:
|
||||
case VAX_AM_WORDRELDEF:
|
||||
case VAX_AM_LONGRELDEF:
|
||||
rz_strbuf_appendf(sb, "*0x%" PFMT64x, o->target);
|
||||
break;
|
||||
default:
|
||||
rz_strbuf_append(sb, "?");
|
||||
break;
|
||||
}
|
||||
if (o->indexed) {
|
||||
rz_strbuf_appendf(sb, "[%s]", vax_reg_names[o->index_reg & 0xf]);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Render a decoded instruction into \p sb (set to "invalid" for a NULL mnemonic). */
|
||||
RZ_API void rz_vax_format(RZ_NONNULL const VaxInst *inst, RZ_NONNULL RzStrBuf *sb) {
|
||||
rz_return_if_fail(inst && sb);
|
||||
if (!inst->name) {
|
||||
rz_strbuf_set(sb, "invalid");
|
||||
return;
|
||||
}
|
||||
rz_strbuf_set(sb, inst->name);
|
||||
for (int i = 0; i < inst->n_ops; i++) {
|
||||
rz_strbuf_append(sb, i == 0 ? " " : ",");
|
||||
vax_format_operand(sb, &inst->ops[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Canonical lowercase name of a VAX general register (low 4 bits of \p reg). */
|
||||
RZ_API const char *rz_vax_reg_name(int reg) {
|
||||
return vax_reg_names[reg & 0xf];
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ arch_plugins_list = [
|
|||
'tms320',
|
||||
'v810',
|
||||
'v850',
|
||||
'vax',
|
||||
'wasm',
|
||||
'x86_as',
|
||||
'x86_nasm',
|
||||
|
|
@ -114,6 +115,7 @@ arch_plugin_sources = [
|
|||
'p/arch_tms320.c',
|
||||
'p/arch_v810.c',
|
||||
'p/arch_v850.c',
|
||||
'p/arch_vax.c',
|
||||
'p/arch_wasm.c',
|
||||
'p/arch_x86_as.c',
|
||||
'p/arch_x86_nasm.c',
|
||||
|
|
@ -331,6 +333,7 @@ arch_isa_sources = [
|
|||
'isa/v810/v810_il.c',
|
||||
'isa/v850/v850_disas.c',
|
||||
'isa/v850/v850_il.c',
|
||||
'isa/vax/vax_dis.c',
|
||||
'isa/wasm/wasm.c',
|
||||
'isa/x86/common.c',
|
||||
'isa/x86/x86_il.c',
|
||||
|
|
|
|||
619
librz/arch/p/analysis/analysis_vax.c
Normal file
619
librz/arch/p/analysis/analysis_vax.c
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <string.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_asm.h>
|
||||
#include <rz_analysis.h>
|
||||
#include <rz_util.h>
|
||||
|
||||
#include "vax/vax.h"
|
||||
|
||||
// Report architecture limits (op size bounds, alignment, pointer support) to the core.
|
||||
static int vax_archinfo(RzAnalysis *a, RzAnalysisInfoType query) {
|
||||
switch (query) {
|
||||
case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
|
||||
return 1;
|
||||
case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
|
||||
return VAX_MAX_OP_SIZE;
|
||||
case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN:
|
||||
return 1;
|
||||
case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN:
|
||||
return 1;
|
||||
case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS:
|
||||
return true;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the VAX register profile (r0-r11, ap, fp, sp, pc, psl and condition flags).
|
||||
static char *vax_reg_profile(RzAnalysis *a) {
|
||||
const char *p =
|
||||
"=PC pc\n"
|
||||
"=SP sp\n"
|
||||
"=BP fp\n"
|
||||
"=A0 r0\n"
|
||||
"=A1 r1\n"
|
||||
"=A2 r2\n"
|
||||
"=A3 r3\n"
|
||||
"=R0 r0\n"
|
||||
"gpr r0 .32 0 0\n"
|
||||
"gpr r1 .32 4 0\n"
|
||||
"gpr r2 .32 8 0\n"
|
||||
"gpr r3 .32 12 0\n"
|
||||
"gpr r4 .32 16 0\n"
|
||||
"gpr r5 .32 20 0\n"
|
||||
"gpr r6 .32 24 0\n"
|
||||
"gpr r7 .32 28 0\n"
|
||||
"gpr r8 .32 32 0\n"
|
||||
"gpr r9 .32 36 0\n"
|
||||
"gpr r10 .32 40 0\n"
|
||||
"gpr r11 .32 44 0\n"
|
||||
"gpr ap .32 48 0\n"
|
||||
"gpr fp .32 52 0\n"
|
||||
"gpr sp .32 56 0\n"
|
||||
"gpr pc .32 60 0\n"
|
||||
"gpr psl .32 64 0\n"
|
||||
"flg c .1 512 0\n"
|
||||
"flg v .1 513 0\n"
|
||||
"flg z .1 514 0\n"
|
||||
"flg n .1 515 0\n";
|
||||
return rz_str_dup(p);
|
||||
}
|
||||
|
||||
// Map a conditional-branch opcode to its RzTypeCond (RZ_TYPE_COND_AL if none).
|
||||
static RzTypeCond vax_cond_for(ut16 oc) {
|
||||
switch (oc) {
|
||||
case VAX_OP_BNEQ: return RZ_TYPE_COND_NE;
|
||||
case VAX_OP_BEQL: return RZ_TYPE_COND_EQ;
|
||||
case VAX_OP_BGTR: return RZ_TYPE_COND_GT;
|
||||
case VAX_OP_BLEQ: return RZ_TYPE_COND_LE;
|
||||
case VAX_OP_BGEQ: return RZ_TYPE_COND_GE;
|
||||
case VAX_OP_BLSS: return RZ_TYPE_COND_LT;
|
||||
case VAX_OP_BGTRU: return RZ_TYPE_COND_HI;
|
||||
case VAX_OP_BLEQU: return RZ_TYPE_COND_LS;
|
||||
case VAX_OP_BVC: return RZ_TYPE_COND_VC;
|
||||
case VAX_OP_BVS: return RZ_TYPE_COND_VS;
|
||||
case VAX_OP_BGEQU: return RZ_TYPE_COND_HS; // bcc
|
||||
case VAX_OP_BLSSU: return RZ_TYPE_COND_LO; // bcs
|
||||
default: return RZ_TYPE_COND_AL;
|
||||
}
|
||||
}
|
||||
|
||||
// Classify a data-manipulation instruction by mnemonic prefix.
|
||||
static void vax_classify_data(RzAnalysisOp *op, const VaxInst *inst) {
|
||||
static const struct {
|
||||
const char *pfx;
|
||||
ut32 type;
|
||||
} map[] = {
|
||||
{ "mova", RZ_ANALYSIS_OP_TYPE_LEA },
|
||||
{ "pusha", RZ_ANALYSIS_OP_TYPE_PUSH },
|
||||
{ "pushr", RZ_ANALYSIS_OP_TYPE_PUSH },
|
||||
{ "pushl", RZ_ANALYSIS_OP_TYPE_PUSH },
|
||||
{ "popr", RZ_ANALYSIS_OP_TYPE_POP },
|
||||
{ "movz", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "movpsl", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "mov", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "clr", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "cvt", RZ_ANALYSIS_OP_TYPE_CAST },
|
||||
{ "adwc", RZ_ANALYSIS_OP_TYPE_ADD },
|
||||
{ "add", RZ_ANALYSIS_OP_TYPE_ADD },
|
||||
{ "inc", RZ_ANALYSIS_OP_TYPE_ADD },
|
||||
{ "sbwc", RZ_ANALYSIS_OP_TYPE_SUB },
|
||||
{ "sub", RZ_ANALYSIS_OP_TYPE_SUB },
|
||||
{ "dec", RZ_ANALYSIS_OP_TYPE_SUB },
|
||||
{ "mneg", RZ_ANALYSIS_OP_TYPE_SUB },
|
||||
{ "emul", RZ_ANALYSIS_OP_TYPE_MUL },
|
||||
{ "mul", RZ_ANALYSIS_OP_TYPE_MUL },
|
||||
{ "ediv", RZ_ANALYSIS_OP_TYPE_DIV },
|
||||
{ "div", RZ_ANALYSIS_OP_TYPE_DIV },
|
||||
{ "bispsw", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "bicpsw", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "bis", RZ_ANALYSIS_OP_TYPE_OR },
|
||||
{ "bic", RZ_ANALYSIS_OP_TYPE_AND },
|
||||
{ "xor", RZ_ANALYSIS_OP_TYPE_XOR },
|
||||
{ "mcom", RZ_ANALYSIS_OP_TYPE_NOT },
|
||||
{ "cmp", RZ_ANALYSIS_OP_TYPE_CMP },
|
||||
{ "tst", RZ_ANALYSIS_OP_TYPE_CMP },
|
||||
{ "bit", RZ_ANALYSIS_OP_TYPE_CMP },
|
||||
{ "ash", RZ_ANALYSIS_OP_TYPE_SHL },
|
||||
{ "rotl", RZ_ANALYSIS_OP_TYPE_ROL },
|
||||
{ "editpc", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "ext", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ "insv", RZ_ANALYSIS_OP_TYPE_MOV },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
const char *n = inst->name;
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; map[i].pfx; i++) {
|
||||
if (!strncmp(n, map[i].pfx, strlen(map[i].pfx))) {
|
||||
op->type = map[i].type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set op type, family, condition, stack effect and control-flow targets.
|
||||
* \param a the RzAnalysis instance
|
||||
* \param op the analysis op to populate
|
||||
* \param inst the decoded instruction
|
||||
* \param addr the instruction address (used to compute fall-through targets)
|
||||
*/
|
||||
static void vax_set_type(RzAnalysis *a, RzAnalysisOp *op, const VaxInst *inst, ut64 addr) {
|
||||
ut16 oc = inst->opcode;
|
||||
int sz = inst->size;
|
||||
ut8 b0 = (ut8)(oc & 0xff);
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
|
||||
switch (oc) {
|
||||
case VAX_OP_HALT:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_PRIV;
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_NOP:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
return;
|
||||
case VAX_OP_BPT:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_REI:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_PRIV;
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_RET:
|
||||
case VAX_OP_RSB:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
op->eob = true;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 4;
|
||||
return;
|
||||
case VAX_OP_LDPCTX:
|
||||
case VAX_OP_SVPCTX:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_PRIV;
|
||||
return;
|
||||
case VAX_OP_BSBB:
|
||||
case VAX_OP_BSBW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = inst->ops[0].target;
|
||||
op->fail = addr + sz;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
return;
|
||||
case VAX_OP_BRB:
|
||||
case VAX_OP_BRW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = inst->ops[0].target;
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_JSB:
|
||||
if (inst->ops[0].has_target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = inst->ops[0].target;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
}
|
||||
op->fail = addr + sz;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
return;
|
||||
case VAX_OP_JMP:
|
||||
if (inst->ops[0].has_target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = inst->ops[0].target;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
|
||||
}
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_BNEQ:
|
||||
case VAX_OP_BEQL:
|
||||
case VAX_OP_BGTR:
|
||||
case VAX_OP_BLEQ:
|
||||
case VAX_OP_BGEQ:
|
||||
case VAX_OP_BLSS:
|
||||
case VAX_OP_BGTRU:
|
||||
case VAX_OP_BLEQU:
|
||||
case VAX_OP_BVC:
|
||||
case VAX_OP_BVS:
|
||||
case VAX_OP_BGEQU:
|
||||
case VAX_OP_BLSSU:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = inst->ops[0].target;
|
||||
op->fail = addr + sz;
|
||||
op->cond = vax_cond_for(oc);
|
||||
return;
|
||||
case VAX_OP_BBS:
|
||||
case VAX_OP_BBC:
|
||||
case VAX_OP_BBSS:
|
||||
case VAX_OP_BBCS:
|
||||
case VAX_OP_BBSC:
|
||||
case VAX_OP_BBCC:
|
||||
case VAX_OP_BBSSI:
|
||||
case VAX_OP_BBCCI:
|
||||
case VAX_OP_BLBS:
|
||||
case VAX_OP_BLBC:
|
||||
case VAX_OP_AOBLSS:
|
||||
case VAX_OP_AOBLEQ:
|
||||
case VAX_OP_SOBGEQ:
|
||||
case VAX_OP_SOBGTR:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
if (inst->n_ops > 0) {
|
||||
op->jump = inst->ops[inst->n_ops - 1].target;
|
||||
}
|
||||
op->fail = addr + sz;
|
||||
return;
|
||||
case VAX_OP_ACBW:
|
||||
case VAX_OP_ACBB:
|
||||
case VAX_OP_ACBL:
|
||||
case VAX_OP_ACBF:
|
||||
case VAX_OP_ACBD:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
if (inst->n_ops > 0) {
|
||||
op->jump = inst->ops[inst->n_ops - 1].target;
|
||||
}
|
||||
op->fail = addr + sz;
|
||||
if (oc == VAX_OP_ACBF || oc == VAX_OP_ACBD) {
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
||||
}
|
||||
return;
|
||||
case VAX_OP_CASEB:
|
||||
case VAX_OP_CASEW:
|
||||
case VAX_OP_CASEL:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
|
||||
op->eob = true;
|
||||
return;
|
||||
case VAX_OP_CALLG:
|
||||
case VAX_OP_CALLS:
|
||||
if (inst->n_ops > 0 && inst->ops[inst->n_ops - 1].has_target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = inst->ops[inst->n_ops - 1].target;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
}
|
||||
op->fail = addr + sz;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
return;
|
||||
case VAX_OP_CHMK:
|
||||
case VAX_OP_CHME:
|
||||
case VAX_OP_CHMS:
|
||||
case VAX_OP_CHMU:
|
||||
case VAX_OP_XFC:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
||||
return;
|
||||
case VAX_OP_MTPR:
|
||||
case VAX_OP_MFPR:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_PRIV;
|
||||
return;
|
||||
case VAX_OP_PROBER:
|
||||
case VAX_OP_PROBEW:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_PRIV;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((b0 >= VAX_OP_ADDF2 && b0 <= VAX_OP_CVTFD) ||
|
||||
(b0 >= VAX_OP_ADDD2 && b0 <= VAX_OP_CVTDF) || oc >= 0xfd00) {
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
||||
}
|
||||
vax_classify_data(op, inst);
|
||||
|
||||
/* push of a longword / address adjusts SP by one longword */
|
||||
switch (b0) {
|
||||
case VAX_OP_PUSHL:
|
||||
case VAX_OP_PUSHAB:
|
||||
case VAX_OP_PUSHAW:
|
||||
case VAX_OP_PUSHAL:
|
||||
case VAX_OP_PUSHAQ: // and pushao on the FD page
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (op->type) {
|
||||
case RZ_ANALYSIS_OP_TYPE_ADD:
|
||||
case RZ_ANALYSIS_OP_TYPE_SUB:
|
||||
case RZ_ANALYSIS_OP_TYPE_MUL:
|
||||
case RZ_ANALYSIS_OP_TYPE_DIV:
|
||||
case RZ_ANALYSIS_OP_TYPE_CMP:
|
||||
op->sign = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Translate a decoded operand into an RzAnalysisValue.
|
||||
* \param a the RzAnalysis instance (for register lookups)
|
||||
* \param o the decoded operand
|
||||
* \param acc the access flags to record on the value
|
||||
* \return a newly allocated RzAnalysisValue, or NULL on allocation failure
|
||||
*/
|
||||
static RzAnalysisValue *vax_make_value(RzAnalysis *a, const VaxOperand *o, RzAnalysisValueAccess acc) {
|
||||
RzAnalysisValue *v = rz_analysis_value_new();
|
||||
if (!v) {
|
||||
return NULL;
|
||||
}
|
||||
v->access = acc;
|
||||
switch (o->mode) {
|
||||
case VAX_AM_LITERAL:
|
||||
v->type = RZ_ANALYSIS_VAL_IMM;
|
||||
v->imm = o->disp & 0x3f;
|
||||
break;
|
||||
case VAX_AM_IMMEDIATE:
|
||||
v->type = RZ_ANALYSIS_VAL_IMM;
|
||||
v->imm = (st64)o->imm;
|
||||
break;
|
||||
case VAX_AM_REG:
|
||||
v->type = RZ_ANALYSIS_VAL_REG;
|
||||
v->reg = rz_reg_get(a->reg, rz_vax_reg_name(o->reg), RZ_REG_TYPE_ANY);
|
||||
break;
|
||||
case VAX_AM_REGDEF:
|
||||
case VAX_AM_AUTODEC:
|
||||
case VAX_AM_AUTOINC:
|
||||
case VAX_AM_AUTOINCDEF:
|
||||
v->type = RZ_ANALYSIS_VAL_MEM;
|
||||
v->reg = rz_reg_get(a->reg, rz_vax_reg_name(o->reg), RZ_REG_TYPE_ANY);
|
||||
v->memref = rz_vax_dt_size(o->dt);
|
||||
break;
|
||||
case VAX_AM_BYTEDISP:
|
||||
case VAX_AM_BYTEDISPDEF:
|
||||
case VAX_AM_WORDDISP:
|
||||
case VAX_AM_WORDDISPDEF:
|
||||
case VAX_AM_LONGDISP:
|
||||
case VAX_AM_LONGDISPDEF:
|
||||
v->type = RZ_ANALYSIS_VAL_MEM;
|
||||
v->reg = rz_reg_get(a->reg, rz_vax_reg_name(o->reg), RZ_REG_TYPE_ANY);
|
||||
v->delta = o->disp;
|
||||
v->memref = rz_vax_dt_size(o->dt);
|
||||
break;
|
||||
case VAX_AM_ABSOLUTE:
|
||||
case VAX_AM_BYTEREL:
|
||||
case VAX_AM_BYTERELDEF:
|
||||
case VAX_AM_WORDREL:
|
||||
case VAX_AM_WORDRELDEF:
|
||||
case VAX_AM_LONGREL:
|
||||
case VAX_AM_LONGRELDEF:
|
||||
v->type = RZ_ANALYSIS_VAL_MEM;
|
||||
v->base = o->target;
|
||||
v->memref = rz_vax_dt_size(o->dt);
|
||||
break;
|
||||
default:
|
||||
v->type = RZ_ANALYSIS_VAL_UNK;
|
||||
break;
|
||||
}
|
||||
if (o->indexed) {
|
||||
v->regdelta = rz_reg_get(a->reg, rz_vax_reg_name(o->index_reg), RZ_REG_TYPE_ANY);
|
||||
v->mul = rz_vax_dt_size(o->dt);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Populate op->src[]/op->dst and the val/ptr/disp scalar hints.
|
||||
* \param a the RzAnalysis instance
|
||||
* \param op the analysis op to populate
|
||||
* \param inst the decoded instruction
|
||||
*/
|
||||
static void vax_fill_vals(RzAnalysis *a, RzAnalysisOp *op, const VaxInst *inst) {
|
||||
int srci = 0;
|
||||
bool have_val = false;
|
||||
bool have_ptr = false;
|
||||
for (int i = 0; i < inst->n_ops; i++) {
|
||||
const VaxOperand *o = &inst->ops[i];
|
||||
if (o->access == VAX_AC_B) {
|
||||
continue;
|
||||
}
|
||||
if (!have_val && (o->mode == VAX_AM_LITERAL || o->mode == VAX_AM_IMMEDIATE)) {
|
||||
op->val = (o->mode == VAX_AM_LITERAL) ? (ut64)(o->disp & 0x3f) : o->imm;
|
||||
have_val = true;
|
||||
}
|
||||
if (!have_ptr && o->has_target) {
|
||||
op->ptr = (st64)o->target;
|
||||
op->refptr = rz_vax_dt_size(o->dt);
|
||||
have_ptr = true;
|
||||
}
|
||||
if (!have_ptr && (o->mode == VAX_AM_BYTEDISP || o->mode == VAX_AM_WORDDISP || o->mode == VAX_AM_LONGDISP)) {
|
||||
op->disp = o->disp;
|
||||
}
|
||||
bool is_read = (o->access == VAX_AC_R || o->access == VAX_AC_M || o->access == VAX_AC_A || o->access == VAX_AC_V);
|
||||
bool is_write = (o->access == VAX_AC_W || o->access == VAX_AC_M);
|
||||
if (is_write && !op->dst) {
|
||||
op->dst = vax_make_value(a, o, is_read ? (RZ_ANALYSIS_ACC_R | RZ_ANALYSIS_ACC_W) : RZ_ANALYSIS_ACC_W);
|
||||
if (o->mode == VAX_AM_REG) {
|
||||
op->reg = rz_vax_reg_name(o->reg);
|
||||
}
|
||||
} else if (is_read && srci < 8) {
|
||||
op->src[srci++] = vax_make_value(a, o, RZ_ANALYSIS_ACC_R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Build the RzAnalysisOp->opex structured operand description.
|
||||
* \param inst the decoded instruction
|
||||
* \return a newly allocated RzStructuredData tree, or NULL on failure
|
||||
*/
|
||||
static RzStructuredData *vax_opex(const VaxInst *inst) {
|
||||
RzStructuredData *root = rz_structured_data_new_map();
|
||||
if (!root) {
|
||||
return NULL;
|
||||
}
|
||||
RzStructuredData *opex = rz_structured_data_map_add_map(root, "opex");
|
||||
if (!opex) {
|
||||
rz_structured_data_free(root);
|
||||
return NULL;
|
||||
}
|
||||
rz_structured_data_map_add_string(opex, "mnemonic", inst->name ? inst->name : "invalid");
|
||||
RzStructuredData *operands = rz_structured_data_map_add_array(opex, "operands");
|
||||
if (!operands) {
|
||||
return root;
|
||||
}
|
||||
for (int i = 0; i < inst->n_ops; i++) {
|
||||
const VaxOperand *o = &inst->ops[i];
|
||||
RzStructuredData *od = rz_structured_data_array_add_map(operands);
|
||||
if (!od) {
|
||||
continue;
|
||||
}
|
||||
switch (o->mode) {
|
||||
case VAX_AM_LITERAL:
|
||||
rz_structured_data_map_add_string(od, "type", "imm");
|
||||
rz_structured_data_map_add_signed(od, "value", o->disp & 0x3f);
|
||||
break;
|
||||
case VAX_AM_IMMEDIATE:
|
||||
rz_structured_data_map_add_string(od, "type", "imm");
|
||||
rz_structured_data_map_add_signed(od, "value", (st64)o->imm);
|
||||
break;
|
||||
case VAX_AM_BRANCH:
|
||||
rz_structured_data_map_add_string(od, "type", "imm");
|
||||
rz_structured_data_map_add_unsigned(od, "value", o->target, true);
|
||||
break;
|
||||
case VAX_AM_REG:
|
||||
rz_structured_data_map_add_string(od, "type", "reg");
|
||||
rz_structured_data_map_add_string(od, "value", rz_vax_reg_name(o->reg));
|
||||
break;
|
||||
case VAX_AM_REGDEF:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
break;
|
||||
case VAX_AM_AUTODEC:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
rz_structured_data_map_add_boolean(od, "autodecrement", true);
|
||||
break;
|
||||
case VAX_AM_AUTOINC:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
rz_structured_data_map_add_boolean(od, "autoincrement", true);
|
||||
break;
|
||||
case VAX_AM_AUTOINCDEF:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
rz_structured_data_map_add_boolean(od, "autoincrement", true);
|
||||
rz_structured_data_map_add_boolean(od, "deferred", true);
|
||||
break;
|
||||
case VAX_AM_BYTEDISP:
|
||||
case VAX_AM_WORDDISP:
|
||||
case VAX_AM_LONGDISP:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
rz_structured_data_map_add_signed(od, "disp", o->disp);
|
||||
break;
|
||||
case VAX_AM_BYTEDISPDEF:
|
||||
case VAX_AM_WORDDISPDEF:
|
||||
case VAX_AM_LONGDISPDEF:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_string(od, "base", rz_vax_reg_name(o->reg));
|
||||
rz_structured_data_map_add_signed(od, "disp", o->disp);
|
||||
rz_structured_data_map_add_boolean(od, "deferred", true);
|
||||
break;
|
||||
case VAX_AM_ABSOLUTE:
|
||||
case VAX_AM_BYTEREL:
|
||||
case VAX_AM_WORDREL:
|
||||
case VAX_AM_LONGREL:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_unsigned(od, "target", o->target, true);
|
||||
break;
|
||||
case VAX_AM_BYTERELDEF:
|
||||
case VAX_AM_WORDRELDEF:
|
||||
case VAX_AM_LONGRELDEF:
|
||||
rz_structured_data_map_add_string(od, "type", "mem");
|
||||
rz_structured_data_map_add_unsigned(od, "target", o->target, true);
|
||||
rz_structured_data_map_add_boolean(od, "deferred", true);
|
||||
break;
|
||||
default:
|
||||
rz_structured_data_map_add_string(od, "type", "invalid");
|
||||
break;
|
||||
}
|
||||
if (o->indexed) {
|
||||
rz_structured_data_map_add_string(od, "index", rz_vax_reg_name(o->index_reg));
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Analyze a single VAX instruction (the RzAnalysisPlugin op callback).
|
||||
* \param a the RzAnalysis instance
|
||||
* \param op the analysis op to fill
|
||||
* \param addr the instruction address
|
||||
* \param buf the instruction bytes
|
||||
* \param len number of valid bytes in \p buf
|
||||
* \param mask which RzAnalysisOp fields are requested
|
||||
* \return the instruction length in bytes, or -1 on failure
|
||||
*/
|
||||
static int vax_op(RzAnalysis *a, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) {
|
||||
if (!a || !op || !buf || len < 1) {
|
||||
return -1;
|
||||
}
|
||||
VaxInst inst;
|
||||
int size = rz_vax_decode(&inst, buf, len, addr);
|
||||
op->addr = addr;
|
||||
if (size < 1) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
|
||||
op->size = 1;
|
||||
if (mask & RZ_ANALYSIS_OP_MASK_DISASM) {
|
||||
op->mnemonic = rz_str_dup("invalid");
|
||||
}
|
||||
return op->size;
|
||||
}
|
||||
op->size = size;
|
||||
op->id = inst.opcode;
|
||||
op->nopcode = (inst.opcode >= 0xfd00) ? 2 : 1;
|
||||
|
||||
if (!inst.name) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
|
||||
if (mask & RZ_ANALYSIS_OP_MASK_DISASM) {
|
||||
op->mnemonic = rz_str_dup("invalid");
|
||||
}
|
||||
return op->size;
|
||||
}
|
||||
|
||||
vax_set_type(a, op, &inst, addr);
|
||||
|
||||
if (mask & RZ_ANALYSIS_OP_MASK_DISASM) {
|
||||
RzStrBuf sb;
|
||||
rz_strbuf_init(&sb);
|
||||
rz_vax_format(&inst, &sb);
|
||||
op->mnemonic = rz_strbuf_drain_nofree(&sb);
|
||||
rz_strbuf_fini(&sb);
|
||||
}
|
||||
|
||||
if (mask & RZ_ANALYSIS_OP_MASK_VAL) {
|
||||
vax_fill_vals(a, op, &inst);
|
||||
}
|
||||
|
||||
if (mask & RZ_ANALYSIS_OP_MASK_OPEX) {
|
||||
op->opex = vax_opex(&inst);
|
||||
}
|
||||
|
||||
return op->size;
|
||||
}
|
||||
|
||||
RzAnalysisPlugin rz_analysis_plugin_vax = {
|
||||
.name = "vax",
|
||||
.desc = "DEC VAX-11 code analysis plugin",
|
||||
.license = "LGPL3",
|
||||
.arch = "vax",
|
||||
.bits = 32,
|
||||
.esil = false,
|
||||
.archinfo = vax_archinfo,
|
||||
.get_reg_profile = vax_reg_profile,
|
||||
.op = vax_op,
|
||||
};
|
||||
9
librz/arch/p/arch_vax.c
Normal file
9
librz/arch/p/arch_vax.c
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <deprecated_arch_helper.h>
|
||||
|
||||
#include "analysis/analysis_vax.c"
|
||||
#include "asm/asm_vax.c"
|
||||
|
||||
RZ_ARCH_PLUGIN_DEFINE_DEPRECATED(vax);
|
||||
46
librz/arch/p/asm/asm_vax.c
Normal file
46
librz/arch/p/asm/asm_vax.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_asm.h>
|
||||
#include "asm_private.h"
|
||||
|
||||
#include "vax/vax.h"
|
||||
|
||||
/**
|
||||
* \brief Disassemble a single VAX instruction (the RzAsmPlugin callback).
|
||||
* \param a the RzAsm instance (provides the program counter via a->pc)
|
||||
* \param op receives the rendered assembly text and instruction size
|
||||
* \param buf the instruction bytes
|
||||
* \param len number of valid bytes in \p buf
|
||||
* \return the instruction length in bytes, or -1 on bad arguments
|
||||
*/
|
||||
static int vax_disassemble(const RzAsm *a, RzAsmOp *op, const ut8 *buf, int len) {
|
||||
if (!a || !op || !buf || len < 1) {
|
||||
return -1;
|
||||
}
|
||||
VaxInst inst;
|
||||
int size = rz_vax_decode(&inst, buf, len, a->pc);
|
||||
if (size < 1 || !inst.name) {
|
||||
rz_asm_op_set_asm(op, "invalid");
|
||||
return op->size = (size > 0) ? size : 1;
|
||||
}
|
||||
RzStrBuf sb;
|
||||
rz_strbuf_init(&sb);
|
||||
rz_vax_format(&inst, &sb);
|
||||
rz_asm_op_set_asm(op, rz_strbuf_get(&sb));
|
||||
rz_strbuf_fini(&sb);
|
||||
return op->size = size;
|
||||
}
|
||||
|
||||
RzAsmPlugin rz_asm_plugin_vax = {
|
||||
.name = "vax",
|
||||
.license = "LGPL3",
|
||||
.desc = "DEC VAX-11 disassembler",
|
||||
.author = "xvilka",
|
||||
.arch = "vax",
|
||||
.bits = 32,
|
||||
.endian = RZ_SYS_ENDIAN_LITTLE,
|
||||
.disassemble = &vax_disassemble,
|
||||
};
|
||||
445
test/db/analysis/vax
Normal file
445
test/db/analysis/vax
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
NAME=vax analysis ops
|
||||
FILE=malloc://4096
|
||||
CMDS=<<EOF
|
||||
e asm.arch=vax
|
||||
e asm.bits=32
|
||||
e analysis.arch=vax
|
||||
wx d05152
|
||||
ao
|
||||
echo ----
|
||||
wx c1515253
|
||||
ao
|
||||
echo ----
|
||||
wx d08f1234567850
|
||||
ao
|
||||
echo ----
|
||||
wx 11fe
|
||||
ao
|
||||
echo ----
|
||||
wx 13fc
|
||||
ao
|
||||
echo ----
|
||||
wx 1761
|
||||
ao
|
||||
echo ----
|
||||
wx 179f00100000
|
||||
ao
|
||||
echo ----
|
||||
wx fb049f00100000
|
||||
ao
|
||||
echo ----
|
||||
wx 04
|
||||
ao
|
||||
echo ----
|
||||
wx dd51
|
||||
ao
|
||||
echo ----
|
||||
wx 405152
|
||||
ao
|
||||
echo ----
|
||||
wx fd505152
|
||||
ao
|
||||
echo ----
|
||||
wx da5152
|
||||
ao
|
||||
echo ----
|
||||
wx bc02
|
||||
ao
|
||||
echo ----
|
||||
wx 8f510102060007000800
|
||||
ao
|
||||
echo ----
|
||||
wx f451fb
|
||||
ao
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
address: 0x0
|
||||
opcode: movl r1,r2
|
||||
disasm: movl r1,r2
|
||||
mnemonic: movl
|
||||
mask: ffffff
|
||||
prefix: 0
|
||||
id: 208
|
||||
bytes: d05152
|
||||
refptr: 0
|
||||
size: 3
|
||||
sign: false
|
||||
type: mov
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "movl"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "reg"
|
||||
value: "r2"
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: addl3 r1,r2,r3
|
||||
disasm: addl3 r1,r2,r3
|
||||
mnemonic: addl3
|
||||
mask: ffffffff
|
||||
prefix: 0
|
||||
id: 193
|
||||
bytes: c1515253
|
||||
refptr: 0
|
||||
size: 4
|
||||
sign: true
|
||||
type: add
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "addl3"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "reg"
|
||||
value: "r2"
|
||||
- type: "reg"
|
||||
value: "r3"
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: movl $0x78563412,r0
|
||||
disasm: movl $0x78563412,r0
|
||||
mnemonic: movl
|
||||
mask: ffffffffffffff
|
||||
prefix: 0
|
||||
id: 208
|
||||
bytes: d08f1234567850
|
||||
refptr: 0
|
||||
size: 7
|
||||
sign: false
|
||||
type: mov
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "movl"
|
||||
operands:
|
||||
- type: "imm"
|
||||
value: 2018915346
|
||||
- type: "reg"
|
||||
value: "r0"
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: brb 0x0
|
||||
disasm: brb 0x0
|
||||
mnemonic: brb
|
||||
mask: ff00
|
||||
prefix: 0
|
||||
id: 17
|
||||
bytes: 11fe
|
||||
refptr: 0
|
||||
size: 2
|
||||
sign: false
|
||||
type: jmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "brb"
|
||||
operands:
|
||||
- type: "imm"
|
||||
value: 0x0
|
||||
jump: 0x00000000
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: beql 0xfffffffffffffffe
|
||||
disasm: beql 0xfffffffffffffffe
|
||||
mnemonic: beql
|
||||
mask: ff00
|
||||
prefix: 0
|
||||
id: 19
|
||||
bytes: 13fc
|
||||
refptr: 0
|
||||
size: 2
|
||||
sign: false
|
||||
type: cjmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "beql"
|
||||
operands:
|
||||
- type: "imm"
|
||||
value: 0xfffffffffffffffe
|
||||
jump: 0xfffffffffffffffe
|
||||
fail: 0x00000002
|
||||
cond: eq
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: jmp (r1)
|
||||
disasm: jmp (r1)
|
||||
mnemonic: jmp
|
||||
mask: ffff
|
||||
prefix: 0
|
||||
id: 23
|
||||
bytes: 1761
|
||||
refptr: 0
|
||||
size: 2
|
||||
sign: false
|
||||
type: ujmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "jmp"
|
||||
operands:
|
||||
- type: "mem"
|
||||
base: "r1"
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: jmp *0x1000
|
||||
disasm: jmp *0x1000
|
||||
mnemonic: jmp
|
||||
mask: ff0000000000
|
||||
prefix: 0
|
||||
id: 23
|
||||
bytes: 179f00100000
|
||||
refptr: 0
|
||||
size: 6
|
||||
sign: false
|
||||
type: jmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "jmp"
|
||||
operands:
|
||||
- type: "mem"
|
||||
target: 0x1000
|
||||
jump: 0x00001000
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: calls $0x4,*0x1000
|
||||
disasm: calls $0x4,*0x1000
|
||||
mnemonic: calls
|
||||
mask: ff000000000000
|
||||
prefix: 0
|
||||
id: 251
|
||||
bytes: fb049f00100000
|
||||
refptr: 0
|
||||
size: 7
|
||||
sign: false
|
||||
type: call
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "calls"
|
||||
operands:
|
||||
- type: "imm"
|
||||
value: 4
|
||||
- type: "mem"
|
||||
target: 0x1000
|
||||
jump: 0x00001000
|
||||
fail: 0x00000007
|
||||
family: cpu
|
||||
stackop: inc
|
||||
stackptr: -4
|
||||
----
|
||||
address: 0x0
|
||||
opcode: ret
|
||||
disasm: ret
|
||||
mnemonic: ret
|
||||
mask: ff
|
||||
prefix: 0
|
||||
id: 4
|
||||
bytes: 04
|
||||
refptr: 0
|
||||
size: 1
|
||||
sign: false
|
||||
type: ret
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "ret"
|
||||
operands: []
|
||||
family: cpu
|
||||
stackop: inc
|
||||
stackptr: 4
|
||||
----
|
||||
address: 0x0
|
||||
opcode: pushl r1
|
||||
disasm: pushl r1
|
||||
mnemonic: pushl
|
||||
mask: ffff
|
||||
prefix: 0
|
||||
id: 221
|
||||
bytes: dd51
|
||||
refptr: 0
|
||||
size: 2
|
||||
sign: false
|
||||
type: push
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "pushl"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
family: cpu
|
||||
stackop: inc
|
||||
stackptr: -4
|
||||
----
|
||||
address: 0x0
|
||||
opcode: addf2 r1,r2
|
||||
disasm: addf2 r1,r2
|
||||
mnemonic: addf2
|
||||
mask: ffffff
|
||||
prefix: 0
|
||||
id: 64
|
||||
bytes: 405152
|
||||
refptr: 0
|
||||
size: 3
|
||||
sign: true
|
||||
type: add
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "addf2"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "reg"
|
||||
value: "r2"
|
||||
family: fpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: movg r1,r2
|
||||
disasm: movg r1,r2
|
||||
mnemonic: movg
|
||||
mask: ffffffff
|
||||
prefix: 0
|
||||
id: 64848
|
||||
bytes: fd505152
|
||||
refptr: 0
|
||||
size: 4
|
||||
sign: false
|
||||
type: mov
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "movg"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "reg"
|
||||
value: "r2"
|
||||
family: fpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: mtpr r1,r2
|
||||
disasm: mtpr r1,r2
|
||||
mnemonic: mtpr
|
||||
mask: ffffff
|
||||
prefix: 0
|
||||
id: 218
|
||||
bytes: da5152
|
||||
refptr: 0
|
||||
size: 3
|
||||
sign: false
|
||||
type: mov
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "mtpr"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "reg"
|
||||
value: "r2"
|
||||
family: priv
|
||||
----
|
||||
address: 0x0
|
||||
opcode: chmk $0x2
|
||||
disasm: chmk $0x2
|
||||
mnemonic: chmk
|
||||
mask: ffff
|
||||
prefix: 0
|
||||
id: 188
|
||||
bytes: bc02
|
||||
refptr: 0
|
||||
size: 2
|
||||
sign: false
|
||||
type: swi
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "chmk"
|
||||
operands:
|
||||
- type: "imm"
|
||||
value: 2
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: caseb r1,$0x1,$0x2
|
||||
disasm: caseb r1,$0x1,$0x2
|
||||
mnemonic: caseb
|
||||
mask: ffffffffffffffffffff
|
||||
prefix: 0
|
||||
id: 143
|
||||
bytes: 8f510102060007000800
|
||||
refptr: 0
|
||||
size: 10
|
||||
sign: false
|
||||
type: ujmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "caseb"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "imm"
|
||||
value: 1
|
||||
- type: "imm"
|
||||
value: 2
|
||||
family: cpu
|
||||
----
|
||||
address: 0x0
|
||||
opcode: sobgeq r1,0xfffffffffffffffe
|
||||
disasm: sobgeq r1,0xfffffffffffffffe
|
||||
mnemonic: sobgeq
|
||||
mask: ff0000
|
||||
prefix: 0
|
||||
id: 244
|
||||
bytes: f451fb
|
||||
refptr: 0
|
||||
size: 3
|
||||
sign: false
|
||||
type: cjmp
|
||||
cycles: 0
|
||||
opex:
|
||||
mnemonic: "sobgeq"
|
||||
operands:
|
||||
- type: "reg"
|
||||
value: "r1"
|
||||
- type: "imm"
|
||||
value: 0xfffffffffffffffe
|
||||
jump: 0xfffffffffffffffe
|
||||
fail: 0x00000003
|
||||
cond: al
|
||||
family: cpu
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=vax elf32-vax binary disassembly and analysis
|
||||
FILE=bins/vax/b.out
|
||||
CMDS=<<EOF
|
||||
i~arch
|
||||
i~bits
|
||||
echo ---prologue---
|
||||
pi 8 @ section..text
|
||||
echo ---invalids---
|
||||
pD 0x1e7 @ section..text~?invalid
|
||||
echo ---function---
|
||||
af @ section..text
|
||||
afi @ section..text~name,size
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch vax
|
||||
bits 32
|
||||
---prologue---
|
||||
addl2 $0xf,*0xcac(r0)
|
||||
extzv $0x0,$0x0,$0x0,$0x0
|
||||
movl 0x8(ap),0x8000046
|
||||
movl 0x4(ap),0x800004e
|
||||
movl 0x10(ap),0x8000056
|
||||
bgeq 0x800005b
|
||||
brw loc.s2_big
|
||||
clrl r3
|
||||
---invalids---
|
||||
0
|
||||
---function---
|
||||
name: loc.___mpn_addmul_1
|
||||
size: 487
|
||||
EOF
|
||||
RUN
|
||||
80
test/db/asm/vax
Normal file
80
test/db/asm/vax
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# VAX-11 disassembly tests for the LGPL3 'vax' arch plugin
|
||||
d "movl r1,r2" d05152 0x0
|
||||
d "movl (r1),r2" d06152 0x0
|
||||
d "movl -(sp),r2" d07e52 0x0
|
||||
d "movl (r1)+,r2" d08152 0x0
|
||||
d "movl *(r1)+,r2" d09152 0x0
|
||||
d "movl 0x10(r1),r2" d0a11052 0x0
|
||||
d "movl *0x10(r1),r2" d0b11052 0x0
|
||||
d "movl 0x1234(r1),r2" d0c1341252 0x0
|
||||
d "movl 0x123456(r1),r2" d0e15634120052 0x0
|
||||
d "movl $0x78563412,r0" d08f1234567850 0x0
|
||||
d "movl *0x1000,r0" d09f0010000050 0x0
|
||||
d "movl (r1)[r2],r3" d0426153 0x0
|
||||
d "movl $0x7,r2" d00752 0x0
|
||||
d "addl2 r1,r2" c05152 0x0
|
||||
d "addl3 r1,r2,r3" c1515253 0x0
|
||||
d "subl2 r1,r2" c25152 0x0
|
||||
d "mull2 r1,r2" c45152 0x0
|
||||
d "divl2 r1,r2" c65152 0x0
|
||||
d "bisl2 r1,r2" c85152 0x0
|
||||
d "bicl2 r1,r2" ca5152 0x0
|
||||
d "xorl2 r1,r2" cc5152 0x0
|
||||
d "mnegl r1,r2" ce5152 0x0
|
||||
d "incl r1" d651 0x0
|
||||
d "decl r1" d751 0x0
|
||||
d "clrl r1" d451 0x0
|
||||
d "tstl r1" d551 0x0
|
||||
d "cmpl r1,r2" d15152 0x0
|
||||
d "bitl r1,r2" d35152 0x0
|
||||
d "ashl r1,r2,r3" 78515253 0x0
|
||||
d "rotl r1,r2,r3" 9c515253 0x0
|
||||
d "adwc r1,r2" d85152 0x0
|
||||
d "movb r1,r2" 905152 0x0
|
||||
d "movw r1,r2" b05152 0x0
|
||||
d "movzbl r1,r2" 9a5152 0x0
|
||||
d "cvtwl r1,r2" 325152 0x0
|
||||
d "cvtlb r1,r2" f65152 0x0
|
||||
d "movab (r1),r2" 9e6152 0x0
|
||||
d "moval (r1),r2" de6152 0x0
|
||||
d "pushl r1" dd51 0x0
|
||||
d "pushab (r1)" 9f61 0x0
|
||||
d "pushr $0x4" bb04 0x0
|
||||
d "popr $0x4" ba04 0x0
|
||||
d "brb 0x10" 11fe 0x10
|
||||
d "brb 0x17" 1105 0x10
|
||||
d "brw 0x28" 310500 0x20
|
||||
d "bsbb 0x17" 1005 0x10
|
||||
d "beql 0xe" 13fc 0x10
|
||||
d "bneq 0xe" 12fc 0x10
|
||||
d "bgtr 0xe" 14fc 0x10
|
||||
d "bgequ 0xe" 1efc 0x10
|
||||
d "jsb (r1)" 1661 0x0
|
||||
d "jmp (r1)" 1761 0x0
|
||||
d "jmp *0x1000" 179f00100000 0x0
|
||||
d "calls $0x4,*0x1000" fb049f00100000 0x0
|
||||
d "callg (r1),(r2)" fa6162 0x0
|
||||
d "ret" 04 0x0
|
||||
d "rsb" 05 0x0
|
||||
d "halt" 00 0x0
|
||||
d "nop" 01 0x0
|
||||
d "sobgeq r1,0xe" f451fb 0x10
|
||||
d "aoblss r1,r2,0x10" f25152fc 0x10
|
||||
d "blbs r1,0x65" e85152 0x10
|
||||
d "bbs $0x1,r2,0x10" e00152fc 0x10
|
||||
d "extzv r1,r2,r3,r4" ef51525354 0x0
|
||||
d "insv r1,r2,r3,r4" f051525354 0x0
|
||||
d "caseb r1,$0x1,$0x2" 8f510102060007000800 0x0
|
||||
d "addf2 r1,r2" 405152 0x0
|
||||
d "movf r1,r2" 505152 0x0
|
||||
d "addd2 r1,r2" 605152 0x0
|
||||
d "movd r1,r2" 705152 0x0
|
||||
d "cvtfl r1,r2" 4a5152 0x0
|
||||
d "cvtlf r1,r2" 4e5152 0x0
|
||||
d "movg r1,r2" fd505152 0x0
|
||||
d "addg2 r1,r2" fd405152 0x0
|
||||
d "mtpr r1,r2" da5152 0x0
|
||||
d "mfpr r1,r2" db5152 0x0
|
||||
d "chmk $0x2" bc02 0x0
|
||||
d "rei" 02 0x0
|
||||
d "movc3 r1,(r2),(r3)" 28516263 0x0
|
||||
|
|
@ -61,7 +61,7 @@ _dA__ 32 tms320 LGPL3 Texas Instruments TMS320 DSP family (c54x,c
|
|||
_dA_I 32 tricore BSD Siemens TriCore Capstone-based disassembler (by billow)
|
||||
_dAeI 32 v810 LGPL3 NEC V810 disassembler (by pancake)
|
||||
_dAeI 32 v850 LGPL3 NEC/Renesas V850 disassembler
|
||||
_dA__ 8 32 vax GPL3 DEC VAX disassembler
|
||||
_dA__ 32 vax LGPL3 DEC VAX-11 disassembler (by xvilka)
|
||||
adA__ 32 wasm MIT WebAssembly disassembler (by cgvwzq) v0.1.0
|
||||
_dAeI 16 32 64 x86 MIT X86/X86_64 Zydis-based disassembler
|
||||
a____ 16 32 64 x86.as LGPL3 Intel X86 GNU Assembler (Use RZ_X86_AS env)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue