blob: 90a68caa6da36f6952c3dfca85218a11347b749c [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
Damien George51dfcb42015-01-01 20:27:54 +000033#include "py/emit.h"
34#include "py/asmthumb.h"
Damien826005c2013-10-05 23:17:28 +010035
Damien3ef4abb2013-10-12 16:53:13 +010036#if MICROPY_EMIT_INLINE_THUMB
Damien826005c2013-10-05 23:17:28 +010037
Damien George87210872014-04-13 00:30:32 +010038typedef enum {
Damien George87210872014-04-13 00:30:32 +010039#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
Damien George51dfcb42015-01-01 20:27:54 +000040#include "py/grammar.h"
Damien George87210872014-04-13 00:30:32 +010041#undef DEF_RULE
42 PN_maximum_number_of,
43} pn_kind_t;
44
Damien826005c2013-10-05 23:17:28 +010045struct _emit_inline_asm_t {
Damien Georgea26dc502014-04-12 17:54:52 +010046 uint16_t pass;
Damien826005c2013-10-05 23:17:28 +010047 scope_t *scope;
Damien George8dfbd2d2015-02-13 01:00:51 +000048 mp_obj_t *error_slot;
Damien George7ff996c2014-09-08 23:05:16 +010049 mp_uint_t max_num_labels;
Damien826005c2013-10-05 23:17:28 +010050 qstr *label_lookup;
51 asm_thumb_t *as;
52};
53
Damien George8dfbd2d2015-02-13 01:00:51 +000054STATIC void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, const char *msg) {
55 *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
56}
57
58STATIC void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
59 *emit->error_slot = exc;
Damien Georgea26dc502014-04-12 17:54:52 +010060}
61
Damien George7ff996c2014-09-08 23:05:16 +010062emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
Damien George41d02b62014-01-24 22:42:28 +000063 emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
Damien826005c2013-10-05 23:17:28 +010064 emit->max_num_labels = max_num_labels;
65 emit->label_lookup = m_new(qstr, max_num_labels);
Damien826005c2013-10-05 23:17:28 +010066 emit->as = asm_thumb_new(max_num_labels);
67 return emit;
68}
69
Damien George41d02b62014-01-24 22:42:28 +000070void emit_inline_thumb_free(emit_inline_asm_t *emit) {
71 m_del(qstr, emit->label_lookup, emit->max_num_labels);
72 asm_thumb_free(emit->as, false);
73 m_del_obj(emit_inline_asm_t, emit);
74}
75
Damien George8dfbd2d2015-02-13 01:00:51 +000076STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot) {
Damien826005c2013-10-05 23:17:28 +010077 emit->pass = pass;
78 emit->scope = scope;
Damien George8dfbd2d2015-02-13 01:00:51 +000079 emit->error_slot = error_slot;
Damien Georgedc790972015-03-03 17:34:49 +000080 if (emit->pass == MP_PASS_CODE_SIZE) {
81 memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
82 }
Damien George36db6bc2014-05-07 17:24:22 +010083 asm_thumb_start_pass(emit->as, pass == MP_PASS_EMIT ? ASM_THUMB_PASS_EMIT : ASM_THUMB_PASS_COMPUTE);
Damien826005c2013-10-05 23:17:28 +010084 asm_thumb_entry(emit->as, 0);
85}
86
Damien George8f54c082016-01-15 15:20:43 +000087STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
Damien826005c2013-10-05 23:17:28 +010088 asm_thumb_exit(emit->as);
89 asm_thumb_end_pass(emit->as);
90
Damien George36db6bc2014-05-07 17:24:22 +010091 if (emit->pass == MP_PASS_EMIT) {
Damiend99b0522013-12-21 18:17:45 +000092 void *f = asm_thumb_get_code(emit->as);
Damien George8f54c082016-01-15 15:20:43 +000093 mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f,
94 asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, type_sig);
Damien826005c2013-10-05 23:17:28 +010095 }
96}
97
Damien George7ff996c2014-09-08 23:05:16 +010098STATIC 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 +010099 if (n_params > 4) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000100 emit_inline_thumb_error_msg(emit, "can only have up to 4 parameters to Thumb assembly");
Damiena2f2f7d2013-10-06 00:14:13 +0100101 return 0;
102 }
Damien George7ff996c2014-09-08 23:05:16 +0100103 for (mp_uint_t i = 0; i < n_params; i++) {
Damiend99b0522013-12-21 18:17:45 +0000104 if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000105 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +0100106 return 0;
107 }
Damiend99b0522013-12-21 18:17:45 +0000108 const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
Damiena2f2f7d2013-10-06 00:14:13 +0100109 if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000110 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +0100111 return 0;
112 }
113 }
114 return n_params;
115}
116
Damien George9c5cabb2015-03-03 17:08:02 +0000117STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
Damien826005c2013-10-05 23:17:28 +0100118 assert(label_num < emit->max_num_labels);
Damien George9c5cabb2015-03-03 17:08:02 +0000119 if (emit->pass == MP_PASS_CODE_SIZE) {
120 // check for duplicate label on first pass
Damien George1f92ffb2015-11-09 14:11:47 +0000121 for (uint i = 0; i < emit->max_num_labels; i++) {
Damien George9c5cabb2015-03-03 17:08:02 +0000122 if (emit->label_lookup[i] == label_id) {
123 return false;
124 }
125 }
126 }
Damien826005c2013-10-05 23:17:28 +0100127 emit->label_lookup[label_num] = label_id;
128 asm_thumb_label_assign(emit->as, label_num);
Damien George9c5cabb2015-03-03 17:08:02 +0000129 return true;
Damien826005c2013-10-05 23:17:28 +0100130}
131
Damien George7ff996c2014-09-08 23:05:16 +0100132STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) {
Damien Georgee5f8a772014-04-21 13:33:15 +0100133 asm_thumb_align(emit->as, align);
134}
135
Damien George7ff996c2014-09-08 23:05:16 +0100136STATIC 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 +0100137 asm_thumb_data(emit->as, bytesize, val);
138}
139
Damien Georgea26dc502014-04-12 17:54:52 +0100140typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
141STATIC const reg_name_t reg_name_table[] = {
142 {0, "r0\0"},
143 {1, "r1\0"},
144 {2, "r2\0"},
145 {3, "r3\0"},
146 {4, "r4\0"},
147 {5, "r5\0"},
148 {6, "r6\0"},
149 {7, "r7\0"},
150 {8, "r8\0"},
151 {9, "r9\0"},
152 {10, "r10"},
153 {11, "r11"},
154 {12, "r12"},
155 {13, "r13"},
156 {14, "r14"},
157 {15, "r15"},
158 {10, "sl\0"},
159 {11, "fp\0"},
160 {13, "sp\0"},
161 {14, "lr\0"},
162 {15, "pc\0"},
163};
164
Henrik Sölvere242b172015-11-21 22:27:54 +0100165#define MAX_SPECIAL_REGISTER_NAME_LENGTH 7
166typedef struct _special_reg_name_t { byte reg; char name[MAX_SPECIAL_REGISTER_NAME_LENGTH + 1]; } special_reg_name_t;
167STATIC const special_reg_name_t special_reg_name_table[] = {
168 {5, "IPSR"},
169 {17, "BASEPRI"},
170};
171
Damien George42495392015-02-16 17:46:49 +0000172// return empty string in case of error, so we can attempt to parse the string
173// without a special check if it was in fact a string
174STATIC const char *get_arg_str(mp_parse_node_t pn) {
Damien George87210872014-04-13 00:30:32 +0100175 if (MP_PARSE_NODE_IS_ID(pn)) {
Damien George42495392015-02-16 17:46:49 +0000176 qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
177 return qstr_str(qst);
178 } else {
179 return "";
180 }
181}
182
183STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
184 const char *reg_str = get_arg_str(pn);
185 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
186 const reg_name_t *r = &reg_name_table[i];
187 if (reg_str[0] == r->name[0]
188 && reg_str[1] == r->name[1]
189 && reg_str[2] == r->name[2]
190 && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
191 if (r->reg > max_reg) {
192 emit_inline_thumb_error_exc(emit,
193 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
194 "'%s' expects at most r%d", op, max_reg));
195 return 0;
196 } else {
197 return r->reg;
Damien Georgea26dc502014-04-12 17:54:52 +0100198 }
199 }
Damien826005c2013-10-05 23:17:28 +0100200 }
Damien George42495392015-02-16 17:46:49 +0000201 emit_inline_thumb_error_exc(emit,
202 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
203 "'%s' expects a register", op));
Damien Georgea26dc502014-04-12 17:54:52 +0100204 return 0;
Damien826005c2013-10-05 23:17:28 +0100205}
206
Henrik Sölvere242b172015-11-21 22:27:54 +0100207STATIC mp_uint_t get_arg_special_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
208 const char *reg_str = get_arg_str(pn);
209 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(special_reg_name_table); i++) {
210 const special_reg_name_t *r = &special_reg_name_table[i];
211 if (strcmp(r->name, reg_str) == 0) {
212 return r->reg;
213 }
214 }
215 emit_inline_thumb_error_exc(emit,
216 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
217 "'%s' expects a special register", op));
218 return 0;
219}
220
=50089722015-04-14 13:14:57 +0100221#if MICROPY_EMIT_INLINE_THUMB_FLOAT
222STATIC mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
223 const char *reg_str = get_arg_str(pn);
224 if (reg_str[0] == 's' && reg_str[1] != '\0') {
225 mp_uint_t regno = 0;
226 for (++reg_str; *reg_str; ++reg_str) {
227 mp_uint_t v = *reg_str;
228 if (!('0' <= v && v <= '9')) {
229 goto malformed;
230 }
231 regno = 10 * regno + v - '0';
232 }
233 if (regno > 31) {
234 emit_inline_thumb_error_exc(emit,
235 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
236 "'%s' expects at most r%d", op, 31));
237 return 0;
238 } else {
239 return regno;
240 }
241 }
242malformed:
243 emit_inline_thumb_error_exc(emit,
244 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
245 "'%s' expects an FPU register", op));
246 return 0;
247}
248#endif
249
Damien George0d967b82015-02-13 02:30:35 +0000250STATIC mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
251 // a register list looks like {r0, r1, r2} and is parsed as a Python set
252
253 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) {
254 goto bad_arg;
255 }
256
257 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
258 assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be
259 pn = pns->nodes[0];
260
261 mp_uint_t reglist = 0;
262
263 if (MP_PARSE_NODE_IS_ID(pn)) {
264 // set with one element
265 reglist |= 1 << get_arg_reg(emit, op, pn, 15);
266 } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
267 pns = (mp_parse_node_struct_t*)pn;
268 if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
269 assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
270 mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
271 if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
272 // set with multiple elements
273
274 // get first element of set (we rely on get_arg_reg to catch syntax errors)
275 reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15);
276
277 // get tail elements (2nd, 3rd, ...)
278 mp_parse_node_t *nodes;
279 int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
280
281 // process rest of elements
282 for (int i = 0; i < n; i++) {
283 reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15);
284 }
285 } else {
286 goto bad_arg;
287 }
288 } else {
289 goto bad_arg;
290 }
291 } else {
292 goto bad_arg;
293 }
294
295 return reglist;
296
297bad_arg:
298 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects {r0, r1, ...}", op));
299 return 0;
300}
301
Damien George47dc5922016-01-07 14:45:57 +0000302STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint32_t fit_mask) {
303 mp_obj_t o;
304 if (!mp_parse_node_get_int_maybe(pn, &o)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000305 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects an integer", op));
Damien826005c2013-10-05 23:17:28 +0100306 return 0;
307 }
Damien George47dc5922016-01-07 14:45:57 +0000308 uint32_t i = mp_obj_get_int_truncated(o);
Damien826005c2013-10-05 23:17:28 +0100309 if ((i & (~fit_mask)) != 0) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000310 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer 0x%x does not fit in mask 0x%x", op, i, fit_mask));
Damien826005c2013-10-05 23:17:28 +0100311 return 0;
312 }
313 return i;
314}
315
Damien George87210872014-04-13 00:30:32 +0100316STATIC 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) {
317 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
318 goto bad_arg;
319 }
320 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
321 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
322 goto bad_arg;
323 }
324 pns = (mp_parse_node_struct_t*)pns->nodes[0];
325 if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
326 goto bad_arg;
327 }
328
329 *pn_base = pns->nodes[0];
330 *pn_offset = pns->nodes[1];
331 return true;
332
333bad_arg:
Damien George8dfbd2d2015-02-13 01:00:51 +0000334 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects an address of the form [a, b]", op));
Damien George87210872014-04-13 00:30:32 +0100335 return false;
336}
337
338STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
339 if (!MP_PARSE_NODE_IS_ID(pn)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000340 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects a label", op));
Damien826005c2013-10-05 23:17:28 +0100341 return 0;
342 }
Damien George87210872014-04-13 00:30:32 +0100343 qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
Damien George1f92ffb2015-11-09 14:11:47 +0000344 for (uint i = 0; i < emit->max_num_labels; i++) {
Damien826005c2013-10-05 23:17:28 +0100345 if (emit->label_lookup[i] == label_qstr) {
346 return i;
347 }
348 }
Damiendc833822013-10-06 01:01:01 +0100349 // only need to have the labels on the last pass
Damien George36db6bc2014-05-07 17:24:22 +0100350 if (emit->pass == MP_PASS_EMIT) {
Damien George044c4732015-04-11 13:03:37 +0100351 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "label '%q' not defined", label_qstr));
Damiendc833822013-10-06 01:01:01 +0100352 }
Damien826005c2013-10-05 23:17:28 +0100353 return 0;
354}
355
Damien George47e1b852014-04-08 18:28:33 +0100356typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t;
357STATIC const cc_name_t cc_name_table[] = {
Damien George0b610de2014-09-29 16:25:04 +0100358 { ASM_THUMB_CC_EQ, "eq" },
359 { ASM_THUMB_CC_NE, "ne" },
360 { ASM_THUMB_CC_CS, "cs" },
361 { ASM_THUMB_CC_CC, "cc" },
362 { ASM_THUMB_CC_MI, "mi" },
363 { ASM_THUMB_CC_PL, "pl" },
364 { ASM_THUMB_CC_VS, "vs" },
365 { ASM_THUMB_CC_VC, "vc" },
366 { ASM_THUMB_CC_HI, "hi" },
367 { ASM_THUMB_CC_LS, "ls" },
368 { ASM_THUMB_CC_GE, "ge" },
369 { ASM_THUMB_CC_LT, "lt" },
370 { ASM_THUMB_CC_GT, "gt" },
371 { ASM_THUMB_CC_LE, "le" },
Damien George47e1b852014-04-08 18:28:33 +0100372};
373
Damien George8f7976b2015-02-24 16:10:58 +0000374typedef struct _format_4_op_t { byte op; char name[3]; } format_4_op_t;
375#define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
376STATIC const format_4_op_t format_4_op_table[] = {
377 { X(ASM_THUMB_FORMAT_4_EOR), "eor" },
378 { X(ASM_THUMB_FORMAT_4_LSL), "lsl" },
379 { X(ASM_THUMB_FORMAT_4_LSR), "lsr" },
380 { X(ASM_THUMB_FORMAT_4_ASR), "asr" },
381 { X(ASM_THUMB_FORMAT_4_ADC), "adc" },
382 { X(ASM_THUMB_FORMAT_4_SBC), "sbc" },
383 { X(ASM_THUMB_FORMAT_4_ROR), "ror" },
384 { X(ASM_THUMB_FORMAT_4_TST), "tst" },
385 { X(ASM_THUMB_FORMAT_4_NEG), "neg" },
386 { X(ASM_THUMB_FORMAT_4_CMP), "cmp" },
387 { X(ASM_THUMB_FORMAT_4_CMN), "cmn" },
388 { X(ASM_THUMB_FORMAT_4_ORR), "orr" },
389 { X(ASM_THUMB_FORMAT_4_MUL), "mul" },
390 { X(ASM_THUMB_FORMAT_4_BIC), "bic" },
391 { X(ASM_THUMB_FORMAT_4_MVN), "mvn" },
392};
393#undef X
394
Damien George28adab32016-02-23 15:20:39 +0000395// name is actually a qstr, which should fit in 16 bits
396typedef struct _format_9_10_op_t { uint16_t op; uint16_t name; } format_9_10_op_t;
Damien George8f7976b2015-02-24 16:10:58 +0000397#define X(x) (x)
398STATIC const format_9_10_op_t format_9_10_op_table[] = {
Damien George28adab32016-02-23 15:20:39 +0000399 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_ldr },
400 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_ldrb },
401 { X(ASM_THUMB_FORMAT_10_LDRH), MP_QSTR_ldrh },
402 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_str },
403 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_strb },
404 { X(ASM_THUMB_FORMAT_10_STRH), MP_QSTR_strh },
Damien George8f7976b2015-02-24 16:10:58 +0000405};
406#undef X
407
=50089722015-04-14 13:14:57 +0100408#if MICROPY_EMIT_INLINE_THUMB_FLOAT
409// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
410typedef struct _format_vfp_op_t { byte op; char name[3]; } format_vfp_op_t;
411STATIC const format_vfp_op_t format_vfp_op_table[] = {
412 { 0x30, "add" },
413 { 0x34, "sub" },
414 { 0x20, "mul" },
415 { 0x80, "div" },
416};
417#endif
418
Damien Georgee8135412015-10-16 22:08:57 +0100419// shorthand alias for whether we allow ARMv7-M instructions
420#define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M
421
Damien George7ff996c2014-09-08 23:05:16 +0100422STATIC 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 +0100423 // TODO perhaps make two tables:
Damien03d41242013-10-06 00:36:05 +0100424 // one_args =
425 // "b", LAB, asm_thumb_b_n,
426 // "bgt", LAB, asm_thumb_bgt_n,
Damien826005c2013-10-05 23:17:28 +0100427 // two_args =
428 // "movs", RLO, I8, asm_thumb_movs_reg_i8
429 // "movw", REG, REG, asm_thumb_movw_reg_i16
430 // three_args =
431 // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
432
Damien Georgec3f64d92015-11-27 12:23:18 +0000433 size_t op_len;
Damien George6eb75302015-04-11 21:53:39 +0100434 const char *op_str = (const char*)qstr_data(op, &op_len);
Damien826005c2013-10-05 23:17:28 +0100435
=50089722015-04-14 13:14:57 +0100436 #if MICROPY_EMIT_INLINE_THUMB_FLOAT
437 if (op_str[0] == 'v') {
438 // floating point operations
439 if (n_args == 2) {
440 mp_uint_t op_code = 0x0ac0, op_code_hi;
Damien George28adab32016-02-23 15:20:39 +0000441 if (op == MP_QSTR_vcmp) {
=50089722015-04-14 13:14:57 +0100442 op_code_hi = 0xeeb4;
443 op_vfp_twoargs:;
444 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
445 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
446 asm_thumb_op32(emit->as,
447 op_code_hi | ((vd & 1) << 6),
448 op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1);
Damien George28adab32016-02-23 15:20:39 +0000449 } else if (op == MP_QSTR_vsqrt) {
=50089722015-04-14 13:14:57 +0100450 op_code_hi = 0xeeb1;
451 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000452 } else if (op == MP_QSTR_vneg) {
=50089722015-04-14 13:14:57 +0100453 op_code_hi = 0xeeb1;
454 op_code = 0x0a40;
455 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000456 } else if (op == MP_QSTR_vcvt_f32_s32) {
=50089722015-04-14 13:14:57 +0100457 op_code_hi = 0xeeb8; // int to float
458 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000459 } else if (op == MP_QSTR_vcvt_s32_f32) {
=50089722015-04-14 13:14:57 +0100460 op_code_hi = 0xeebd; // float to int
461 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000462 } else if (op == MP_QSTR_vmrs) {
=50089722015-04-14 13:14:57 +0100463 mp_uint_t reg_dest;
464 const char *reg_str0 = get_arg_str(pn_args[0]);
465 if (strcmp(reg_str0, "APSR_nzcv") == 0) {
466 reg_dest = 15;
467 } else {
468 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
469 }
470 const char *reg_str1 = get_arg_str(pn_args[1]);
471 if (strcmp(reg_str1, "FPSCR") == 0) {
472 // FP status to ARM reg
473 asm_thumb_op32(emit->as, 0xeef1, 0x0a10 | (reg_dest << 12));
474 } else {
475 goto unknown_op;
476 }
Damien George28adab32016-02-23 15:20:39 +0000477 } else if (op == MP_QSTR_vmov) {
=50089722015-04-14 13:14:57 +0100478 op_code_hi = 0xee00;
479 mp_uint_t r_arm, vm;
480 const char *reg_str = get_arg_str(pn_args[0]);
481 if (reg_str[0] == 'r') {
482 r_arm = get_arg_reg(emit, op_str, pn_args[0], 15);
483 vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
484 op_code_hi |= 0x10;
485 } else {
486 vm = get_arg_vfpreg(emit, op_str, pn_args[0]);
487 r_arm = get_arg_reg(emit, op_str, pn_args[1], 15);
488 }
489 asm_thumb_op32(emit->as,
490 op_code_hi | ((vm & 0x1e) >> 1),
491 0x0a10 | (r_arm << 12) | ((vm & 1) << 7));
Damien George28adab32016-02-23 15:20:39 +0000492 } else if (op == MP_QSTR_vldr) {
=50089722015-04-14 13:14:57 +0100493 op_code_hi = 0xed90;
494 op_vldr_vstr:;
495 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
496 mp_parse_node_t pn_base, pn_offset;
497 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
498 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
499 mp_uint_t i8;
adminpeted6201fc2015-10-31 09:38:22 +0000500 i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2;
=50089722015-04-14 13:14:57 +0100501 asm_thumb_op32(emit->as,
502 op_code_hi | rlo_base | ((vd & 1) << 6),
503 0x0a00 | ((vd & 0x1e) << 11) | i8);
504 }
Damien George28adab32016-02-23 15:20:39 +0000505 } else if (op == MP_QSTR_vstr) {
=50089722015-04-14 13:14:57 +0100506 op_code_hi = 0xed80;
507 goto op_vldr_vstr;
508 } else {
509 goto unknown_op;
510 }
511 } else if (n_args == 3) {
512 // search table for arith ops
513 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) {
514 if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') {
515 mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0);
516 mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4);
517 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
518 mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]);
519 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]);
520 asm_thumb_op32(emit->as,
521 op_code_hi | ((vd & 1) << 6) | (vn >> 1),
522 op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7));
523 return;
524 }
525 }
526 goto unknown_op;
527 } else {
528 goto unknown_op;
529 }
530 } else
531 #endif
Damien George47e1b852014-04-08 18:28:33 +0100532 if (n_args == 0) {
Damien George28adab32016-02-23 15:20:39 +0000533 if (op == MP_QSTR_nop) {
Damien George90edf9e2014-04-18 16:56:54 +0100534 asm_thumb_op16(emit->as, ASM_THUMB_OP_NOP);
Damien George28adab32016-02-23 15:20:39 +0000535 } else if (op == MP_QSTR_wfi) {
Damien George90edf9e2014-04-18 16:56:54 +0100536 asm_thumb_op16(emit->as, ASM_THUMB_OP_WFI);
Damien George47e1b852014-04-08 18:28:33 +0100537 } else {
538 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100539 }
Damien826005c2013-10-05 23:17:28 +0100540
Damien George47e1b852014-04-08 18:28:33 +0100541 } else if (n_args == 1) {
Damien George28adab32016-02-23 15:20:39 +0000542 if (op == MP_QSTR_b) {
Damien George87210872014-04-13 00:30:32 +0100543 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George53457432015-02-25 15:45:55 +0000544 if (!asm_thumb_b_n_label(emit->as, label_num)) {
545 goto branch_not_in_range;
546 }
Damien George28adab32016-02-23 15:20:39 +0000547 } else if (op == MP_QSTR_bl) {
Damien Georgeeff10f62015-02-16 18:17:07 +0000548 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George53457432015-02-25 15:45:55 +0000549 if (!asm_thumb_bl_label(emit->as, label_num)) {
550 goto branch_not_in_range;
551 }
Damien George28adab32016-02-23 15:20:39 +0000552 } else if (op == MP_QSTR_bx) {
Damien Georgeeff10f62015-02-16 18:17:07 +0000553 mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15);
554 asm_thumb_op16(emit->as, 0x4700 | (r << 3));
Damien George9f142f02015-03-02 14:29:52 +0000555 } else if (op_str[0] == 'b' && (op_len == 3
556 || (op_len == 5 && op_str[3] == '_'
Damien Georgee8135412015-10-16 22:08:57 +0100557 && (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) {
Damien George7ff996c2014-09-08 23:05:16 +0100558 mp_uint_t cc = -1;
559 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
Damien George47e1b852014-04-08 18:28:33 +0100560 if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
561 cc = cc_name_table[i].cc;
562 }
563 }
Damien George1f92ffb2015-11-09 14:11:47 +0000564 if (cc == (mp_uint_t)-1) {
Damien George47e1b852014-04-08 18:28:33 +0100565 goto unknown_op;
566 }
Damien George87210872014-04-13 00:30:32 +0100567 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George9f142f02015-03-02 14:29:52 +0000568 if (!asm_thumb_bcc_nw_label(emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) {
Damien George53457432015-02-25 15:45:55 +0000569 goto branch_not_in_range;
570 }
Damien Georgee8135412015-10-16 22:08:57 +0100571 } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') {
Damien George42495392015-02-16 17:46:49 +0000572 const char *arg_str = get_arg_str(pn_args[0]);
573 mp_uint_t cc = -1;
574 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
575 if (arg_str[0] == cc_name_table[i].name[0]
576 && arg_str[1] == cc_name_table[i].name[1]
577 && arg_str[2] == '\0') {
578 cc = cc_name_table[i].cc;
579 break;
580 }
581 }
Damien George1f92ffb2015-11-09 14:11:47 +0000582 if (cc == (mp_uint_t)-1) {
Damien George42495392015-02-16 17:46:49 +0000583 goto unknown_op;
584 }
585 const char *os = op_str + 2;
586 while (*os != '\0') {
587 os++;
588 }
589 if (os > op_str + 5) {
590 goto unknown_op;
591 }
592 mp_uint_t it_mask = 8;
593 while (--os >= op_str + 2) {
594 it_mask >>= 1;
595 if (*os == 't') {
596 it_mask |= (cc & 1) << 3;
597 } else if (*os == 'e') {
598 it_mask |= ((~cc) & 1) << 3;
599 } else {
600 goto unknown_op;
601 }
602 }
603 asm_thumb_it_cc(emit->as, cc, it_mask);
Damien George28adab32016-02-23 15:20:39 +0000604 } else if (op == MP_QSTR_cpsid) {
Damien George90edf9e2014-04-18 16:56:54 +0100605 // TODO check pn_args[0] == i
606 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSID_I);
Damien George28adab32016-02-23 15:20:39 +0000607 } else if (op == MP_QSTR_cpsie) {
Damien George90edf9e2014-04-18 16:56:54 +0100608 // TODO check pn_args[0] == i
609 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSIE_I);
Damien George28adab32016-02-23 15:20:39 +0000610 } else if (op == MP_QSTR_push) {
Damien George0d967b82015-02-13 02:30:35 +0000611 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
612 if ((reglist & 0xff00) == 0) {
613 asm_thumb_op16(emit->as, 0xb400 | reglist);
614 } else {
Damien Georgee8135412015-10-16 22:08:57 +0100615 if (!ARMV7M) {
616 goto unknown_op;
617 }
Damien George0d967b82015-02-13 02:30:35 +0000618 asm_thumb_op32(emit->as, 0xe92d, reglist);
619 }
Damien George28adab32016-02-23 15:20:39 +0000620 } else if (op == MP_QSTR_pop) {
Damien George0d967b82015-02-13 02:30:35 +0000621 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
622 if ((reglist & 0xff00) == 0) {
623 asm_thumb_op16(emit->as, 0xbc00 | reglist);
624 } else {
Damien Georgee8135412015-10-16 22:08:57 +0100625 if (!ARMV7M) {
626 goto unknown_op;
627 }
Damien George0d967b82015-02-13 02:30:35 +0000628 asm_thumb_op32(emit->as, 0xe8bd, reglist);
629 }
Damien George47e1b852014-04-08 18:28:33 +0100630 } else {
631 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100632 }
Damien826005c2013-10-05 23:17:28 +0100633
Damien George47e1b852014-04-08 18:28:33 +0100634 } else if (n_args == 2) {
Damien George87210872014-04-13 00:30:32 +0100635 if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
636 // second arg is a register (or should be)
Damien George192d5362015-02-13 11:06:23 +0000637 mp_uint_t op_code, op_code_hi;
Damien George28adab32016-02-23 15:20:39 +0000638 if (op == MP_QSTR_mov) {
Damien George7ff996c2014-09-08 23:05:16 +0100639 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
640 mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
Damien George87210872014-04-13 00:30:32 +0100641 asm_thumb_mov_reg_reg(emit->as, reg_dest, reg_src);
Damien George28adab32016-02-23 15:20:39 +0000642 } else if (ARMV7M && op == MP_QSTR_clz) {
Damien George192d5362015-02-13 11:06:23 +0000643 op_code_hi = 0xfab0;
644 op_code = 0xf080;
645 mp_uint_t rd, rm;
646 op_clz_rbit:
647 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
648 rm = get_arg_reg(emit, op_str, pn_args[1], 15);
649 asm_thumb_op32(emit->as, op_code_hi | rm, op_code | (rd << 8) | rm);
Damien George28adab32016-02-23 15:20:39 +0000650 } else if (ARMV7M && op == MP_QSTR_rbit) {
Damien George192d5362015-02-13 11:06:23 +0000651 op_code_hi = 0xfa90;
652 op_code = 0xf0a0;
653 goto op_clz_rbit;
Damien George28adab32016-02-23 15:20:39 +0000654 } else if (ARMV7M && op == MP_QSTR_mrs){
Henrik Sölvere242b172015-11-21 22:27:54 +0100655 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12);
656 mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]);
657 asm_thumb_op32(emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src);
Damien George87210872014-04-13 00:30:32 +0100658 } else {
Damien George28adab32016-02-23 15:20:39 +0000659 if (op == MP_QSTR_and_) {
Damien George8f7976b2015-02-24 16:10:58 +0000660 op_code = ASM_THUMB_FORMAT_4_AND;
661 mp_uint_t reg_dest, reg_src;
662 op_format_4:
663 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
664 reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
665 asm_thumb_format_4(emit->as, op_code, reg_dest, reg_src);
Damien George993f0672015-02-24 22:43:01 +0000666 return;
Damien George8f7976b2015-02-24 16:10:58 +0000667 }
Damien Georgee5315f72015-02-24 16:35:37 +0000668 // search table for ALU ops
Damien George8f7976b2015-02-24 16:10:58 +0000669 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) {
670 if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') {
671 op_code = 0x4000 | (format_4_op_table[i].op << 4);
672 goto op_format_4;
673 }
674 }
Damien George87210872014-04-13 00:30:32 +0100675 goto unknown_op;
676 }
Damien George47e1b852014-04-08 18:28:33 +0100677 } else {
Damien George87210872014-04-13 00:30:32 +0100678 // second arg is not a register
Damien George7ff996c2014-09-08 23:05:16 +0100679 mp_uint_t op_code;
Damien George28adab32016-02-23 15:20:39 +0000680 if (op == MP_QSTR_mov) {
Damien George87210872014-04-13 00:30:32 +0100681 op_code = ASM_THUMB_FORMAT_3_MOV;
Damien George7ff996c2014-09-08 23:05:16 +0100682 mp_uint_t rlo_dest, i8_src;
Damien George87210872014-04-13 00:30:32 +0100683 op_format_3:
684 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
685 i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
686 asm_thumb_format_3(emit->as, op_code, rlo_dest, i8_src);
Damien George28adab32016-02-23 15:20:39 +0000687 } else if (op == MP_QSTR_cmp) {
Damien George87210872014-04-13 00:30:32 +0100688 op_code = ASM_THUMB_FORMAT_3_CMP;
689 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000690 } else if (op == MP_QSTR_add) {
Damien George87210872014-04-13 00:30:32 +0100691 op_code = ASM_THUMB_FORMAT_3_ADD;
692 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000693 } else if (op == MP_QSTR_sub) {
Damien George87210872014-04-13 00:30:32 +0100694 op_code = ASM_THUMB_FORMAT_3_SUB;
695 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000696 } else if (ARMV7M && op == MP_QSTR_movw) {
Damien Georgee5315f72015-02-24 16:35:37 +0000697 op_code = ASM_THUMB_OP_MOVW;
698 mp_uint_t reg_dest;
699 op_movw_movt:
700 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100701 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
Damien Georgee5315f72015-02-24 16:35:37 +0000702 asm_thumb_mov_reg_i16(emit->as, op_code, reg_dest, i_src);
Damien George28adab32016-02-23 15:20:39 +0000703 } else if (ARMV7M && op == MP_QSTR_movt) {
Damien Georgee5315f72015-02-24 16:35:37 +0000704 op_code = ASM_THUMB_OP_MOVT;
705 goto op_movw_movt;
Damien George28adab32016-02-23 15:20:39 +0000706 } else if (ARMV7M && op == MP_QSTR_movwt) {
Damien George87210872014-04-13 00:30:32 +0100707 // this is a convenience instruction
Damien George7ff996c2014-09-08 23:05:16 +0100708 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien Georgeea8be372016-01-07 14:54:13 +0000709 uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
Damien Georgee41b21c2015-02-24 16:32:52 +0000710 asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff);
Damien Georgeea8be372016-01-07 14:54:13 +0000711 asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff);
Damien George28adab32016-02-23 15:20:39 +0000712 } else if (ARMV7M && op == MP_QSTR_ldrex) {
Damien George1bf5a022015-02-12 22:52:42 +0000713 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
714 mp_parse_node_t pn_base, pn_offset;
715 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
716 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
717 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
718 asm_thumb_op32(emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8);
719 }
Damien George87210872014-04-13 00:30:32 +0100720 } else {
Damien George8f7976b2015-02-24 16:10:58 +0000721 // search table for ldr/str instructions
722 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) {
Damien George28adab32016-02-23 15:20:39 +0000723 if (op == format_9_10_op_table[i].name) {
Damien George8f7976b2015-02-24 16:10:58 +0000724 op_code = format_9_10_op_table[i].op;
725 mp_parse_node_t pn_base, pn_offset;
726 mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
727 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
728 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
729 mp_uint_t i5;
730 if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
731 i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
732 } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
733 i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
734 } else {
735 i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
736 }
737 asm_thumb_format_9_10(emit->as, op_code, rlo_dest, rlo_base, i5);
738 return;
739 }
740 break;
741 }
742 }
Damien George87210872014-04-13 00:30:32 +0100743 goto unknown_op;
744 }
Damien George47e1b852014-04-08 18:28:33 +0100745 }
746
747 } else if (n_args == 3) {
Damien George7ff996c2014-09-08 23:05:16 +0100748 mp_uint_t op_code;
Damien George28adab32016-02-23 15:20:39 +0000749 if (op == MP_QSTR_lsl) {
Damien George096d1e42015-10-19 14:26:19 +0100750 op_code = ASM_THUMB_FORMAT_1_LSL;
751 mp_uint_t rlo_dest, rlo_src, i5;
752 op_format_1:
753 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
754 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
755 i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f);
756 asm_thumb_format_1(emit->as, op_code, rlo_dest, rlo_src, i5);
Damien George28adab32016-02-23 15:20:39 +0000757 } else if (op == MP_QSTR_lsr) {
Damien George096d1e42015-10-19 14:26:19 +0100758 op_code = ASM_THUMB_FORMAT_1_LSR;
759 goto op_format_1;
Damien George28adab32016-02-23 15:20:39 +0000760 } else if (op == MP_QSTR_asr) {
Damien George096d1e42015-10-19 14:26:19 +0100761 op_code = ASM_THUMB_FORMAT_1_ASR;
762 goto op_format_1;
Damien George28adab32016-02-23 15:20:39 +0000763 } else if (op == MP_QSTR_add) {
Damien George87210872014-04-13 00:30:32 +0100764 op_code = ASM_THUMB_FORMAT_2_ADD;
Damien George7ff996c2014-09-08 23:05:16 +0100765 mp_uint_t rlo_dest, rlo_src;
Damien George87210872014-04-13 00:30:32 +0100766 op_format_2:
767 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
768 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
769 int src_b;
770 if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
771 op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
772 src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
773 } else {
774 op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
775 src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
776 }
777 asm_thumb_format_2(emit->as, op_code, rlo_dest, rlo_src, src_b);
Damien George28adab32016-02-23 15:20:39 +0000778 } else if (ARMV7M && op == MP_QSTR_sdiv) {
Damien George32f0b792015-02-13 10:43:05 +0000779 op_code = 0xfb90; // sdiv high part
780 mp_uint_t rd, rn, rm;
781 op_sdiv_udiv:
782 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
783 rn = get_arg_reg(emit, op_str, pn_args[1], 15);
784 rm = get_arg_reg(emit, op_str, pn_args[2], 15);
785 asm_thumb_op32(emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm);
Damien George28adab32016-02-23 15:20:39 +0000786 } else if (ARMV7M && op == MP_QSTR_udiv) {
Damien George32f0b792015-02-13 10:43:05 +0000787 op_code = 0xfbb0; // udiv high part
788 goto op_sdiv_udiv;
Damien George28adab32016-02-23 15:20:39 +0000789 } else if (op == MP_QSTR_sub) {
Damien George87210872014-04-13 00:30:32 +0100790 op_code = ASM_THUMB_FORMAT_2_SUB;
791 goto op_format_2;
Damien George28adab32016-02-23 15:20:39 +0000792 } else if (ARMV7M && op == MP_QSTR_strex) {
Damien George1bf5a022015-02-12 22:52:42 +0000793 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
794 mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15);
795 mp_parse_node_t pn_base, pn_offset;
796 if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) {
797 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
798 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
799 asm_thumb_op32(emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8);
800 }
Damien George47e1b852014-04-08 18:28:33 +0100801 } else {
802 goto unknown_op;
803 }
804
Damien826005c2013-10-05 23:17:28 +0100805 } else {
Damien George47e1b852014-04-08 18:28:33 +0100806 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100807 }
Damien George47e1b852014-04-08 18:28:33 +0100808
809 return;
810
811unknown_op:
Damien George8dfbd2d2015-02-13 01:00:51 +0000812 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "unsupported Thumb instruction '%s' with %d arguments", op_str, n_args));
Damien George53457432015-02-25 15:45:55 +0000813 return;
814
815branch_not_in_range:
816 emit_inline_thumb_error_msg(emit, "branch not in range");
817 return;
Damien826005c2013-10-05 23:17:28 +0100818}
819
820const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
821 emit_inline_thumb_start_pass,
822 emit_inline_thumb_end_pass,
Damiena2f2f7d2013-10-06 00:14:13 +0100823 emit_inline_thumb_count_params,
Damien826005c2013-10-05 23:17:28 +0100824 emit_inline_thumb_label,
Damien Georgee5f8a772014-04-21 13:33:15 +0100825 emit_inline_thumb_align,
826 emit_inline_thumb_data,
Damien826005c2013-10-05 23:17:28 +0100827 emit_inline_thumb_op,
828};
829
Damien3ef4abb2013-10-12 16:53:13 +0100830#endif // MICROPY_EMIT_INLINE_THUMB