xref: /freebsd/sys/arm64/arm64/disassem.c (revision 4a07c778)
18a1867f4SWojciech Macek /*-
28a1867f4SWojciech Macek  * Copyright (c) 2016 Cavium
38a1867f4SWojciech Macek  * All rights reserved.
48a1867f4SWojciech Macek  *
58a1867f4SWojciech Macek  * This software was developed by Semihalf.
68a1867f4SWojciech Macek  *
78a1867f4SWojciech Macek  * Redistribution and use in source and binary forms, with or without
88a1867f4SWojciech Macek  * modification, are permitted provided that the following conditions
98a1867f4SWojciech Macek  * are met:
108a1867f4SWojciech Macek  * 1. Redistributions of source code must retain the above copyright
118a1867f4SWojciech Macek  *    notice, this list of conditions and the following disclaimer.
128a1867f4SWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
138a1867f4SWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
148a1867f4SWojciech Macek  *    documentation and/or other materials provided with the distribution.
158a1867f4SWojciech Macek  *
168a1867f4SWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178a1867f4SWojciech Macek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188a1867f4SWojciech Macek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198a1867f4SWojciech Macek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208a1867f4SWojciech Macek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218a1867f4SWojciech Macek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228a1867f4SWojciech Macek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238a1867f4SWojciech Macek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248a1867f4SWojciech Macek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258a1867f4SWojciech Macek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268a1867f4SWojciech Macek  * SUCH DAMAGE.
278a1867f4SWojciech Macek  */
288a1867f4SWojciech Macek 
298a1867f4SWojciech Macek #include <sys/cdefs.h>
308a1867f4SWojciech Macek __FBSDID("$FreeBSD$");
318a1867f4SWojciech Macek 
325b61ad4bSMykola Hohsadze #include <sys/param.h>
338a1867f4SWojciech Macek #include <sys/systm.h>
345b61ad4bSMykola Hohsadze 
358a1867f4SWojciech Macek #include <machine/armreg.h>
365b61ad4bSMykola Hohsadze #include <machine/disassem.h>
375b61ad4bSMykola Hohsadze 
388a1867f4SWojciech Macek #include <ddb/ddb.h>
398a1867f4SWojciech Macek 
408a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_LEN	8
418a1867f4SWojciech Macek #define	ARM64_MAX_TOKEN_CNT	10
428a1867f4SWojciech Macek 
43c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_OFFSET	30
44c7fc655fSWojciech Macek #define	ARM_INSN_SIZE_MASK	0x3
45c7fc655fSWojciech Macek 
46c7fc655fSWojciech Macek /* Special options for instruction printing */
47c7fc655fSWojciech Macek #define	OP_SIGN_EXT	(1UL << 0)	/* Sign-extend immediate value */
48c7fc655fSWojciech Macek #define	OP_LITERAL	(1UL << 1)	/* Use literal (memory offset) */
49c7fc655fSWojciech Macek #define	OP_MULT_4	(1UL << 2)	/* Multiply immediate by 4 */
50c7fc655fSWojciech Macek #define	OP_SF32		(1UL << 3)	/* Force 32-bit access */
51c7fc655fSWojciech Macek #define	OP_SF_INV	(1UL << 6)	/* SF is inverted (1 means 32 bit access) */
529f60b8ceSMykola Hohsadze #define	OP_RD_SP	(1UL << 7)	/* Use sp for RD otherwise xzr */
539f60b8ceSMykola Hohsadze #define	OP_RT_SP	(1UL << 8)	/* Use sp for RT otherwise xzr */
549f60b8ceSMykola Hohsadze #define	OP_RN_SP	(1UL << 9)	/* Use sp for RN otherwise xzr */
559f60b8ceSMykola Hohsadze #define	OP_RM_SP	(1UL << 10)	/* Use sp for RM otherwise xzr */
569aef25d2SMykola Hohsadze #define	OP_SHIFT_ROR	(1UL << 11)	/* Use ror shift type */
57c7fc655fSWojciech Macek 
588a1867f4SWojciech Macek static const char *w_reg[] = {
598a1867f4SWojciech Macek 	"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
608a1867f4SWojciech Macek 	"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
618a1867f4SWojciech Macek 	"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23",
629f60b8ceSMykola Hohsadze 	"w24", "w25", "w26", "w27", "w28", "w29", "w30"
638a1867f4SWojciech Macek };
648a1867f4SWojciech Macek 
658a1867f4SWojciech Macek static const char *x_reg[] = {
668a1867f4SWojciech Macek 	"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
678a1867f4SWojciech Macek 	"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
688a1867f4SWojciech Macek 	"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
698a852d3eSMykola Hohsadze 	"x24", "x25", "x26", "x27", "x28", "x29", "lr"
708a1867f4SWojciech Macek };
718a1867f4SWojciech Macek 
728a1867f4SWojciech Macek static const char *shift_2[] = {
739aef25d2SMykola Hohsadze 	"lsl", "lsr", "asr", "ror"
748a1867f4SWojciech Macek };
758a1867f4SWojciech Macek 
764a07c778SMykola Hohsadze static const char *extend_types[] = {
774a07c778SMykola Hohsadze 	"uxtb", "uxth", "uxtw", "uxtx",
784a07c778SMykola Hohsadze 	"sxtb", "sxth", "sxtw", "sxtx",
794a07c778SMykola Hohsadze };
804a07c778SMykola Hohsadze 
818a1867f4SWojciech Macek /*
828a1867f4SWojciech Macek  * Structure representing single token (operand) inside instruction.
838a1867f4SWojciech Macek  * name   - name of operand
848a1867f4SWojciech Macek  * pos    - position within the instruction (in bits)
858a1867f4SWojciech Macek  * len    - operand length (in bits)
868a1867f4SWojciech Macek  */
878a1867f4SWojciech Macek struct arm64_insn_token {
888a1867f4SWojciech Macek 	char name[ARM64_MAX_TOKEN_LEN];
898a1867f4SWojciech Macek 	int pos;
908a1867f4SWojciech Macek 	int len;
918a1867f4SWojciech Macek };
928a1867f4SWojciech Macek 
938a1867f4SWojciech Macek /*
948a1867f4SWojciech Macek  * Define generic types for instruction printing.
958a1867f4SWojciech Macek  */
968a1867f4SWojciech Macek enum arm64_format_type {
975b61ad4bSMykola Hohsadze 	/*
985b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #imm} SF32/64
995b61ad4bSMykola Hohsadze 	 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
100ffa75b57SMykola Hohsadze 	 * OP <RD>, <RM> {, <shift> #<imm> }
101ffa75b57SMykola Hohsadze 	 * OP <RN>, <RM> {, <shift> #<imm> }
1025b61ad4bSMykola Hohsadze 	 */
1035b61ad4bSMykola Hohsadze 	TYPE_01,
1045b61ad4bSMykola Hohsadze 
1055b61ad4bSMykola Hohsadze 	/*
106e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>, #<simm>]!
107e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>], #<simm>
108e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP> {, #<pimm> }]
109e57b8626SMykola Hohsadze 	 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }]
1105b61ad4bSMykola Hohsadze 	 */
1115b61ad4bSMykola Hohsadze 	TYPE_02,
1125b61ad4bSMykola Hohsadze 
1135b61ad4bSMykola Hohsadze 	/* OP <RT>, #imm SF32/64 */
1145b61ad4bSMykola Hohsadze 	TYPE_03,
1154a07c778SMykola Hohsadze 
1164a07c778SMykola Hohsadze 	/*
1174a07c778SMykola Hohsadze 	 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } }
1184a07c778SMykola Hohsadze 	 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } }
1194a07c778SMykola Hohsadze 	 */
1204a07c778SMykola Hohsadze 	TYPE_04,
1218a1867f4SWojciech Macek };
1228a1867f4SWojciech Macek 
1238a1867f4SWojciech Macek /*
1248a1867f4SWojciech Macek  * Structure representing single parsed instruction format.
1258a1867f4SWojciech Macek  * name   - opcode name
1268a1867f4SWojciech Macek  * format - opcode format in a human-readable way
1278a1867f4SWojciech Macek  * type   - syntax type for printing
1288a1867f4SWojciech Macek  * special_ops  - special options passed to a printer (if any)
1298a1867f4SWojciech Macek  * mask   - bitmask for instruction matching
1308a1867f4SWojciech Macek  * pattern      - pattern to look for
1318a1867f4SWojciech Macek  * tokens - array of tokens (operands) inside instruction
1328a1867f4SWojciech Macek  */
1338a1867f4SWojciech Macek struct arm64_insn {
1348a1867f4SWojciech Macek 	char *name;
1358a1867f4SWojciech Macek 	char *format;
1368a1867f4SWojciech Macek 	enum arm64_format_type type;
1378a1867f4SWojciech Macek 	uint64_t special_ops;
1388a1867f4SWojciech Macek 	uint32_t mask;
1398a1867f4SWojciech Macek 	uint32_t pattern;
1408a1867f4SWojciech Macek 	struct arm64_insn_token tokens[ARM64_MAX_TOKEN_CNT];
1418a1867f4SWojciech Macek };
1428a1867f4SWojciech Macek 
1438a1867f4SWojciech Macek /*
1448a1867f4SWojciech Macek  * Specify instruction opcode format in a human-readable way. Use notation
1458a1867f4SWojciech Macek  * obtained from ARM Architecture Reference Manual for ARMv8-A.
1468a1867f4SWojciech Macek  *
1478a1867f4SWojciech Macek  * Format string description:
1488a1867f4SWojciech Macek  *  Each group must be separated by "|". Group made of 0/1 is used to
1498a1867f4SWojciech Macek  *  generate mask and pattern for instruction matching. Groups containing
1508a1867f4SWojciech Macek  *  an operand token (in format NAME(length_bits)) are used to retrieve any
1518a1867f4SWojciech Macek  *  operand data from the instruction. Names here must be meaningful
1528a1867f4SWojciech Macek  *  and match the one described in the Manual.
1538a1867f4SWojciech Macek  *
1548a1867f4SWojciech Macek  * Token description:
1558a1867f4SWojciech Macek  * SF     - "0" represents 32-bit access, "1" represents 64-bit access
1568a1867f4SWojciech Macek  * SHIFT  - type of shift (instruction dependent)
1578a1867f4SWojciech Macek  * IMM    - immediate value
1588a1867f4SWojciech Macek  * Rx     - register number
159c7fc655fSWojciech Macek  * OPTION - command specific options
160c7fc655fSWojciech Macek  * SCALE  - scaling of immediate value
1618a1867f4SWojciech Macek  */
1628a1867f4SWojciech Macek static struct arm64_insn arm64_i[] = {
163c7fc655fSWojciech Macek 	{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
1649f60b8ceSMykola Hohsadze 	    TYPE_01, 0 },			/* add shifted register */
165c7fc655fSWojciech Macek 	{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
1669f60b8ceSMykola Hohsadze 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* mov (to/from sp) */
167c7fc655fSWojciech Macek 	{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
1689f60b8ceSMykola Hohsadze 	    TYPE_01, OP_RD_SP | OP_RN_SP },	/* add immediate */
169ffa75b57SMykola Hohsadze 	{ "cmn", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
170ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* cmn shifted register */
171ffa75b57SMykola Hohsadze 	{ "adds", "SF(1)|0101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
172ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* adds shifted register */
173c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
174e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
175e57b8626SMykola Hohsadze 	    /* ldr immediate post/pre index */
176c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
177e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldr immediate unsigned */
178c7fc655fSWojciech Macek 	{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
179e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldr register */
180c7fc655fSWojciech Macek 	{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
181c7fc655fSWojciech Macek 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldr literal */
182c7fc655fSWojciech Macek 	{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
183e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },
1849f60b8ceSMykola Hohsadze 	    /* ldrb immediate post/pre index */
185c7fc655fSWojciech Macek 	{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
186e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrb immediate unsigned */
187c7fc655fSWojciech Macek 	{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
188e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrb register */
189c7fc655fSWojciech Macek 	{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
190e57b8626SMykola Hohsadze 	    OP_SIGN_EXT | OP_SF32 },
191e57b8626SMykola Hohsadze 	    /* ldrh immediate post/pre index */
192c7fc655fSWojciech Macek 	{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
193e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrh immediate unsigned */
194c7fc655fSWojciech Macek 	{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
195e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* ldrh register */
196c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
197e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },
1989f60b8ceSMykola Hohsadze 	    /* ldrsb immediate post/pre index */
199c7fc655fSWojciech Macek 	{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
200e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsb immediate unsigned */
201c7fc655fSWojciech Macek 	{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
202e57b8626SMykola Hohsadze 	    TYPE_02,  OP_SF_INV },		/* ldrsb register */
203c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
204e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF_INV },
2059f60b8ceSMykola Hohsadze 	    /* ldrsh immediate post/pre index */
206c7fc655fSWojciech Macek 	{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
207e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsh immediate unsigned */
208c7fc655fSWojciech Macek 	{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
209e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF_INV },		/* ldrsh register */
210c7fc655fSWojciech Macek 	{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
211e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
212e57b8626SMykola Hohsadze 	    /* ldrsw immediate post/pre index */
213c7fc655fSWojciech Macek 	{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
214e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldrsw immediate unsigned */
215c7fc655fSWojciech Macek 	{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
216e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* ldrsw register */
217c7fc655fSWojciech Macek 	{ "ldrsw", "10011000|IMM(19)|RT(5)",
2189f60b8ceSMykola Hohsadze 	    TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 },	/* ldrsw literal */
2197edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
220e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT },
221e57b8626SMykola Hohsadze 	    /* str immediate post/pre index */
2227edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|11100100|IMM(12)|RN(5)|RT(5)",
223e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* str immediate unsigned */
2247edb7adfSMykola Hohsadze 	{ "str", "1|SF(1)|111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
225e57b8626SMykola Hohsadze 	    TYPE_02, 0 },			/* str register */
2267edb7adfSMykola Hohsadze 	{ "strb", "00111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
227e57b8626SMykola Hohsadze 	    TYPE_02, OP_SIGN_EXT | OP_SF32 },
2289f60b8ceSMykola Hohsadze 	    /* strb immediate post/pre index */
2297edb7adfSMykola Hohsadze 	{ "strb", "0011100100|IMM(12)|RN(5)|RT(5)",
230e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* strb immediate unsigned */
2317edb7adfSMykola Hohsadze 	{ "strb", "00111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
232e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },			/* strb register */
2337edb7adfSMykola Hohsadze 	{ "strh", "01111000000|IMM(9)|OPTION(2)|RN(5)|RT(5)",
234e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 | OP_SIGN_EXT },
2359f60b8ceSMykola Hohsadze 	    /* strh immediate post/pre index */
2367edb7adfSMykola Hohsadze 	{ "strh", "0111100100|IMM(12)|RN(5)|RT(5)",
237e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },
2389f60b8ceSMykola Hohsadze 	    /* strh immediate unsigned */
2397edb7adfSMykola Hohsadze 	{ "strh", "01111000001|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
240e57b8626SMykola Hohsadze 	    TYPE_02, OP_SF32 },
2419f60b8ceSMykola Hohsadze 	    /* strh register */
242ffa75b57SMykola Hohsadze 	{ "neg", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)",
243ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* neg shifted register */
244ffa75b57SMykola Hohsadze 	{ "sub", "SF(1)|1001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
245ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* sub shifted register */
246ffa75b57SMykola Hohsadze 	{ "cmp", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
247ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* cmp shifted register */
248ffa75b57SMykola Hohsadze 	{ "negs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|11111|RD(5)",
249ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* negs shifted register */
250ffa75b57SMykola Hohsadze 	{ "subs", "SF(1)|1101011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
251ffa75b57SMykola Hohsadze 	    TYPE_01, 0 },			/* subs shifted register */
2529aef25d2SMykola Hohsadze 	{ "mvn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|11111|RD(5)",
2539aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* mvn shifted register */
2549aef25d2SMykola Hohsadze 	{ "orn", "SF(1)|0101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2559aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* orn shifted register */
2569aef25d2SMykola Hohsadze 	{ "mov", "SF(1)|0101010000|RM(5)|000000|11111|RD(5)",
2579aef25d2SMykola Hohsadze 	    TYPE_01, 0 },			/* mov register */
2589aef25d2SMykola Hohsadze 	{ "orr", "SF(1)|0101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2599aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* orr shifted register */
2609aef25d2SMykola Hohsadze 	{ "and", "SF(1)|0001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2619aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* and shifted register */
2629aef25d2SMykola Hohsadze 	{ "tst", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|11111",
2639aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* tst shifted register */
2649aef25d2SMykola Hohsadze 	{ "ands", "SF(1)|1101010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2659aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* ands shifted register */
2669aef25d2SMykola Hohsadze 	{ "bic", "SF(1)|0001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2679aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* bic shifted register */
2689aef25d2SMykola Hohsadze 	{ "bics", "SF(1)|1101010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2699aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* bics shifted register */
2709aef25d2SMykola Hohsadze 	{ "eon", "SF(1)|1001010|SHIFT(2)|1|RM(5)|IMM(6)|RN(5)|RD(5)",
2719aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* eon shifted register */
2729aef25d2SMykola Hohsadze 	{ "eor", "SF(1)|1001010|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
2739aef25d2SMykola Hohsadze 	    TYPE_01, OP_SHIFT_ROR },		/* eor shifted register */
2744a07c778SMykola Hohsadze 	{ "add", "SF(1)|0001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2754a07c778SMykola Hohsadze 	    TYPE_04, OP_RD_SP },		/* add extended register */
2764a07c778SMykola Hohsadze 	{ "cmn", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111",
2774a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* cmn extended register */
2784a07c778SMykola Hohsadze 	{ "adds", "SF(1)|0101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2794a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* adds extended register */
2804a07c778SMykola Hohsadze 	{ "sub", "SF(1)|1001011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2814a07c778SMykola Hohsadze 	    TYPE_04, OP_RD_SP },		/* sub extended register */
2824a07c778SMykola Hohsadze 	{ "cmp", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|11111",
2834a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* cmp extended register */
2844a07c778SMykola Hohsadze 	{ "subs", "SF(1)|1101011001|RM(5)|OPTION(3)|IMM(3)|RN(5)|RD(5)",
2854a07c778SMykola Hohsadze 	    TYPE_04, 0 },			/* subs extended register */
2868a1867f4SWojciech Macek 	{ NULL, NULL }
2878a1867f4SWojciech Macek };
2888a1867f4SWojciech Macek 
2898a1867f4SWojciech Macek static void
2908a1867f4SWojciech Macek arm64_disasm_generate_masks(struct arm64_insn *tab)
2918a1867f4SWojciech Macek {
2928a1867f4SWojciech Macek 	uint32_t mask, val;
2938a1867f4SWojciech Macek 	int a, i;
2948a1867f4SWojciech Macek 	int len, ret;
2958a1867f4SWojciech Macek 	int token = 0;
2968a1867f4SWojciech Macek 	char *format;
2978a1867f4SWojciech Macek 	int error;
2988a1867f4SWojciech Macek 
2998a1867f4SWojciech Macek 	while (tab->name != NULL) {
3008a1867f4SWojciech Macek 		mask = 0;
3018a1867f4SWojciech Macek 		val = 0;
3028a1867f4SWojciech Macek 		format = tab->format;
3038a1867f4SWojciech Macek 		token = 0;
3048a1867f4SWojciech Macek 		error = 0;
3058a1867f4SWojciech Macek 
3068a1867f4SWojciech Macek 		/*
3078a1867f4SWojciech Macek 		 * For each entry analyze format strings from the
3088a1867f4SWojciech Macek 		 * left (i.e. from the MSB).
3098a1867f4SWojciech Macek 		 */
3108a1867f4SWojciech Macek 		a = (INSN_SIZE * NBBY) - 1;
3118a1867f4SWojciech Macek 		while (*format != '\0' && (a >= 0)) {
3128a1867f4SWojciech Macek 			switch (*format) {
3138a1867f4SWojciech Macek 			case '0':
3148a1867f4SWojciech Macek 				/* Bit is 0, add to mask and pattern */
3158a1867f4SWojciech Macek 				mask |= (1 << a);
3168a1867f4SWojciech Macek 				a--;
3178a1867f4SWojciech Macek 				format++;
3188a1867f4SWojciech Macek 				break;
3198a1867f4SWojciech Macek 			case '1':
3208a1867f4SWojciech Macek 				/* Bit is 1, add to mask and pattern */
3218a1867f4SWojciech Macek 				mask |= (1 << a);
3228a1867f4SWojciech Macek 				val |= (1 << a);
3238a1867f4SWojciech Macek 				a--;
3248a1867f4SWojciech Macek 				format++;
3258a1867f4SWojciech Macek 				break;
3268a1867f4SWojciech Macek 			case '|':
3278a1867f4SWojciech Macek 				/* skip */
3288a1867f4SWojciech Macek 				format++;
3298a1867f4SWojciech Macek 				break;
3308a1867f4SWojciech Macek 			default:
3318a1867f4SWojciech Macek 				/* Token found, copy the name */
3328a1867f4SWojciech Macek 				memset(tab->tokens[token].name, 0,
3338a1867f4SWojciech Macek 				    sizeof(tab->tokens[token].name));
3348a1867f4SWojciech Macek 				i = 0;
3358a1867f4SWojciech Macek 				while (*format != '(') {
3368a1867f4SWojciech Macek 					tab->tokens[token].name[i] = *format;
3378a1867f4SWojciech Macek 					i++;
3388a1867f4SWojciech Macek 					format++;
3398a1867f4SWojciech Macek 					if (i >= ARM64_MAX_TOKEN_LEN) {
3405b61ad4bSMykola Hohsadze 						printf("ERROR: "
3415b61ad4bSMykola Hohsadze 						    "token too long in op %s\n",
3428a1867f4SWojciech Macek 						    tab->name);
3438a1867f4SWojciech Macek 						error = 1;
3448a1867f4SWojciech Macek 						break;
3458a1867f4SWojciech Macek 					}
3468a1867f4SWojciech Macek 				}
3478a1867f4SWojciech Macek 				if (error != 0)
3488a1867f4SWojciech Macek 					break;
3498a1867f4SWojciech Macek 
3508a1867f4SWojciech Macek 				/* Read the length value */
3518a1867f4SWojciech Macek 				ret = sscanf(format, "(%d)", &len);
3528a1867f4SWojciech Macek 				if (ret == 1) {
3538a1867f4SWojciech Macek 					if (token >= ARM64_MAX_TOKEN_CNT) {
3545b61ad4bSMykola Hohsadze 						printf("ERROR: "
3555b61ad4bSMykola Hohsadze 						    "too many tokens in op %s\n",
3568a1867f4SWojciech Macek 						    tab->name);
3578a1867f4SWojciech Macek 						error = 1;
3588a1867f4SWojciech Macek 						break;
3598a1867f4SWojciech Macek 					}
3608a1867f4SWojciech Macek 
3618a1867f4SWojciech Macek 					a -= len;
3628a1867f4SWojciech Macek 					tab->tokens[token].pos = a + 1;
3638a1867f4SWojciech Macek 					tab->tokens[token].len = len;
3648a1867f4SWojciech Macek 					token++;
3658a1867f4SWojciech Macek 				}
3668a1867f4SWojciech Macek 
3678a1867f4SWojciech Macek 				/* Skip to the end of the token */
3688a1867f4SWojciech Macek 				while (*format != 0 && *format != '|')
3698a1867f4SWojciech Macek 					format++;
3708a1867f4SWojciech Macek 			}
3718a1867f4SWojciech Macek 		}
3728a1867f4SWojciech Macek 
3738a1867f4SWojciech Macek 		/* Write mask and pattern to the instruction array */
3748a1867f4SWojciech Macek 		tab->mask = mask;
3758a1867f4SWojciech Macek 		tab->pattern = val;
3768a1867f4SWojciech Macek 
3778a1867f4SWojciech Macek 		/*
3788a1867f4SWojciech Macek 		 * If we got here, format string must be parsed and "a"
3798a1867f4SWojciech Macek 		 * should point to -1. If it's not, wrong number of bits
3808a1867f4SWojciech Macek 		 * in format string. Mark this as invalid and prevent
3818a1867f4SWojciech Macek 		 * from being matched.
3828a1867f4SWojciech Macek 		 */
3838a1867f4SWojciech Macek 		if (*format != 0 || (a != -1) || (error != 0)) {
3848a1867f4SWojciech Macek 			tab->mask = 0;
3858a1867f4SWojciech Macek 			tab->pattern = 0xffffffff;
3868a1867f4SWojciech Macek 			printf("ERROR: skipping instruction op %s\n",
3878a1867f4SWojciech Macek 			    tab->name);
3888a1867f4SWojciech Macek 		}
3898a1867f4SWojciech Macek 
3908a1867f4SWojciech Macek 		tab++;
3918a1867f4SWojciech Macek 	}
3928a1867f4SWojciech Macek }
3938a1867f4SWojciech Macek 
3948a1867f4SWojciech Macek static int
3958a1867f4SWojciech Macek arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
3968a1867f4SWojciech Macek     const char *token, int *val)
3978a1867f4SWojciech Macek {
3988a1867f4SWojciech Macek 	int i;
3998a1867f4SWojciech Macek 
4008a1867f4SWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
4018a1867f4SWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
4028a1867f4SWojciech Macek 			*val = (opcode >> insn->tokens[i].pos &
4038a1867f4SWojciech Macek 			    ((1 << insn->tokens[i].len) - 1));
4048a1867f4SWojciech Macek 			return (0);
4058a1867f4SWojciech Macek 		}
4068a1867f4SWojciech Macek 	}
4078a1867f4SWojciech Macek 
4088a1867f4SWojciech Macek 	return (EINVAL);
4098a1867f4SWojciech Macek }
4108a1867f4SWojciech Macek 
411c7fc655fSWojciech Macek static int
412c7fc655fSWojciech Macek arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
413c7fc655fSWojciech Macek     const char *token, int *val)
414c7fc655fSWojciech Macek {
415c7fc655fSWojciech Macek 	int i;
416c7fc655fSWojciech Macek 	int msk;
417c7fc655fSWojciech Macek 
418c7fc655fSWojciech Macek 	for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
419c7fc655fSWojciech Macek 		if (strcmp(insn->tokens[i].name, token) == 0) {
420c7fc655fSWojciech Macek 			msk = (1 << insn->tokens[i].len) - 1;
421c7fc655fSWojciech Macek 			*val = ((opcode >> insn->tokens[i].pos) & msk);
422c7fc655fSWojciech Macek 
423c7fc655fSWojciech Macek 			/* If last bit is 1, sign-extend the value */
424c7fc655fSWojciech Macek 			if (*val & (1 << (insn->tokens[i].len - 1)))
425c7fc655fSWojciech Macek 				*val |= ~msk;
426c7fc655fSWojciech Macek 
427c7fc655fSWojciech Macek 			return (0);
428c7fc655fSWojciech Macek 		}
429c7fc655fSWojciech Macek 	}
430c7fc655fSWojciech Macek 
431c7fc655fSWojciech Macek 	return (EINVAL);
432c7fc655fSWojciech Macek }
433c7fc655fSWojciech Macek 
4348a1867f4SWojciech Macek static const char *
4354a07c778SMykola Hohsadze arm64_disasm_reg_extend(int sf, int option, int rd, int rn, int amount)
4364a07c778SMykola Hohsadze {
4374a07c778SMykola Hohsadze 	bool is_sp, lsl_preferred_uxtw, lsl_preferred_uxtx, lsl_preferred;
4384a07c778SMykola Hohsadze 
4394a07c778SMykola Hohsadze 	is_sp = rd == 31 || rn == 31;
4404a07c778SMykola Hohsadze 	lsl_preferred_uxtw = sf == 0 && option == 2;
4414a07c778SMykola Hohsadze 	lsl_preferred_uxtx = sf == 1 && option == 3;
4424a07c778SMykola Hohsadze 	lsl_preferred = is_sp && (lsl_preferred_uxtw || lsl_preferred_uxtx);
4434a07c778SMykola Hohsadze 
4444a07c778SMykola Hohsadze 	/*
4454a07c778SMykola Hohsadze 	 * LSL may be omitted when <amount> is 0.
4464a07c778SMykola Hohsadze 	 * In all other cases <extend> is required.
4474a07c778SMykola Hohsadze 	 */
4484a07c778SMykola Hohsadze 	if (lsl_preferred && amount == 0)
4494a07c778SMykola Hohsadze 		return (NULL);
4504a07c778SMykola Hohsadze 	if (lsl_preferred)
4514a07c778SMykola Hohsadze 		return ("lsl");
4524a07c778SMykola Hohsadze 	return (extend_types[option]);
4534a07c778SMykola Hohsadze }
4544a07c778SMykola Hohsadze 
4554a07c778SMykola Hohsadze static const char *
4569f60b8ceSMykola Hohsadze arm64_w_reg(int num, int wsp)
4578a1867f4SWojciech Macek {
4589f60b8ceSMykola Hohsadze 	if (num == 31)
4599f60b8ceSMykola Hohsadze 		return (wsp != 0 ? "wsp" : "wzr");
4608a1867f4SWojciech Macek 	return (w_reg[num]);
4618a1867f4SWojciech Macek }
4628a1867f4SWojciech Macek 
4639f60b8ceSMykola Hohsadze static const char *
4649f60b8ceSMykola Hohsadze arm64_x_reg(int num, int sp)
4659f60b8ceSMykola Hohsadze {
4669f60b8ceSMykola Hohsadze 	if (num == 31)
4679f60b8ceSMykola Hohsadze 		return (sp != 0 ? "sp" : "xzr");
4689f60b8ceSMykola Hohsadze 	return (x_reg[num]);
4699f60b8ceSMykola Hohsadze }
4709f60b8ceSMykola Hohsadze 
4719f60b8ceSMykola Hohsadze static const char *
4729f60b8ceSMykola Hohsadze arm64_reg(int b64, int num, int sp)
4739f60b8ceSMykola Hohsadze {
4749f60b8ceSMykola Hohsadze 	if (b64 != 0)
4759f60b8ceSMykola Hohsadze 		return (arm64_x_reg(num, sp));
4769f60b8ceSMykola Hohsadze 	return (arm64_w_reg(num, sp));
4779f60b8ceSMykola Hohsadze }
4789f60b8ceSMykola Hohsadze 
4794a07c778SMykola Hohsadze /*
4804a07c778SMykola Hohsadze  * Decodes OPTION(3) to get <Xn|Wn> register or <WZR|XZR>
4814a07c778SMykola Hohsadze  * for extended register instruction.
4824a07c778SMykola Hohsadze  */
4834a07c778SMykola Hohsadze static const char *
4844a07c778SMykola Hohsadze arm64_disasm_reg_width(int option, int reg)
4854a07c778SMykola Hohsadze {
4864a07c778SMykola Hohsadze 	if (option == 3 || option == 7)
4874a07c778SMykola Hohsadze 		return (arm64_x_reg(reg, 0));
4884a07c778SMykola Hohsadze 	return (arm64_w_reg(reg, 0));
4894a07c778SMykola Hohsadze }
4904a07c778SMykola Hohsadze 
4918a1867f4SWojciech Macek vm_offset_t
4928a1867f4SWojciech Macek disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
4938a1867f4SWojciech Macek {
4948a1867f4SWojciech Macek 	struct arm64_insn *i_ptr = arm64_i;
4958a1867f4SWojciech Macek 	uint32_t insn;
4968a1867f4SWojciech Macek 	int matchp;
4978a1867f4SWojciech Macek 	int ret;
498c7fc655fSWojciech Macek 	int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
499c7fc655fSWojciech Macek 	int sign_ext;
500ffa75b57SMykola Hohsadze 	bool rm_absent, rd_absent, rn_absent;
501c7fc655fSWojciech Macek 	/* Indicate if immediate should be outside or inside brackets */
502c7fc655fSWojciech Macek 	int inside;
503c7fc655fSWojciech Macek 	/* Print exclamation mark if pre-incremented */
504c7fc655fSWojciech Macek 	int pre;
5059f60b8ceSMykola Hohsadze 	/* Indicate if x31 register should be printed as sp or xzr */
5069f60b8ceSMykola Hohsadze 	int rm_sp, rt_sp, rd_sp, rn_sp;
5079aef25d2SMykola Hohsadze 	/* Indicate if shift type ror is supported */
5089aef25d2SMykola Hohsadze 	bool has_shift_ror;
5098a1867f4SWojciech Macek 
5104a07c778SMykola Hohsadze 	const char *extend;
5114a07c778SMykola Hohsadze 
5128a1867f4SWojciech Macek 	/* Initialize defaults, all are 0 except SF indicating 64bit access */
513c7fc655fSWojciech Macek 	shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
514c7fc655fSWojciech Macek 	sign_ext = 0;
5158a1867f4SWojciech Macek 	sf = 1;
5164a07c778SMykola Hohsadze 	extend = NULL;
5178a1867f4SWojciech Macek 
5188a1867f4SWojciech Macek 	matchp = 0;
5198a1867f4SWojciech Macek 	insn = di->di_readword(loc);
5208a1867f4SWojciech Macek 	while (i_ptr->name) {
5218a1867f4SWojciech Macek 		/* If mask is 0 then the parser was not initialized yet */
5228a1867f4SWojciech Macek 		if ((i_ptr->mask != 0) &&
5238a1867f4SWojciech Macek 		    ((insn & i_ptr->mask) == i_ptr->pattern)) {
5248a1867f4SWojciech Macek 			matchp = 1;
5258a1867f4SWojciech Macek 			break;
5268a1867f4SWojciech Macek 		}
5278a1867f4SWojciech Macek 		i_ptr++;
5288a1867f4SWojciech Macek 	}
5298a1867f4SWojciech Macek 	if (matchp == 0)
5308a1867f4SWojciech Macek 		goto undefined;
5318a1867f4SWojciech Macek 
532c7fc655fSWojciech Macek 	/* Global options */
533c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF32)
534c7fc655fSWojciech Macek 		sf = 0;
535c7fc655fSWojciech Macek 
536c7fc655fSWojciech Macek 	/* Global optional tokens */
537c7fc655fSWojciech Macek 	arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
538c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SF_INV)
539c7fc655fSWojciech Macek 		sf = 1 - sf;
540c7fc655fSWojciech Macek 	if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
541c7fc655fSWojciech Macek 		sign_ext = 1 - sign_ext;
542c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_SIGN_EXT)
543c7fc655fSWojciech Macek 		sign_ext = 1;
544c7fc655fSWojciech Macek 	if (sign_ext != 0)
545c7fc655fSWojciech Macek 		arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
546c7fc655fSWojciech Macek 	else
547c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
548c7fc655fSWojciech Macek 	if (i_ptr->special_ops & OP_MULT_4)
549c7fc655fSWojciech Macek 		imm <<= 2;
550c7fc655fSWojciech Macek 
5519f60b8ceSMykola Hohsadze 	rm_sp = i_ptr->special_ops & OP_RM_SP;
5529f60b8ceSMykola Hohsadze 	rt_sp = i_ptr->special_ops & OP_RT_SP;
5539f60b8ceSMykola Hohsadze 	rd_sp = i_ptr->special_ops & OP_RD_SP;
5549f60b8ceSMykola Hohsadze 	rn_sp = i_ptr->special_ops & OP_RN_SP;
5559f60b8ceSMykola Hohsadze 
5569aef25d2SMykola Hohsadze 	has_shift_ror = i_ptr->special_ops & OP_SHIFT_ROR;
5579aef25d2SMykola Hohsadze 
558c7fc655fSWojciech Macek 	/* Print opcode by type */
5598a1867f4SWojciech Macek 	switch (i_ptr->type) {
5608a1867f4SWojciech Macek 	case TYPE_01:
5615b61ad4bSMykola Hohsadze 		/*
5625b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
5635b61ad4bSMykola Hohsadze 		 * OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
564ffa75b57SMykola Hohsadze 		 * OP <RD>, <RM> {, <shift> #<imm> }
565ffa75b57SMykola Hohsadze 		 * OP <RN>, <RM> {, <shift> #<imm> }
5665b61ad4bSMykola Hohsadze 		 */
5678a1867f4SWojciech Macek 
568ffa75b57SMykola Hohsadze 		rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
569ffa75b57SMykola Hohsadze 		rn_absent = arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
5708a1867f4SWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
571ffa75b57SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
5728a1867f4SWojciech Macek 
5739aef25d2SMykola Hohsadze 		/*
5749aef25d2SMykola Hohsadze 		 * if shift type is RESERVED for shifted register instruction,
5759aef25d2SMykola Hohsadze 		 * print undefined
5769aef25d2SMykola Hohsadze 		 */
5779aef25d2SMykola Hohsadze 		if (shift == 3 && !has_shift_ror)
5789aef25d2SMykola Hohsadze 			goto undefined;
5799aef25d2SMykola Hohsadze 
580ffa75b57SMykola Hohsadze 		di->di_printf("%s\t", i_ptr->name);
581ffa75b57SMykola Hohsadze 
582ffa75b57SMykola Hohsadze 		/*
583ffa75b57SMykola Hohsadze 		 * If RD and RN are present, we will display the following
584ffa75b57SMykola Hohsadze 		 * patterns:
585ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
586ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64
587ffa75b57SMykola Hohsadze 		 * Otherwise if only RD is present:
588ffa75b57SMykola Hohsadze 		 * - OP <RD>, <RM> {, <shift> #<imm> }
589ffa75b57SMykola Hohsadze 		 * Otherwise if only RN is present:
590ffa75b57SMykola Hohsadze 		 * - OP <RN>, <RM> {, <shift> #<imm> }
591ffa75b57SMykola Hohsadze 		 */
592ffa75b57SMykola Hohsadze 		if (!rd_absent && !rn_absent)
593ffa75b57SMykola Hohsadze 			di->di_printf("%s, %s", arm64_reg(sf, rd, rd_sp),
594ffa75b57SMykola Hohsadze 			    arm64_reg(sf, rn, rn_sp));
595ffa75b57SMykola Hohsadze 		else if (!rd_absent)
596ffa75b57SMykola Hohsadze 			di->di_printf("%s", arm64_reg(sf, rd, rd_sp));
597ffa75b57SMykola Hohsadze 		else
598ffa75b57SMykola Hohsadze 			di->di_printf("%s", arm64_reg(sf, rn, rn_sp));
5998a1867f4SWojciech Macek 
6008a1867f4SWojciech Macek 		/* If RM is present use it, otherwise use immediate notation */
601ffa75b57SMykola Hohsadze 		if (!rm_absent) {
6029f60b8ceSMykola Hohsadze 			di->di_printf(", %s", arm64_reg(sf, rm, rm_sp));
6038a1867f4SWojciech Macek 			if (imm != 0)
6048a1867f4SWojciech Macek 				di->di_printf(", %s #%d", shift_2[shift], imm);
6058a1867f4SWojciech Macek 		} else {
6068a1867f4SWojciech Macek 			if (imm != 0 || shift != 0)
6078a1867f4SWojciech Macek 				di->di_printf(", #0x%x", imm);
6088a1867f4SWojciech Macek 			if (shift != 0)
6098a852d3eSMykola Hohsadze 				di->di_printf(" lsl #12");
6108a1867f4SWojciech Macek 		}
6118a1867f4SWojciech Macek 		break;
612c7fc655fSWojciech Macek 	case TYPE_02:
6135b61ad4bSMykola Hohsadze 		/*
614e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>, #<simm>]!
615e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>], #<simm>
616e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP> {, #<pimm> }]
617e57b8626SMykola Hohsadze 		 * OP <RT>, [<XN|SP>, <RM> {, EXTEND AMOUNT }]
6185b61ad4bSMykola Hohsadze 		 */
619c7fc655fSWojciech Macek 
620c7fc655fSWojciech Macek 		/* Mandatory tokens */
621c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
622c7fc655fSWojciech Macek 		ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
623c7fc655fSWojciech Macek 		if (ret != 0) {
6245b61ad4bSMykola Hohsadze 			printf("ERROR: "
6255b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
626c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
627c7fc655fSWojciech Macek 			goto undefined;
628c7fc655fSWojciech Macek 		}
629c7fc655fSWojciech Macek 
630c7fc655fSWojciech Macek 		/* Optional tokens */
631c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
632c7fc655fSWojciech Macek 		arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
633c7fc655fSWojciech Macek 		rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
634c7fc655fSWojciech Macek 
635c7fc655fSWojciech Macek 		if (rm_absent) {
636c7fc655fSWojciech Macek 			/*
637c7fc655fSWojciech Macek 			 * In unsigned operation, shift immediate value
638c7fc655fSWojciech Macek 			 * and reset options to default.
639c7fc655fSWojciech Macek 			 */
640c7fc655fSWojciech Macek 			if (sign_ext == 0) {
641c7fc655fSWojciech Macek 				imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
642c7fc655fSWojciech Macek 				    ARM_INSN_SIZE_MASK);
643c7fc655fSWojciech Macek 				option = 0;
644c7fc655fSWojciech Macek 			}
645c7fc655fSWojciech Macek 			switch (option) {
646c7fc655fSWojciech Macek 			case 0x0:
647c7fc655fSWojciech Macek 				pre = 0;
648c7fc655fSWojciech Macek 				inside = 1;
649c7fc655fSWojciech Macek 				break;
650c7fc655fSWojciech Macek 			case 0x1:
651c7fc655fSWojciech Macek 				pre = 0;
652c7fc655fSWojciech Macek 				inside = 0;
653c7fc655fSWojciech Macek 				break;
654c7fc655fSWojciech Macek 			case 0x2:
655c7fc655fSWojciech Macek 			default:
656c7fc655fSWojciech Macek 				pre = 1;
657c7fc655fSWojciech Macek 				inside = 1;
658c7fc655fSWojciech Macek 				break;
659c7fc655fSWojciech Macek 			}
660c7fc655fSWojciech Macek 
6615b61ad4bSMykola Hohsadze 			di->di_printf("%s\t%s, ", i_ptr->name,
6629f60b8ceSMykola Hohsadze 			    arm64_reg(sf, rt, rt_sp));
663c7fc655fSWojciech Macek 			if (inside != 0) {
664e57b8626SMykola Hohsadze 				di->di_printf("[%s", arm64_x_reg(rn, 1));
665c7fc655fSWojciech Macek 				if (imm != 0)
666c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
667c7fc655fSWojciech Macek 				di->di_printf("]");
668c7fc655fSWojciech Macek 			} else {
669e57b8626SMykola Hohsadze 				di->di_printf("[%s]", arm64_x_reg(rn, 1));
670c7fc655fSWojciech Macek 				if (imm != 0)
671c7fc655fSWojciech Macek 					di->di_printf(", #%d", imm);
672c7fc655fSWojciech Macek 			}
673c7fc655fSWojciech Macek 			if (pre != 0)
674c7fc655fSWojciech Macek 				di->di_printf("!");
675c7fc655fSWojciech Macek 		} else {
676c7fc655fSWojciech Macek 			/* Last bit of option field determines 32/64 bit offset */
677c7fc655fSWojciech Macek 			di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
678e57b8626SMykola Hohsadze 			    arm64_reg(sf, rt, rt_sp), arm64_x_reg(rn, 1),
6799f60b8ceSMykola Hohsadze 			    arm64_reg(option & 1, rm, rm_sp));
680c7fc655fSWojciech Macek 
681cb923f03SMykola Hohsadze 			if (scale == 0)
682cb923f03SMykola Hohsadze 				amount = 0;
683cb923f03SMykola Hohsadze 			else {
684c7fc655fSWojciech Macek 				/* Calculate amount, it's op(31:30) */
685c7fc655fSWojciech Macek 				amount = (insn >> ARM_INSN_SIZE_OFFSET) &
686c7fc655fSWojciech Macek 			            ARM_INSN_SIZE_MASK;
687cb923f03SMykola Hohsadze 			}
688c7fc655fSWojciech Macek 
689c7fc655fSWojciech Macek 			switch (option) {
690c7fc655fSWojciech Macek 			case 0x2:
691c7fc655fSWojciech Macek 				di->di_printf(", uxtw #%d", amount);
692c7fc655fSWojciech Macek 				break;
693c7fc655fSWojciech Macek 			case 0x3:
694c7fc655fSWojciech Macek 				if (scale != 0)
695c7fc655fSWojciech Macek 					di->di_printf(", lsl #%d", amount);
696c7fc655fSWojciech Macek 				break;
697c7fc655fSWojciech Macek 			case 0x6:
698c7fc655fSWojciech Macek 				di->di_printf(", sxtw #%d", amount);
699c7fc655fSWojciech Macek 				break;
700c7fc655fSWojciech Macek 			case 0x7:
701cb923f03SMykola Hohsadze 				di->di_printf(", sxtx #%d", amount);
702c7fc655fSWojciech Macek 				break;
703c7fc655fSWojciech Macek 			default:
7048a852d3eSMykola Hohsadze 				di->di_printf(", rsv");
705c7fc655fSWojciech Macek 				break;
706c7fc655fSWojciech Macek 			}
707c7fc655fSWojciech Macek 			di->di_printf("]");
708c7fc655fSWojciech Macek 		}
709c7fc655fSWojciech Macek 
710c7fc655fSWojciech Macek 		break;
711c7fc655fSWojciech Macek 
712c7fc655fSWojciech Macek 	case TYPE_03:
713c7fc655fSWojciech Macek 		/* OP <RT>, #imm SF32/64 */
714c7fc655fSWojciech Macek 
715c7fc655fSWojciech Macek 		/* Mandatory tokens */
716c7fc655fSWojciech Macek 		ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
717c7fc655fSWojciech Macek 		if (ret != 0) {
7185b61ad4bSMykola Hohsadze 			printf("ERROR: "
7195b61ad4bSMykola Hohsadze 			    "Missing mandatory token for op %s type %d\n",
720c7fc655fSWojciech Macek 			    i_ptr->name, i_ptr->type);
721c7fc655fSWojciech Macek 			goto undefined;
722c7fc655fSWojciech Macek 		}
723c7fc655fSWojciech Macek 
7249f60b8ceSMykola Hohsadze 		di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt, rt_sp));
725c7fc655fSWojciech Macek 		if (i_ptr->special_ops & OP_LITERAL)
726c7fc655fSWojciech Macek 			di->di_printf("0x%lx", loc + imm);
727c7fc655fSWojciech Macek 		else
728c7fc655fSWojciech Macek 			di->di_printf("#%d", imm);
729c7fc655fSWojciech Macek 
730c7fc655fSWojciech Macek 		break;
7314a07c778SMykola Hohsadze 
7324a07c778SMykola Hohsadze 	case TYPE_04:
7334a07c778SMykola Hohsadze 		/*
7344a07c778SMykola Hohsadze 		 * OP <RD>, <RN|SP>, <RM> {, <extend> { #<amount> } }
7354a07c778SMykola Hohsadze 		 * OP <RN|SP>, <RM>, {, <extend> { #<amount> } }
7364a07c778SMykola Hohsadze 		 */
7374a07c778SMykola Hohsadze 
7384a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
7394a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
7404a07c778SMykola Hohsadze 		arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
7414a07c778SMykola Hohsadze 
7424a07c778SMykola Hohsadze 		rd_absent = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
7434a07c778SMykola Hohsadze 		extend = arm64_disasm_reg_extend(sf, option, rd, rn, imm);
7444a07c778SMykola Hohsadze 
7454a07c778SMykola Hohsadze 		di->di_printf("%s\t", i_ptr->name);
7464a07c778SMykola Hohsadze 
7474a07c778SMykola Hohsadze 		if (!rd_absent)
7484a07c778SMykola Hohsadze 			di->di_printf("%s, ", arm64_reg(sf, rd, rd_sp));
7494a07c778SMykola Hohsadze 
7504a07c778SMykola Hohsadze 		di->di_printf("%s, ", arm64_reg(sf, rn, 1));
7514a07c778SMykola Hohsadze 
7524a07c778SMykola Hohsadze 		if (sf != 0)
7534a07c778SMykola Hohsadze 			di->di_printf("%s",
7544a07c778SMykola Hohsadze 			    arm64_disasm_reg_width(option, rm));
7554a07c778SMykola Hohsadze 		else
7564a07c778SMykola Hohsadze 			di->di_printf("%s", arm64_w_reg(rm, 0));
7574a07c778SMykola Hohsadze 
7584a07c778SMykola Hohsadze 		if (extend != NULL)
7594a07c778SMykola Hohsadze 			di->di_printf(", %s #%d", extend, imm);
7604a07c778SMykola Hohsadze 
7614a07c778SMykola Hohsadze 		break;
7628a1867f4SWojciech Macek 	default:
7638a1867f4SWojciech Macek 		goto undefined;
7648a1867f4SWojciech Macek 	}
7658a1867f4SWojciech Macek 
7668a1867f4SWojciech Macek 	di->di_printf("\n");
7678a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
7688a1867f4SWojciech Macek 
7698a1867f4SWojciech Macek undefined:
7708a1867f4SWojciech Macek 	di->di_printf("undefined\t%08x\n", insn);
7718a1867f4SWojciech Macek 	return (loc + INSN_SIZE);
7728a1867f4SWojciech Macek }
7738a1867f4SWojciech Macek 
7748a1867f4SWojciech Macek /* Parse format strings at the very beginning */
7755b61ad4bSMykola Hohsadze SYSINIT(arm64_disasm_generate_masks, SI_SUB_DDB_SERVICES, SI_ORDER_FIRST,
7765b61ad4bSMykola Hohsadze     arm64_disasm_generate_masks, arm64_i);
777