blob: 09342f1f90ff1c9128ff82a692191d51aea2c1ff [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damien826005c2013-10-05 23:17:28 +010027#include <stdint.h>
28#include <stdio.h>
29#include <string.h>
Damien Georgea26dc502014-04-12 17:54:52 +010030#include <stdarg.h>
Damien826005c2013-10-05 23:17:28 +010031#include <assert.h>
32
Damiend99b0522013-12-21 18:17:45 +000033#include "mpconfig.h"
Paul Sokolovsky59c675a2014-06-21 22:43:22 +030034#include "misc.h"
Damien George55baff42014-01-21 21:40:13 +000035#include "qstr.h"
Damien826005c2013-10-05 23:17:28 +010036#include "lexer.h"
Damien826005c2013-10-05 23:17:28 +010037#include "parse.h"
Damien Georgedf8127a2014-04-13 11:04:33 +010038#include "obj.h"
39#include "emitglue.h"
Damien826005c2013-10-05 23:17:28 +010040#include "scope.h"
Damiend99b0522013-12-21 18:17:45 +000041#include "runtime0.h"
Damien826005c2013-10-05 23:17:28 +010042#include "emit.h"
43#include "asmthumb.h"
44
Damien3ef4abb2013-10-12 16:53:13 +010045#if MICROPY_EMIT_INLINE_THUMB
Damien826005c2013-10-05 23:17:28 +010046
Damien George87210872014-04-13 00:30:32 +010047typedef enum {
48 PN_none = 0,
49#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
50#include "grammar.h"
51#undef DEF_RULE
52 PN_maximum_number_of,
53} pn_kind_t;
54
Damien826005c2013-10-05 23:17:28 +010055struct _emit_inline_asm_t {
Damien Georgea26dc502014-04-12 17:54:52 +010056 uint16_t pass;
57 uint16_t success;
Damien826005c2013-10-05 23:17:28 +010058 scope_t *scope;
Damien George7ff996c2014-09-08 23:05:16 +010059 mp_uint_t max_num_labels;
Damien826005c2013-10-05 23:17:28 +010060 qstr *label_lookup;
61 asm_thumb_t *as;
62};
63
Damien Georgea26dc502014-04-12 17:54:52 +010064void emit_inline_thumb_error(emit_inline_asm_t *emit, const char *fmt, ...) {
65 printf("SyntaxError: ");
66 emit->success = false;
67 va_list ap;
68 va_start(ap, fmt);
69 vprintf(fmt, ap);
70 va_end(ap);
71}
72
Damien George7ff996c2014-09-08 23:05:16 +010073emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
Damien George41d02b62014-01-24 22:42:28 +000074 emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
Damien826005c2013-10-05 23:17:28 +010075 emit->max_num_labels = max_num_labels;
76 emit->label_lookup = m_new(qstr, max_num_labels);
77 memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
78 emit->as = asm_thumb_new(max_num_labels);
79 return emit;
80}
81
Damien George41d02b62014-01-24 22:42:28 +000082void emit_inline_thumb_free(emit_inline_asm_t *emit) {
83 m_del(qstr, emit->label_lookup, emit->max_num_labels);
84 asm_thumb_free(emit->as, false);
85 m_del_obj(emit_inline_asm_t, emit);
86}
87
Paul Sokolovsky520e2f52014-02-12 18:31:30 +020088STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
Damien826005c2013-10-05 23:17:28 +010089 emit->pass = pass;
Damien Georgea26dc502014-04-12 17:54:52 +010090 emit->success = true;
Damien826005c2013-10-05 23:17:28 +010091 emit->scope = scope;
Damien George36db6bc2014-05-07 17:24:22 +010092 asm_thumb_start_pass(emit->as, pass == MP_PASS_EMIT ? ASM_THUMB_PASS_EMIT : ASM_THUMB_PASS_COMPUTE);
Damien826005c2013-10-05 23:17:28 +010093 asm_thumb_entry(emit->as, 0);
94}
95
Damien Georgea26dc502014-04-12 17:54:52 +010096STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
Damien826005c2013-10-05 23:17:28 +010097 asm_thumb_exit(emit->as);
98 asm_thumb_end_pass(emit->as);
99
Damien George36db6bc2014-05-07 17:24:22 +0100100 if (emit->pass == MP_PASS_EMIT) {
Damiend99b0522013-12-21 18:17:45 +0000101 void *f = asm_thumb_get_code(emit->as);
Damien George2ac4af62014-08-15 16:45:41 +0100102 mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0);
Damien826005c2013-10-05 23:17:28 +0100103 }
Damien Georgea26dc502014-04-12 17:54:52 +0100104
105 return emit->success;
Damien826005c2013-10-05 23:17:28 +0100106}
107
Damien George7ff996c2014-09-08 23:05:16 +0100108STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
Damiena2f2f7d2013-10-06 00:14:13 +0100109 if (n_params > 4) {
Damien Georgea26dc502014-04-12 17:54:52 +0100110 emit_inline_thumb_error(emit, "can only have up to 4 parameters to inline thumb assembly\n");
Damiena2f2f7d2013-10-06 00:14:13 +0100111 return 0;
112 }
Damien George7ff996c2014-09-08 23:05:16 +0100113 for (mp_uint_t i = 0; i < n_params; i++) {
Damiend99b0522013-12-21 18:17:45 +0000114 if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
Damien Georgea26dc502014-04-12 17:54:52 +0100115 emit_inline_thumb_error(emit, "parameter to inline assembler must be an identifier\n");
Damiena2f2f7d2013-10-06 00:14:13 +0100116 return 0;
117 }
Damiend99b0522013-12-21 18:17:45 +0000118 const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
Damiena2f2f7d2013-10-06 00:14:13 +0100119 if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
Damien Georgea26dc502014-04-12 17:54:52 +0100120 emit_inline_thumb_error(emit, "parameter %d to inline assembler must be r%d\n", i + 1, i);
Damiena2f2f7d2013-10-06 00:14:13 +0100121 return 0;
122 }
123 }
124 return n_params;
125}
126
Damien George7ff996c2014-09-08 23:05:16 +0100127STATIC void emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
Damien826005c2013-10-05 23:17:28 +0100128 assert(label_num < emit->max_num_labels);
129 emit->label_lookup[label_num] = label_id;
130 asm_thumb_label_assign(emit->as, label_num);
131}
132
Damien George7ff996c2014-09-08 23:05:16 +0100133STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) {
Damien Georgee5f8a772014-04-21 13:33:15 +0100134 asm_thumb_align(emit->as, align);
135}
136
Damien George7ff996c2014-09-08 23:05:16 +0100137STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) {
Damien Georgee5f8a772014-04-21 13:33:15 +0100138 asm_thumb_data(emit->as, bytesize, val);
139}
140
Damien Georgea26dc502014-04-12 17:54:52 +0100141typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
142STATIC const reg_name_t reg_name_table[] = {
143 {0, "r0\0"},
144 {1, "r1\0"},
145 {2, "r2\0"},
146 {3, "r3\0"},
147 {4, "r4\0"},
148 {5, "r5\0"},
149 {6, "r6\0"},
150 {7, "r7\0"},
151 {8, "r8\0"},
152 {9, "r9\0"},
153 {10, "r10"},
154 {11, "r11"},
155 {12, "r12"},
156 {13, "r13"},
157 {14, "r14"},
158 {15, "r15"},
159 {10, "sl\0"},
160 {11, "fp\0"},
161 {13, "sp\0"},
162 {14, "lr\0"},
163 {15, "pc\0"},
164};
165
Damien George7ff996c2014-09-08 23:05:16 +0100166STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
Damien George87210872014-04-13 00:30:32 +0100167 if (MP_PARSE_NODE_IS_ID(pn)) {
168 qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
Damien Georgea26dc502014-04-12 17:54:52 +0100169 const char *reg_str = qstr_str(reg_qstr);
Damien George7ff996c2014-09-08 23:05:16 +0100170 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
Damien Georgea26dc502014-04-12 17:54:52 +0100171 const reg_name_t *r = &reg_name_table[i];
172 if (reg_str[0] == r->name[0] && reg_str[1] == r->name[1] && reg_str[2] == r->name[2] && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
173 if (r->reg > max_reg) {
Damien George87210872014-04-13 00:30:32 +0100174 emit_inline_thumb_error(emit, "'%s' expects at most r%d\n", op, max_reg);
Damien Georgea26dc502014-04-12 17:54:52 +0100175 return 0;
176 } else {
177 return r->reg;
178 }
179 }
180 }
Damien826005c2013-10-05 23:17:28 +0100181 }
Damien George87210872014-04-13 00:30:32 +0100182 emit_inline_thumb_error(emit, "'%s' expects a register\n", op);
Damien Georgea26dc502014-04-12 17:54:52 +0100183 return 0;
Damien826005c2013-10-05 23:17:28 +0100184}
185
Damien George87210872014-04-13 00:30:32 +0100186STATIC int get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int fit_mask) {
187 if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) {
188 emit_inline_thumb_error(emit, "'%s' expects an integer\n", op);
Damien826005c2013-10-05 23:17:28 +0100189 return 0;
190 }
Damien George87210872014-04-13 00:30:32 +0100191 int i = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
Damien826005c2013-10-05 23:17:28 +0100192 if ((i & (~fit_mask)) != 0) {
Damien Georgea26dc502014-04-12 17:54:52 +0100193 emit_inline_thumb_error(emit, "'%s' integer 0x%x does not fit in mask 0x%x\n", op, i, fit_mask);
Damien826005c2013-10-05 23:17:28 +0100194 return 0;
195 }
196 return i;
197}
198
Damien George87210872014-04-13 00:30:32 +0100199STATIC bool get_arg_addr(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_parse_node_t *pn_base, mp_parse_node_t *pn_offset) {
200 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
201 goto bad_arg;
202 }
203 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
204 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
205 goto bad_arg;
206 }
207 pns = (mp_parse_node_struct_t*)pns->nodes[0];
208 if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
209 goto bad_arg;
210 }
211
212 *pn_base = pns->nodes[0];
213 *pn_offset = pns->nodes[1];
214 return true;
215
216bad_arg:
217 emit_inline_thumb_error(emit, "'%s' expects an address of the form [a, b]\n", op);
218 return false;
219}
220
221STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
222 if (!MP_PARSE_NODE_IS_ID(pn)) {
223 emit_inline_thumb_error(emit, "'%s' expects a label\n", op);
Damien826005c2013-10-05 23:17:28 +0100224 return 0;
225 }
Damien George87210872014-04-13 00:30:32 +0100226 qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
Damien826005c2013-10-05 23:17:28 +0100227 for (int i = 0; i < emit->max_num_labels; i++) {
228 if (emit->label_lookup[i] == label_qstr) {
229 return i;
230 }
231 }
Damiendc833822013-10-06 01:01:01 +0100232 // only need to have the labels on the last pass
Damien George36db6bc2014-05-07 17:24:22 +0100233 if (emit->pass == MP_PASS_EMIT) {
Damien Georgea26dc502014-04-12 17:54:52 +0100234 emit_inline_thumb_error(emit, "label '%s' not defined\n", qstr_str(label_qstr));
Damiendc833822013-10-06 01:01:01 +0100235 }
Damien826005c2013-10-05 23:17:28 +0100236 return 0;
237}
238
Damien George47e1b852014-04-08 18:28:33 +0100239typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t;
240STATIC const cc_name_t cc_name_table[] = {
Damien George0b610de2014-09-29 16:25:04 +0100241 { ASM_THUMB_CC_EQ, "eq" },
242 { ASM_THUMB_CC_NE, "ne" },
243 { ASM_THUMB_CC_CS, "cs" },
244 { ASM_THUMB_CC_CC, "cc" },
245 { ASM_THUMB_CC_MI, "mi" },
246 { ASM_THUMB_CC_PL, "pl" },
247 { ASM_THUMB_CC_VS, "vs" },
248 { ASM_THUMB_CC_VC, "vc" },
249 { ASM_THUMB_CC_HI, "hi" },
250 { ASM_THUMB_CC_LS, "ls" },
251 { ASM_THUMB_CC_GE, "ge" },
252 { ASM_THUMB_CC_LT, "lt" },
253 { ASM_THUMB_CC_GT, "gt" },
254 { ASM_THUMB_CC_LE, "le" },
Damien George47e1b852014-04-08 18:28:33 +0100255};
256
Damien George7ff996c2014-09-08 23:05:16 +0100257STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) {
Damien826005c2013-10-05 23:17:28 +0100258 // TODO perhaps make two tables:
Damien03d41242013-10-06 00:36:05 +0100259 // one_args =
260 // "b", LAB, asm_thumb_b_n,
261 // "bgt", LAB, asm_thumb_bgt_n,
Damien826005c2013-10-05 23:17:28 +0100262 // two_args =
263 // "movs", RLO, I8, asm_thumb_movs_reg_i8
264 // "movw", REG, REG, asm_thumb_movw_reg_i16
265 // three_args =
266 // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
267
Damien George47e1b852014-04-08 18:28:33 +0100268 const char *op_str = qstr_str(op);
Damien George7ff996c2014-09-08 23:05:16 +0100269 mp_uint_t op_len = strlen(op_str);
Damien826005c2013-10-05 23:17:28 +0100270
Damien George47e1b852014-04-08 18:28:33 +0100271 if (n_args == 0) {
Damien George90edf9e2014-04-18 16:56:54 +0100272 if (strcmp(op_str, "nop") == 0) {
273 asm_thumb_op16(emit->as, ASM_THUMB_OP_NOP);
274 } else if (strcmp(op_str, "wfi") == 0) {
275 asm_thumb_op16(emit->as, ASM_THUMB_OP_WFI);
276 } else if (strcmp(op_str, "ite.ge") == 0) { // TODO correct name for this op?
Damien George851f15f2014-09-29 10:05:32 +0100277 asm_thumb_op16(emit->as, ASM_THUMB_OP_ITE_GE);
Damien George47e1b852014-04-08 18:28:33 +0100278 } else {
279 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100280 }
Damien826005c2013-10-05 23:17:28 +0100281
Damien George47e1b852014-04-08 18:28:33 +0100282 } else if (n_args == 1) {
283 if (strcmp(op_str, "b") == 0) {
Damien George87210872014-04-13 00:30:32 +0100284 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George47e1b852014-04-08 18:28:33 +0100285 // TODO check that this succeeded, ie branch was within range
286 asm_thumb_b_n(emit->as, label_num);
287 } else if (op_str[0] == 'b' && op_len == 3) {
Damien George7ff996c2014-09-08 23:05:16 +0100288 mp_uint_t cc = -1;
289 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
Damien George47e1b852014-04-08 18:28:33 +0100290 if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
291 cc = cc_name_table[i].cc;
292 }
293 }
294 if (cc == -1) {
295 goto unknown_op;
296 }
Damien George87210872014-04-13 00:30:32 +0100297 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George47e1b852014-04-08 18:28:33 +0100298 // TODO check that this succeeded, ie branch was within range
299 asm_thumb_bcc_n(emit->as, cc, label_num);
Damien George90edf9e2014-04-18 16:56:54 +0100300 } else if (strcmp(op_str, "cpsid")) {
301 // TODO check pn_args[0] == i
302 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSID_I);
303 } else if (strcmp(op_str, "cpsie")) {
304 // TODO check pn_args[0] == i
305 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSIE_I);
Damien George47e1b852014-04-08 18:28:33 +0100306 } else {
307 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100308 }
Damien826005c2013-10-05 23:17:28 +0100309
Damien George47e1b852014-04-08 18:28:33 +0100310 } else if (n_args == 2) {
Damien George87210872014-04-13 00:30:32 +0100311 if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
312 // second arg is a register (or should be)
Damien George7ff996c2014-09-08 23:05:16 +0100313 mp_uint_t op_code;
Damien George87210872014-04-13 00:30:32 +0100314 if (strcmp(op_str, "mov") == 0) {
Damien George7ff996c2014-09-08 23:05:16 +0100315 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
316 mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
Damien George87210872014-04-13 00:30:32 +0100317 asm_thumb_mov_reg_reg(emit->as, reg_dest, reg_src);
Damien Georgea4022c92014-07-17 12:37:56 +0100318 } else if (strcmp(op_str, "and_") == 0) {
Damien George87210872014-04-13 00:30:32 +0100319 op_code = ASM_THUMB_FORMAT_4_AND;
Damien George7ff996c2014-09-08 23:05:16 +0100320 mp_uint_t reg_dest, reg_src;
Damien George87210872014-04-13 00:30:32 +0100321 op_format_4:
322 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
323 reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
324 asm_thumb_format_4(emit->as, op_code, reg_dest, reg_src);
325 // TODO probably uses less ROM if these ops are in a lookup table
Damien George87210872014-04-13 00:30:32 +0100326 } else if (strcmp(op_str, "eor") == 0) { op_code = ASM_THUMB_FORMAT_4_EOR; goto op_format_4;
327 } else if (strcmp(op_str, "lsl") == 0) { op_code = ASM_THUMB_FORMAT_4_LSL; goto op_format_4;
328 } else if (strcmp(op_str, "lsr") == 0) { op_code = ASM_THUMB_FORMAT_4_LSR; goto op_format_4;
329 } else if (strcmp(op_str, "asr") == 0) { op_code = ASM_THUMB_FORMAT_4_ASR; goto op_format_4;
330 } else if (strcmp(op_str, "adc") == 0) { op_code = ASM_THUMB_FORMAT_4_ADC; goto op_format_4;
331 } else if (strcmp(op_str, "sbc") == 0) { op_code = ASM_THUMB_FORMAT_4_SBC; goto op_format_4;
332 } else if (strcmp(op_str, "ror") == 0) { op_code = ASM_THUMB_FORMAT_4_ROR; goto op_format_4;
333 } else if (strcmp(op_str, "tst") == 0) { op_code = ASM_THUMB_FORMAT_4_TST; goto op_format_4;
334 } else if (strcmp(op_str, "neg") == 0) { op_code = ASM_THUMB_FORMAT_4_NEG; goto op_format_4;
335 } else if (strcmp(op_str, "cmp") == 0) { op_code = ASM_THUMB_FORMAT_4_CMP; goto op_format_4;
336 } else if (strcmp(op_str, "cmn") == 0) { op_code = ASM_THUMB_FORMAT_4_CMN; goto op_format_4;
337 } else if (strcmp(op_str, "orr") == 0) { op_code = ASM_THUMB_FORMAT_4_ORR; goto op_format_4;
338 } else if (strcmp(op_str, "mul") == 0) { op_code = ASM_THUMB_FORMAT_4_MUL; goto op_format_4;
339 } else if (strcmp(op_str, "bic") == 0) { op_code = ASM_THUMB_FORMAT_4_BIC; goto op_format_4;
340 } else if (strcmp(op_str, "mvn") == 0) { op_code = ASM_THUMB_FORMAT_4_MVN; goto op_format_4;
341 } else {
342 goto unknown_op;
343 }
Damien George47e1b852014-04-08 18:28:33 +0100344 } else {
Damien George87210872014-04-13 00:30:32 +0100345 // second arg is not a register
Damien George7ff996c2014-09-08 23:05:16 +0100346 mp_uint_t op_code;
Damien George87210872014-04-13 00:30:32 +0100347 if (strcmp(op_str, "mov") == 0) {
348 op_code = ASM_THUMB_FORMAT_3_MOV;
Damien George7ff996c2014-09-08 23:05:16 +0100349 mp_uint_t rlo_dest, i8_src;
Damien George87210872014-04-13 00:30:32 +0100350 op_format_3:
351 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
352 i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
353 asm_thumb_format_3(emit->as, op_code, rlo_dest, i8_src);
354 } else if (strcmp(op_str, "cmp") == 0) {
355 op_code = ASM_THUMB_FORMAT_3_CMP;
356 goto op_format_3;
357 } else if (strcmp(op_str, "add") == 0) {
358 op_code = ASM_THUMB_FORMAT_3_ADD;
359 goto op_format_3;
360 } else if (strcmp(op_str, "sub") == 0) {
361 op_code = ASM_THUMB_FORMAT_3_SUB;
362 goto op_format_3;
363 } else if (strcmp(op_str, "movw") == 0) {
Damien George7ff996c2014-09-08 23:05:16 +0100364 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100365 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
366 asm_thumb_movw_reg_i16(emit->as, reg_dest, i_src);
367 } else if (strcmp(op_str, "movt") == 0) {
Damien George7ff996c2014-09-08 23:05:16 +0100368 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100369 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
370 asm_thumb_movt_reg_i16(emit->as, reg_dest, i_src);
371 } else if (strcmp(op_str, "movwt") == 0) {
372 // this is a convenience instruction
373 // we clear the MSB since it might be set from extracting the small int value
Damien George7ff996c2014-09-08 23:05:16 +0100374 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100375 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
376 asm_thumb_movw_reg_i16(emit->as, reg_dest, i_src & 0xffff);
377 asm_thumb_movt_reg_i16(emit->as, reg_dest, (i_src >> 16) & 0x7fff);
378 } else if (strcmp(op_str, "ldr") == 0) {
379 op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER;
Damien George7ff996c2014-09-08 23:05:16 +0100380 mp_uint_t rlo_dest, rlo_base, i5;
Damien George87210872014-04-13 00:30:32 +0100381 mp_parse_node_t pn_base, pn_offset;
382 op_format_9_10:
383 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
384 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
385 rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
386 if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
387 i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
388 } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
389 i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
390 } else {
391 i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
392 }
393 asm_thumb_format_9_10(emit->as, op_code, rlo_dest, rlo_base, i5);
394 }
395 } else if (strcmp(op_str, "ldrb") == 0) {
396 op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER;
397 goto op_format_9_10;
398 } else if (strcmp(op_str, "ldrh") == 0) {
399 op_code = ASM_THUMB_FORMAT_10_LDRH;
400 goto op_format_9_10;
401 } else if (strcmp(op_str, "str") == 0) {
402 op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER;
403 goto op_format_9_10;
404 } else if (strcmp(op_str, "strb") == 0) {
405 op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER;
406 goto op_format_9_10;
407 } else if (strcmp(op_str, "strh") == 0) {
408 op_code = ASM_THUMB_FORMAT_10_STRH;
409 goto op_format_9_10;
410 } else {
411 goto unknown_op;
412 }
Damien George47e1b852014-04-08 18:28:33 +0100413 }
414
415 } else if (n_args == 3) {
Damien George7ff996c2014-09-08 23:05:16 +0100416 mp_uint_t op_code;
Damien George47e1b852014-04-08 18:28:33 +0100417 if (strcmp(op_str, "add") == 0) {
Damien George87210872014-04-13 00:30:32 +0100418 op_code = ASM_THUMB_FORMAT_2_ADD;
Damien George7ff996c2014-09-08 23:05:16 +0100419 mp_uint_t rlo_dest, rlo_src;
Damien George87210872014-04-13 00:30:32 +0100420 op_format_2:
421 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
422 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
423 int src_b;
424 if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
425 op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
426 src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
427 } else {
428 op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
429 src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
430 }
431 asm_thumb_format_2(emit->as, op_code, rlo_dest, rlo_src, src_b);
432 } else if (strcmp(op_str, "sub") == 0) {
433 op_code = ASM_THUMB_FORMAT_2_SUB;
434 goto op_format_2;
Damien George47e1b852014-04-08 18:28:33 +0100435 } else {
436 goto unknown_op;
437 }
438
Damien826005c2013-10-05 23:17:28 +0100439 } else {
Damien George47e1b852014-04-08 18:28:33 +0100440 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100441 }
Damien George47e1b852014-04-08 18:28:33 +0100442
443 return;
444
445unknown_op:
Damien Georgea26dc502014-04-12 17:54:52 +0100446 emit_inline_thumb_error(emit, "unsupported Thumb instruction '%s' with %d arguments\n", op_str, n_args);
Damien826005c2013-10-05 23:17:28 +0100447}
448
449const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
450 emit_inline_thumb_start_pass,
451 emit_inline_thumb_end_pass,
Damiena2f2f7d2013-10-06 00:14:13 +0100452 emit_inline_thumb_count_params,
Damien826005c2013-10-05 23:17:28 +0100453 emit_inline_thumb_label,
Damien Georgee5f8a772014-04-21 13:33:15 +0100454 emit_inline_thumb_align,
455 emit_inline_thumb_data,
Damien826005c2013-10-05 23:17:28 +0100456 emit_inline_thumb_op,
457};
458
Damien3ef4abb2013-10-12 16:53:13 +0100459#endif // MICROPY_EMIT_INLINE_THUMB