blob: 4062728136d8b993fba080995d612b7b81b9877c [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 Georgea7fd7862016-12-09 20:35:21 +110046 asm_thumb_t as;
Damien Georgea26dc502014-04-12 17:54:52 +010047 uint16_t pass;
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;
Damien826005c2013-10-05 23:17:28 +010051};
52
Damien George8dfbd2d2015-02-13 01:00:51 +000053STATIC void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, const char *msg) {
54 *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
55}
56
57STATIC void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
58 *emit->error_slot = exc;
Damien Georgea26dc502014-04-12 17:54:52 +010059}
60
Damien George7ff996c2014-09-08 23:05:16 +010061emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
Damien George41d02b62014-01-24 22:42:28 +000062 emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
Damien Georgea7fd7862016-12-09 20:35:21 +110063 memset(&emit->as, 0, sizeof(emit->as));
64 mp_asm_base_init(&emit->as.base, max_num_labels);
Damien826005c2013-10-05 23:17:28 +010065 emit->max_num_labels = max_num_labels;
66 emit->label_lookup = m_new(qstr, max_num_labels);
Damien826005c2013-10-05 23:17:28 +010067 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);
Damien Georgea7fd7862016-12-09 20:35:21 +110072 mp_asm_base_deinit(&emit->as.base, false);
Damien George41d02b62014-01-24 22:42:28 +000073 m_del_obj(emit_inline_asm_t, emit);
74}
75
Damien Georgee920bab2016-12-09 21:23:17 +110076STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
Damien826005c2013-10-05 23:17:28 +010077 emit->pass = pass;
Damien George8dfbd2d2015-02-13 01:00:51 +000078 emit->error_slot = error_slot;
Damien Georgedc790972015-03-03 17:34:49 +000079 if (emit->pass == MP_PASS_CODE_SIZE) {
80 memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
81 }
Damien Georgea7fd7862016-12-09 20:35:21 +110082 mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
83 asm_thumb_entry(&emit->as, 0);
Damien826005c2013-10-05 23:17:28 +010084}
85
Damien George8f54c082016-01-15 15:20:43 +000086STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
Damien Georgea7fd7862016-12-09 20:35:21 +110087 asm_thumb_exit(&emit->as);
88 asm_thumb_end_pass(&emit->as);
Damien826005c2013-10-05 23:17:28 +010089}
90
Damien George7ff996c2014-09-08 23:05:16 +010091STATIC 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 +010092 if (n_params > 4) {
Damien George8dfbd2d2015-02-13 01:00:51 +000093 emit_inline_thumb_error_msg(emit, "can only have up to 4 parameters to Thumb assembly");
Damiena2f2f7d2013-10-06 00:14:13 +010094 return 0;
95 }
Damien George7ff996c2014-09-08 23:05:16 +010096 for (mp_uint_t i = 0; i < n_params; i++) {
Damiend99b0522013-12-21 18:17:45 +000097 if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
Damien George8dfbd2d2015-02-13 01:00:51 +000098 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +010099 return 0;
100 }
Damiend99b0522013-12-21 18:17:45 +0000101 const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
Damiena2f2f7d2013-10-06 00:14:13 +0100102 if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000103 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +0100104 return 0;
105 }
106 }
107 return n_params;
108}
109
Damien George9c5cabb2015-03-03 17:08:02 +0000110STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
Damien826005c2013-10-05 23:17:28 +0100111 assert(label_num < emit->max_num_labels);
Damien George9c5cabb2015-03-03 17:08:02 +0000112 if (emit->pass == MP_PASS_CODE_SIZE) {
113 // check for duplicate label on first pass
Damien George1f92ffb2015-11-09 14:11:47 +0000114 for (uint i = 0; i < emit->max_num_labels; i++) {
Damien George9c5cabb2015-03-03 17:08:02 +0000115 if (emit->label_lookup[i] == label_id) {
116 return false;
117 }
118 }
119 }
Damien826005c2013-10-05 23:17:28 +0100120 emit->label_lookup[label_num] = label_id;
Damien Georgea7fd7862016-12-09 20:35:21 +1100121 mp_asm_base_label_assign(&emit->as.base, label_num);
Damien George9c5cabb2015-03-03 17:08:02 +0000122 return true;
Damien826005c2013-10-05 23:17:28 +0100123}
124
Damien Georgea26dc502014-04-12 17:54:52 +0100125typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
126STATIC const reg_name_t reg_name_table[] = {
127 {0, "r0\0"},
128 {1, "r1\0"},
129 {2, "r2\0"},
130 {3, "r3\0"},
131 {4, "r4\0"},
132 {5, "r5\0"},
133 {6, "r6\0"},
134 {7, "r7\0"},
135 {8, "r8\0"},
136 {9, "r9\0"},
137 {10, "r10"},
138 {11, "r11"},
139 {12, "r12"},
140 {13, "r13"},
141 {14, "r14"},
142 {15, "r15"},
143 {10, "sl\0"},
144 {11, "fp\0"},
145 {13, "sp\0"},
146 {14, "lr\0"},
147 {15, "pc\0"},
148};
149
Henrik Sölvere242b172015-11-21 22:27:54 +0100150#define MAX_SPECIAL_REGISTER_NAME_LENGTH 7
151typedef struct _special_reg_name_t { byte reg; char name[MAX_SPECIAL_REGISTER_NAME_LENGTH + 1]; } special_reg_name_t;
152STATIC const special_reg_name_t special_reg_name_table[] = {
153 {5, "IPSR"},
154 {17, "BASEPRI"},
155};
156
Damien George42495392015-02-16 17:46:49 +0000157// return empty string in case of error, so we can attempt to parse the string
158// without a special check if it was in fact a string
159STATIC const char *get_arg_str(mp_parse_node_t pn) {
Damien George87210872014-04-13 00:30:32 +0100160 if (MP_PARSE_NODE_IS_ID(pn)) {
Damien George42495392015-02-16 17:46:49 +0000161 qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
162 return qstr_str(qst);
163 } else {
164 return "";
165 }
166}
167
168STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
169 const char *reg_str = get_arg_str(pn);
170 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
171 const reg_name_t *r = &reg_name_table[i];
172 if (reg_str[0] == r->name[0]
173 && reg_str[1] == r->name[1]
174 && reg_str[2] == r->name[2]
175 && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
176 if (r->reg > max_reg) {
177 emit_inline_thumb_error_exc(emit,
178 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
179 "'%s' expects at most r%d", op, max_reg));
180 return 0;
181 } else {
182 return r->reg;
Damien Georgea26dc502014-04-12 17:54:52 +0100183 }
184 }
Damien826005c2013-10-05 23:17:28 +0100185 }
Damien George42495392015-02-16 17:46:49 +0000186 emit_inline_thumb_error_exc(emit,
187 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
188 "'%s' expects a register", op));
Damien Georgea26dc502014-04-12 17:54:52 +0100189 return 0;
Damien826005c2013-10-05 23:17:28 +0100190}
191
Henrik Sölvere242b172015-11-21 22:27:54 +0100192STATIC mp_uint_t get_arg_special_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
193 const char *reg_str = get_arg_str(pn);
194 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(special_reg_name_table); i++) {
195 const special_reg_name_t *r = &special_reg_name_table[i];
196 if (strcmp(r->name, reg_str) == 0) {
197 return r->reg;
198 }
199 }
200 emit_inline_thumb_error_exc(emit,
201 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
202 "'%s' expects a special register", op));
203 return 0;
204}
205
=50089722015-04-14 13:14:57 +0100206#if MICROPY_EMIT_INLINE_THUMB_FLOAT
207STATIC mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
208 const char *reg_str = get_arg_str(pn);
209 if (reg_str[0] == 's' && reg_str[1] != '\0') {
210 mp_uint_t regno = 0;
211 for (++reg_str; *reg_str; ++reg_str) {
212 mp_uint_t v = *reg_str;
213 if (!('0' <= v && v <= '9')) {
214 goto malformed;
215 }
216 regno = 10 * regno + v - '0';
217 }
218 if (regno > 31) {
219 emit_inline_thumb_error_exc(emit,
220 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
221 "'%s' expects at most r%d", op, 31));
222 return 0;
223 } else {
224 return regno;
225 }
226 }
227malformed:
228 emit_inline_thumb_error_exc(emit,
229 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
230 "'%s' expects an FPU register", op));
231 return 0;
232}
233#endif
234
Damien George0d967b82015-02-13 02:30:35 +0000235STATIC mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
236 // a register list looks like {r0, r1, r2} and is parsed as a Python set
237
238 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) {
239 goto bad_arg;
240 }
241
242 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
243 assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be
244 pn = pns->nodes[0];
245
246 mp_uint_t reglist = 0;
247
248 if (MP_PARSE_NODE_IS_ID(pn)) {
249 // set with one element
250 reglist |= 1 << get_arg_reg(emit, op, pn, 15);
251 } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
252 pns = (mp_parse_node_struct_t*)pn;
253 if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
254 assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
255 mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
256 if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
257 // set with multiple elements
258
259 // get first element of set (we rely on get_arg_reg to catch syntax errors)
260 reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15);
261
262 // get tail elements (2nd, 3rd, ...)
263 mp_parse_node_t *nodes;
264 int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
265
266 // process rest of elements
267 for (int i = 0; i < n; i++) {
268 reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15);
269 }
270 } else {
271 goto bad_arg;
272 }
273 } else {
274 goto bad_arg;
275 }
276 } else {
277 goto bad_arg;
278 }
279
280 return reglist;
281
282bad_arg:
283 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects {r0, r1, ...}", op));
284 return 0;
285}
286
Damien George47dc5922016-01-07 14:45:57 +0000287STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint32_t fit_mask) {
288 mp_obj_t o;
289 if (!mp_parse_node_get_int_maybe(pn, &o)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000290 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 +0100291 return 0;
292 }
Damien George47dc5922016-01-07 14:45:57 +0000293 uint32_t i = mp_obj_get_int_truncated(o);
Damien826005c2013-10-05 23:17:28 +0100294 if ((i & (~fit_mask)) != 0) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000295 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 +0100296 return 0;
297 }
298 return i;
299}
300
Damien George87210872014-04-13 00:30:32 +0100301STATIC 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) {
302 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
303 goto bad_arg;
304 }
305 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
306 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
307 goto bad_arg;
308 }
309 pns = (mp_parse_node_struct_t*)pns->nodes[0];
310 if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
311 goto bad_arg;
312 }
313
314 *pn_base = pns->nodes[0];
315 *pn_offset = pns->nodes[1];
316 return true;
317
318bad_arg:
Damien George8dfbd2d2015-02-13 01:00:51 +0000319 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 +0100320 return false;
321}
322
323STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
324 if (!MP_PARSE_NODE_IS_ID(pn)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000325 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 +0100326 return 0;
327 }
Damien George87210872014-04-13 00:30:32 +0100328 qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
Damien George1f92ffb2015-11-09 14:11:47 +0000329 for (uint i = 0; i < emit->max_num_labels; i++) {
Damien826005c2013-10-05 23:17:28 +0100330 if (emit->label_lookup[i] == label_qstr) {
331 return i;
332 }
333 }
Damiendc833822013-10-06 01:01:01 +0100334 // only need to have the labels on the last pass
Damien George36db6bc2014-05-07 17:24:22 +0100335 if (emit->pass == MP_PASS_EMIT) {
Damien George044c4732015-04-11 13:03:37 +0100336 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 +0100337 }
Damien826005c2013-10-05 23:17:28 +0100338 return 0;
339}
340
Damien George47e1b852014-04-08 18:28:33 +0100341typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t;
342STATIC const cc_name_t cc_name_table[] = {
Damien George0b610de2014-09-29 16:25:04 +0100343 { ASM_THUMB_CC_EQ, "eq" },
344 { ASM_THUMB_CC_NE, "ne" },
345 { ASM_THUMB_CC_CS, "cs" },
346 { ASM_THUMB_CC_CC, "cc" },
347 { ASM_THUMB_CC_MI, "mi" },
348 { ASM_THUMB_CC_PL, "pl" },
349 { ASM_THUMB_CC_VS, "vs" },
350 { ASM_THUMB_CC_VC, "vc" },
351 { ASM_THUMB_CC_HI, "hi" },
352 { ASM_THUMB_CC_LS, "ls" },
353 { ASM_THUMB_CC_GE, "ge" },
354 { ASM_THUMB_CC_LT, "lt" },
355 { ASM_THUMB_CC_GT, "gt" },
356 { ASM_THUMB_CC_LE, "le" },
Damien George47e1b852014-04-08 18:28:33 +0100357};
358
Damien George8f7976b2015-02-24 16:10:58 +0000359typedef struct _format_4_op_t { byte op; char name[3]; } format_4_op_t;
360#define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
361STATIC const format_4_op_t format_4_op_table[] = {
362 { X(ASM_THUMB_FORMAT_4_EOR), "eor" },
363 { X(ASM_THUMB_FORMAT_4_LSL), "lsl" },
364 { X(ASM_THUMB_FORMAT_4_LSR), "lsr" },
365 { X(ASM_THUMB_FORMAT_4_ASR), "asr" },
366 { X(ASM_THUMB_FORMAT_4_ADC), "adc" },
367 { X(ASM_THUMB_FORMAT_4_SBC), "sbc" },
368 { X(ASM_THUMB_FORMAT_4_ROR), "ror" },
369 { X(ASM_THUMB_FORMAT_4_TST), "tst" },
370 { X(ASM_THUMB_FORMAT_4_NEG), "neg" },
371 { X(ASM_THUMB_FORMAT_4_CMP), "cmp" },
372 { X(ASM_THUMB_FORMAT_4_CMN), "cmn" },
373 { X(ASM_THUMB_FORMAT_4_ORR), "orr" },
374 { X(ASM_THUMB_FORMAT_4_MUL), "mul" },
375 { X(ASM_THUMB_FORMAT_4_BIC), "bic" },
376 { X(ASM_THUMB_FORMAT_4_MVN), "mvn" },
377};
378#undef X
379
Damien George28adab32016-02-23 15:20:39 +0000380// name is actually a qstr, which should fit in 16 bits
381typedef struct _format_9_10_op_t { uint16_t op; uint16_t name; } format_9_10_op_t;
Damien George8f7976b2015-02-24 16:10:58 +0000382#define X(x) (x)
383STATIC const format_9_10_op_t format_9_10_op_table[] = {
Damien George28adab32016-02-23 15:20:39 +0000384 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_ldr },
385 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_ldrb },
386 { X(ASM_THUMB_FORMAT_10_LDRH), MP_QSTR_ldrh },
387 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_str },
388 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_strb },
389 { X(ASM_THUMB_FORMAT_10_STRH), MP_QSTR_strh },
Damien George8f7976b2015-02-24 16:10:58 +0000390};
391#undef X
392
=50089722015-04-14 13:14:57 +0100393#if MICROPY_EMIT_INLINE_THUMB_FLOAT
394// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
395typedef struct _format_vfp_op_t { byte op; char name[3]; } format_vfp_op_t;
396STATIC const format_vfp_op_t format_vfp_op_table[] = {
397 { 0x30, "add" },
398 { 0x34, "sub" },
399 { 0x20, "mul" },
400 { 0x80, "div" },
401};
402#endif
403
Damien Georgee8135412015-10-16 22:08:57 +0100404// shorthand alias for whether we allow ARMv7-M instructions
405#define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M
406
Damien George7ff996c2014-09-08 23:05:16 +0100407STATIC 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 +0100408 // TODO perhaps make two tables:
Damien03d41242013-10-06 00:36:05 +0100409 // one_args =
410 // "b", LAB, asm_thumb_b_n,
411 // "bgt", LAB, asm_thumb_bgt_n,
Damien826005c2013-10-05 23:17:28 +0100412 // two_args =
413 // "movs", RLO, I8, asm_thumb_movs_reg_i8
414 // "movw", REG, REG, asm_thumb_movw_reg_i16
415 // three_args =
416 // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
417
Damien Georgec3f64d92015-11-27 12:23:18 +0000418 size_t op_len;
Damien George6eb75302015-04-11 21:53:39 +0100419 const char *op_str = (const char*)qstr_data(op, &op_len);
Damien826005c2013-10-05 23:17:28 +0100420
=50089722015-04-14 13:14:57 +0100421 #if MICROPY_EMIT_INLINE_THUMB_FLOAT
422 if (op_str[0] == 'v') {
423 // floating point operations
424 if (n_args == 2) {
425 mp_uint_t op_code = 0x0ac0, op_code_hi;
Damien George28adab32016-02-23 15:20:39 +0000426 if (op == MP_QSTR_vcmp) {
=50089722015-04-14 13:14:57 +0100427 op_code_hi = 0xeeb4;
428 op_vfp_twoargs:;
429 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
430 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100431 asm_thumb_op32(&emit->as,
=50089722015-04-14 13:14:57 +0100432 op_code_hi | ((vd & 1) << 6),
433 op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1);
Damien George28adab32016-02-23 15:20:39 +0000434 } else if (op == MP_QSTR_vsqrt) {
=50089722015-04-14 13:14:57 +0100435 op_code_hi = 0xeeb1;
436 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000437 } else if (op == MP_QSTR_vneg) {
=50089722015-04-14 13:14:57 +0100438 op_code_hi = 0xeeb1;
439 op_code = 0x0a40;
440 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000441 } else if (op == MP_QSTR_vcvt_f32_s32) {
=50089722015-04-14 13:14:57 +0100442 op_code_hi = 0xeeb8; // int to float
443 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000444 } else if (op == MP_QSTR_vcvt_s32_f32) {
=50089722015-04-14 13:14:57 +0100445 op_code_hi = 0xeebd; // float to int
446 goto op_vfp_twoargs;
Damien George28adab32016-02-23 15:20:39 +0000447 } else if (op == MP_QSTR_vmrs) {
=50089722015-04-14 13:14:57 +0100448 mp_uint_t reg_dest;
449 const char *reg_str0 = get_arg_str(pn_args[0]);
450 if (strcmp(reg_str0, "APSR_nzcv") == 0) {
451 reg_dest = 15;
452 } else {
453 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
454 }
455 const char *reg_str1 = get_arg_str(pn_args[1]);
456 if (strcmp(reg_str1, "FPSCR") == 0) {
457 // FP status to ARM reg
Damien Georgea7fd7862016-12-09 20:35:21 +1100458 asm_thumb_op32(&emit->as, 0xeef1, 0x0a10 | (reg_dest << 12));
=50089722015-04-14 13:14:57 +0100459 } else {
460 goto unknown_op;
461 }
Damien George28adab32016-02-23 15:20:39 +0000462 } else if (op == MP_QSTR_vmov) {
=50089722015-04-14 13:14:57 +0100463 op_code_hi = 0xee00;
464 mp_uint_t r_arm, vm;
465 const char *reg_str = get_arg_str(pn_args[0]);
466 if (reg_str[0] == 'r') {
467 r_arm = get_arg_reg(emit, op_str, pn_args[0], 15);
468 vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
469 op_code_hi |= 0x10;
470 } else {
471 vm = get_arg_vfpreg(emit, op_str, pn_args[0]);
472 r_arm = get_arg_reg(emit, op_str, pn_args[1], 15);
473 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100474 asm_thumb_op32(&emit->as,
=50089722015-04-14 13:14:57 +0100475 op_code_hi | ((vm & 0x1e) >> 1),
476 0x0a10 | (r_arm << 12) | ((vm & 1) << 7));
Damien George28adab32016-02-23 15:20:39 +0000477 } else if (op == MP_QSTR_vldr) {
=50089722015-04-14 13:14:57 +0100478 op_code_hi = 0xed90;
479 op_vldr_vstr:;
480 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
481 mp_parse_node_t pn_base, pn_offset;
482 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
483 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
484 mp_uint_t i8;
adminpeted6201fc2015-10-31 09:38:22 +0000485 i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2;
Damien Georgea7fd7862016-12-09 20:35:21 +1100486 asm_thumb_op32(&emit->as,
=50089722015-04-14 13:14:57 +0100487 op_code_hi | rlo_base | ((vd & 1) << 6),
488 0x0a00 | ((vd & 0x1e) << 11) | i8);
489 }
Damien George28adab32016-02-23 15:20:39 +0000490 } else if (op == MP_QSTR_vstr) {
=50089722015-04-14 13:14:57 +0100491 op_code_hi = 0xed80;
492 goto op_vldr_vstr;
493 } else {
494 goto unknown_op;
495 }
496 } else if (n_args == 3) {
497 // search table for arith ops
498 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) {
499 if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') {
500 mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0);
501 mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4);
502 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
503 mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]);
504 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100505 asm_thumb_op32(&emit->as,
=50089722015-04-14 13:14:57 +0100506 op_code_hi | ((vd & 1) << 6) | (vn >> 1),
507 op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7));
508 return;
509 }
510 }
511 goto unknown_op;
512 } else {
513 goto unknown_op;
514 }
515 } else
516 #endif
Damien George47e1b852014-04-08 18:28:33 +0100517 if (n_args == 0) {
Damien George28adab32016-02-23 15:20:39 +0000518 if (op == MP_QSTR_nop) {
Damien Georgea7fd7862016-12-09 20:35:21 +1100519 asm_thumb_op16(&emit->as, ASM_THUMB_OP_NOP);
Damien George28adab32016-02-23 15:20:39 +0000520 } else if (op == MP_QSTR_wfi) {
Damien Georgea7fd7862016-12-09 20:35:21 +1100521 asm_thumb_op16(&emit->as, ASM_THUMB_OP_WFI);
Damien George47e1b852014-04-08 18:28:33 +0100522 } else {
523 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100524 }
Damien826005c2013-10-05 23:17:28 +0100525
Damien George47e1b852014-04-08 18:28:33 +0100526 } else if (n_args == 1) {
Damien George28adab32016-02-23 15:20:39 +0000527 if (op == MP_QSTR_b) {
Damien George87210872014-04-13 00:30:32 +0100528 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100529 if (!asm_thumb_b_n_label(&emit->as, label_num)) {
Damien George53457432015-02-25 15:45:55 +0000530 goto branch_not_in_range;
531 }
Damien George28adab32016-02-23 15:20:39 +0000532 } else if (op == MP_QSTR_bl) {
Damien Georgeeff10f62015-02-16 18:17:07 +0000533 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100534 if (!asm_thumb_bl_label(&emit->as, label_num)) {
Damien George53457432015-02-25 15:45:55 +0000535 goto branch_not_in_range;
536 }
Damien George28adab32016-02-23 15:20:39 +0000537 } else if (op == MP_QSTR_bx) {
Damien Georgeeff10f62015-02-16 18:17:07 +0000538 mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien Georgea7fd7862016-12-09 20:35:21 +1100539 asm_thumb_op16(&emit->as, 0x4700 | (r << 3));
Damien George9f142f02015-03-02 14:29:52 +0000540 } else if (op_str[0] == 'b' && (op_len == 3
541 || (op_len == 5 && op_str[3] == '_'
Damien Georgee8135412015-10-16 22:08:57 +0100542 && (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) {
Damien George7ff996c2014-09-08 23:05:16 +0100543 mp_uint_t cc = -1;
544 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
Damien George47e1b852014-04-08 18:28:33 +0100545 if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
546 cc = cc_name_table[i].cc;
547 }
548 }
Damien George1f92ffb2015-11-09 14:11:47 +0000549 if (cc == (mp_uint_t)-1) {
Damien George47e1b852014-04-08 18:28:33 +0100550 goto unknown_op;
551 }
Damien George87210872014-04-13 00:30:32 +0100552 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100553 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 +0000554 goto branch_not_in_range;
555 }
Damien Georgee8135412015-10-16 22:08:57 +0100556 } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') {
Damien George42495392015-02-16 17:46:49 +0000557 const char *arg_str = get_arg_str(pn_args[0]);
558 mp_uint_t cc = -1;
559 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
560 if (arg_str[0] == cc_name_table[i].name[0]
561 && arg_str[1] == cc_name_table[i].name[1]
562 && arg_str[2] == '\0') {
563 cc = cc_name_table[i].cc;
564 break;
565 }
566 }
Damien George1f92ffb2015-11-09 14:11:47 +0000567 if (cc == (mp_uint_t)-1) {
Damien George42495392015-02-16 17:46:49 +0000568 goto unknown_op;
569 }
570 const char *os = op_str + 2;
571 while (*os != '\0') {
572 os++;
573 }
574 if (os > op_str + 5) {
575 goto unknown_op;
576 }
577 mp_uint_t it_mask = 8;
578 while (--os >= op_str + 2) {
579 it_mask >>= 1;
580 if (*os == 't') {
581 it_mask |= (cc & 1) << 3;
582 } else if (*os == 'e') {
583 it_mask |= ((~cc) & 1) << 3;
584 } else {
585 goto unknown_op;
586 }
587 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100588 asm_thumb_it_cc(&emit->as, cc, it_mask);
Damien George28adab32016-02-23 15:20:39 +0000589 } else if (op == MP_QSTR_cpsid) {
Damien George90edf9e2014-04-18 16:56:54 +0100590 // TODO check pn_args[0] == i
Damien Georgea7fd7862016-12-09 20:35:21 +1100591 asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSID_I);
Damien George28adab32016-02-23 15:20:39 +0000592 } else if (op == MP_QSTR_cpsie) {
Damien George90edf9e2014-04-18 16:56:54 +0100593 // TODO check pn_args[0] == i
Damien Georgea7fd7862016-12-09 20:35:21 +1100594 asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSIE_I);
Damien George28adab32016-02-23 15:20:39 +0000595 } else if (op == MP_QSTR_push) {
Damien George0d967b82015-02-13 02:30:35 +0000596 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
597 if ((reglist & 0xff00) == 0) {
Damien Georgea7fd7862016-12-09 20:35:21 +1100598 asm_thumb_op16(&emit->as, 0xb400 | reglist);
Damien George0d967b82015-02-13 02:30:35 +0000599 } else {
Damien Georgee8135412015-10-16 22:08:57 +0100600 if (!ARMV7M) {
601 goto unknown_op;
602 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100603 asm_thumb_op32(&emit->as, 0xe92d, reglist);
Damien George0d967b82015-02-13 02:30:35 +0000604 }
Damien George28adab32016-02-23 15:20:39 +0000605 } else if (op == MP_QSTR_pop) {
Damien George0d967b82015-02-13 02:30:35 +0000606 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
607 if ((reglist & 0xff00) == 0) {
Damien Georgea7fd7862016-12-09 20:35:21 +1100608 asm_thumb_op16(&emit->as, 0xbc00 | reglist);
Damien George0d967b82015-02-13 02:30:35 +0000609 } else {
Damien Georgee8135412015-10-16 22:08:57 +0100610 if (!ARMV7M) {
611 goto unknown_op;
612 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100613 asm_thumb_op32(&emit->as, 0xe8bd, reglist);
Damien George0d967b82015-02-13 02:30:35 +0000614 }
Damien George47e1b852014-04-08 18:28:33 +0100615 } else {
616 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100617 }
Damien826005c2013-10-05 23:17:28 +0100618
Damien George47e1b852014-04-08 18:28:33 +0100619 } else if (n_args == 2) {
Damien George87210872014-04-13 00:30:32 +0100620 if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
621 // second arg is a register (or should be)
Damien George192d5362015-02-13 11:06:23 +0000622 mp_uint_t op_code, op_code_hi;
Damien George28adab32016-02-23 15:20:39 +0000623 if (op == MP_QSTR_mov) {
Damien George7ff996c2014-09-08 23:05:16 +0100624 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
625 mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
Damien Georgea7fd7862016-12-09 20:35:21 +1100626 asm_thumb_mov_reg_reg(&emit->as, reg_dest, reg_src);
Damien George28adab32016-02-23 15:20:39 +0000627 } else if (ARMV7M && op == MP_QSTR_clz) {
Damien George192d5362015-02-13 11:06:23 +0000628 op_code_hi = 0xfab0;
629 op_code = 0xf080;
630 mp_uint_t rd, rm;
631 op_clz_rbit:
632 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
633 rm = get_arg_reg(emit, op_str, pn_args[1], 15);
Damien Georgea7fd7862016-12-09 20:35:21 +1100634 asm_thumb_op32(&emit->as, op_code_hi | rm, op_code | (rd << 8) | rm);
Damien George28adab32016-02-23 15:20:39 +0000635 } else if (ARMV7M && op == MP_QSTR_rbit) {
Damien George192d5362015-02-13 11:06:23 +0000636 op_code_hi = 0xfa90;
637 op_code = 0xf0a0;
638 goto op_clz_rbit;
Damien George28adab32016-02-23 15:20:39 +0000639 } else if (ARMV7M && op == MP_QSTR_mrs){
Henrik Sölvere242b172015-11-21 22:27:54 +0100640 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12);
641 mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]);
Damien Georgea7fd7862016-12-09 20:35:21 +1100642 asm_thumb_op32(&emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src);
Damien George87210872014-04-13 00:30:32 +0100643 } else {
Damien George28adab32016-02-23 15:20:39 +0000644 if (op == MP_QSTR_and_) {
Damien George8f7976b2015-02-24 16:10:58 +0000645 op_code = ASM_THUMB_FORMAT_4_AND;
646 mp_uint_t reg_dest, reg_src;
647 op_format_4:
648 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
649 reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
Damien Georgea7fd7862016-12-09 20:35:21 +1100650 asm_thumb_format_4(&emit->as, op_code, reg_dest, reg_src);
Damien George993f0672015-02-24 22:43:01 +0000651 return;
Damien George8f7976b2015-02-24 16:10:58 +0000652 }
Damien Georgee5315f72015-02-24 16:35:37 +0000653 // search table for ALU ops
Damien George8f7976b2015-02-24 16:10:58 +0000654 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) {
655 if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') {
656 op_code = 0x4000 | (format_4_op_table[i].op << 4);
657 goto op_format_4;
658 }
659 }
Damien George87210872014-04-13 00:30:32 +0100660 goto unknown_op;
661 }
Damien George47e1b852014-04-08 18:28:33 +0100662 } else {
Damien George87210872014-04-13 00:30:32 +0100663 // second arg is not a register
Damien George7ff996c2014-09-08 23:05:16 +0100664 mp_uint_t op_code;
Damien George28adab32016-02-23 15:20:39 +0000665 if (op == MP_QSTR_mov) {
Damien George87210872014-04-13 00:30:32 +0100666 op_code = ASM_THUMB_FORMAT_3_MOV;
Damien George7ff996c2014-09-08 23:05:16 +0100667 mp_uint_t rlo_dest, i8_src;
Damien George87210872014-04-13 00:30:32 +0100668 op_format_3:
669 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
670 i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
Damien Georgea7fd7862016-12-09 20:35:21 +1100671 asm_thumb_format_3(&emit->as, op_code, rlo_dest, i8_src);
Damien George28adab32016-02-23 15:20:39 +0000672 } else if (op == MP_QSTR_cmp) {
Damien George87210872014-04-13 00:30:32 +0100673 op_code = ASM_THUMB_FORMAT_3_CMP;
674 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000675 } else if (op == MP_QSTR_add) {
Damien George87210872014-04-13 00:30:32 +0100676 op_code = ASM_THUMB_FORMAT_3_ADD;
677 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000678 } else if (op == MP_QSTR_sub) {
Damien George87210872014-04-13 00:30:32 +0100679 op_code = ASM_THUMB_FORMAT_3_SUB;
680 goto op_format_3;
Damien George28adab32016-02-23 15:20:39 +0000681 } else if (ARMV7M && op == MP_QSTR_movw) {
Damien Georgee5315f72015-02-24 16:35:37 +0000682 op_code = ASM_THUMB_OP_MOVW;
683 mp_uint_t reg_dest;
684 op_movw_movt:
685 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100686 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
Damien Georgea7fd7862016-12-09 20:35:21 +1100687 asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src);
Damien George28adab32016-02-23 15:20:39 +0000688 } else if (ARMV7M && op == MP_QSTR_movt) {
Damien Georgee5315f72015-02-24 16:35:37 +0000689 op_code = ASM_THUMB_OP_MOVT;
690 goto op_movw_movt;
Damien George28adab32016-02-23 15:20:39 +0000691 } else if (ARMV7M && op == MP_QSTR_movwt) {
Damien George87210872014-04-13 00:30:32 +0100692 // this is a convenience instruction
Damien George7ff996c2014-09-08 23:05:16 +0100693 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien Georgeea8be372016-01-07 14:54:13 +0000694 uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
Damien Georgea7fd7862016-12-09 20:35:21 +1100695 asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff);
696 asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff);
Damien George28adab32016-02-23 15:20:39 +0000697 } else if (ARMV7M && op == MP_QSTR_ldrex) {
Damien George1bf5a022015-02-12 22:52:42 +0000698 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
699 mp_parse_node_t pn_base, pn_offset;
700 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
701 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
702 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
Damien Georgea7fd7862016-12-09 20:35:21 +1100703 asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8);
Damien George1bf5a022015-02-12 22:52:42 +0000704 }
Damien George87210872014-04-13 00:30:32 +0100705 } else {
Damien George8f7976b2015-02-24 16:10:58 +0000706 // search table for ldr/str instructions
707 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) {
Damien George28adab32016-02-23 15:20:39 +0000708 if (op == format_9_10_op_table[i].name) {
Damien George8f7976b2015-02-24 16:10:58 +0000709 op_code = format_9_10_op_table[i].op;
710 mp_parse_node_t pn_base, pn_offset;
711 mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
712 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
713 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
714 mp_uint_t i5;
715 if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
716 i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
717 } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
718 i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
719 } else {
720 i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
721 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100722 asm_thumb_format_9_10(&emit->as, op_code, rlo_dest, rlo_base, i5);
Damien George8f7976b2015-02-24 16:10:58 +0000723 return;
724 }
725 break;
726 }
727 }
Damien George87210872014-04-13 00:30:32 +0100728 goto unknown_op;
729 }
Damien George47e1b852014-04-08 18:28:33 +0100730 }
731
732 } else if (n_args == 3) {
Damien George7ff996c2014-09-08 23:05:16 +0100733 mp_uint_t op_code;
Damien George28adab32016-02-23 15:20:39 +0000734 if (op == MP_QSTR_lsl) {
Damien George096d1e42015-10-19 14:26:19 +0100735 op_code = ASM_THUMB_FORMAT_1_LSL;
736 mp_uint_t rlo_dest, rlo_src, i5;
737 op_format_1:
738 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
739 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
740 i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f);
Damien Georgea7fd7862016-12-09 20:35:21 +1100741 asm_thumb_format_1(&emit->as, op_code, rlo_dest, rlo_src, i5);
Damien George28adab32016-02-23 15:20:39 +0000742 } else if (op == MP_QSTR_lsr) {
Damien George096d1e42015-10-19 14:26:19 +0100743 op_code = ASM_THUMB_FORMAT_1_LSR;
744 goto op_format_1;
Damien George28adab32016-02-23 15:20:39 +0000745 } else if (op == MP_QSTR_asr) {
Damien George096d1e42015-10-19 14:26:19 +0100746 op_code = ASM_THUMB_FORMAT_1_ASR;
747 goto op_format_1;
Damien George28adab32016-02-23 15:20:39 +0000748 } else if (op == MP_QSTR_add) {
Damien George87210872014-04-13 00:30:32 +0100749 op_code = ASM_THUMB_FORMAT_2_ADD;
Damien George7ff996c2014-09-08 23:05:16 +0100750 mp_uint_t rlo_dest, rlo_src;
Damien George87210872014-04-13 00:30:32 +0100751 op_format_2:
752 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
753 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
754 int src_b;
755 if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
756 op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
757 src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
758 } else {
759 op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
760 src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
761 }
Damien Georgea7fd7862016-12-09 20:35:21 +1100762 asm_thumb_format_2(&emit->as, op_code, rlo_dest, rlo_src, src_b);
Damien George28adab32016-02-23 15:20:39 +0000763 } else if (ARMV7M && op == MP_QSTR_sdiv) {
Damien George32f0b792015-02-13 10:43:05 +0000764 op_code = 0xfb90; // sdiv high part
765 mp_uint_t rd, rn, rm;
766 op_sdiv_udiv:
767 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
768 rn = get_arg_reg(emit, op_str, pn_args[1], 15);
769 rm = get_arg_reg(emit, op_str, pn_args[2], 15);
Damien Georgea7fd7862016-12-09 20:35:21 +1100770 asm_thumb_op32(&emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm);
Damien George28adab32016-02-23 15:20:39 +0000771 } else if (ARMV7M && op == MP_QSTR_udiv) {
Damien George32f0b792015-02-13 10:43:05 +0000772 op_code = 0xfbb0; // udiv high part
773 goto op_sdiv_udiv;
Damien George28adab32016-02-23 15:20:39 +0000774 } else if (op == MP_QSTR_sub) {
Damien George87210872014-04-13 00:30:32 +0100775 op_code = ASM_THUMB_FORMAT_2_SUB;
776 goto op_format_2;
Damien George28adab32016-02-23 15:20:39 +0000777 } else if (ARMV7M && op == MP_QSTR_strex) {
Damien George1bf5a022015-02-12 22:52:42 +0000778 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
779 mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15);
780 mp_parse_node_t pn_base, pn_offset;
781 if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) {
782 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
783 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
Damien Georgea7fd7862016-12-09 20:35:21 +1100784 asm_thumb_op32(&emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8);
Damien George1bf5a022015-02-12 22:52:42 +0000785 }
Damien George47e1b852014-04-08 18:28:33 +0100786 } else {
787 goto unknown_op;
788 }
789
Damien826005c2013-10-05 23:17:28 +0100790 } else {
Damien George47e1b852014-04-08 18:28:33 +0100791 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100792 }
Damien George47e1b852014-04-08 18:28:33 +0100793
794 return;
795
796unknown_op:
Damien George8dfbd2d2015-02-13 01:00:51 +0000797 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 +0000798 return;
799
800branch_not_in_range:
801 emit_inline_thumb_error_msg(emit, "branch not in range");
802 return;
Damien826005c2013-10-05 23:17:28 +0100803}
804
805const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
806 emit_inline_thumb_start_pass,
807 emit_inline_thumb_end_pass,
Damiena2f2f7d2013-10-06 00:14:13 +0100808 emit_inline_thumb_count_params,
Damien826005c2013-10-05 23:17:28 +0100809 emit_inline_thumb_label,
810 emit_inline_thumb_op,
811};
812
Damien3ef4abb2013-10-12 16:53:13 +0100813#endif // MICROPY_EMIT_INLINE_THUMB