Numworks Epsilon  1.4.1
Graphing Calculator Operating System
asmarm.c
Go to the documentation of this file.
1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2014 Fabian Vogt
7  * Copyright (c) 2013, 2014 Damien P. George
8  *
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 
32 #include "py/mpconfig.h"
33 
34 // wrapper around everything in this file
35 #if MICROPY_EMIT_ARM
36 
37 #include "py/asmarm.h"
38 
39 #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000)
40 
41 void asm_arm_end_pass(asm_arm_t *as) {
42  if (as->base.pass == MP_ASM_PASS_EMIT) {
43 #ifdef __arm__
44  // flush I- and D-cache
45  asm volatile(
46  "0:"
47  "mrc p15, 0, r15, c7, c10, 3\n"
48  "bne 0b\n"
49  "mov r0, #0\n"
50  "mcr p15, 0, r0, c7, c7, 0\n"
51  : : : "r0", "cc");
52 #endif
53  }
54 }
55 
56 // Insert word into instruction flow
57 STATIC void emit(asm_arm_t *as, uint op) {
59  if (c != NULL) {
60  *(uint32_t*)c = op;
61  }
62 }
63 
64 // Insert word into instruction flow, add "ALWAYS" condition code
65 STATIC void emit_al(asm_arm_t *as, uint op) {
66  emit(as, op | ASM_ARM_CC_AL);
67 }
68 
69 // Basic instructions without condition code
70 STATIC uint asm_arm_op_push(uint reglist) {
71  // stmfd sp!, {reglist}
72  return 0x92d0000 | (reglist & 0xFFFF);
73 }
74 
75 STATIC uint asm_arm_op_pop(uint reglist) {
76  // ldmfd sp!, {reglist}
77  return 0x8bd0000 | (reglist & 0xFFFF);
78 }
79 
80 STATIC uint asm_arm_op_mov_reg(uint rd, uint rn) {
81  // mov rd, rn
82  return 0x1a00000 | (rd << 12) | rn;
83 }
84 
85 STATIC uint asm_arm_op_mov_imm(uint rd, uint imm) {
86  // mov rd, #imm
87  return 0x3a00000 | (rd << 12) | imm;
88 }
89 
90 STATIC uint asm_arm_op_mvn_imm(uint rd, uint imm) {
91  // mvn rd, #imm
92  return 0x3e00000 | (rd << 12) | imm;
93 }
94 
95 STATIC 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 
100 STATIC 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 
105 STATIC 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 
110 STATIC 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 
115 STATIC 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 
121 STATIC 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 
126 STATIC 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 
131 STATIC 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 
136 void 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
143 // - numbered 0 through num_locals-1
144 // - 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 
152 void 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;
159  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;
167 
168  // Only adjust the stack if there are more locals than usable registers
169  if (num_locals > 3) {
170  as->stack_adjust = num_locals * 4;
171  // Align stack to 8 bytes
172  if (num_locals & 1) {
173  as->stack_adjust += 4;
174  }
175  }
176 
177  emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR));
178  if (as->stack_adjust > 0) {
179  emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
180  }
181 }
182 
183 void asm_arm_exit(asm_arm_t *as) {
184  if (as->stack_adjust > 0) {
185  emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
186  }
187 
188  emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC)));
189 }
190 
191 void asm_arm_push(asm_arm_t *as, uint reglist) {
192  emit_al(as, asm_arm_op_push(reglist));
193 }
194 
195 void asm_arm_pop(asm_arm_t *as, uint reglist) {
196  emit_al(as, asm_arm_op_pop(reglist));
197 }
198 
199 void 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 
203 void 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));
207  } 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));
210  } 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 
218 void 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 
223 void 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 
228 void 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 
233 void 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 
238 void 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
241 }
242 
243 void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
244  // add rd, rn, rm
245  emit_al(as, asm_arm_op_add_reg(rd, rn, rm));
246 }
247 
248 void 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 
253 void 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 
259 void 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 
264 void 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 
269 void 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 
274 void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) {
275  // add rd, sp, #local_num*4
276  emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2));
277 }
278 
279 void 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 
284 void 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 
289 void 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);
292 }
293 
294 void 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 
299 void 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 
304 void 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);
307 }
308 
309 void 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 
314 void 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 
319 void 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 
324 void 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 
330 void 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 
335 void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) {
336  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;
339  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 
349 void asm_arm_b_label(asm_arm_t *as, uint label) {
350  asm_arm_bcc_label(as, ASM_ARM_CC_AL, label);
351 }
352 
353 void 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
355  if (fun_id < (0x1000 / 4)) {
356  emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc
357  emit_al(as, 0x597f000 | (fun_id << 2)); // ldr pc, [r7, #fun_id*4]
358  return;
359  }
360 
361  emit_al(as, 0x59f0004 | (reg_temp << 12)); // ldr rd, [pc, #4]
362  // Set lr after fun_ptr
363  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
365  emit(as, (uint) fun_ptr);
366 }
367 
368 #endif // MICROPY_EMIT_ARM
void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn)
uint8_t * mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write)
intptr_t mp_int_t
Definition: mpconfigport.h:73
void asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm)
uintptr_t mp_uint_t
Definition: mpconfigport.h:74
void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src)
void asm_arm_end_pass(asm_arm_t *as)
#define ASM_ARM_REG_R5
Definition: asmarm.h:38
#define assert(e)
Definition: assert.h:9
void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm)
size_t code_offset
Definition: asmbase.h:37
uint push_reglist
Definition: asmarm.h:71
#define ASM_ARM_REG_PC
Definition: asmarm.h:51
void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm)
void asm_arm_entry(asm_arm_t *as, int num_locals)
size_t * label_offsets
Definition: asmbase.h:42
#define ASM_ARM_REG_R2
Definition: asmarm.h:35
void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
unsigned char uint8_t
Definition: stdint.h:4
#define MP_ASM_PASS_EMIT
Definition: asmbase.h:33
void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs)
#define STATIC
Definition: mpconfig.h:1178
void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
#define ASM_ARM_REG_R4
Definition: asmarm.h:37
void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond)
c(generic_all_nodes)
void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label)
#define ASM_ARM_REG_R3
Definition: asmarm.h:36
uint stack_adjust
Definition: asmarm.h:72
unsigned int uint32_t
Definition: stdint.h:6
#define NULL
Definition: stddef.h:4
void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn)
#define ASM_ARM_REG_R1
Definition: asmarm.h:34
void asm_arm_bkpt(asm_arm_t *as)
void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset)
void asm_arm_b_label(asm_arm_t *as, uint label)
void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn)
#define ASM_ARM_REG_R7
Definition: asmarm.h:40
#define ASM_ARM_REG_R6
Definition: asmarm.h:39
void asm_arm_exit(asm_arm_t *as)
void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn)
void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num)
void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp)
void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num)
void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs)
#define ASM_ARM_REG_LR
Definition: asmarm.h:50
void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn)
void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset)
void asm_arm_push(asm_arm_t *as, uint reglist)
void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
void asm_arm_pop(asm_arm_t *as, uint reglist)
#define ASM_ARM_REG_R8
Definition: asmarm.h:41
#define ASM_ARM_REG_SP
Definition: asmarm.h:49
void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm)
void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd)
void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn)
void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm)
#define ASM_ARM_CC_AL
Definition: asmarm.h:67
mp_asm_base_t base
Definition: asmarm.h:70
unsigned int uint
Definition: misc.h:38