1112 lines
35 KiB
C
1112 lines
35 KiB
C
// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "assembler.h"
|
|
#include "common.h"
|
|
|
|
#define MAX_TOKENS 6
|
|
#define IS_INDIRECT_ADDRESS_REGISTER(x) ((x)=='x'||(x)=='y'||(x)=='z')
|
|
|
|
#define throw_error(msg, ...) \
|
|
do { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: " msg, ##__VA_ARGS__); \
|
|
return AVR_INVALID_SIZE; \
|
|
} while (0)
|
|
|
|
#define return_error_if_empty_input(a, b) \
|
|
do { \
|
|
if (RZ_STR_ISEMPTY(a) || b < 1) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: the input is empty.\n"); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define expected_const_or_error(a,exp) \
|
|
do { \
|
|
if (RZ_STR_ISEMPTY((a)) || strcmp((a),(exp))) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: expected '%s' but got '%s'.\n", (exp), (a)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define parse_register_or_error_limit(rn,rs,min,max) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
if (*tmp == 'r') { \
|
|
tmp++; \
|
|
} \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid register '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
(rn) = strtoll(tmp, NULL, 0); \
|
|
if ((rn) < (min) || (rn) > (max)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: expected register %u <= reg <= 31 (parsed %u).\n", (min), (rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define parse_register_or_error(rn,rs) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
if (*tmp == 'r') { \
|
|
tmp++; \
|
|
} \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid register '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
(rn) = strtoll(tmp, NULL, 0); \
|
|
if ((rn) > 31) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: expected register 0 <= reg <= 31 (parsed %u).\n", (rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
/// Parse things like "r25:r24" or just "r24". Result would be 24 in both cases.
|
|
#define parse_register_pair_or_error(rn,rs) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
if (*tmp == 'r') { \
|
|
tmp++; \
|
|
} \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid register '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
(rn) = strtoll(tmp, (char **)&tmp, 0); \
|
|
if ((rn) > 31) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: expected register 0 <= reg <= 31 (parsed %u).\n", (rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
if (*tmp == ':') { \
|
|
tmp++; \
|
|
ut16 high = (rn); \
|
|
if (*tmp == 'r') { \
|
|
tmp++; \
|
|
} \
|
|
(rn) = strtoll(tmp, NULL, 0); \
|
|
if ((rn) > 31) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: expected register 0 <= reg <= 31 (parsed %u).\n", (rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
if (high != (rn) + 1) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: register pair r%u:r%u invalid: %u != %u + 1.\n", \
|
|
(unsigned int)high, (unsigned int)(rn), (unsigned int)high, (unsigned int)(rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define parse_unsigned_or_error(rn,rs,limit) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
ut32 base = 0; \
|
|
if (tmp[0] == '$') { \
|
|
tmp++; \
|
|
base = 16; \
|
|
} \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid unsigned number '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
(rn) = strtoull(tmp, NULL, base); \
|
|
if ((rn) > (limit)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: unsigned number '%s' >= %u.\n", (rs), limit); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define parse_address_or_error(rn,rs,pc,llow,lhigh) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid address '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
if (tmp[0] == '.') { \
|
|
(rn) = (st64)strtoull(tmp + 1, NULL, 0); \
|
|
} else { \
|
|
st64 abs = strtoull(tmp, NULL, 0); \
|
|
(rn) = abs - pc; \
|
|
} \
|
|
if ((rn) < 0) { \
|
|
(rn) = ~(-((rn) - 1)); \
|
|
} else { \
|
|
(rn) -= 2; \
|
|
} \
|
|
(rn) /= 2; \
|
|
if (((rn) < (llow) || (rn) > (lhigh))) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid address -64 <= addr <= 63 (parsed %d).\n", (rn)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define parse_signed_or_error(rn,rs,min,max) \
|
|
do { \
|
|
cchar *tmp = (rs); \
|
|
if (RZ_STR_ISEMPTY(tmp)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid unsigned number '%s'.\n", (rs)); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
(rn) = atoi(tmp); \
|
|
if ((rn) < (min)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: signed number '%s' < %u.\n", (rs), min); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
if ((rn) > (max)) { \
|
|
RZ_LOG_ERROR("[!] avr_assembler: signed number '%s' > %u.\n", (rs), max); \
|
|
return AVR_INVALID_SIZE; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define auto_write16(buf,val,be) \
|
|
do { \
|
|
if (be) { \
|
|
rz_write_be16(buf, val); \
|
|
} else { \
|
|
rz_write_le16(buf, val); \
|
|
} \
|
|
} while (0)
|
|
|
|
typedef ut32 (*Encode)(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be);
|
|
|
|
typedef struct avr_decoder_t {
|
|
cchar* opcode; /* instruction name */
|
|
ut16 cbits; /* constant bits */
|
|
ut32 mintoks; /* required min token number */
|
|
ut32 maxtoks; /* required max token number */
|
|
Encode encode;
|
|
} AvrInstruction;
|
|
|
|
|
|
static ut32 avr_unique(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_rdddddrrrr(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd,Rr | 0 <= d <= 31 | 0 <= r <= 31 */
|
|
ut16 Rd, Rr;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_register_or_error(Rr, tokens[2]);
|
|
|
|
|
|
cbins |= (Rr & 0x000F);
|
|
cbins |= ((Rr << 5) & 0x0200);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_KKddKKKK(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, K | d = {24,26,28,30}, 0 <= K <= 63 */
|
|
ut16 Rd, K;
|
|
parse_register_pair_or_error(Rd, tokens[1]);
|
|
parse_unsigned_or_error(K, tokens[2], 63);
|
|
|
|
if (Rd < 24 || Rd & 1) {
|
|
throw_error("register must be Rd = {24,26,28,30} (parsed r%u)\n", Rd);
|
|
}
|
|
|
|
Rd -= 24;
|
|
Rd >>= 1;
|
|
|
|
cbins |= (K & 0x000F);
|
|
cbins |= ((K << 2) & 0x00C0);
|
|
cbins |= ((Rd << 4) & 0x0030);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_KKKKddddKKKK(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, K | 16 <= d <= 31, 0 <= K <= 255 */
|
|
ut16 Rd, K;
|
|
parse_register_or_error_limit(Rd, tokens[1], 16, 31);
|
|
parse_unsigned_or_error(K, tokens[2], 255);
|
|
|
|
Rd -= 16;
|
|
|
|
cbins |= (K & 0x000F);
|
|
cbins |= ((K << 4) & 0x0F00);
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_cbr(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, K | 16 <= d <= 31, 0 <= K <= 255 */
|
|
ut16 Rd, K;
|
|
parse_register_or_error_limit(Rd, tokens[1], 16, 31);
|
|
parse_unsigned_or_error(K, tokens[2], 255);
|
|
|
|
Rd -= 16;
|
|
K = 0xFF - K;
|
|
|
|
cbins |= (K & 0x000F);
|
|
cbins |= ((K << 4) & 0x0F00);
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_dddddcccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd | 0 <= d <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_dddddcbbb(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, b | 0 <= d <= 31, 0 <= b <= 7 */
|
|
|
|
ut16 Rd, b;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_unsigned_or_error(b, tokens[2], 7);
|
|
|
|
cbins |= (b & 0x0007);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_kkkkkkkccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> k | -64 <= k <= 63 */
|
|
st16 k;
|
|
parse_address_or_error(k, tokens[1], pc, -64, 63);
|
|
cbins |= ((k << 3) & 0x03F8);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_kkkkkccck(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
// <opcode> k | 0 <= k < 0x7ffffe
|
|
ut32 k;
|
|
parse_unsigned_or_error(k, tokens[1], 0x7ffffe);
|
|
|
|
k /= 2;
|
|
|
|
ut16 kh = k >> 16;
|
|
ut16 kl = k & 0xFFFF;
|
|
|
|
cbins |= (kh & 0x0001);
|
|
cbins |= ((kh << 3) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
auto_write16(data + 2, kl, be);
|
|
return 4;
|
|
}
|
|
|
|
|
|
static ut32 avr_AAAAAbbb(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
// CBI A,b | 0 <= A <= 31 | 0 <= b <= 7
|
|
ut16 A, b;
|
|
parse_unsigned_or_error(A, tokens[1], 31);
|
|
parse_unsigned_or_error(b, tokens[2], 7);
|
|
|
|
|
|
cbins |= (b & 0x0007);
|
|
cbins |= ((A << 3) & 0x00F8);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_dddddddddd(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd | 0 <= d <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
|
|
cbins |= (Rd & 0x000F);
|
|
cbins |= ((Rd << 5) & 0x0200);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_KKKKcccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> K | 0 <= K <= 0xF */
|
|
ut16 K;
|
|
parse_unsigned_or_error(K, tokens[1], 0xF);
|
|
|
|
cbins |= ((K << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_elpm(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
if (ntokens == 1) {
|
|
/* elpm */
|
|
/* 1001010111011000 */
|
|
cbins = 0x95D8;
|
|
} else if (ntokens == 3) {
|
|
/* elpm Rd, Z | 0 <= Rd <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
expected_const_or_error(tokens[2], "z");
|
|
|
|
/* 1001000ddddd0110 */
|
|
cbins = 0x9006;
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
} else if (ntokens == 4) {
|
|
/* elpm Rd, Z+ | 0 <= Rd <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
expected_const_or_error(tokens[2], "z");
|
|
expected_const_or_error(tokens[3], "+");
|
|
|
|
/* 1001000ddddd0111 */
|
|
cbins = 0x9007;
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
} else {
|
|
throw_error("expected 'elpm' or 'elpm Rd, M' | 0 <= d <= 31 | M = {Z,Z+}\n");
|
|
}
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
static ut32 avr_dddcrrr(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd,Rr | 16 <= d <= 23 | 16 <= r <= 23 */
|
|
ut16 Rd, Rr;
|
|
parse_register_or_error_limit(Rd, tokens[1], 16, 23);
|
|
parse_register_or_error_limit(Rr, tokens[2], 16, 23);
|
|
|
|
Rd -= 16;
|
|
Rr -= 16;
|
|
|
|
cbins |= (Rr & 0x0007);
|
|
cbins |= ((Rd << 4) & 0x0070);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_AAdddddAAAA(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, A | 0 <= d <= 31, 0 <= A <= 63 */
|
|
ut16 Rd, A;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_unsigned_or_error(A, tokens[2], 63);
|
|
|
|
cbins |= (A & 0x000F);
|
|
cbins |= ((A << 5) & 0x0600);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
static ut32 avr_rrrrrcccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd | 0 <= d <= 31 */
|
|
ut16 Rd;
|
|
expected_const_or_error(tokens[1], "z");
|
|
parse_register_or_error(Rd, tokens[2]);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
|
|
static ut32 avr_ld(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* ld Rd, M | 0 <= d <= 31 | M = {X,Y,Z,X+,Y+,Z+,-X,-Y,-Z} */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
|
|
if (ntokens == 3) {
|
|
if (!strcmp(tokens[2], "x")) {
|
|
/* ld Rd, X */
|
|
/* 1001000ddddd1100 */
|
|
cbins = 0x900C;
|
|
} else if (!strcmp(tokens[2], "y")) {
|
|
/* ld Rd, Y */
|
|
/* 1000000ddddd1000 */
|
|
cbins = 0x8008;
|
|
} else if (!strcmp(tokens[2], "z")) {
|
|
/* ld Rd, Z */
|
|
/* 1001000ddddd0000 */
|
|
cbins = 0x8000;
|
|
} else if (!strcmp(tokens[2], "-x")) {
|
|
/* ld Rd, -X */
|
|
/* 1001000ddddd1110 */
|
|
cbins = 0x900E;
|
|
} else if (!strcmp(tokens[2], "-y")) {
|
|
/* ld Rd, -Y */
|
|
/* 1001000ddddd1010 */
|
|
cbins = 0x900A;
|
|
} else if (!strcmp(tokens[2], "-z")) {
|
|
/* ld Rd, -Z */
|
|
/* 1001000ddddd0010 */
|
|
cbins = 0x9002;
|
|
} else {
|
|
throw_error("expected 'X' or 'Y' or 'Z' or '-X' or '-Y' or '-Z', but got '%s'\n", tokens[2]);
|
|
}
|
|
} else if (ntokens == 4 && !strcmp(tokens[3], "+")) {
|
|
if (!strcmp(tokens[2], "x")) {
|
|
/* ld Rd, X+ */
|
|
/* 1001000ddddd1101 */
|
|
cbins = 0x900D;
|
|
} else if (!strcmp(tokens[2], "y")) {
|
|
/* ld Rd, Y+ */
|
|
/* 1001000ddddd1001 */
|
|
cbins = 0x9009;
|
|
} else if (!strcmp(tokens[2], "z")) {
|
|
/* ld Rd, Z+ */
|
|
/* 1001000ddddd0001 */
|
|
cbins = 0x9001;
|
|
} else {
|
|
throw_error("expected 'X+' or 'Y+' or 'Z+', but got '%s+'\n", tokens[2]);
|
|
}
|
|
} else {
|
|
throw_error("expected ld Rd, M | 0 <= d <= 31 | M = {X,Y,Z,X+,Y+,Z+,-X,-Y,-Z}\n");
|
|
}
|
|
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_ldd(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* ldd Rd, M + q | 0 <= d <= 31 | M = {Y,Z} | 0 <= q <= 63*/
|
|
ut16 Rd, q;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_unsigned_or_error(q, tokens[4], 63);
|
|
|
|
if (!strcmp(tokens[3], "+")) {
|
|
if (!strcmp(tokens[2], "y")) {
|
|
/* ldd Rd, Y+q */
|
|
/* 10q0qq0ddddd1qqq */
|
|
cbins = 0x8008;
|
|
} else if (!strcmp(tokens[2], "z")) {
|
|
/* ldd Rd, Z+q */
|
|
/* 10q0qq0ddddd0qqq */
|
|
cbins = 0x8000;
|
|
} else {
|
|
throw_error("expected 'Y+' or 'Z+', but got '%s+'\n", tokens[2]);
|
|
}
|
|
} else {
|
|
throw_error("expected ldd Rd, M + q | 0 <= d <= 31 | M = {Y,Z} | 0 <= q <= 63\n");
|
|
}
|
|
|
|
cbins |= q & 0x0007;
|
|
cbins |= ((q << 7) & 0x0C00);
|
|
cbins |= ((q << 8) & 0x2000);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_lds(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
ut16 Rd;
|
|
ut32 k;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_unsigned_or_error(k, tokens[2], 0xFFFF);
|
|
|
|
#if 0
|
|
// The STS (16-bit) and LDS (16-bit) instructions only exist in the reduced AVR cores.
|
|
// This includes only the ATtiny4/5/9/10 family, and the ATtiny20/40 family.
|
|
// They also lack some features like the lack of CPU registers R0 to R15.
|
|
// On rizin these platforms are not supported, therefore this code is commented, but works as intended.
|
|
if (k <= 127 && Rd >= 16) {
|
|
/* lds Rd, k | 16 <= d <= 31 | 0 <= k <= 127 */
|
|
/* 10100kkkddddkkkk */
|
|
cbins = 0xA000;
|
|
Rd -= 16;
|
|
cbins |= k & 0x000F;
|
|
cbins |= ((k << 4) & 0x0700);
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
#endif
|
|
/* lds Rd, k | 0 <= d <= 31 | 0 <= k <= 0xFFFF */
|
|
/* 1001000ddddd0000 kkkkkkkkkkkkkkkk */
|
|
cbins = 0x9000;
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
auto_write16(data, cbins, be);
|
|
auto_write16(data + 2, k, be);
|
|
return 4;
|
|
}
|
|
|
|
|
|
static ut32 avr_lpm(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
if (ntokens == 1) {
|
|
/* lpm */
|
|
/* 1001010111001000 */
|
|
cbins = 0x95C8;
|
|
} else if (ntokens == 3) {
|
|
/* lpm Rd, Z | 0 <= Rd <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
expected_const_or_error(tokens[2], "z");
|
|
|
|
/* 1001000ddddd0100 */
|
|
cbins = 0x9004;
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
} else if (ntokens == 4) {
|
|
/* lpm Rd, Z+ | 0 <= Rd <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
expected_const_or_error(tokens[2], "z");
|
|
expected_const_or_error(tokens[3], "+");
|
|
|
|
/* 1001000ddddd0101 */
|
|
cbins = 0x9005;
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
} else {
|
|
throw_error("expected 'lpm' or 'lpm Rd, M' | 0 <= d <= 31 | M = {Z,Z+}\n");
|
|
}
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_kkkkkkkkkkkk(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> k | -4096 <= k <= 4096 */
|
|
st16 k;
|
|
parse_address_or_error(k, tokens[1], pc, -4096, 4096);
|
|
cbins |= (k & 0x0FFF);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_ddddrrrr(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, Rr | d = {0,2,...,30} | r = {0,2,...,30} */
|
|
ut16 Rd, Rr;
|
|
parse_register_or_error(Rd, tokens[1]);
|
|
parse_register_or_error(Rr, tokens[2]);
|
|
|
|
if (Rd & 1) {
|
|
throw_error("register must be even, Rd = {0,2,...,30} (parsed r%u)\n", Rd);
|
|
} else if (Rr & 1) {
|
|
throw_error("register must be even, Rr = {0,2,...,30} (parsed r%u)\n", Rr);
|
|
}
|
|
|
|
Rr /= 2;
|
|
Rd /= 2;
|
|
cbins |= Rr & 0x000F;
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_ddddrrrr_2x(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, Rr | 16 <= d <= 31 | 16 <= r <= 31 */
|
|
ut16 Rd, Rr;
|
|
parse_register_or_error_limit(Rd, tokens[1], 16, 31);
|
|
parse_register_or_error_limit(Rr, tokens[2], 16, 31);
|
|
|
|
Rr -= 16;
|
|
Rd -= 16;
|
|
cbins |= Rr & 0x000F;
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_AArrrrrAAAA(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd, A | 0 <= d <= 31, 0 <= A <= 63 */
|
|
ut16 Rd, A;
|
|
parse_unsigned_or_error(A, tokens[1], 63);
|
|
parse_register_or_error(Rd, tokens[2]);
|
|
|
|
cbins |= (A & 0x000F);
|
|
cbins |= ((A << 5) & 0x0600);
|
|
cbins |= ((Rd << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_rrrrrcbbb(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rr, b | 0 <= d <= 31, 0 <= b <= 7 */
|
|
ut16 Rr, b;
|
|
parse_register_or_error(Rr, tokens[1]);
|
|
parse_unsigned_or_error(b, tokens[2], 63);
|
|
|
|
cbins |= (b & 0x0007);
|
|
cbins |= ((Rr << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
static ut32 avr_ddddcccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> Rd | 16 <= d <= 31 */
|
|
ut16 Rd;
|
|
parse_register_or_error_limit(Rd, tokens[1], 16, 31);
|
|
|
|
Rd -= 16;
|
|
cbins |= ((Rd << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_spm(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
if (ntokens == 1) {
|
|
/* spm */
|
|
/* 1001010111101000 */
|
|
cbins = 0x95E8;
|
|
} else if (ntokens == 3) {
|
|
/* spm Z+ | 0 <= Rd <= 31 */
|
|
expected_const_or_error(tokens[1], "z");
|
|
expected_const_or_error(tokens[2], "+");
|
|
|
|
/* 1001010111111000 */
|
|
cbins = 0x95F8;
|
|
} else {
|
|
throw_error("expected 'spm' or 'spm Z+'\n");
|
|
}
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_st(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* st M, Rr | 0 <= r <= 31 | M = {X,Y,Z,-X,-Y,-Z} */
|
|
ut16 Rr;
|
|
parse_register_or_error(Rr, tokens[2]);
|
|
|
|
if (ntokens == 3) {
|
|
if (!strcmp(tokens[1], "x")) {
|
|
/* st X, Rr */
|
|
/* 1001001rrrrr1100 */
|
|
cbins = 0x920C;
|
|
} else if (!strcmp(tokens[1], "y")) {
|
|
/* st Y, Rr */
|
|
/* 1000001rrrrr1000 */
|
|
cbins = 0x8208;
|
|
} else if (!strcmp(tokens[1], "z")) {
|
|
/* st Z, Rr */
|
|
/* 1000001rrrrr0000 */
|
|
cbins = 0x8200;
|
|
} else if (!strcmp(tokens[1], "-x")) {
|
|
/* st -X, Rr */
|
|
/* 1001001rrrrr1110 */
|
|
cbins = 0x920E;
|
|
} else if (!strcmp(tokens[1], "-y")) {
|
|
/* st -Y, Rr */
|
|
/* 1001001rrrrr1010 */
|
|
cbins = 0x920A;
|
|
} else if (!strcmp(tokens[1], "-z")) {
|
|
/* st -Z, Rr */
|
|
/* 1001001rrrrr0010 */
|
|
cbins = 0x9202;
|
|
} else {
|
|
throw_error("expected 'X' or 'Y' or 'Z' or '-X' or '-Y' or '-Z', but got '%s'\n", tokens[1]);
|
|
}
|
|
} else if (ntokens == 5 && !strcmp(tokens[2], "+") && !strcmp(tokens[3], "1")) {
|
|
if (!strcmp(tokens[1], "x")) {
|
|
/* st X+1, Rr */
|
|
/* 1001001rrrrr1101 */
|
|
cbins = 0x920D;
|
|
} else if (!strcmp(tokens[1], "y")) {
|
|
/* st Y+1, Rr */
|
|
/* 1001001rrrrr1001 */
|
|
cbins = 0x9209;
|
|
} else if (!strcmp(tokens[1], "z")) {
|
|
/* st Z+1, Rr */
|
|
/* 1001001rrrrr0001 */
|
|
cbins = 0x9201;
|
|
} else {
|
|
throw_error("expected 'X+' or 'Y+' or 'Z+', but got '%s+'\n", tokens[2]);
|
|
}
|
|
} else {
|
|
throw_error("expected st M, Rr | 0 <= r <= 31 | M = {X,Y,Z,-X,-Y,-Z}\n");
|
|
}
|
|
|
|
cbins |= ((Rr << 4) & 0x01F0);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_std(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
if (ntokens == 4) {
|
|
/* std M, Rr | 0 <= r <= 31 | M = {X+,Y+,Z+} */
|
|
ut16 Rr;
|
|
expected_const_or_error(tokens[2], "+");
|
|
parse_register_or_error(Rr, tokens[3]);
|
|
|
|
if (!strcmp(tokens[1], "x")) {
|
|
/* std X+, Rr */
|
|
/* 1001001rrrrr1101 */
|
|
cbins = 0x900D;
|
|
} else if (!strcmp(tokens[1], "y")) {
|
|
/* std Y+, Rr */
|
|
/* 1001001rrrrr1001 */
|
|
cbins = 0x9009;
|
|
} else if (!strcmp(tokens[1], "z")) {
|
|
/* std Z+, Rr */
|
|
/* 1001001rrrrr0001 */
|
|
cbins = 0x9001;
|
|
} else {
|
|
throw_error("expected 'X+' or 'Y+' or 'Z+', but got '%s+'\n", tokens[1]);
|
|
}
|
|
cbins |= ((Rr << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
} else if (ntokens == 5) {
|
|
/* std M + q, Rr | 0 <= r <= 31 | M = {Y,Z} | 0 <= q <= 63*/
|
|
ut16 Rr, q;
|
|
expected_const_or_error(tokens[2], "+");
|
|
parse_unsigned_or_error(q, tokens[3], 63);
|
|
parse_register_or_error(Rr, tokens[4]);
|
|
|
|
if (!strcmp(tokens[1], "y")) {
|
|
/* std Y+q, Rr */
|
|
/* 10q0qq1rrrrr1qqq */
|
|
cbins = 0x8208;
|
|
} else if (!strcmp(tokens[1], "z")) {
|
|
/* std Z+q, Rr */
|
|
/* 10q0qq1rrrrr0qqq */
|
|
cbins = 0x8200;
|
|
} else {
|
|
throw_error("expected 'Y' or 'Z', but got '%s'\n", tokens[1]);
|
|
}
|
|
|
|
cbins |= q & 0x0007;
|
|
cbins |= ((q << 7) & 0x0C00);
|
|
cbins |= ((q << 8) & 0x2000);
|
|
cbins |= ((Rr << 4) & 0x01F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
throw_error("expected std Rr, M + q | 0 <= d <= 31 | M = {Y,Z} | 0 <= q <= 63\n");
|
|
}
|
|
|
|
|
|
|
|
static ut32 avr_sts(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* sts k, Rr | 0 <= r <= 31, 0 <= k <= 0xFFFF */
|
|
ut16 Rr;
|
|
ut32 k;
|
|
parse_unsigned_or_error(k, tokens[1], 0xFFFF);
|
|
parse_register_or_error(Rr, tokens[2]);
|
|
|
|
#if 0
|
|
// The STS (16-bit) and LDS (16-bit) instructions only exist in the reduced AVR cores.
|
|
// This includes only the ATtiny4/5/9/10 family, and the ATtiny20/40 family.
|
|
// They also lack some features like the lack of CPU registers R0 to R15.
|
|
// On rizin these platforms are not supported, therefore this code is commented, but works as intended.
|
|
if (k <= 127 && Rr >= 16) {
|
|
/* sts k, Rr | 16 <= d <= 31 | 0 <= k <= 127 */
|
|
/* 10101kkkddddkkkk */
|
|
cbins = 0xA800;
|
|
Rr -= 16;
|
|
cbins |= k & 0x000F;
|
|
cbins |= ((k << 4) & 0x0700);
|
|
cbins |= ((Rr << 4) & 0x00F0);
|
|
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
#endif
|
|
/* sts k, Rr | 0 <= d <= 31 | 0 <= k <= 0xFFFF */
|
|
/* 1001001ddddd0000 kkkkkkkkkkkkkkkk */
|
|
cbins = 0x9200;
|
|
cbins |= ((Rr << 4) & 0x01F0);
|
|
auto_write16(data, cbins, be);
|
|
auto_write16(data + 2, k, be);
|
|
return 4;
|
|
}
|
|
|
|
|
|
static ut32 avr_ssscccc(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> b | 0 <= b <= 7 */
|
|
ut16 b;
|
|
parse_unsigned_or_error(b, tokens[1], 7);
|
|
|
|
cbins |= ((b << 4) & 0x0070);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static ut32 avr_kkkkkkksss(ut16 cbins, cchar** tokens, ut32 ntokens, ut8 *data, ut64 pc, bool be) {
|
|
/* <opcode> b, k | -64 <= k <= 63 | 0 <= b <= 7 */
|
|
ut16 b;
|
|
st16 k;
|
|
parse_unsigned_or_error(b, tokens[1], 7);
|
|
parse_address_or_error(k, tokens[2], pc, -64, 63);
|
|
|
|
cbins |= (b & 0x0007);
|
|
cbins |= ((k << 3) & 0x03F8);
|
|
auto_write16(data, cbins, be);
|
|
return 2;
|
|
}
|
|
|
|
|
|
static const AvrInstruction instructions[] = {
|
|
{ "adc" /* 000111rdddddrrrr */ , 0x1C00, 3, 3, avr_rdddddrrrr },
|
|
{ "add" /* 000011rdddddrrrr */ , 0x0C00, 3, 3, avr_rdddddrrrr },
|
|
{ "adiw" /* 10010110KKddKKKK */ , 0x9600, 3, 3, avr_KKddKKKK },
|
|
{ "and" /* 001000rdddddrrrr */ , 0x2000, 3, 3, avr_rdddddrrrr },
|
|
{ "andi" /* 0111KKKKddddKKKK */ , 0x7000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "asr" /* 1001010ddddd0101 */ , 0x9405, 2, 2, avr_dddddcccc },
|
|
{ "bclr" /* 100101001sss1000 */ , 0x9488, 2, 2, avr_ssscccc },
|
|
{ "bld" /* 1111100ddddd0bbb */ , 0xF800, 3, 3, avr_dddddcbbb },
|
|
{ "brbc" /* 111101kkkkkkksss */ , 0xF400, 3, 3, avr_kkkkkkksss },
|
|
{ "brbs" /* 111100kkkkkkksss */ , 0xF000, 3, 3, avr_kkkkkkksss },
|
|
{ "brcc" /* 111101kkkkkkk000 */ , 0xF400, 2, 2, avr_kkkkkkkccc },
|
|
{ "brcs" /* 111100kkkkkkk000 */ , 0xF000, 2, 2, avr_kkkkkkkccc },
|
|
{ "break" /* 1001010110011000 */ , 0x9598, 1, 1, avr_unique },
|
|
{ "breq" /* 111100kkkkkkk001 */ , 0xF001, 2, 2, avr_kkkkkkkccc },
|
|
{ "brge" /* 111101kkkkkkk100 */ , 0xF404, 2, 2, avr_kkkkkkkccc },
|
|
{ "brhc" /* 111101kkkkkkk101 */ , 0xF405, 2, 2, avr_kkkkkkkccc },
|
|
{ "brhs" /* 111100kkkkkkk101 */ , 0xF005, 2, 2, avr_kkkkkkkccc },
|
|
{ "brid" /* 111101kkkkkkk111 */ , 0xF407, 2, 2, avr_kkkkkkkccc },
|
|
{ "brie" /* 111100kkkkkkk111 */ , 0xF007, 2, 2, avr_kkkkkkkccc },
|
|
{ "brlo" /* 111100kkkkkkk000 */ , 0xF000, 2, 2, avr_kkkkkkkccc },
|
|
{ "brlt" /* 111100kkkkkkk100 */ , 0xF004, 2, 2, avr_kkkkkkkccc },
|
|
{ "brmi" /* 111100kkkkkkk010 */ , 0xF002, 2, 2, avr_kkkkkkkccc },
|
|
{ "brne" /* 111101kkkkkkk001 */ , 0xF401, 2, 2, avr_kkkkkkkccc },
|
|
{ "brpl" /* 111101kkkkkkk010 */ , 0xF402, 2, 2, avr_kkkkkkkccc },
|
|
{ "brsh" /* 111101kkkkkkk000 */ , 0xF400, 2, 2, avr_kkkkkkkccc },
|
|
{ "brtc" /* 111101kkkkkkk110 */ , 0xF406, 2, 2, avr_kkkkkkkccc },
|
|
{ "brts" /* 111100kkkkkkk110 */ , 0xF006, 2, 2, avr_kkkkkkkccc },
|
|
{ "brvc" /* 111101kkkkkkk011 */ , 0xF403, 2, 2, avr_kkkkkkkccc },
|
|
{ "brvs" /* 111100kkkkkkk011 */ , 0xF003, 2, 2, avr_kkkkkkkccc },
|
|
{ "bset" /* 100101000sss1000 */ , 0x9408, 2, 2, avr_ssscccc },
|
|
{ "bst" /* 1111101ddddd0bbb */ , 0xFA00, 3, 3, avr_dddddcbbb },
|
|
{ "call" /* 1001010kkkkk111k kkkkkkkkkkkkkkkk */ , 0x940E, 2, 2, avr_kkkkkccck },
|
|
{ "cbi" /* 10011000AAAAAbbb */ , 0x9800, 3, 3, avr_AAAAAbbb },
|
|
{ "cbr" /* 0111KKKKddddKKKK */ , 0x7000, 3, 3, avr_cbr },
|
|
{ "clc" /* 1001010010001000 */ , 0x9488, 1, 1, avr_unique },
|
|
{ "clh" /* 1001010011011000 */ , 0x94D8, 1, 1, avr_unique },
|
|
{ "cli" /* 1001010011111000 */ , 0x94F8, 1, 1, avr_unique },
|
|
{ "cln" /* 1001010010101000 */ , 0x94A8, 1, 1, avr_unique },
|
|
{ "clr" /* 001001dddddddddd */ , 0x2400, 2, 2, avr_dddddddddd },
|
|
{ "cls" /* 1001010011001000 */ , 0x94C8, 1, 1, avr_unique },
|
|
{ "clt" /* 1001010011101000 */ , 0x94E8, 1, 1, avr_unique },
|
|
{ "clv" /* 1001010010111000 */ , 0x94B8, 1, 1, avr_unique },
|
|
{ "clz" /* 1001010010011000 */ , 0x9498, 1, 1, avr_unique },
|
|
{ "com" /* 1001010ddddd0000 */ , 0x9400, 2, 2, avr_dddddcccc },
|
|
{ "cp" /* 000101rdddddrrrr */ , 0x1400, 3, 3, avr_rdddddrrrr },
|
|
{ "cpc" /* 000001rdddddrrrr */ , 0x0400, 3, 3, avr_rdddddrrrr },
|
|
{ "cpi" /* 0011KKKKddddKKKK */ , 0x3000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "cpse" /* 000100rdddddrrrr */ , 0x1000, 3, 3, avr_rdddddrrrr },
|
|
{ "dec" /* 1001010ddddd1010 */ , 0x940A, 2, 2, avr_dddddcccc },
|
|
{ "des" /* 10010100KKKK1011 */ , 0x940B, 2, 2, avr_KKKKcccc },
|
|
{ "eicall" /* 1001010100011001 */ , 0x9519, 1, 1, avr_unique },
|
|
{ "eijmp" /* 1001010000011001 */ , 0x9419, 1, 1, avr_unique },
|
|
{ "elpm" /* */ , 0x0000, 1, 5, avr_elpm },
|
|
{ "eor" /* 001001rdddddrrrr */ , 0x2400, 3, 3, avr_rdddddrrrr },
|
|
{ "fmul" /* 000000110ddd1rrr */ , 0x0308, 3, 3, avr_dddcrrr },
|
|
{ "fmuls" /* 000000111ddd0rrr */ , 0x0380, 3, 3, avr_dddcrrr },
|
|
{ "fmulsu" /* 000000111ddd1rrr */ , 0x0388, 3, 3, avr_dddcrrr },
|
|
{ "icall" /* 1001010100001001 */ , 0x9509, 1, 1, avr_unique },
|
|
{ "ijmp" /* 1001010000001001 */ , 0x9409, 1, 1, avr_unique },
|
|
{ "in" /* 10110AAdddddAAAA */ , 0xB000, 3, 3, avr_AAdddddAAAA },
|
|
{ "inc" /* 1001010ddddd0011 */ , 0x9403, 2, 2, avr_dddddcccc },
|
|
{ "jmp" /* 1001010kkkkk110k kkkkkkkkkkkkkkkk */ , 0x940C, 2, 2, avr_kkkkkccck },
|
|
{ "lac" /* 1001001rrrrr0110 */ , 0x9206, 3, 3, avr_rrrrrcccc },
|
|
{ "las" /* 1001001rrrrr0101 */ , 0x9205, 3, 3, avr_rrrrrcccc },
|
|
{ "lat" /* 1001001rrrrr0111 */ , 0x9207, 3, 3, avr_rrrrrcccc },
|
|
{ "ld" /* */ , 0x0000, 3, 5, avr_ld },
|
|
{ "ldd" /* */ , 0x0000, 5, 5, avr_ldd },
|
|
{ "ldi" /* 1110KKKKddddKKKK */ , 0xE000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "lds" /* */ , 0x0000, 3, 3, avr_lds },
|
|
{ "lpm" /* */ , 0x0000, 1, 5, avr_lpm },
|
|
{ "lsl" /* 000011dddddddddd */ , 0x0C00, 2, 2, avr_dddddddddd },
|
|
{ "lsr" /* 1001010ddddd0110 */ , 0x9406, 2, 2, avr_dddddcccc },
|
|
{ "mov" /* 001011rdddddrrrr */ , 0x2C00, 3, 3, avr_rdddddrrrr },
|
|
{ "movw" /* 00000001ddddrrrr */ , 0x0100, 3, 3, avr_ddddrrrr },
|
|
{ "mul" /* 100111rdddddrrrr */ , 0x9C00, 3, 3, avr_rdddddrrrr },
|
|
{ "muls" /* 00000010ddddrrrr */ , 0x0200, 3, 3, avr_ddddrrrr_2x },
|
|
{ "mulsu" /* 000000110ddd0rrr */ , 0x0300, 3, 3, avr_dddcrrr },
|
|
{ "neg" /* 1001010ddddd0001 */ , 0x9401, 2, 2, avr_dddddcccc },
|
|
{ "nop" /* 0000000000000000 */ , 0x0000, 1, 1, avr_unique },
|
|
{ "or" /* 001010rdddddrrrr */ , 0x2800, 3, 3, avr_rdddddrrrr },
|
|
{ "ori" /* 0110KKKKddddKKKK */ , 0x6000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "out" /* 10111AArrrrrAAAA */ , 0xB800, 3, 3, avr_AArrrrrAAAA },
|
|
{ "pop" /* 1001000ddddd1111 */ , 0x900F, 2, 2, avr_dddddcccc },
|
|
{ "push" /* 1001001ddddd1111 */ , 0x920F, 2, 2, avr_dddddcccc },
|
|
{ "rcall" /* 1101kkkkkkkkkkkk */ , 0xD000, 2, 2, avr_kkkkkkkkkkkk },
|
|
{ "ret" /* 1001010100001000 */ , 0x9508, 1, 1, avr_unique },
|
|
{ "reti" /* 1001010100011000 */ , 0x9518, 1, 1, avr_unique },
|
|
{ "rjmp" /* 1100kkkkkkkkkkkk */ , 0xC000, 2, 2, avr_kkkkkkkkkkkk },
|
|
{ "rol" /* 000111dddddddddd */ , 0x1C00, 2, 2, avr_dddddddddd },
|
|
{ "ror" /* 1001010ddddd0111 */ , 0x9407, 2, 2, avr_dddddcccc },
|
|
{ "sbc" /* 000010rdddddrrrr */ , 0x0800, 3, 3, avr_rdddddrrrr },
|
|
{ "sbci" /* 0100KKKKddddKKKK */ , 0x4000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "sbi" /* 10011010AAAAAbbb */ , 0x9A00, 3, 3, avr_AAAAAbbb },
|
|
{ "sbic" /* 10011001AAAAAbbb */ , 0x9900, 3, 3, avr_AAAAAbbb },
|
|
{ "sbis" /* 10011011AAAAAbbb */ , 0x9B00, 3, 3, avr_AAAAAbbb },
|
|
{ "sbiw" /* 10010111KKddKKKK */ , 0x9700, 3, 3, avr_KKddKKKK },
|
|
{ "sbr" /* 0110KKKKddddKKKK */ , 0x6000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "sbrc" /* 1111110rrrrr0bbb */ , 0xFC00, 3, 3, avr_rrrrrcbbb },
|
|
{ "sbrs" /* 1111111rrrrr0bbb */ , 0xFE00, 3, 3, avr_rrrrrcbbb },
|
|
{ "sec" /* 1001010000001000 */ , 0x9408, 1, 1, avr_unique },
|
|
{ "seh" /* 1001010001011000 */ , 0x9458, 1, 1, avr_unique },
|
|
{ "sei" /* 1001010001111000 */ , 0x9478, 1, 1, avr_unique },
|
|
{ "sen" /* 1001010000101000 */ , 0x9428, 1, 1, avr_unique },
|
|
{ "ser" /* 11101111dddd1111 */ , 0xEF0F, 2, 2, avr_ddddcccc },
|
|
{ "ses" /* 1001010001001000 */ , 0x9448, 1, 1, avr_unique },
|
|
{ "set" /* 1001010001101000 */ , 0x9468, 1, 1, avr_unique },
|
|
{ "sev" /* 1001010000111000 */ , 0x9438, 1, 1, avr_unique },
|
|
{ "sez" /* 1001010000011000 */ , 0x9418, 1, 1, avr_unique },
|
|
{ "sleep" /* 1001010110001000 */ , 0x9588, 1, 1, avr_unique },
|
|
{ "spm" /* */ , 0x0000, 1, 5, avr_spm },
|
|
{ "st" /* */ , 0x0000, 3, 5, avr_st },
|
|
{ "std" /* */ , 0x0000, 4, 5, avr_std },
|
|
{ "sts" /* */ , 0x0000, 3, 3, avr_sts },
|
|
{ "sub" /* 000110rdddddrrrr */ , 0x1800, 3, 3, avr_rdddddrrrr },
|
|
{ "subi" /* 0101KKKKddddKKKK */ , 0x5000, 3, 3, avr_KKKKddddKKKK },
|
|
{ "swap" /* 1001010ddddd0010 */ , 0x9402, 2, 2, avr_dddddcccc },
|
|
{ "tst" /* 001000dddddddddd */ , 0x2000, 2, 2, avr_dddddddddd },
|
|
{ "wdr" /* 1001010110101000 */ , 0x95A8, 1, 1, avr_unique },
|
|
{ "xch" /* 1001001rrrrr0100 */ , 0x9204, 3, 3, avr_rrrrrcccc }
|
|
};
|
|
|
|
static char *strdup_limit(cchar *begin, cchar *end) {
|
|
ssize_t size = end - begin;
|
|
if (size < 1) {
|
|
return NULL;
|
|
}
|
|
char* str = malloc(size + 1);
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
memcpy(str, begin, size);
|
|
str[size] = 0;
|
|
return str;
|
|
}
|
|
|
|
static void sanitize_input(char *cinput, st32 input_size) {
|
|
for (st32 i = 0; i < input_size; ++i) {
|
|
if (cinput[i] == ',') {
|
|
cinput[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
static char **tokens_new(cchar *input, st32 input_size, ut32 *ntokens) {
|
|
|
|
char* cinput = strdup(input);
|
|
if (!cinput) {
|
|
rz_warn_if_reached();
|
|
return NULL;
|
|
}
|
|
|
|
sanitize_input(cinput, input_size);
|
|
|
|
char **tokens = RZ_NEWS0(char*, MAX_TOKENS);
|
|
if (!tokens) {
|
|
free(cinput);
|
|
rz_warn_if_reached();
|
|
return NULL;
|
|
}
|
|
|
|
ut32 count;
|
|
cchar *start, *end;
|
|
char* copy;
|
|
|
|
start = rz_str_trim_head_ro(cinput);
|
|
for (count = 0; *start && count < MAX_TOKENS; count++) {
|
|
end = rz_str_trim_head_wp(start);
|
|
|
|
for (ut32 i = 0; i < end - start; ++i) {
|
|
if (start[i] == '+') {
|
|
end = start + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
copy = strdup_limit(start, end);
|
|
if (!copy) {
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
|
|
tokens[count] = copy;
|
|
start = rz_str_trim_head_ro(end);
|
|
}
|
|
|
|
rz_warn_if_fail(count < MAX_TOKENS);
|
|
|
|
*ntokens = count;
|
|
free(cinput);
|
|
return tokens;
|
|
}
|
|
|
|
static void tokens_free(char **tokens) {
|
|
if (!tokens) {
|
|
return;
|
|
}
|
|
for (ut32 i = 0; i < MAX_TOKENS; ++i) {
|
|
free(tokens[i]);
|
|
}
|
|
free(tokens);
|
|
}
|
|
|
|
// TODO handle endianness
|
|
ut32 avr_assembler(const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, bool be) {
|
|
return_error_if_empty_input(input, input_size);
|
|
|
|
ut32 written = AVR_INVALID_SIZE;
|
|
ut32 ntokens = 0;
|
|
char** tokens = tokens_new(input, input_size, &ntokens);
|
|
if (!tokens || ntokens < 1) {
|
|
RZ_LOG_ERROR("[!] avr_assembler: invalid assembly.\n");
|
|
goto avr_assembler_end;
|
|
}
|
|
|
|
|
|
for (ut32 i = 0; i < RZ_ARRAY_SIZE(instructions); ++i) {
|
|
if (!rz_str_casecmp(tokens[0], instructions[i].opcode)) {
|
|
ut16 mintoks = instructions[i].mintoks;
|
|
ut16 maxtoks = instructions[i].maxtoks;
|
|
if (ntokens < mintoks || ntokens > maxtoks) {
|
|
RZ_LOG_ERROR("[!] avr_assembler: '%s' requires %u <= ntokens <= %u, but %u tokens was provided.\n", tokens[0], mintoks, maxtoks, ntokens);
|
|
goto avr_assembler_end;
|
|
}
|
|
written = instructions[i].encode(instructions[i].cbits, (cchar**)tokens, ntokens, output, pc, be);
|
|
break;
|
|
}
|
|
}
|
|
|
|
avr_assembler_end:
|
|
tokens_free(tokens);
|
|
return written;
|
|
}
|