blob: 56e05cf56062193465faa1bb8f2b9caf8e06c3f6 [file] [log] [blame]
Fabian Vogtfe3d16e2014-08-16 22:55:53 +02001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
Fabian Vogt16ee30c2014-08-28 01:18:56 +02006 * Copyright (c) 2014 Fabian Vogt
7 * Copyright (c) 2013, 2014 Damien P. George
Fabian Vogtfe3d16e2014-08-16 22:55:53 +02008 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 */
27
28#include <stdio.h>
29#include <assert.h>
30#include <string.h>
31
Damien George51dfcb42015-01-01 20:27:54 +000032#include "py/mpconfig.h"
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020033
34// wrapper around everything in this file
35#if MICROPY_EMIT_ARM
36
Damien George51dfcb42015-01-01 20:27:54 +000037#include "py/asmarm.h"
38
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020039#define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000)
40
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020041void asm_arm_end_pass(asm_arm_t *as) {
Damien George93ee6602016-12-09 22:54:45 +110042 if (as->base.pass == ASM_ARM_PASS_EMIT) {
Fabian Vogtb7235b82014-09-03 16:59:33 +020043#ifdef __arm__
44 // flush I- and D-cache
Damien Georgedda46462014-09-03 22:47:23 +010045 asm volatile(
Fabian Vogtb7235b82014-09-03 16:59:33 +020046 "0:"
47 "mrc p15, 0, r15, c7, c10, 3\n"
48 "bne 0b\n"
49 "mov r0, #0\n"
Damien Georgedda46462014-09-03 22:47:23 +010050 "mcr p15, 0, r0, c7, c7, 0\n"
Fabian Vogtb7235b82014-09-03 16:59:33 +020051 : : : "r0", "cc");
52#endif
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020053 }
54}
55
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020056// Insert word into instruction flow
57STATIC void emit(asm_arm_t *as, uint op) {
Damien George155fdc72016-12-09 22:50:58 +110058 uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4);
59 if (c != NULL) {
60 *(uint32_t*)c = op;
61 }
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020062}
63
64// Insert word into instruction flow, add "ALWAYS" condition code
65STATIC void emit_al(asm_arm_t *as, uint op) {
Damien George0b610de2014-09-29 16:25:04 +010066 emit(as, op | ASM_ARM_CC_AL);
Fabian Vogtfe3d16e2014-08-16 22:55:53 +020067}
68
69// Basic instructions without condition code
70STATIC uint asm_arm_op_push(uint reglist) {
71 // stmfd sp!, {reglist}
72 return 0x92d0000 | (reglist & 0xFFFF);
73}
74
75STATIC uint asm_arm_op_pop(uint reglist) {
76 // ldmfd sp!, {reglist}
77 return 0x8bd0000 | (reglist & 0xFFFF);
78}
79
80STATIC uint asm_arm_op_mov_reg(uint rd, uint rn) {
81 // mov rd, rn
82 return 0x1a00000 | (rd << 12) | rn;
83}
84
85STATIC uint asm_arm_op_mov_imm(uint rd, uint imm) {
86 // mov rd, #imm
87 return 0x3a00000 | (rd << 12) | imm;
88}
89
90STATIC uint asm_arm_op_mvn_imm(uint rd, uint imm) {
91 // mvn rd, #imm
92 return 0x3e00000 | (rd << 12) | imm;
93}
94
95STATIC uint asm_arm_op_add_imm(uint rd, uint rn, uint imm) {
96 // add rd, rn, #imm
97 return 0x2800000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
98}
99
100STATIC uint asm_arm_op_add_reg(uint rd, uint rn, uint rm) {
101 // add rd, rn, rm
102 return 0x0800000 | (rn << 16) | (rd << 12) | rm;
103}
104
105STATIC uint asm_arm_op_sub_imm(uint rd, uint rn, uint imm) {
106 // sub rd, rn, #imm
107 return 0x2400000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
108}
109
Damien George3112cde2014-09-29 18:45:42 +0100110STATIC uint asm_arm_op_sub_reg(uint rd, uint rn, uint rm) {
111 // sub rd, rn, rm
112 return 0x0400000 | (rn << 16) | (rd << 12) | rm;
113}
114
Damien George567b3492015-06-04 14:00:29 +0000115STATIC uint asm_arm_op_mul_reg(uint rd, uint rm, uint rs) {
116 // mul rd, rm, rs
117 assert(rd != rm);
118 return 0x0000090 | (rd << 16) | (rs << 8) | rm;
119}
120
Damien George1ef23482014-10-12 14:21:06 +0100121STATIC uint asm_arm_op_and_reg(uint rd, uint rn, uint rm) {
122 // and rd, rn, rm
123 return 0x0000000 | (rn << 16) | (rd << 12) | rm;
124}
125
126STATIC uint asm_arm_op_eor_reg(uint rd, uint rn, uint rm) {
127 // eor rd, rn, rm
128 return 0x0200000 | (rn << 16) | (rd << 12) | rm;
129}
130
131STATIC uint asm_arm_op_orr_reg(uint rd, uint rn, uint rm) {
132 // orr rd, rn, rm
133 return 0x1800000 | (rn << 16) | (rd << 12) | rm;
134}
135
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200136void asm_arm_bkpt(asm_arm_t *as) {
137 // bkpt #0
138 emit_al(as, 0x1200070);
139}
140
141// locals:
142// - stored on the stack in ascending order
Damien George0b610de2014-09-29 16:25:04 +0100143// - numbered 0 through num_locals-1
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200144// - SP points to first local
145//
146// | SP
147// v
148// l0 l1 l2 ... l(n-1)
149// ^ ^
150// | low address | high address in RAM
151
152void asm_arm_entry(asm_arm_t *as, int num_locals) {
153
154 if (num_locals < 0) {
155 num_locals = 0;
156 }
157
158 as->stack_adjust = 0;
Damien George0b610de2014-09-29 16:25:04 +0100159 as->push_reglist = 1 << ASM_ARM_REG_R1
160 | 1 << ASM_ARM_REG_R2
161 | 1 << ASM_ARM_REG_R3
162 | 1 << ASM_ARM_REG_R4
163 | 1 << ASM_ARM_REG_R5
164 | 1 << ASM_ARM_REG_R6
165 | 1 << ASM_ARM_REG_R7
166 | 1 << ASM_ARM_REG_R8;
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200167
168 // Only adjust the stack if there are more locals than usable registers
Damien George4dea9222015-04-09 15:29:54 +0000169 if (num_locals > 3) {
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200170 as->stack_adjust = num_locals * 4;
171 // Align stack to 8 bytes
Damien George0b610de2014-09-29 16:25:04 +0100172 if (num_locals & 1) {
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200173 as->stack_adjust += 4;
Damien George0b610de2014-09-29 16:25:04 +0100174 }
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200175 }
176
Damien George0b610de2014-09-29 16:25:04 +0100177 emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200178 if (as->stack_adjust > 0) {
Damien George0b610de2014-09-29 16:25:04 +0100179 emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200180 }
181}
182
183void asm_arm_exit(asm_arm_t *as) {
184 if (as->stack_adjust > 0) {
Damien George0b610de2014-09-29 16:25:04 +0100185 emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200186 }
187
Damien George0b610de2014-09-29 16:25:04 +0100188 emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC)));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200189}
190
Paul Sokolovsky351424e2015-05-07 23:08:09 +0300191void asm_arm_push(asm_arm_t *as, uint reglist) {
192 emit_al(as, asm_arm_op_push(reglist));
193}
194
195void asm_arm_pop(asm_arm_t *as, uint reglist) {
196 emit_al(as, asm_arm_op_pop(reglist));
197}
198
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200199void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) {
200 emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src));
201}
202
203void asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm) {
204 // TODO: There are more variants of immediate values
205 if ((imm & 0xFF) == imm) {
206 emit_al(as, asm_arm_op_mov_imm(rd, imm));
Paul Sokolovskyc0bc3bd2014-12-14 03:24:17 +0200207 } else if (imm < 0 && imm >= -256) {
208 // mvn is "move not", not "move negative"
209 emit_al(as, asm_arm_op_mvn_imm(rd, ~imm));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200210 } else {
211 //Insert immediate into code and jump over it
212 emit_al(as, 0x59f0000 | (rd << 12)); // ldr rd, [pc]
213 emit_al(as, 0xa000000); // b pc
214 emit(as, imm);
215 }
216}
217
218void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd) {
219 // str rd, [sp, #local_num*4]
220 emit_al(as, 0x58d0000 | (rd << 12) | (local_num << 2));
221}
222
223void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num) {
224 // ldr rd, [sp, #local_num*4]
225 emit_al(as, 0x59d0000 | (rd << 12) | (local_num << 2));
226}
227
228void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm) {
229 // cmp rd, #imm
230 emit_al(as, 0x3500000 | (rd << 16) | (imm & 0xFF));
231}
232
233void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn) {
234 // cmp rd, rn
235 emit_al(as, 0x1500000 | (rd << 16) | rn);
236}
237
Fabian Vogte5268962014-10-04 00:53:46 +0200238void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond) {
239 emit(as, asm_arm_op_mov_imm(rd, 1) | cond); // movCOND rd, #1
240 emit(as, asm_arm_op_mov_imm(rd, 0) | (cond ^ (1 << 28))); // mov!COND rd, #0
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200241}
242
Damien George3112cde2014-09-29 18:45:42 +0100243void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200244 // add rd, rn, rm
245 emit_al(as, asm_arm_op_add_reg(rd, rn, rm));
246}
247
Damien George3112cde2014-09-29 18:45:42 +0100248void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
249 // sub rd, rn, rm
250 emit_al(as, asm_arm_op_sub_reg(rd, rn, rm));
251}
252
Damien George567b3492015-06-04 14:00:29 +0000253void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rs, uint rm) {
254 // rs and rm are swapped because of restriction rd!=rm
255 // mul rd, rm, rs
256 emit_al(as, asm_arm_op_mul_reg(rd, rm, rs));
257}
258
Damien George1ef23482014-10-12 14:21:06 +0100259void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
260 // and rd, rn, rm
261 emit_al(as, asm_arm_op_and_reg(rd, rn, rm));
262}
263
264void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
265 // eor rd, rn, rm
266 emit_al(as, asm_arm_op_eor_reg(rd, rn, rm));
267}
268
269void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
270 // orr rd, rn, rm
271 emit_al(as, asm_arm_op_orr_reg(rd, rn, rm));
272}
273
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200274void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) {
275 // add rd, sp, #local_num*4
Damien George0b610de2014-09-29 16:25:04 +0100276 emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2));
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200277}
278
Fabian Vogte5268962014-10-04 00:53:46 +0200279void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs) {
280 // mov rd, rd, lsl rs
281 emit_al(as, 0x1a00010 | (rd << 12) | (rs << 8) | rd);
282}
283
284void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) {
285 // mov rd, rd, asr rs
286 emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd);
287}
288
Paul Sokolovsky351424e2015-05-07 23:08:09 +0300289void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) {
290 // ldr rd, [rn, #off]
291 emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset);
Damien George91cfd412014-10-12 16:59:29 +0100292}
293
294void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) {
295 // ldrh rd, [rn]
296 emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12));
297}
298
299void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) {
300 // ldrb rd, [rn]
301 emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12));
302}
303
Paul Sokolovsky351424e2015-05-07 23:08:09 +0300304void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) {
305 // str rd, [rm, #off]
306 emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset);
Fabian Vogte5268962014-10-04 00:53:46 +0200307}
308
309void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) {
310 // strh rd, [rm]
311 emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12));
312}
313
314void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) {
315 // strb rd, [rm]
316 emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12));
317}
318
319void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
320 // str rd, [rm, rn, lsl #2]
321 emit_al(as, 0x7800100 | (rm << 16) | (rd << 12) | rn);
322}
323
324void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
325 // strh doesn't support scaled register index
326 emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1
327 emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8]
328}
329
330void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
331 // strb rd, [rm, rn]
332 emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | rn);
333}
334
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200335void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) {
Damien George93ee6602016-12-09 22:54:45 +1100336 assert(label < as->base.max_num_labels);
337 mp_uint_t dest = as->base.label_offsets[label];
338 mp_int_t rel = dest - as->base.code_offset;
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200339 rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction
340 rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted
341
342 if (SIGNED_FIT24(rel)) {
343 emit(as, cond | 0xa000000 | (rel & 0xffffff));
344 } else {
345 printf("asm_arm_bcc: branch does not fit in 24 bits\n");
346 }
347}
348
349void asm_arm_b_label(asm_arm_t *as, uint label) {
Damien George0b610de2014-09-29 16:25:04 +0100350 asm_arm_bcc_label(as, ASM_ARM_CC_AL, label);
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200351}
352
353void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp) {
354 // If the table offset fits into the ldr instruction
Damien George4dea9222015-04-09 15:29:54 +0000355 if (fun_id < (0x1000 / 4)) {
Damien George0b610de2014-09-29 16:25:04 +0100356 emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200357 emit_al(as, 0x597f000 | (fun_id << 2)); // ldr pc, [r7, #fun_id*4]
358 return;
359 }
Damien George4dea9222015-04-09 15:29:54 +0000360
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200361 emit_al(as, 0x59f0004 | (reg_temp << 12)); // ldr rd, [pc, #4]
362 // Set lr after fun_ptr
Damien George0b610de2014-09-29 16:25:04 +0100363 emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_LR, ASM_ARM_REG_PC, 4)); // add lr, pc, #4
364 emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_PC, reg_temp)); // mov pc, reg_temp
Fabian Vogtfe3d16e2014-08-16 22:55:53 +0200365 emit(as, (uint) fun_ptr);
366}
367
368#endif // MICROPY_EMIT_ARM