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:
Anton Kochkov 2026-06-12 15:26:54 +00:00 committed by NOT XVilka
parent 423104df4a
commit a2454fec15
10 changed files with 2119 additions and 5 deletions

196
librz/arch/isa/vax/vax.h Normal file
View 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 */

View 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];
}

View file

@ -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',

View 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
View 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);

View 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
View 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
View 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

View file

@ -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