blob: 073dfa06040c2a117e18a72384cf3043eca26b76 [file] [log] [blame]
Damien826005c2013-10-05 23:17:28 +01001#include <unistd.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <stdio.h>
5#include <string.h>
6#include <assert.h>
7
8#include "misc.h"
Damiend99b0522013-12-21 18:17:45 +00009#include "mpconfig.h"
Damien826005c2013-10-05 23:17:28 +010010#include "lexer.h"
Damien826005c2013-10-05 23:17:28 +010011#include "parse.h"
12#include "scope.h"
Damiend99b0522013-12-21 18:17:45 +000013#include "runtime0.h"
Damien826005c2013-10-05 23:17:28 +010014#include "emit.h"
15#include "asmthumb.h"
16
Damien3ef4abb2013-10-12 16:53:13 +010017#if MICROPY_EMIT_INLINE_THUMB
Damien826005c2013-10-05 23:17:28 +010018
19struct _emit_inline_asm_t {
20 int pass;
21 scope_t *scope;
22 int max_num_labels;
23 qstr *label_lookup;
24 asm_thumb_t *as;
25};
26
27emit_inline_asm_t *emit_inline_thumb_new(uint max_num_labels) {
28 emit_inline_asm_t *emit = m_new(emit_inline_asm_t, 1);
29 emit->max_num_labels = max_num_labels;
30 emit->label_lookup = m_new(qstr, max_num_labels);
31 memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
32 emit->as = asm_thumb_new(max_num_labels);
33 return emit;
34}
35
36static void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope) {
37 emit->pass = pass;
38 emit->scope = scope;
39 asm_thumb_start_pass(emit->as, pass);
40 asm_thumb_entry(emit->as, 0);
41}
42
43static void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
44 asm_thumb_exit(emit->as);
45 asm_thumb_end_pass(emit->as);
46
47 if (emit->pass == PASS_3) {
Damiend99b0522013-12-21 18:17:45 +000048 void *f = asm_thumb_get_code(emit->as);
Damien826005c2013-10-05 23:17:28 +010049 rt_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
50 }
51}
52
Damiend99b0522013-12-21 18:17:45 +000053static int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
Damiena2f2f7d2013-10-06 00:14:13 +010054 if (n_params > 4) {
Damien03d41242013-10-06 00:36:05 +010055 printf("SyntaxError: can only have up to 4 parameters to inline thumb assembly\n");
Damiena2f2f7d2013-10-06 00:14:13 +010056 return 0;
57 }
58 for (int i = 0; i < n_params; i++) {
Damiend99b0522013-12-21 18:17:45 +000059 if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
Damiendc833822013-10-06 01:01:01 +010060 printf("SyntaxError: parameter to inline assembler must be an identifier\n");
Damiena2f2f7d2013-10-06 00:14:13 +010061 return 0;
62 }
Damiend99b0522013-12-21 18:17:45 +000063 const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
Damiena2f2f7d2013-10-06 00:14:13 +010064 if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
Damien03d41242013-10-06 00:36:05 +010065 printf("SyntaxError: parameter %d to inline assembler must be r%d\n", i + 1, i);
Damiena2f2f7d2013-10-06 00:14:13 +010066 return 0;
67 }
68 }
69 return n_params;
70}
71
Damien826005c2013-10-05 23:17:28 +010072static void emit_inline_thumb_label(emit_inline_asm_t *emit, int label_num, qstr label_id) {
73 assert(label_num < emit->max_num_labels);
74 emit->label_lookup[label_num] = label_id;
75 asm_thumb_label_assign(emit->as, label_num);
76}
77
78static bool check_n_arg(qstr op, int n_args, int wanted_n_args) {
79 if (wanted_n_args == n_args) {
80 return true;
81 } else {
82 printf("SyntaxError: '%s' expects %d arguments'\n", qstr_str(op), wanted_n_args);
83 return false;
84 }
85}
86
Damiend99b0522013-12-21 18:17:45 +000087static uint get_arg_rlo(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
88 if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
Damien826005c2013-10-05 23:17:28 +010089 printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
90 return 0;
91 }
Damiend99b0522013-12-21 18:17:45 +000092 qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
Damien826005c2013-10-05 23:17:28 +010093 const char *reg_str = qstr_str(reg_qstr);
94 if (!(strlen(reg_str) == 2 && reg_str[0] == 'r' && ('0' <= reg_str[1] && reg_str[1] <= '7'))) {
95 printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
96 return 0;
97 }
98 return reg_str[1] - '0';
99}
100
Damiend99b0522013-12-21 18:17:45 +0000101static int get_arg_i(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num, int fit_mask) {
102 if (!MP_PARSE_NODE_IS_SMALL_INT(pn_args[wanted_arg_num])) {
Damien826005c2013-10-05 23:17:28 +0100103 printf("SyntaxError: '%s' expects an integer in position %d\n", qstr_str(op), wanted_arg_num);
104 return 0;
105 }
Damiend99b0522013-12-21 18:17:45 +0000106 int i = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
Damien826005c2013-10-05 23:17:28 +0100107 if ((i & (~fit_mask)) != 0) {
108 printf("SyntaxError: '%s' integer 0x%x does not fit in mask 0x%x\n", qstr_str(op), i, fit_mask);
109 return 0;
110 }
111 return i;
112}
113
Damiend99b0522013-12-21 18:17:45 +0000114static int get_arg_label(emit_inline_asm_t *emit, qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
115 if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
Damien826005c2013-10-05 23:17:28 +0100116 printf("SyntaxError: '%s' expects a label in position %d\n", qstr_str(op), wanted_arg_num);
117 return 0;
118 }
Damiend99b0522013-12-21 18:17:45 +0000119 qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
Damien826005c2013-10-05 23:17:28 +0100120 for (int i = 0; i < emit->max_num_labels; i++) {
121 if (emit->label_lookup[i] == label_qstr) {
122 return i;
123 }
124 }
Damiendc833822013-10-06 01:01:01 +0100125 // only need to have the labels on the last pass
126 if (emit->pass == PASS_3) {
127 printf("SyntaxError: label '%s' not defined\n", qstr_str(label_qstr));
128 }
Damien826005c2013-10-05 23:17:28 +0100129 return 0;
130}
131
Damiend99b0522013-12-21 18:17:45 +0000132static void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
Damien826005c2013-10-05 23:17:28 +0100133 // TODO perhaps make two tables:
Damien03d41242013-10-06 00:36:05 +0100134 // one_args =
135 // "b", LAB, asm_thumb_b_n,
136 // "bgt", LAB, asm_thumb_bgt_n,
Damien826005c2013-10-05 23:17:28 +0100137 // two_args =
138 // "movs", RLO, I8, asm_thumb_movs_reg_i8
139 // "movw", REG, REG, asm_thumb_movw_reg_i16
140 // three_args =
141 // "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
142
143 // 1 arg
Damien03d41242013-10-06 00:36:05 +0100144 if (strcmp(qstr_str(op), "b") == 0) {
145 if (!check_n_arg(op, n_args, 1)) {
146 return;
147 }
148 int label_num = get_arg_label(emit, op, pn_args, 0);
149 // TODO check that this succeeded, ie branch was within range
150 asm_thumb_b_n(emit->as, label_num);
151 } else if (strcmp(qstr_str(op), "bgt") == 0) {
Damien826005c2013-10-05 23:17:28 +0100152 if (!check_n_arg(op, n_args, 1)) {
153 return;
154 }
Damiena2f2f7d2013-10-06 00:14:13 +0100155 int label_num = get_arg_label(emit, op, pn_args, 0);
156 // TODO check that this succeeded, ie branch was within range
Damien1a6633a2013-11-03 13:58:19 +0000157 asm_thumb_bcc_n(emit->as, THUMB_CC_GT, label_num);
Damien826005c2013-10-05 23:17:28 +0100158
159 // 2 args
160 } else if (strcmp(qstr_str(op), "movs") == 0) {
161 if (!check_n_arg(op, n_args, 2)) {
162 return;
163 }
Damiena2f2f7d2013-10-06 00:14:13 +0100164 uint rlo_dest = get_arg_rlo(op, pn_args, 0);
165 int i_src = get_arg_i(op, pn_args, 1, 0xff);
Damien826005c2013-10-05 23:17:28 +0100166 asm_thumb_movs_rlo_i8(emit->as, rlo_dest, i_src);
167 } else if (strcmp(qstr_str(op), "movw") == 0) {
168 if (!check_n_arg(op, n_args, 2)) {
169 return;
170 }
Damiena2f2f7d2013-10-06 00:14:13 +0100171 uint rlo_dest = get_arg_rlo(op, pn_args, 0); // TODO can be reg lo or hi
172 int i_src = get_arg_i(op, pn_args, 1, 0xffff);
Damien826005c2013-10-05 23:17:28 +0100173 asm_thumb_movw_reg_i16(emit->as, rlo_dest, i_src);
174 } else if (strcmp(qstr_str(op), "cmp") == 0) {
175 if (!check_n_arg(op, n_args, 2)) {
176 return;
177 }
Damiena2f2f7d2013-10-06 00:14:13 +0100178 uint rlo = get_arg_rlo(op, pn_args, 0);
179 int i8 = get_arg_i(op, pn_args, 1, 0xff);
Damien826005c2013-10-05 23:17:28 +0100180 asm_thumb_cmp_rlo_i8(emit->as, rlo, i8);
181
182 // 3 args
183 } else if (strcmp(qstr_str(op), "subs") == 0) {
184 if (!check_n_arg(op, n_args, 3)) {
185 return;
186 }
Damiena2f2f7d2013-10-06 00:14:13 +0100187 uint rlo_dest = get_arg_rlo(op, pn_args, 0);
188 uint rlo_src = get_arg_rlo(op, pn_args, 1);
189 int i3_src = get_arg_i(op, pn_args, 2, 0x7);
Damien826005c2013-10-05 23:17:28 +0100190 asm_thumb_subs_rlo_rlo_i3(emit->as, rlo_dest, rlo_src, i3_src);
191
192 // unknown op
193 } else {
194 printf("SyntaxError: unsupported ARM Thumb instruction '%s'\n", qstr_str(op));
195 return;
196 }
197}
198
199const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
200 emit_inline_thumb_start_pass,
201 emit_inline_thumb_end_pass,
Damiena2f2f7d2013-10-06 00:14:13 +0100202 emit_inline_thumb_count_params,
Damien826005c2013-10-05 23:17:28 +0100203 emit_inline_thumb_label,
204 emit_inline_thumb_op,
205};
206
Damien3ef4abb2013-10-12 16:53:13 +0100207#endif // MICROPY_EMIT_INLINE_THUMB