blob: 391d057891b10ce7739943895f6b75cb8ba2f50f [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 George8dfbd2d2015-02-13 01:00:51 +000087STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
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 George99886182015-04-06 22:38:53 +010093 mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0, 0, 0);
Damien826005c2013-10-05 23:17:28 +010094 }
95}
96
Damien George7ff996c2014-09-08 23:05:16 +010097STATIC 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 +010098 if (n_params > 4) {
Damien George8dfbd2d2015-02-13 01:00:51 +000099 emit_inline_thumb_error_msg(emit, "can only have up to 4 parameters to Thumb assembly");
Damiena2f2f7d2013-10-06 00:14:13 +0100100 return 0;
101 }
Damien George7ff996c2014-09-08 23:05:16 +0100102 for (mp_uint_t i = 0; i < n_params; i++) {
Damiend99b0522013-12-21 18:17:45 +0000103 if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000104 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +0100105 return 0;
106 }
Damiend99b0522013-12-21 18:17:45 +0000107 const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
Damiena2f2f7d2013-10-06 00:14:13 +0100108 if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000109 emit_inline_thumb_error_msg(emit, "parameters must be registers in sequence r0 to r3");
Damiena2f2f7d2013-10-06 00:14:13 +0100110 return 0;
111 }
112 }
113 return n_params;
114}
115
Damien George9c5cabb2015-03-03 17:08:02 +0000116STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
Damien826005c2013-10-05 23:17:28 +0100117 assert(label_num < emit->max_num_labels);
Damien George9c5cabb2015-03-03 17:08:02 +0000118 if (emit->pass == MP_PASS_CODE_SIZE) {
119 // check for duplicate label on first pass
120 for (int i = 0; i < emit->max_num_labels; i++) {
121 if (emit->label_lookup[i] == label_id) {
122 return false;
123 }
124 }
125 }
Damien826005c2013-10-05 23:17:28 +0100126 emit->label_lookup[label_num] = label_id;
127 asm_thumb_label_assign(emit->as, label_num);
Damien George9c5cabb2015-03-03 17:08:02 +0000128 return true;
Damien826005c2013-10-05 23:17:28 +0100129}
130
Damien George7ff996c2014-09-08 23:05:16 +0100131STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) {
Damien Georgee5f8a772014-04-21 13:33:15 +0100132 asm_thumb_align(emit->as, align);
133}
134
Damien George7ff996c2014-09-08 23:05:16 +0100135STATIC 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 +0100136 asm_thumb_data(emit->as, bytesize, val);
137}
138
Damien Georgea26dc502014-04-12 17:54:52 +0100139typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
140STATIC const reg_name_t reg_name_table[] = {
141 {0, "r0\0"},
142 {1, "r1\0"},
143 {2, "r2\0"},
144 {3, "r3\0"},
145 {4, "r4\0"},
146 {5, "r5\0"},
147 {6, "r6\0"},
148 {7, "r7\0"},
149 {8, "r8\0"},
150 {9, "r9\0"},
151 {10, "r10"},
152 {11, "r11"},
153 {12, "r12"},
154 {13, "r13"},
155 {14, "r14"},
156 {15, "r15"},
157 {10, "sl\0"},
158 {11, "fp\0"},
159 {13, "sp\0"},
160 {14, "lr\0"},
161 {15, "pc\0"},
162};
163
Damien George42495392015-02-16 17:46:49 +0000164// return empty string in case of error, so we can attempt to parse the string
165// without a special check if it was in fact a string
166STATIC const char *get_arg_str(mp_parse_node_t pn) {
Damien George87210872014-04-13 00:30:32 +0100167 if (MP_PARSE_NODE_IS_ID(pn)) {
Damien George42495392015-02-16 17:46:49 +0000168 qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
169 return qstr_str(qst);
170 } else {
171 return "";
172 }
173}
174
175STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
176 const char *reg_str = get_arg_str(pn);
177 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
178 const reg_name_t *r = &reg_name_table[i];
179 if (reg_str[0] == r->name[0]
180 && reg_str[1] == r->name[1]
181 && reg_str[2] == r->name[2]
182 && (reg_str[2] == '\0' || reg_str[3] == '\0')) {
183 if (r->reg > max_reg) {
184 emit_inline_thumb_error_exc(emit,
185 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
186 "'%s' expects at most r%d", op, max_reg));
187 return 0;
188 } else {
189 return r->reg;
Damien Georgea26dc502014-04-12 17:54:52 +0100190 }
191 }
Damien826005c2013-10-05 23:17:28 +0100192 }
Damien George42495392015-02-16 17:46:49 +0000193 emit_inline_thumb_error_exc(emit,
194 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
195 "'%s' expects a register", op));
Damien Georgea26dc502014-04-12 17:54:52 +0100196 return 0;
Damien826005c2013-10-05 23:17:28 +0100197}
198
=50089722015-04-14 13:14:57 +0100199#if MICROPY_EMIT_INLINE_THUMB_FLOAT
200STATIC mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
201 const char *reg_str = get_arg_str(pn);
202 if (reg_str[0] == 's' && reg_str[1] != '\0') {
203 mp_uint_t regno = 0;
204 for (++reg_str; *reg_str; ++reg_str) {
205 mp_uint_t v = *reg_str;
206 if (!('0' <= v && v <= '9')) {
207 goto malformed;
208 }
209 regno = 10 * regno + v - '0';
210 }
211 if (regno > 31) {
212 emit_inline_thumb_error_exc(emit,
213 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
214 "'%s' expects at most r%d", op, 31));
215 return 0;
216 } else {
217 return regno;
218 }
219 }
220malformed:
221 emit_inline_thumb_error_exc(emit,
222 mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
223 "'%s' expects an FPU register", op));
224 return 0;
225}
226#endif
227
Damien George0d967b82015-02-13 02:30:35 +0000228STATIC mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
229 // a register list looks like {r0, r1, r2} and is parsed as a Python set
230
231 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) {
232 goto bad_arg;
233 }
234
235 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
236 assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be
237 pn = pns->nodes[0];
238
239 mp_uint_t reglist = 0;
240
241 if (MP_PARSE_NODE_IS_ID(pn)) {
242 // set with one element
243 reglist |= 1 << get_arg_reg(emit, op, pn, 15);
244 } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
245 pns = (mp_parse_node_struct_t*)pn;
246 if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
247 assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
248 mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
249 if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
250 // set with multiple elements
251
252 // get first element of set (we rely on get_arg_reg to catch syntax errors)
253 reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15);
254
255 // get tail elements (2nd, 3rd, ...)
256 mp_parse_node_t *nodes;
257 int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
258
259 // process rest of elements
260 for (int i = 0; i < n; i++) {
261 reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15);
262 }
263 } else {
264 goto bad_arg;
265 }
266 } else {
267 goto bad_arg;
268 }
269 } else {
270 goto bad_arg;
271 }
272
273 return reglist;
274
275bad_arg:
276 emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects {r0, r1, ...}", op));
277 return 0;
278}
279
Damien George87210872014-04-13 00:30:32 +0100280STATIC int get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int fit_mask) {
281 if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000282 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 +0100283 return 0;
284 }
Damien George87210872014-04-13 00:30:32 +0100285 int i = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
Damien826005c2013-10-05 23:17:28 +0100286 if ((i & (~fit_mask)) != 0) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000287 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 +0100288 return 0;
289 }
290 return i;
291}
292
Damien George87210872014-04-13 00:30:32 +0100293STATIC 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) {
294 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
295 goto bad_arg;
296 }
297 mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
298 if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
299 goto bad_arg;
300 }
301 pns = (mp_parse_node_struct_t*)pns->nodes[0];
302 if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
303 goto bad_arg;
304 }
305
306 *pn_base = pns->nodes[0];
307 *pn_offset = pns->nodes[1];
308 return true;
309
310bad_arg:
Damien George8dfbd2d2015-02-13 01:00:51 +0000311 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 +0100312 return false;
313}
314
315STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
316 if (!MP_PARSE_NODE_IS_ID(pn)) {
Damien George8dfbd2d2015-02-13 01:00:51 +0000317 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 +0100318 return 0;
319 }
Damien George87210872014-04-13 00:30:32 +0100320 qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
Damien826005c2013-10-05 23:17:28 +0100321 for (int i = 0; i < emit->max_num_labels; i++) {
322 if (emit->label_lookup[i] == label_qstr) {
323 return i;
324 }
325 }
Damiendc833822013-10-06 01:01:01 +0100326 // only need to have the labels on the last pass
Damien George36db6bc2014-05-07 17:24:22 +0100327 if (emit->pass == MP_PASS_EMIT) {
Damien George044c4732015-04-11 13:03:37 +0100328 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 +0100329 }
Damien826005c2013-10-05 23:17:28 +0100330 return 0;
331}
332
Damien George47e1b852014-04-08 18:28:33 +0100333typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t;
334STATIC const cc_name_t cc_name_table[] = {
Damien George0b610de2014-09-29 16:25:04 +0100335 { ASM_THUMB_CC_EQ, "eq" },
336 { ASM_THUMB_CC_NE, "ne" },
337 { ASM_THUMB_CC_CS, "cs" },
338 { ASM_THUMB_CC_CC, "cc" },
339 { ASM_THUMB_CC_MI, "mi" },
340 { ASM_THUMB_CC_PL, "pl" },
341 { ASM_THUMB_CC_VS, "vs" },
342 { ASM_THUMB_CC_VC, "vc" },
343 { ASM_THUMB_CC_HI, "hi" },
344 { ASM_THUMB_CC_LS, "ls" },
345 { ASM_THUMB_CC_GE, "ge" },
346 { ASM_THUMB_CC_LT, "lt" },
347 { ASM_THUMB_CC_GT, "gt" },
348 { ASM_THUMB_CC_LE, "le" },
Damien George47e1b852014-04-08 18:28:33 +0100349};
350
Damien George8f7976b2015-02-24 16:10:58 +0000351typedef struct _format_4_op_t { byte op; char name[3]; } format_4_op_t;
352#define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
353STATIC const format_4_op_t format_4_op_table[] = {
354 { X(ASM_THUMB_FORMAT_4_EOR), "eor" },
355 { X(ASM_THUMB_FORMAT_4_LSL), "lsl" },
356 { X(ASM_THUMB_FORMAT_4_LSR), "lsr" },
357 { X(ASM_THUMB_FORMAT_4_ASR), "asr" },
358 { X(ASM_THUMB_FORMAT_4_ADC), "adc" },
359 { X(ASM_THUMB_FORMAT_4_SBC), "sbc" },
360 { X(ASM_THUMB_FORMAT_4_ROR), "ror" },
361 { X(ASM_THUMB_FORMAT_4_TST), "tst" },
362 { X(ASM_THUMB_FORMAT_4_NEG), "neg" },
363 { X(ASM_THUMB_FORMAT_4_CMP), "cmp" },
364 { X(ASM_THUMB_FORMAT_4_CMN), "cmn" },
365 { X(ASM_THUMB_FORMAT_4_ORR), "orr" },
366 { X(ASM_THUMB_FORMAT_4_MUL), "mul" },
367 { X(ASM_THUMB_FORMAT_4_BIC), "bic" },
368 { X(ASM_THUMB_FORMAT_4_MVN), "mvn" },
369};
370#undef X
371
372typedef struct _format_9_10_op_t { uint16_t op; char name[5]; } format_9_10_op_t;
373#define X(x) (x)
374STATIC const format_9_10_op_t format_9_10_op_table[] = {
375 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), "ldr" },
376 { X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), "ldrb" },
377 { X(ASM_THUMB_FORMAT_10_LDRH), "ldrh" },
378 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), "str" },
379 { X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), "strb" },
380 { X(ASM_THUMB_FORMAT_10_STRH), "strh" },
381};
382#undef X
383
=50089722015-04-14 13:14:57 +0100384#if MICROPY_EMIT_INLINE_THUMB_FLOAT
385// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
386typedef struct _format_vfp_op_t { byte op; char name[3]; } format_vfp_op_t;
387STATIC const format_vfp_op_t format_vfp_op_table[] = {
388 { 0x30, "add" },
389 { 0x34, "sub" },
390 { 0x20, "mul" },
391 { 0x80, "div" },
392};
393#endif
394
Damien George7ff996c2014-09-08 23:05:16 +0100395STATIC 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 +0100396 // TODO perhaps make two tables:
Damien03d41242013-10-06 00:36:05 +0100397 // one_args =
398 // "b", LAB, asm_thumb_b_n,
399 // "bgt", LAB, asm_thumb_bgt_n,
Damien826005c2013-10-05 23:17:28 +0100400 // two_args =
401 // "movs", RLO, I8, asm_thumb_movs_reg_i8
402 // "movw", REG, REG, asm_thumb_movw_reg_i16
403 // three_args =
404 // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
405
Damien George6eb75302015-04-11 21:53:39 +0100406 mp_uint_t op_len;
407 const char *op_str = (const char*)qstr_data(op, &op_len);
Damien826005c2013-10-05 23:17:28 +0100408
=50089722015-04-14 13:14:57 +0100409 #if MICROPY_EMIT_INLINE_THUMB_FLOAT
410 if (op_str[0] == 'v') {
411 // floating point operations
412 if (n_args == 2) {
413 mp_uint_t op_code = 0x0ac0, op_code_hi;
414 if (strcmp(op_str, "vcmp") == 0) {
415 op_code_hi = 0xeeb4;
416 op_vfp_twoargs:;
417 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
418 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
419 asm_thumb_op32(emit->as,
420 op_code_hi | ((vd & 1) << 6),
421 op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1);
422 } else if (strcmp(op_str, "vsqrt") == 0) {
423 op_code_hi = 0xeeb1;
424 goto op_vfp_twoargs;
425 } else if (strcmp(op_str, "vneg") == 0) {
426 op_code_hi = 0xeeb1;
427 op_code = 0x0a40;
428 goto op_vfp_twoargs;
429 } else if (strcmp(op_str, "vcvt_f32_s32") == 0) {
430 op_code_hi = 0xeeb8; // int to float
431 goto op_vfp_twoargs;
432 } else if (strcmp(op_str, "vcvt_s32_f32") == 0) {
433 op_code_hi = 0xeebd; // float to int
434 goto op_vfp_twoargs;
435 } else if (strcmp(op_str, "vmrs") == 0) {
436 mp_uint_t reg_dest;
437 const char *reg_str0 = get_arg_str(pn_args[0]);
438 if (strcmp(reg_str0, "APSR_nzcv") == 0) {
439 reg_dest = 15;
440 } else {
441 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
442 }
443 const char *reg_str1 = get_arg_str(pn_args[1]);
444 if (strcmp(reg_str1, "FPSCR") == 0) {
445 // FP status to ARM reg
446 asm_thumb_op32(emit->as, 0xeef1, 0x0a10 | (reg_dest << 12));
447 } else {
448 goto unknown_op;
449 }
450 } else if (strcmp(op_str, "vmov") == 0) {
451 op_code_hi = 0xee00;
452 mp_uint_t r_arm, vm;
453 const char *reg_str = get_arg_str(pn_args[0]);
454 if (reg_str[0] == 'r') {
455 r_arm = get_arg_reg(emit, op_str, pn_args[0], 15);
456 vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
457 op_code_hi |= 0x10;
458 } else {
459 vm = get_arg_vfpreg(emit, op_str, pn_args[0]);
460 r_arm = get_arg_reg(emit, op_str, pn_args[1], 15);
461 }
462 asm_thumb_op32(emit->as,
463 op_code_hi | ((vm & 0x1e) >> 1),
464 0x0a10 | (r_arm << 12) | ((vm & 1) << 7));
465 } else if (strcmp(op_str, "vldr") == 0) {
466 op_code_hi = 0xed90;
467 op_vldr_vstr:;
468 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
469 mp_parse_node_t pn_base, pn_offset;
470 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
471 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
472 mp_uint_t i8;
473 i8 = get_arg_i(emit, op_str, pn_offset, 0xff);
474 asm_thumb_op32(emit->as,
475 op_code_hi | rlo_base | ((vd & 1) << 6),
476 0x0a00 | ((vd & 0x1e) << 11) | i8);
477 }
478 } else if (strcmp(op_str, "vstr") == 0) {
479 op_code_hi = 0xed80;
480 goto op_vldr_vstr;
481 } else {
482 goto unknown_op;
483 }
484 } else if (n_args == 3) {
485 // search table for arith ops
486 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) {
487 if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') {
488 mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0);
489 mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4);
490 mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
491 mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]);
492 mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]);
493 asm_thumb_op32(emit->as,
494 op_code_hi | ((vd & 1) << 6) | (vn >> 1),
495 op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7));
496 return;
497 }
498 }
499 goto unknown_op;
500 } else {
501 goto unknown_op;
502 }
503 } else
504 #endif
Damien George47e1b852014-04-08 18:28:33 +0100505 if (n_args == 0) {
Damien George90edf9e2014-04-18 16:56:54 +0100506 if (strcmp(op_str, "nop") == 0) {
507 asm_thumb_op16(emit->as, ASM_THUMB_OP_NOP);
508 } else if (strcmp(op_str, "wfi") == 0) {
509 asm_thumb_op16(emit->as, ASM_THUMB_OP_WFI);
Damien George47e1b852014-04-08 18:28:33 +0100510 } else {
511 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100512 }
Damien826005c2013-10-05 23:17:28 +0100513
Damien George47e1b852014-04-08 18:28:33 +0100514 } else if (n_args == 1) {
515 if (strcmp(op_str, "b") == 0) {
Damien George87210872014-04-13 00:30:32 +0100516 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George53457432015-02-25 15:45:55 +0000517 if (!asm_thumb_b_n_label(emit->as, label_num)) {
518 goto branch_not_in_range;
519 }
Damien Georgeeff10f62015-02-16 18:17:07 +0000520 } else if (strcmp(op_str, "bl") == 0) {
521 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George53457432015-02-25 15:45:55 +0000522 if (!asm_thumb_bl_label(emit->as, label_num)) {
523 goto branch_not_in_range;
524 }
Damien Georgeeff10f62015-02-16 18:17:07 +0000525 } else if (strcmp(op_str, "bx") == 0) {
526 mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15);
527 asm_thumb_op16(emit->as, 0x4700 | (r << 3));
Damien George9f142f02015-03-02 14:29:52 +0000528 } else if (op_str[0] == 'b' && (op_len == 3
529 || (op_len == 5 && op_str[3] == '_'
530 && (op_str[4] == 'n' || op_str[4] == 'w')))) {
Damien George7ff996c2014-09-08 23:05:16 +0100531 mp_uint_t cc = -1;
532 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
Damien George47e1b852014-04-08 18:28:33 +0100533 if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
534 cc = cc_name_table[i].cc;
535 }
536 }
537 if (cc == -1) {
538 goto unknown_op;
539 }
Damien George87210872014-04-13 00:30:32 +0100540 int label_num = get_arg_label(emit, op_str, pn_args[0]);
Damien George9f142f02015-03-02 14:29:52 +0000541 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 +0000542 goto branch_not_in_range;
543 }
Damien George42495392015-02-16 17:46:49 +0000544 } else if (op_str[0] == 'i' && op_str[1] == 't') {
545 const char *arg_str = get_arg_str(pn_args[0]);
546 mp_uint_t cc = -1;
547 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
548 if (arg_str[0] == cc_name_table[i].name[0]
549 && arg_str[1] == cc_name_table[i].name[1]
550 && arg_str[2] == '\0') {
551 cc = cc_name_table[i].cc;
552 break;
553 }
554 }
555 if (cc == -1) {
556 goto unknown_op;
557 }
558 const char *os = op_str + 2;
559 while (*os != '\0') {
560 os++;
561 }
562 if (os > op_str + 5) {
563 goto unknown_op;
564 }
565 mp_uint_t it_mask = 8;
566 while (--os >= op_str + 2) {
567 it_mask >>= 1;
568 if (*os == 't') {
569 it_mask |= (cc & 1) << 3;
570 } else if (*os == 'e') {
571 it_mask |= ((~cc) & 1) << 3;
572 } else {
573 goto unknown_op;
574 }
575 }
576 asm_thumb_it_cc(emit->as, cc, it_mask);
Damien George0d967b82015-02-13 02:30:35 +0000577 } else if (strcmp(op_str, "cpsid") == 0) {
Damien George90edf9e2014-04-18 16:56:54 +0100578 // TODO check pn_args[0] == i
579 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSID_I);
Damien George0d967b82015-02-13 02:30:35 +0000580 } else if (strcmp(op_str, "cpsie") == 0) {
Damien George90edf9e2014-04-18 16:56:54 +0100581 // TODO check pn_args[0] == i
582 asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSIE_I);
Damien George0d967b82015-02-13 02:30:35 +0000583 } else if (strcmp(op_str, "push") == 0) {
584 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
585 if ((reglist & 0xff00) == 0) {
586 asm_thumb_op16(emit->as, 0xb400 | reglist);
587 } else {
588 asm_thumb_op32(emit->as, 0xe92d, reglist);
589 }
590 } else if (strcmp(op_str, "pop") == 0) {
591 mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
592 if ((reglist & 0xff00) == 0) {
593 asm_thumb_op16(emit->as, 0xbc00 | reglist);
594 } else {
595 asm_thumb_op32(emit->as, 0xe8bd, reglist);
596 }
Damien George47e1b852014-04-08 18:28:33 +0100597 } else {
598 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100599 }
Damien826005c2013-10-05 23:17:28 +0100600
Damien George47e1b852014-04-08 18:28:33 +0100601 } else if (n_args == 2) {
Damien George87210872014-04-13 00:30:32 +0100602 if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
603 // second arg is a register (or should be)
Damien George192d5362015-02-13 11:06:23 +0000604 mp_uint_t op_code, op_code_hi;
Damien George87210872014-04-13 00:30:32 +0100605 if (strcmp(op_str, "mov") == 0) {
Damien George7ff996c2014-09-08 23:05:16 +0100606 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
607 mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
Damien George87210872014-04-13 00:30:32 +0100608 asm_thumb_mov_reg_reg(emit->as, reg_dest, reg_src);
Damien George192d5362015-02-13 11:06:23 +0000609 } else if (strcmp(op_str, "clz") == 0) {
610 op_code_hi = 0xfab0;
611 op_code = 0xf080;
612 mp_uint_t rd, rm;
613 op_clz_rbit:
614 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
615 rm = get_arg_reg(emit, op_str, pn_args[1], 15);
616 asm_thumb_op32(emit->as, op_code_hi | rm, op_code | (rd << 8) | rm);
617 } else if (strcmp(op_str, "rbit") == 0) {
618 op_code_hi = 0xfa90;
619 op_code = 0xf0a0;
620 goto op_clz_rbit;
Damien George87210872014-04-13 00:30:32 +0100621 } else {
Damien George8f7976b2015-02-24 16:10:58 +0000622 if (strcmp(op_str, "and_") == 0) {
623 op_code = ASM_THUMB_FORMAT_4_AND;
624 mp_uint_t reg_dest, reg_src;
625 op_format_4:
626 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
627 reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
628 asm_thumb_format_4(emit->as, op_code, reg_dest, reg_src);
Damien George993f0672015-02-24 22:43:01 +0000629 return;
Damien George8f7976b2015-02-24 16:10:58 +0000630 }
Damien Georgee5315f72015-02-24 16:35:37 +0000631 // search table for ALU ops
Damien George8f7976b2015-02-24 16:10:58 +0000632 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) {
633 if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') {
634 op_code = 0x4000 | (format_4_op_table[i].op << 4);
635 goto op_format_4;
636 }
637 }
Damien George87210872014-04-13 00:30:32 +0100638 goto unknown_op;
639 }
Damien George47e1b852014-04-08 18:28:33 +0100640 } else {
Damien George87210872014-04-13 00:30:32 +0100641 // second arg is not a register
Damien George7ff996c2014-09-08 23:05:16 +0100642 mp_uint_t op_code;
Damien George87210872014-04-13 00:30:32 +0100643 if (strcmp(op_str, "mov") == 0) {
644 op_code = ASM_THUMB_FORMAT_3_MOV;
Damien George7ff996c2014-09-08 23:05:16 +0100645 mp_uint_t rlo_dest, i8_src;
Damien George87210872014-04-13 00:30:32 +0100646 op_format_3:
647 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
648 i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
649 asm_thumb_format_3(emit->as, op_code, rlo_dest, i8_src);
650 } else if (strcmp(op_str, "cmp") == 0) {
651 op_code = ASM_THUMB_FORMAT_3_CMP;
652 goto op_format_3;
653 } else if (strcmp(op_str, "add") == 0) {
654 op_code = ASM_THUMB_FORMAT_3_ADD;
655 goto op_format_3;
656 } else if (strcmp(op_str, "sub") == 0) {
657 op_code = ASM_THUMB_FORMAT_3_SUB;
658 goto op_format_3;
659 } else if (strcmp(op_str, "movw") == 0) {
Damien Georgee5315f72015-02-24 16:35:37 +0000660 op_code = ASM_THUMB_OP_MOVW;
661 mp_uint_t reg_dest;
662 op_movw_movt:
663 reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100664 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
Damien Georgee5315f72015-02-24 16:35:37 +0000665 asm_thumb_mov_reg_i16(emit->as, op_code, reg_dest, i_src);
Damien George87210872014-04-13 00:30:32 +0100666 } else if (strcmp(op_str, "movt") == 0) {
Damien Georgee5315f72015-02-24 16:35:37 +0000667 op_code = ASM_THUMB_OP_MOVT;
668 goto op_movw_movt;
Damien George87210872014-04-13 00:30:32 +0100669 } else if (strcmp(op_str, "movwt") == 0) {
670 // this is a convenience instruction
671 // we clear the MSB since it might be set from extracting the small int value
Damien George7ff996c2014-09-08 23:05:16 +0100672 mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
Damien George87210872014-04-13 00:30:32 +0100673 int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
Damien Georgee41b21c2015-02-24 16:32:52 +0000674 asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff);
675 asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0x7fff);
Damien George1bf5a022015-02-12 22:52:42 +0000676 } else if (strcmp(op_str, "ldrex") == 0) {
677 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
678 mp_parse_node_t pn_base, pn_offset;
679 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
680 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
681 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
682 asm_thumb_op32(emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8);
683 }
Damien George87210872014-04-13 00:30:32 +0100684 } else {
Damien George8f7976b2015-02-24 16:10:58 +0000685 // search table for ldr/str instructions
686 for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) {
687 if (strcmp(op_str, format_9_10_op_table[i].name) == 0) {
688 op_code = format_9_10_op_table[i].op;
689 mp_parse_node_t pn_base, pn_offset;
690 mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
691 if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
692 mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
693 mp_uint_t i5;
694 if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
695 i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
696 } else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
697 i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
698 } else {
699 i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
700 }
701 asm_thumb_format_9_10(emit->as, op_code, rlo_dest, rlo_base, i5);
702 return;
703 }
704 break;
705 }
706 }
Damien George87210872014-04-13 00:30:32 +0100707 goto unknown_op;
708 }
Damien George47e1b852014-04-08 18:28:33 +0100709 }
710
711 } else if (n_args == 3) {
Damien George7ff996c2014-09-08 23:05:16 +0100712 mp_uint_t op_code;
Damien George47e1b852014-04-08 18:28:33 +0100713 if (strcmp(op_str, "add") == 0) {
Damien George87210872014-04-13 00:30:32 +0100714 op_code = ASM_THUMB_FORMAT_2_ADD;
Damien George7ff996c2014-09-08 23:05:16 +0100715 mp_uint_t rlo_dest, rlo_src;
Damien George87210872014-04-13 00:30:32 +0100716 op_format_2:
717 rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
718 rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
719 int src_b;
720 if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
721 op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
722 src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
723 } else {
724 op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
725 src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
726 }
727 asm_thumb_format_2(emit->as, op_code, rlo_dest, rlo_src, src_b);
Damien George32f0b792015-02-13 10:43:05 +0000728 } else if (strcmp(op_str, "sdiv") == 0) {
729 op_code = 0xfb90; // sdiv high part
730 mp_uint_t rd, rn, rm;
731 op_sdiv_udiv:
732 rd = get_arg_reg(emit, op_str, pn_args[0], 15);
733 rn = get_arg_reg(emit, op_str, pn_args[1], 15);
734 rm = get_arg_reg(emit, op_str, pn_args[2], 15);
735 asm_thumb_op32(emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm);
736 } else if (strcmp(op_str, "udiv") == 0) {
737 op_code = 0xfbb0; // udiv high part
738 goto op_sdiv_udiv;
Damien George87210872014-04-13 00:30:32 +0100739 } else if (strcmp(op_str, "sub") == 0) {
740 op_code = ASM_THUMB_FORMAT_2_SUB;
741 goto op_format_2;
Damien George1bf5a022015-02-12 22:52:42 +0000742 } else if (strcmp(op_str, "strex") == 0) {
743 mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
744 mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15);
745 mp_parse_node_t pn_base, pn_offset;
746 if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) {
747 mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
748 mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
749 asm_thumb_op32(emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8);
750 }
Damien George47e1b852014-04-08 18:28:33 +0100751 } else {
752 goto unknown_op;
753 }
754
Damien826005c2013-10-05 23:17:28 +0100755 } else {
Damien George47e1b852014-04-08 18:28:33 +0100756 goto unknown_op;
Damien826005c2013-10-05 23:17:28 +0100757 }
Damien George47e1b852014-04-08 18:28:33 +0100758
759 return;
760
761unknown_op:
Damien George8dfbd2d2015-02-13 01:00:51 +0000762 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 +0000763 return;
764
765branch_not_in_range:
766 emit_inline_thumb_error_msg(emit, "branch not in range");
767 return;
Damien826005c2013-10-05 23:17:28 +0100768}
769
770const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
771 emit_inline_thumb_start_pass,
772 emit_inline_thumb_end_pass,
Damiena2f2f7d2013-10-06 00:14:13 +0100773 emit_inline_thumb_count_params,
Damien826005c2013-10-05 23:17:28 +0100774 emit_inline_thumb_label,
Damien Georgee5f8a772014-04-21 13:33:15 +0100775 emit_inline_thumb_align,
776 emit_inline_thumb_data,
Damien826005c2013-10-05 23:17:28 +0100777 emit_inline_thumb_op,
778};
779
Damien3ef4abb2013-10-12 16:53:13 +0100780#endif // MICROPY_EMIT_INLINE_THUMB