1 /*
2  * This file is part of the MicroPython 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 
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <assert.h>
32 
33 #include "py/emit.h"
34 #include "py/asmthumb.h"
35 
36 #if MICROPY_EMIT_INLINE_THUMB
37 
38 typedef enum {
39 // define rules with a compile function
40 #define DEF_RULE(rule, comp, kind, ...) PN_##rule,
41 #define DEF_RULE_NC(rule, kind, ...)
42     #include "py/grammar.h"
43 #undef DEF_RULE
44 #undef DEF_RULE_NC
45     PN_const_object, // special node for a constant, generic Python object
46 // define rules without a compile function
47 #define DEF_RULE(rule, comp, kind, ...)
48 #define DEF_RULE_NC(rule, kind, ...) PN_##rule,
49     #include "py/grammar.h"
50 #undef DEF_RULE
51 #undef DEF_RULE_NC
52 } pn_kind_t;
53 
54 struct _emit_inline_asm_t {
55     asm_thumb_t as;
56     uint16_t pass;
57     mp_obj_t *error_slot;
58     mp_uint_t max_num_labels;
59     qstr *label_lookup;
60 };
61 
emit_inline_thumb_error_msg(emit_inline_asm_t * emit,mp_rom_error_text_t msg)62 STATIC void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) {
63     *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
64 }
65 
emit_inline_thumb_error_exc(emit_inline_asm_t * emit,mp_obj_t exc)66 STATIC void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
67     *emit->error_slot = exc;
68 }
69 
emit_inline_thumb_new(mp_uint_t max_num_labels)70 emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
71     emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
72     memset(&emit->as, 0, sizeof(emit->as));
73     mp_asm_base_init(&emit->as.base, max_num_labels);
74     emit->max_num_labels = max_num_labels;
75     emit->label_lookup = m_new(qstr, max_num_labels);
76     return emit;
77 }
78 
emit_inline_thumb_free(emit_inline_asm_t * emit)79 void emit_inline_thumb_free(emit_inline_asm_t *emit) {
80     m_del(qstr, emit->label_lookup, emit->max_num_labels);
81     mp_asm_base_deinit(&emit->as.base, false);
82     m_del_obj(emit_inline_asm_t, emit);
83 }
84 
emit_inline_thumb_start_pass(emit_inline_asm_t * emit,pass_kind_t pass,mp_obj_t * error_slot)85 STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
86     emit->pass = pass;
87     emit->error_slot = error_slot;
88     if (emit->pass == MP_PASS_CODE_SIZE) {
89         memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
90     }
91     mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
92     asm_thumb_entry(&emit->as, 0);
93 }
94 
emit_inline_thumb_end_pass(emit_inline_asm_t * emit,mp_uint_t type_sig)95 STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
96     asm_thumb_exit(&emit->as);
97     asm_thumb_end_pass(&emit->as);
98 }
99 
emit_inline_thumb_count_params(emit_inline_asm_t * emit,mp_uint_t n_params,mp_parse_node_t * pn_params)100 STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
101     if (n_params > 4) {
102         emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters to Thumb assembly"));
103         return 0;
104     }
105     for (mp_uint_t i = 0; i < n_params; i++) {
106         if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
107             emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3"));
108             return 0;
109         }
110         const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
111         if (!(strlen(p) == 2 && p[0] == 'r' && (mp_uint_t)p[1] == '0' + i)) {
112             emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3"));
113             return 0;
114         }
115     }
116     return n_params;
117 }
118 
emit_inline_thumb_label(emit_inline_asm_t * emit,mp_uint_t label_num,qstr label_id)119 STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
120     assert(label_num < emit->max_num_labels);
121     if (emit->pass == MP_PASS_CODE_SIZE) {
122         // check for duplicate label on first pass
123         for (uint i = 0; i < emit->max_num_labels; i++) {
124             if (emit->label_lookup[i] == label_id) {
125                 return false;
126             }
127         }
128     }
129     emit->label_lookup[label_num] = label_id;
130     mp_asm_base_label_assign(&emit->as.base, label_num);
131     return true;
132 }
133 
134 typedef struct _reg_name_t { byte reg;
135                              byte name[3];
136 } reg_name_t;
137 STATIC const reg_name_t reg_name_table[] = {
138     {0, "r0\0"},
139     {1, "r1\0"},
140     {2, "r2\0"},
141     {3, "r3\0"},
142     {4, "r4\0"},
143     {5, "r5\0"},
144     {6, "r6\0"},
145     {7, "r7\0"},
146     {8, "r8\0"},
147     {9, "r9\0"},
148     {10, "r10"},
149     {11, "r11"},
150     {12, "r12"},
151     {13, "r13"},
152     {14, "r14"},
153     {15, "r15"},
154     {10, "sl\0"},
155     {11, "fp\0"},
156     {13, "sp\0"},
157     {14, "lr\0"},
158     {15, "pc\0"},
159 };
160 
161 #define MAX_SPECIAL_REGISTER_NAME_LENGTH 7
162 typedef struct _special_reg_name_t { byte reg;
163                                      char name[MAX_SPECIAL_REGISTER_NAME_LENGTH + 1];
164 } special_reg_name_t;
165 STATIC const special_reg_name_t special_reg_name_table[] = {
166     {5, "IPSR"},
167     {17, "BASEPRI"},
168 };
169 
170 // return empty string in case of error, so we can attempt to parse the string
171 // without a special check if it was in fact a string
get_arg_str(mp_parse_node_t pn)172 STATIC const char *get_arg_str(mp_parse_node_t pn) {
173     if (MP_PARSE_NODE_IS_ID(pn)) {
174         qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
175         return qstr_str(qst);
176     } else {
177         return "";
178     }
179 }
180 
get_arg_reg(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn,mp_uint_t max_reg)181 STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
182     const char *reg_str = get_arg_str(pn);
183     for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
184         const reg_name_t *r = &reg_name_table[i];
185         if (reg_str[0] == r->name[0]
186             && reg_str[1] == r->name[1]
187             && reg_str[2] == r->name[2]
188             && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
189             if (r->reg > max_reg) {
190                 emit_inline_thumb_error_exc(emit,
191                     mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
192                         MP_ERROR_TEXT("'%s' expects at most r%d"), op, max_reg));
193                 return 0;
194             } else {
195                 return r->reg;
196             }
197         }
198     }
199     emit_inline_thumb_error_exc(emit,
200         mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
201             MP_ERROR_TEXT("'%s' expects a register"), op));
202     return 0;
203 }
204 
get_arg_special_reg(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn)205 STATIC mp_uint_t get_arg_special_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
206     const char *reg_str = get_arg_str(pn);
207     for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(special_reg_name_table); i++) {
208         const special_reg_name_t *r = &special_reg_name_table[i];
209         if (strcmp(r->name, reg_str) == 0) {
210             return r->reg;
211         }
212     }
213     emit_inline_thumb_error_exc(emit,
214         mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
215             MP_ERROR_TEXT("'%s' expects a special register"), op));
216     return 0;
217 }
218 
219 #if MICROPY_EMIT_INLINE_THUMB_FLOAT
get_arg_vfpreg(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn)220 STATIC mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
221     const char *reg_str = get_arg_str(pn);
222     if (reg_str[0] == 's' && reg_str[1] != '\0') {
223         mp_uint_t regno = 0;
224         for (++reg_str; *reg_str; ++reg_str) {
225             mp_uint_t v = *reg_str;
226             if (!('0' <= v && v <= '9')) {
227                 goto malformed;
228             }
229             regno = 10 * regno + v - '0';
230         }
231         if (regno > 31) {
232             emit_inline_thumb_error_exc(emit,
233                 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
234                     MP_ERROR_TEXT("'%s' expects at most r%d"), op, 31));
235             return 0;
236         } else {
237             return regno;
238         }
239     }
240 malformed:
241     emit_inline_thumb_error_exc(emit,
242         mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
243             MP_ERROR_TEXT("'%s' expects an FPU register"), op));
244     return 0;
245 }
246 #endif
247 
get_arg_reglist(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn)248 STATIC mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
249     // a register list looks like {r0, r1, r2} and is parsed as a Python set
250 
251     if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) {
252         goto bad_arg;
253     }
254 
255     mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn;
256     assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be
257     pn = pns->nodes[0];
258 
259     mp_uint_t reglist = 0;
260 
261     if (MP_PARSE_NODE_IS_ID(pn)) {
262         // set with one element
263         reglist |= 1 << get_arg_reg(emit, op, pn, 15);
264     } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
265         pns = (mp_parse_node_struct_t *)pn;
266         if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
267             assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
268             mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1];
269             if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
270                 // set with multiple elements
271 
272                 // get first element of set (we rely on get_arg_reg to catch syntax errors)
273                 reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15);
274 
275                 // get tail elements (2nd, 3rd, ...)
276                 mp_parse_node_t *nodes;
277                 int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
278 
279                 // process rest of elements
280                 for (int i = 0; i < n; i++) {
281                     reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15);
282                 }
283             } else {
284                 goto bad_arg;
285             }
286         } else {
287             goto bad_arg;
288         }
289     } else {
290         goto bad_arg;
291     }
292 
293     return reglist;
294 
295 bad_arg:
296     emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects {r0, r1, ...}"), op));
297     return 0;
298 }
299 
get_arg_i(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn,uint32_t fit_mask)300 STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint32_t fit_mask) {
301     mp_obj_t o;
302     if (!mp_parse_node_get_int_maybe(pn, &o)) {
303         emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an integer"), op));
304         return 0;
305     }
306     uint32_t i = mp_obj_get_int_truncated(o);
307     if ((i & (~fit_mask)) != 0) {
308         emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' integer 0x%x doesn't fit in mask 0x%x"), op, i, fit_mask));
309         return 0;
310     }
311     return i;
312 }
313 
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)314 STATIC 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) {
315     if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
316         goto bad_arg;
317     }
318     mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn;
319     if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
320         goto bad_arg;
321     }
322     pns = (mp_parse_node_struct_t *)pns->nodes[0];
323     if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
324         goto bad_arg;
325     }
326 
327     *pn_base = pns->nodes[0];
328     *pn_offset = pns->nodes[1];
329     return true;
330 
331 bad_arg:
332     emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an address of the form [a, b]"), op));
333     return false;
334 }
335 
get_arg_label(emit_inline_asm_t * emit,const char * op,mp_parse_node_t pn)336 STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
337     if (!MP_PARSE_NODE_IS_ID(pn)) {
338         emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a label"), op));
339         return 0;
340     }
341     qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
342     for (uint i = 0; i < emit->max_num_labels; i++) {
343         if (emit->label_lookup[i] == label_qstr) {
344             return i;
345         }
346     }
347     // only need to have the labels on the last pass
348     if (emit->pass == MP_PASS_EMIT) {
349         emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("label '%q' not defined"), label_qstr));
350     }
351     return 0;
352 }
353 
354 typedef struct _cc_name_t { byte cc;
355                             byte name[2];
356 } cc_name_t;
357 STATIC const cc_name_t cc_name_table[] = {
358     { 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" },
372 };
373 
374 typedef struct _format_4_op_t { byte op;
375                                 char name[3];
376 } format_4_op_t;
377 #define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
378 STATIC const format_4_op_t format_4_op_table[] = {
379     { X(ASM_THUMB_FORMAT_4_EOR), "eor" },
380     { X(ASM_THUMB_FORMAT_4_LSL), "lsl" },
381     { X(ASM_THUMB_FORMAT_4_LSR), "lsr" },
382     { X(ASM_THUMB_FORMAT_4_ASR), "asr" },
383     { X(ASM_THUMB_FORMAT_4_ADC), "adc" },
384     { X(ASM_THUMB_FORMAT_4_SBC), "sbc" },
385     { X(ASM_THUMB_FORMAT_4_ROR), "ror" },
386     { X(ASM_THUMB_FORMAT_4_TST), "tst" },
387     { X(ASM_THUMB_FORMAT_4_NEG), "neg" },
388     { X(ASM_THUMB_FORMAT_4_CMP), "cmp" },
389     { X(ASM_THUMB_FORMAT_4_CMN), "cmn" },
390     { X(ASM_THUMB_FORMAT_4_ORR), "orr" },
391     { X(ASM_THUMB_FORMAT_4_MUL), "mul" },
392     { X(ASM_THUMB_FORMAT_4_BIC), "bic" },
393     { X(ASM_THUMB_FORMAT_4_MVN), "mvn" },
394 };
395 #undef X
396 
397 // name is actually a qstr, which should fit in 16 bits
398 typedef struct _format_9_10_op_t { uint16_t op;
399                                    uint16_t name;
400 } format_9_10_op_t;
401 #define X(x) (x)
402 STATIC const format_9_10_op_t format_9_10_op_table[] = {
403     { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_ldr },
404     { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_ldrb },
405     { X(ASM_THUMB_FORMAT_10_LDRH), MP_QSTR_ldrh },
406     { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_str },
407     { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_strb },
408     { X(ASM_THUMB_FORMAT_10_STRH), MP_QSTR_strh },
409 };
410 #undef X
411 
412 #if MICROPY_EMIT_INLINE_THUMB_FLOAT
413 // actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
414 typedef struct _format_vfp_op_t { byte op;
415                                   char name[3];
416 } format_vfp_op_t;
417 STATIC const format_vfp_op_t format_vfp_op_table[] = {
418     { 0x30, "add" },
419     { 0x34, "sub" },
420     { 0x20, "mul" },
421     { 0x80, "div" },
422 };
423 #endif
424 
425 // shorthand alias for whether we allow ARMv7-M instructions
426 #define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M
427 
emit_inline_thumb_op(emit_inline_asm_t * emit,qstr op,mp_uint_t n_args,mp_parse_node_t * pn_args)428 STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) {
429     // TODO perhaps make two tables:
430     // one_args =
431     // "b", LAB, asm_thumb_b_n,
432     // "bgt", LAB, asm_thumb_bgt_n,
433     // two_args =
434     // "movs", RLO, I8, asm_thumb_movs_reg_i8
435     // "movw", REG, REG, asm_thumb_movw_reg_i16
436     // three_args =
437     // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
438 
439     size_t op_len;
440     const char *op_str = (const char *)qstr_data(op, &op_len);
441 
442     #if MICROPY_EMIT_INLINE_THUMB_FLOAT
443     if (op_str[0] == 'v') {
444         // floating point operations
445         if (n_args == 2) {
446             mp_uint_t op_code = 0x0ac0, op_code_hi;
447             if (op == MP_QSTR_vcmp) {
448                 op_code_hi = 0xeeb4;
449             op_vfp_twoargs:;
450                 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
451                 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
452                 asm_thumb_op32(&emit->as,
453                     op_code_hi | ((vd & 1) << 6),
454                     op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1);
455             } else if (op == MP_QSTR_vsqrt) {
456                 op_code_hi = 0xeeb1;
457                 goto op_vfp_twoargs;
458             } else if (op == MP_QSTR_vneg) {
459                 op_code_hi = 0xeeb1;
460                 op_code = 0x0a40;
461                 goto op_vfp_twoargs;
462             } else if (op == MP_QSTR_vcvt_f32_s32) {
463                 op_code_hi = 0xeeb8; // int to float
464                 goto op_vfp_twoargs;
465             } else if (op == MP_QSTR_vcvt_s32_f32) {
466                 op_code_hi = 0xeebd; // float to int
467                 goto op_vfp_twoargs;
468             } else if (op == MP_QSTR_vmrs) {
469                 mp_uint_t reg_dest;
470                 const char *reg_str0 = get_arg_str(pn_args[0]);
471                 if (strcmp(reg_str0, "APSR_nzcv") == 0) {
472                     reg_dest = 15;
473                 } else {
474                     reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
475                 }
476                 const char *reg_str1 = get_arg_str(pn_args[1]);
477                 if (strcmp(reg_str1, "FPSCR") == 0) {
478                     // FP status to ARM reg
479                     asm_thumb_op32(&emit->as, 0xeef1, 0x0a10 | (reg_dest << 12));
480                 } else {
481                     goto unknown_op;
482                 }
483             } else if (op == MP_QSTR_vmov) {
484                 op_code_hi = 0xee00;
485                 mp_uint_t r_arm, vm;
486                 const char *reg_str = get_arg_str(pn_args[0]);
487                 if (reg_str[0] == 'r') {
488                     r_arm = get_arg_reg(emit, op_str, pn_args[0], 15);
489                     vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
490                     op_code_hi |= 0x10;
491                 } else {
492                     vm = get_arg_vfpreg(emit, op_str, pn_args[0]);
493                     r_arm = get_arg_reg(emit, op_str, pn_args[1], 15);
494                 }
495                 asm_thumb_op32(&emit->as,
496                     op_code_hi | ((vm & 0x1e) >> 1),
497                     0x0a10 | (r_arm << 12) | ((vm & 1) << 7));
498             } else if (op == MP_QSTR_vldr) {
499                 op_code_hi = 0xed90;
500             op_vldr_vstr:;
501                 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
502                 mp_parse_node_t pn_base, pn_offset;
503                 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
504                     mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
505                     mp_uint_t i8;
506                     i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2;
507                     asm_thumb_op32(&emit->as,
508                         op_code_hi | rlo_base | ((vd & 1) << 6),
509                         0x0a00 | ((vd & 0x1e) << 11) | i8);
510                 }
511             } else if (op == MP_QSTR_vstr) {
512                 op_code_hi = 0xed80;
513                 goto op_vldr_vstr;
514             } else {
515                 goto unknown_op;
516             }
517         } else if (n_args == 3) {
518             // search table for arith ops
519             for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) {
520                 if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') {
521                     mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0);
522                     mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4);
523                     mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
524                     mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]);
525                     mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]);
526                     asm_thumb_op32(&emit->as,
527                         op_code_hi | ((vd & 1) << 6) | (vn >> 1),
528                         op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7));
529                     return;
530                 }
531             }
532             goto unknown_op;
533         } else {
534             goto unknown_op;
535         }
536         return;
537     }
538     #endif
539 
540     if (n_args == 0) {
541         if (op == MP_QSTR_nop) {
542             asm_thumb_op16(&emit->as, ASM_THUMB_OP_NOP);
543         } else if (op == MP_QSTR_wfi) {
544             asm_thumb_op16(&emit->as, ASM_THUMB_OP_WFI);
545         } else {
546             goto unknown_op;
547         }
548 
549     } else if (n_args == 1) {
550         if (op == MP_QSTR_b) {
551             int label_num = get_arg_label(emit, op_str, pn_args[0]);
552             if (!asm_thumb_b_n_label(&emit->as, label_num)) {
553                 goto branch_not_in_range;
554             }
555         } else if (op == MP_QSTR_bl) {
556             int label_num = get_arg_label(emit, op_str, pn_args[0]);
557             if (!asm_thumb_bl_label(&emit->as, label_num)) {
558                 goto branch_not_in_range;
559             }
560         } else if (op == MP_QSTR_bx) {
561             mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15);
562             asm_thumb_op16(&emit->as, 0x4700 | (r << 3));
563         } else if (op_str[0] == 'b' && (op_len == 3
564                                         || (op_len == 5 && op_str[3] == '_'
565                                             && (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) {
566             mp_uint_t cc = -1;
567             for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
568                 if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
569                     cc = cc_name_table[i].cc;
570                 }
571             }
572             if (cc == (mp_uint_t)-1) {
573                 goto unknown_op;
574             }
575             int label_num = get_arg_label(emit, op_str, pn_args[0]);
576             bool wide = op_len == 5 && op_str[4] == 'w';
577             if (wide && !ARMV7M) {
578                 goto unknown_op;
579             }
580             if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, wide)) {
581                 goto branch_not_in_range;
582             }
583         } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') {
584             const char *arg_str = get_arg_str(pn_args[0]);
585             mp_uint_t cc = -1;
586             for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
587                 if (arg_str[0] == cc_name_table[i].name[0]
588                     && arg_str[1] == cc_name_table[i].name[1]
589                     && arg_str[2] == '\0') {
590                     cc = cc_name_table[i].cc;
591                     break;
592                 }
593             }
594             if (cc == (mp_uint_t)-1) {
595                 goto unknown_op;
596             }
597             const char *os = op_str + 2;
598             while (*os != '\0') {
599                 os++;
600             }
601             if (os > op_str + 5) {
602                 goto unknown_op;
603             }
604             mp_uint_t it_mask = 8;
605             while (--os >= op_str + 2) {
606                 it_mask >>= 1;
607                 if (*os == 't') {
608                     it_mask |= (cc & 1) << 3;
609                 } else if (*os == 'e') {
610                     it_mask |= ((~cc) & 1) << 3;
611                 } else {
612                     goto unknown_op;
613                 }
614             }
615             asm_thumb_it_cc(&emit->as, cc, it_mask);
616         } else if (op == MP_QSTR_cpsid) {
617             // TODO check pn_args[0] == i
618             asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSID_I);
619         } else if (op == MP_QSTR_cpsie) {
620             // TODO check pn_args[0] == i
621             asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSIE_I);
622         } else if (op == MP_QSTR_push) {
623             mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
624             if ((reglist & 0xff00) == 0) {
625                 asm_thumb_op16(&emit->as, 0xb400 | reglist);
626             } else {
627                 if (!ARMV7M) {
628                     goto unknown_op;
629                 }
630                 asm_thumb_op32(&emit->as, 0xe92d, reglist);
631             }
632         } else if (op == MP_QSTR_pop) {
633             mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
634             if ((reglist & 0xff00) == 0) {
635                 asm_thumb_op16(&emit->as, 0xbc00 | reglist);
636             } else {
637                 if (!ARMV7M) {
638                     goto unknown_op;
639                 }
640                 asm_thumb_op32(&emit->as, 0xe8bd, reglist);
641             }
642         } else {
643             goto unknown_op;
644         }
645 
646     } else if (n_args == 2) {
647         if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
648             // second arg is a register (or should be)
649             mp_uint_t op_code, op_code_hi;
650             if (op == MP_QSTR_mov) {
651                 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
652                 mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
653                 asm_thumb_mov_reg_reg(&emit->as, reg_dest, reg_src);
654             } else if (ARMV7M && op == MP_QSTR_clz) {
655                 op_code_hi = 0xfab0;
656                 op_code = 0xf080;
657                 mp_uint_t rd, rm;
658             op_clz_rbit:
659                 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
660                 rm = get_arg_reg(emit, op_str, pn_args[1], 15);
661                 asm_thumb_op32(&emit->as, op_code_hi | rm, op_code | (rd << 8) | rm);
662             } else if (ARMV7M && op == MP_QSTR_rbit) {
663                 op_code_hi = 0xfa90;
664                 op_code = 0xf0a0;
665                 goto op_clz_rbit;
666             } else if (ARMV7M && op == MP_QSTR_mrs) {
667                 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12);
668                 mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]);
669                 asm_thumb_op32(&emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src);
670             } else {
671                 if (op == MP_QSTR_and_) {
672                     op_code = ASM_THUMB_FORMAT_4_AND;
673                     mp_uint_t reg_dest, reg_src;
674                 op_format_4:
675                     reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
676                     reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
677                     asm_thumb_format_4(&emit->as, op_code, reg_dest, reg_src);
678                     return;
679                 }
680                 // search table for ALU ops
681                 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) {
682                     if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') {
683                         op_code = 0x4000 | (format_4_op_table[i].op << 4);
684                         goto op_format_4;
685                     }
686                 }
687                 goto unknown_op;
688             }
689         } else {
690             // second arg is not a register
691             mp_uint_t op_code;
692             if (op == MP_QSTR_mov) {
693                 op_code = ASM_THUMB_FORMAT_3_MOV;
694                 mp_uint_t rlo_dest, i8_src;
695             op_format_3:
696                 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
697                 i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
698                 asm_thumb_format_3(&emit->as, op_code, rlo_dest, i8_src);
699             } else if (op == MP_QSTR_cmp) {
700                 op_code = ASM_THUMB_FORMAT_3_CMP;
701                 goto op_format_3;
702             } else if (op == MP_QSTR_add) {
703                 op_code = ASM_THUMB_FORMAT_3_ADD;
704                 goto op_format_3;
705             } else if (op == MP_QSTR_sub) {
706                 op_code = ASM_THUMB_FORMAT_3_SUB;
707                 goto op_format_3;
708             #if ARMV7M
709             } else if (op == MP_QSTR_movw) {
710                 op_code = ASM_THUMB_OP_MOVW;
711                 mp_uint_t reg_dest;
712             op_movw_movt:
713                 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
714                 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
715                 asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src);
716             } else if (op == MP_QSTR_movt) {
717                 op_code = ASM_THUMB_OP_MOVT;
718                 goto op_movw_movt;
719             } else if (op == MP_QSTR_movwt) {
720                 // this is a convenience instruction
721                 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
722                 uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
723                 asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff);
724                 asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff);
725             } else if (op == MP_QSTR_ldrex) {
726                 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
727                 mp_parse_node_t pn_base, pn_offset;
728                 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
729                     mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
730                     mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
731                     asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8);
732                 }
733             #endif
734             } else {
735                 // search table for ldr/str instructions
736                 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) {
737                     if (op == format_9_10_op_table[i].name) {
738                         op_code = format_9_10_op_table[i].op;
739                         mp_parse_node_t pn_base, pn_offset;
740                         mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
741                         if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
742                             mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
743                             mp_uint_t i5;
744                             if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
745                                 i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
746                             } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
747                                 i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
748                             } else {
749                                 i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
750                             }
751                             asm_thumb_format_9_10(&emit->as, op_code, rlo_dest, rlo_base, i5);
752                             return;
753                         }
754                         break;
755                     }
756                 }
757                 goto unknown_op;
758             }
759         }
760 
761     } else if (n_args == 3) {
762         mp_uint_t op_code;
763         if (op == MP_QSTR_lsl) {
764             op_code = ASM_THUMB_FORMAT_1_LSL;
765             mp_uint_t rlo_dest, rlo_src, i5;
766         op_format_1:
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             i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f);
770             asm_thumb_format_1(&emit->as, op_code, rlo_dest, rlo_src, i5);
771         } else if (op == MP_QSTR_lsr) {
772             op_code = ASM_THUMB_FORMAT_1_LSR;
773             goto op_format_1;
774         } else if (op == MP_QSTR_asr) {
775             op_code = ASM_THUMB_FORMAT_1_ASR;
776             goto op_format_1;
777         } else if (op == MP_QSTR_add) {
778             op_code = ASM_THUMB_FORMAT_2_ADD;
779             mp_uint_t rlo_dest, rlo_src;
780         op_format_2:
781             rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
782             rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
783             int src_b;
784             if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
785                 op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
786                 src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
787             } else {
788                 op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
789                 src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
790             }
791             asm_thumb_format_2(&emit->as, op_code, rlo_dest, rlo_src, src_b);
792         } else if (ARMV7M && op == MP_QSTR_sdiv) {
793             op_code = 0xfb90; // sdiv high part
794             mp_uint_t rd, rn, rm;
795         op_sdiv_udiv:
796             rd = get_arg_reg(emit, op_str, pn_args[0], 15);
797             rn = get_arg_reg(emit, op_str, pn_args[1], 15);
798             rm = get_arg_reg(emit, op_str, pn_args[2], 15);
799             asm_thumb_op32(&emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm);
800         } else if (ARMV7M && op == MP_QSTR_udiv) {
801             op_code = 0xfbb0; // udiv high part
802             goto op_sdiv_udiv;
803         } else if (op == MP_QSTR_sub) {
804             op_code = ASM_THUMB_FORMAT_2_SUB;
805             goto op_format_2;
806         } else if (ARMV7M && op == MP_QSTR_strex) {
807             mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
808             mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15);
809             mp_parse_node_t pn_base, pn_offset;
810             if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) {
811                 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
812                 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
813                 asm_thumb_op32(&emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8);
814             }
815         } else {
816             goto unknown_op;
817         }
818 
819     } else {
820         goto unknown_op;
821     }
822 
823     return;
824 
825 unknown_op:
826     emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("unsupported Thumb instruction '%s' with %d arguments"), op_str, n_args));
827     return;
828 
829 branch_not_in_range:
830     emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("branch not in range"));
831     return;
832 }
833 
834 const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
835     #if MICROPY_DYNAMIC_COMPILER
836     emit_inline_thumb_new,
837     emit_inline_thumb_free,
838     #endif
839 
840     emit_inline_thumb_start_pass,
841     emit_inline_thumb_end_pass,
842     emit_inline_thumb_count_params,
843     emit_inline_thumb_label,
844     emit_inline_thumb_op,
845 };
846 
847 #endif // MICROPY_EMIT_INLINE_THUMB
848