winedbg: Avoid TRUE:FALSE conditional expressions.
[wine/multimedia.git] / programs / winedbg / be_arm.c
blobf375fe80e7ca568cdfb1042973f01ca4ebe8ac40
1 /*
2 * Debugger ARM specific functions
4 * Copyright 2000-2003 Marcus Meissner
5 * 2004 Eric Pouech
6 * 2010-2012 André Hentschel
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "debugger.h"
25 #if defined(__arm__) && !defined(__ARMEB__)
28 * Switch to disassemble Thumb code.
30 static BOOL db_disasm_thumb = FALSE;
33 * Flag to indicate whether we need to display instruction,
34 * or whether we just need to know the address of the next
35 * instruction.
37 static BOOL db_display = FALSE;
39 #define ARM_INSN_SIZE 4
40 #define THUMB_INSN_SIZE 2
41 #define THUMB2_INSN_SIZE 4
43 #define ROR32(n, r) (((n) >> (r)) | ((n) << (32 - (r))))
45 #define get_cond(ins) tbl_cond[(ins >> 28) & 0x0f]
46 #define get_nibble(ins, num) ((ins >> (num * 4)) & 0x0f)
48 static char const tbl_regs[][4] = {
49 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
50 "fp", "ip", "sp", "lr", "pc", "cpsr"
53 static char const tbl_addrmode[][3] = {
54 "da", "ia", "db", "ib"
57 static char const tbl_cond[][3] = {
58 "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", ""
61 static char const tbl_dataops[][4] = {
62 "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr",
63 "mov", "bic", "mvn"
66 static char const tbl_shifts[][4] = {
67 "lsl", "lsr", "asr", "ror"
70 static char const tbl_hiops_t[][4] = {
71 "add", "cmp", "mov", "bx"
74 static char const tbl_aluops_t[][4] = {
75 "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror", "tst", "neg", "cmp", "cmn", "orr",
76 "mul", "bic", "mvn"
79 static char const tbl_immops_t[][4] = {
80 "mov", "cmp", "add", "sub"
83 static char const tbl_sregops_t[][5] = {
84 "strh", "ldsb", "ldrh", "ldsh"
87 static UINT db_get_inst(void* addr, int size)
89 UINT result = 0;
90 char buffer[4];
92 if (dbg_read_memory(addr, buffer, size))
94 switch (size)
96 case 4:
97 result = *(UINT*)buffer;
98 break;
99 case 2:
100 result = *(WORD*)buffer;
101 break;
104 return result;
107 static void db_printsym(unsigned int addr)
109 ADDRESS64 a;
111 a.Mode = AddrModeFlat;
112 a.Offset = addr;
114 print_address(&a, TRUE);
117 static UINT arm_disasm_branch(UINT inst, ADDRESS64 *addr)
119 short link = (inst >> 24) & 0x01;
120 int offset = (inst << 2) & 0x03ffffff;
122 if (offset & 0x02000000) offset |= 0xfc000000;
123 offset += 8;
125 dbg_printf("\n\tb%s%s\t", link ? "l" : "", get_cond(inst));
126 db_printsym(addr->Offset + offset);
127 return 0;
130 static UINT arm_disasm_mul(UINT inst, ADDRESS64 *addr)
132 short accu = (inst >> 21) & 0x01;
133 short condcodes = (inst >> 20) & 0x01;
135 if (accu)
136 dbg_printf("\n\tmla%s%s\t%s, %s, %s, %s", get_cond(inst), condcodes ? "s" : "",
137 tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
138 tbl_regs[get_nibble(inst, 2)], tbl_regs[get_nibble(inst, 3)]);
139 else
140 dbg_printf("\n\tmul%s%s\t%s, %s, %s", get_cond(inst), condcodes ? "s" : "",
141 tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
142 tbl_regs[get_nibble(inst, 2)]);
143 return 0;
146 static UINT arm_disasm_longmul(UINT inst, ADDRESS64 *addr)
148 short sign = (inst >> 22) & 0x01;
149 short accu = (inst >> 21) & 0x01;
150 short condcodes = (inst >> 20) & 0x01;
152 dbg_printf("\n\t%s%s%s%s\t%s, %s, %s, %s", sign ? "s" : "u", accu ? "mlal" : "mull",
153 get_cond(inst), condcodes ? "s" : "",
154 tbl_regs[get_nibble(inst, 3)], tbl_regs[get_nibble(inst, 4)],
155 tbl_regs[get_nibble(inst, 0)], tbl_regs[get_nibble(inst, 2)]);
156 return 0;
159 static UINT arm_disasm_swp(UINT inst, ADDRESS64 *addr)
161 short byte = (inst >> 22) & 0x01;
163 dbg_printf("\n\tswp%s%s\t%s, %s, [%s]", get_cond(inst), byte ? "b" : "",
164 tbl_regs[get_nibble(inst, 3)], tbl_regs[get_nibble(inst, 0)],
165 tbl_regs[get_nibble(inst, 4)]);
166 return 0;
169 static UINT arm_disasm_branchreg(UINT inst, ADDRESS64 *addr)
171 dbg_printf("\n\tb%s\t%s", get_cond(inst), tbl_regs[get_nibble(inst, 0)]);
172 return 0;
175 static UINT arm_disasm_branchxchg(UINT inst, ADDRESS64 *addr)
177 dbg_printf("\n\tbx%s\t%s", get_cond(inst), tbl_regs[get_nibble(inst, 0)]);
178 return 0;
181 static UINT arm_disasm_mrstrans(UINT inst, ADDRESS64 *addr)
183 short src = (inst >> 22) & 0x01;
185 dbg_printf("\n\tmrs%s\t%s, %s", get_cond(inst), tbl_regs[get_nibble(inst, 3)],
186 src ? "spsr" : "cpsr");
187 return 0;
190 static UINT arm_disasm_msrtrans(UINT inst, ADDRESS64 *addr)
192 short immediate = (inst >> 25) & 0x01;
193 short dst = (inst >> 22) & 0x01;
194 short simple = (inst >> 16) & 0x01;
196 if (simple || !immediate)
198 dbg_printf("\n\tmsr%s\t%s, %s", get_cond(inst), dst ? "spsr" : "cpsr",
199 tbl_regs[get_nibble(inst, 0)]);
200 return 0;
203 dbg_printf("\n\tmsr%s\t%s, #%u", get_cond(inst), dst ? "spsr" : "cpsr",
204 ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
205 return 0;
208 static UINT arm_disasm_wordmov(UINT inst, ADDRESS64 *addr)
210 short top = (inst >> 22) & 0x01;
212 dbg_printf("\n\tmov%s%s\t%s, #%u", top ? "t" : "w", get_cond(inst),
213 tbl_regs[get_nibble(inst, 3)], (get_nibble(inst, 4) << 12) | (inst & 0x0fff));
214 return 0;
217 static UINT arm_disasm_nop(UINT inst, ADDRESS64 *addr)
219 dbg_printf("\n\tnop%s", get_cond(inst));
220 return 0;
223 static UINT arm_disasm_dataprocessing(UINT inst, ADDRESS64 *addr)
225 short condcodes = (inst >> 20) & 0x01;
226 short opcode = (inst >> 21) & 0x0f;
227 short immediate = (inst >> 25) & 0x01;
228 short no_op1 = (opcode & 0x0d) == 0x0d;
229 short no_dst = (opcode & 0x0c) == 0x08;
231 dbg_printf("\n\t%s%s%s", tbl_dataops[opcode], condcodes ? "s" : "", get_cond(inst));
232 if (!no_dst) dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
233 else dbg_printf("\t");
235 if (no_op1)
237 if (immediate)
238 dbg_printf("#%u", ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
239 else
240 dbg_printf("%s", tbl_regs[get_nibble(inst, 0)]);
242 else
244 if (immediate)
245 dbg_printf("%s, #%u", tbl_regs[get_nibble(inst, 4)],
246 ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
247 else if (((inst >> 4) & 0xff) == 0x00) /* no shift */
248 dbg_printf("%s, %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
249 else if (((inst >> 4) & 0x09) == 0x01) /* register shift */
250 dbg_printf("%s, %s, %s %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
251 tbl_shifts[(inst >> 5) & 0x03], tbl_regs[(inst >> 8) & 0x0f]);
252 else if (((inst >> 4) & 0x01) == 0x00) /* immediate shift */
253 dbg_printf("%s, %s, %s #%d", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
254 tbl_shifts[(inst >> 5) & 0x03], (inst >> 7) & 0x1f);
255 else
256 return inst;
258 return 0;
261 static UINT arm_disasm_singletrans(UINT inst, ADDRESS64 *addr)
263 short load = (inst >> 20) & 0x01;
264 short writeback = (inst >> 21) & 0x01;
265 short byte = (inst >> 22) & 0x01;
266 short direction = (inst >> 23) & 0x01;
267 short indexing = (inst >> 24) & 0x01;
268 short immediate = !((inst >> 25) & 0x01);
269 short offset = inst & 0x0fff;
271 if (!direction) offset *= -1;
273 dbg_printf("\n\t%s%s%s%s", load ? "ldr" : "str", byte ? "b" : "", writeback ? "t" : "",
274 get_cond(inst));
275 dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
276 if (indexing)
278 if (immediate)
279 dbg_printf("[%s, #%d]", tbl_regs[get_nibble(inst, 4)], offset);
280 else if (((inst >> 4) & 0xff) == 0x00) /* no shift */
281 dbg_printf("[%s, %s]", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
282 else if (((inst >> 4) & 0x01) == 0x00) /* immediate shift (there's no register shift) */
283 dbg_printf("[%s, %s, %s #%d]", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
284 tbl_shifts[(inst >> 5) & 0x03], (inst >> 7) & 0x1f);
285 else
286 return inst;
288 else
290 if (immediate)
291 dbg_printf("[%s], #%d", tbl_regs[get_nibble(inst, 4)], offset);
292 else if (((inst >> 4) & 0xff) == 0x00) /* no shift */
293 dbg_printf("[%s], %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
294 else if (((inst >> 4) & 0x01) == 0x00) /* immediate shift (there's no register shift) */
295 dbg_printf("[%s], %s, %s #%d", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)],
296 tbl_shifts[(inst >> 5) & 0x03], (inst >> 7) & 0x1f);
297 else
298 return inst;
300 return 0;
303 static UINT arm_disasm_halfwordtrans(UINT inst, ADDRESS64 *addr)
305 short halfword = (inst >> 5) & 0x01;
306 short sign = (inst >> 6) & 0x01;
307 short load = (inst >> 20) & 0x01;
308 short writeback = (inst >> 21) & 0x01;
309 short immediate = (inst >> 22) & 0x01;
310 short direction = (inst >> 23) & 0x01;
311 short indexing = (inst >> 24) & 0x01;
312 short offset = ((inst >> 4) & 0xf0) + (inst & 0x0f);
314 if (!direction) offset *= -1;
316 dbg_printf("\n\t%s%s%s%s%s", load ? "ldr" : "str", sign ? "s" : "",
317 halfword ? "h" : (sign ? "b" : ""), writeback ? "t" : "", get_cond(inst));
318 dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
319 if (indexing)
321 if (immediate)
322 dbg_printf("[%s, #%d]", tbl_regs[get_nibble(inst, 4)], offset);
323 else
324 dbg_printf("[%s, %s]", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
326 else
328 if (immediate)
329 dbg_printf("[%s], #%d", tbl_regs[get_nibble(inst, 4)], offset);
330 else
331 dbg_printf("[%s], %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
333 return 0;
336 static UINT arm_disasm_blocktrans(UINT inst, ADDRESS64 *addr)
338 short load = (inst >> 20) & 0x01;
339 short writeback = (inst >> 21) & 0x01;
340 short psr = (inst >> 22) & 0x01;
341 short addrmode = (inst >> 23) & 0x03;
342 short i;
343 short last=15;
344 for (i=15;i>=0;i--)
345 if ((inst>>i) & 1)
347 last = i;
348 break;
351 dbg_printf("\n\t%s%s%s\t%s%s, {", load ? "ldm" : "stm", tbl_addrmode[addrmode], get_cond(inst),
352 tbl_regs[get_nibble(inst, 4)], writeback ? "!" : "");
353 for (i=0;i<=15;i++)
354 if ((inst>>i) & 1)
356 if (i == last) dbg_printf("%s", tbl_regs[i]);
357 else dbg_printf("%s, ", tbl_regs[i]);
359 dbg_printf("}%s", psr ? "^" : "");
360 return 0;
363 static UINT arm_disasm_swi(UINT inst, ADDRESS64 *addr)
365 UINT comment = inst & 0x00ffffff;
366 dbg_printf("\n\tswi%s\t#%d", get_cond(inst), comment);
367 return 0;
370 static UINT arm_disasm_coproctrans(UINT inst, ADDRESS64 *addr)
372 WORD CRm = inst & 0x0f;
373 WORD CP = (inst >> 5) & 0x07;
374 WORD CPnum = (inst >> 8) & 0x0f;
375 WORD CRn = (inst >> 16) & 0x0f;
376 WORD load = (inst >> 20) & 0x01;
377 WORD CP_Opc = (inst >> 21) & 0x07;
379 dbg_printf("\n\t%s%s\t%u, %u, %s, cr%u, cr%u, {%u}", load ? "mrc" : "mcr", get_cond(inst), CPnum,
380 CP, tbl_regs[get_nibble(inst, 3)], CRn, CRm, CP_Opc);
381 return 0;
384 static UINT arm_disasm_coprocdataop(UINT inst, ADDRESS64 *addr)
386 WORD CRm = inst & 0x0f;
387 WORD CP = (inst >> 5) & 0x07;
388 WORD CPnum = (inst >> 8) & 0x0f;
389 WORD CRd = (inst >> 12) & 0x0f;
390 WORD CRn = (inst >> 16) & 0x0f;
391 WORD CP_Opc = (inst >> 20) & 0x0f;
393 dbg_printf("\n\tcdp%s\t%u, %u, cr%u, cr%u, cr%u, {%u}", get_cond(inst),
394 CPnum, CP, CRd, CRn, CRm, CP_Opc);
395 return 0;
398 static UINT arm_disasm_coprocdatatrans(UINT inst, ADDRESS64 *addr)
400 WORD CPnum = (inst >> 8) & 0x0f;
401 WORD CRd = (inst >> 12) & 0x0f;
402 WORD load = (inst >> 20) & 0x01;
403 WORD writeback = (inst >> 21) & 0x01;
404 WORD translen = (inst >> 22) & 0x01;
405 WORD direction = (inst >> 23) & 0x01;
406 WORD indexing = (inst >> 24) & 0x01;
407 short offset = (inst & 0xff) << 2;
409 if (!direction) offset *= -1;
411 dbg_printf("\n\t%s%s%s", load ? "ldc" : "stc", translen ? "l" : "", get_cond(inst));
412 if (indexing)
413 dbg_printf("\t%u, cr%u, [%s, #%d]%s", CPnum, CRd, tbl_regs[get_nibble(inst, 4)], offset, writeback?"!":"");
414 else
415 dbg_printf("\t%u, cr%u, [%s], #%d", CPnum, CRd, tbl_regs[get_nibble(inst, 4)], offset);
416 return 0;
419 static WORD thumb_disasm_hireg(WORD inst, ADDRESS64 *addr)
421 short dst = inst & 0x07;
422 short src = (inst >> 3) & 0x07;
423 short h2 = (inst >> 6) & 0x01;
424 short h1 = (inst >> 7) & 0x01;
425 short op = (inst >> 8) & 0x03;
427 if (h1) dst += 8;
428 if (h2) src += 8;
430 if (op == 2 && dst == src) /* mov rx, rx */
432 dbg_printf("\n\tnop");
433 return 0;
436 if (op == 3)
437 dbg_printf("\n\tb%sx\t%s", h1?"l":"", tbl_regs[src]);
438 else
439 dbg_printf("\n\t%s\t%s, %s", tbl_hiops_t[op], tbl_regs[dst], tbl_regs[src]);
441 return 0;
444 static WORD thumb_disasm_aluop(WORD inst, ADDRESS64 *addr)
446 short dst = inst & 0x07;
447 short src = (inst >> 3) & 0x07;
448 short op = (inst >> 6) & 0x0f;
450 dbg_printf("\n\t%s\t%s, %s", tbl_aluops_t[op], tbl_regs[dst], tbl_regs[src]);
452 return 0;
455 static WORD thumb_disasm_pushpop(WORD inst, ADDRESS64 *addr)
457 short lrpc = (inst >> 8) & 0x01;
458 short load = (inst >> 11) & 0x01;
459 short i;
460 short last;
462 for (i=7;i>=0;i--)
463 if ((inst>>i) & 1) break;
464 last = i;
466 dbg_printf("\n\t%s\t{", load ? "pop" : "push");
468 for (i=0;i<=7;i++)
469 if ((inst>>i) & 1)
471 if (i == last) dbg_printf("%s", tbl_regs[i]);
472 else dbg_printf("%s, ", tbl_regs[i]);
474 if (lrpc)
475 dbg_printf("%s%s", last ? ", " : "", load ? "pc" : "lr");
477 dbg_printf("}");
478 return 0;
481 static WORD thumb_disasm_blocktrans(WORD inst, ADDRESS64 *addr)
483 short load = (inst >> 11) & 0x01;
484 short i;
485 short last;
487 for (i=7;i>=0;i--)
488 if ((inst>>i) & 1) break;
489 last = i;
491 dbg_printf("\n\t%s\t%s!, {", load ? "ldmia" : "stmia", tbl_regs[(inst >> 8) & 0x07]);
493 for (i=0;i<=7;i++)
494 if ((inst>>i) & 1)
496 if (i == last) dbg_printf("%s", tbl_regs[i]);
497 else dbg_printf("%s, ", tbl_regs[i]);
500 dbg_printf("}");
501 return 0;
504 static WORD thumb_disasm_condbranch(WORD inst, ADDRESS64 *addr)
506 WORD offset = inst & 0x00ff;
507 dbg_printf("\n\tb%s\t", tbl_cond[(inst >> 8) & 0x0f]);
508 db_printsym(addr->Offset + offset);
509 return 0;
512 static WORD thumb_disasm_uncondbranch(WORD inst, ADDRESS64 *addr)
514 short offset = (inst & 0x07ff) << 1;
516 if (offset & 0x0800) offset |= 0xf000;
517 offset += 4;
519 dbg_printf("\n\tb\t");
520 db_printsym(addr->Offset + offset);
521 return 0;
524 static WORD thumb_disasm_loadadr(WORD inst, ADDRESS64 *addr)
526 WORD src = (inst >> 11) & 0x01;
527 WORD offset = (inst & 0xff) << 2;
529 dbg_printf("\n\tadd\t%s, %s, #%d", tbl_regs[(inst >> 8) & 0x07], src ? "sp" : "pc", offset);
530 return 0;
533 static WORD thumb_disasm_swi(WORD inst, ADDRESS64 *addr)
535 WORD comment = inst & 0x00ff;
536 dbg_printf("\n\tswi\t#%d", comment);
537 return 0;
540 static WORD thumb_disasm_nop(WORD inst, ADDRESS64 *addr)
542 dbg_printf("\n\tnop");
543 return 0;
546 static WORD thumb_disasm_ldrpcrel(WORD inst, ADDRESS64 *addr)
548 WORD offset = (inst & 0xff) << 2;
549 dbg_printf("\n\tldr\t%s, [pc, #%u]", tbl_regs[(inst >> 8) & 0x07], offset);
550 return 0;
553 static WORD thumb_disasm_ldrsprel(WORD inst, ADDRESS64 *addr)
555 WORD offset = (inst & 0xff) << 2;
556 dbg_printf("\n\t%s\t%s, [sp, #%u]", (inst & 0x0800)?"ldr":"str", tbl_regs[(inst >> 8) & 0x07], offset);
557 return 0;
560 static WORD thumb_disasm_addsprel(WORD inst, ADDRESS64 *addr)
562 WORD offset = (inst & 0x7f) << 2;
563 if ((inst >> 7) & 0x01)
564 dbg_printf("\n\tsub\tsp, sp, #%u", offset);
565 else
566 dbg_printf("\n\tadd\tsp, sp, #%u", offset);
567 return 0;
570 static WORD thumb_disasm_ldrimm(WORD inst, ADDRESS64 *addr)
572 WORD offset = (inst & 0x07c0) >> 6;
573 dbg_printf("\n\t%s%s\t%s, [%s, #%u]", (inst & 0x0800)?"ldr":"str", (inst & 0x1000)?"b":"",
574 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], (inst & 0x1000)?offset:(offset << 2));
575 return 0;
578 static WORD thumb_disasm_ldrhimm(WORD inst, ADDRESS64 *addr)
580 WORD offset = (inst & 0x07c0) >> 5;
581 dbg_printf("\n\t%s\t%s, [%s, #%u]", (inst & 0x0800)?"ldrh":"strh",
582 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], offset);
583 return 0;
586 static WORD thumb_disasm_ldrreg(WORD inst, ADDRESS64 *addr)
588 dbg_printf("\n\t%s%s\t%s, [%s, %s]", (inst & 0x0800)?"ldr":"str", (inst & 0x0400)?"b":"",
589 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], tbl_regs[(inst >> 6) & 0x07]);
590 return 0;
593 static WORD thumb_disasm_ldrsreg(WORD inst, ADDRESS64 *addr)
595 dbg_printf("\n\t%s\t%s, [%s, %s]", tbl_sregops_t[(inst >> 10) & 0x03],
596 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], tbl_regs[(inst >> 6) & 0x07]);
597 return 0;
600 static WORD thumb_disasm_immop(WORD inst, ADDRESS64 *addr)
602 WORD op = (inst >> 11) & 0x03;
603 dbg_printf("\n\t%s\t%s, #%u", tbl_immops_t[op], tbl_regs[(inst >> 8) & 0x07], inst & 0xff);
604 return 0;
607 static WORD thumb_disasm_addsub(WORD inst, ADDRESS64 *addr)
609 WORD op = (inst >> 9) & 0x01;
610 WORD immediate = (inst >> 10) & 0x01;
612 dbg_printf("\n\t%s\t%s, %s, ", op ? "sub" : "add",
613 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07]);
614 if (immediate)
615 dbg_printf("#%d", (inst >> 6) & 0x07);
616 else
617 dbg_printf("%s", tbl_regs[(inst >> 6) & 0x07]);
618 return 0;
621 static WORD thumb_disasm_movshift(WORD inst, ADDRESS64 *addr)
623 WORD op = (inst >> 11) & 0x03;
624 dbg_printf("\n\t%s\t%s, %s, #%u", tbl_shifts[op],
625 tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], (inst >> 6) & 0x1f);
626 return 0;
629 static UINT thumb2_disasm_branchlinked(UINT inst, ADDRESS64 *addr)
631 UINT offset = (((inst & 0x07ff0000) >> 4) | ((inst & 0x000007ff) << 1)) + 4;
633 dbg_printf("\n\tbl\t");
634 db_printsym(addr->Offset + offset);
635 return 0;
638 static UINT thumb2_disasm_misc(UINT inst, ADDRESS64 *addr)
640 WORD op1 = (inst >> 20) & 0x03;
641 WORD op2 = (inst >> 4) & 0x03;
643 if (get_nibble(inst, 4) != get_nibble(inst, 0))
644 return inst;
646 if (op1 == 3 && op2 == 0)
648 dbg_printf("\n\tclz\t%s, %s", tbl_regs[get_nibble(inst, 2)], tbl_regs[get_nibble(inst, 0)]);
649 return 0;
652 if (op1 == 1)
654 switch (op2)
656 case 0:
657 dbg_printf("\n\trev\t");
658 break;
659 case 1:
660 dbg_printf("\n\trev16\t");
661 break;
662 case 2:
663 dbg_printf("\n\trbit\t");
664 break;
665 case 3:
666 dbg_printf("\n\trevsh\t");
667 break;
669 dbg_printf("%s, %s", tbl_regs[get_nibble(inst, 2)], tbl_regs[get_nibble(inst, 0)]);
670 return 0;
673 return inst;
676 static UINT thumb2_disasm_mul(UINT inst, ADDRESS64 *addr)
678 WORD op1 = (inst >> 20) & 0x07;
679 WORD op2 = (inst >> 4) & 0x03;
681 if (op1)
682 return inst;
684 if (op2 == 0 && get_nibble(inst, 3) != 0xf)
686 dbg_printf("\n\tmla\t%s, %s, %s, %s", tbl_regs[get_nibble(inst, 2)],
687 tbl_regs[get_nibble(inst, 4)],
688 tbl_regs[get_nibble(inst, 0)],
689 tbl_regs[get_nibble(inst, 3)]);
690 return 0;
693 if (op2 == 0 && get_nibble(inst, 3) == 0xf)
695 dbg_printf("\n\tmul\t%s, %s, %s", tbl_regs[get_nibble(inst, 2)],
696 tbl_regs[get_nibble(inst, 4)],
697 tbl_regs[get_nibble(inst, 0)]);
698 return 0;
701 if (op2 == 1)
703 dbg_printf("\n\tmls\t%s, %s, %s, %s", tbl_regs[get_nibble(inst, 2)],
704 tbl_regs[get_nibble(inst, 4)],
705 tbl_regs[get_nibble(inst, 0)],
706 tbl_regs[get_nibble(inst, 3)]);
707 return 0;
710 return inst;
713 static UINT thumb2_disasm_longmuldiv(UINT inst, ADDRESS64 *addr)
715 WORD op1 = (inst >> 20) & 0x07;
716 WORD op2 = (inst >> 4) & 0x0f;
718 if (op2 == 0)
720 switch (op1)
722 case 0:
723 dbg_printf("\n\tsmull\t");
724 break;
725 case 2:
726 dbg_printf("\n\tumull\t");
727 break;
728 case 4:
729 dbg_printf("\n\tsmlal\t");
730 break;
731 case 6:
732 dbg_printf("\n\tumlal\t");
733 break;
734 default:
735 return inst;
737 dbg_printf("%s, %s, %s, %s", tbl_regs[get_nibble(inst, 3)], tbl_regs[get_nibble(inst, 2)],
738 tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
739 return 0;
742 if (op2 == 0xffff)
744 switch (op1)
746 case 1:
747 dbg_printf("\n\tsdiv\t");
748 break;
749 case 3:
750 dbg_printf("\n\tudiv\t");
751 break;
752 default:
753 return inst;
755 dbg_printf("%s, %s, %s", tbl_regs[get_nibble(inst, 2)], tbl_regs[get_nibble(inst, 4)],
756 tbl_regs[get_nibble(inst, 0)]);
757 return 0;
760 return inst;
763 static UINT thumb2_disasm_coprocmov1(UINT inst, ADDRESS64 *addr)
765 WORD opc1 = (inst >> 21) & 0x07;
766 WORD opc2 = (inst >> 5) & 0x07;
768 if (opc2)
769 dbg_printf("\n\t%s%s\tp%u, #%u, %s, cr%u, cr%u, #%u", (inst & 0x00100000)?"mrc":"mcr",
770 (inst & 0x10000000)?"2":"", get_nibble(inst, 2), opc1,
771 tbl_regs[get_nibble(inst, 3)], get_nibble(inst, 4), get_nibble(inst, 0), opc2);
772 else
773 dbg_printf("\n\t%s%s\tp%u, #%u, %s, cr%u, cr%u", (inst & 0x00100000)?"mrc":"mcr",
774 (inst & 0x10000000)?"2":"", get_nibble(inst, 2), opc1,
775 tbl_regs[get_nibble(inst, 3)], get_nibble(inst, 4), get_nibble(inst, 0));
777 return 0;
780 struct inst_arm
782 UINT mask;
783 UINT pattern;
784 UINT (*func)(UINT, ADDRESS64*);
787 static const struct inst_arm tbl_arm[] = {
788 { 0x0e000000, 0x0a000000, arm_disasm_branch },
789 { 0x0fc000f0, 0x00000090, arm_disasm_mul },
790 { 0x0f8000f0, 0x00800090, arm_disasm_longmul },
791 { 0x0fb00ff0, 0x01000090, arm_disasm_swp },
792 { 0x0e000090, 0x00000090, arm_disasm_halfwordtrans },
793 { 0x0ffffff0, 0x012fff00, arm_disasm_branchreg },
794 { 0x0ffffff0, 0x012fff10, arm_disasm_branchxchg },
795 { 0x0fbf0fff, 0x010f0000, arm_disasm_mrstrans },
796 { 0x0dbef000, 0x0128f000, arm_disasm_msrtrans },
797 { 0x0fb00000, 0x03000000, arm_disasm_wordmov },
798 { 0x0fffffff, 0x0320f000, arm_disasm_nop },
799 { 0x0c000000, 0x00000000, arm_disasm_dataprocessing },
800 { 0x0c000000, 0x04000000, arm_disasm_singletrans },
801 { 0x0e000000, 0x08000000, arm_disasm_blocktrans },
802 { 0x0f000000, 0x0f000000, arm_disasm_swi },
803 { 0x0f000010, 0x0e000010, arm_disasm_coproctrans },
804 { 0x0f000010, 0x0e000000, arm_disasm_coprocdataop },
805 { 0x0e000000, 0x0c000000, arm_disasm_coprocdatatrans },
806 { 0x00000000, 0x00000000, NULL }
809 struct inst_thumb16
811 WORD mask;
812 WORD pattern;
813 WORD (*func)(WORD, ADDRESS64*);
816 static const struct inst_thumb16 tbl_thumb16[] = {
817 { 0xfc00, 0x4400, thumb_disasm_hireg },
818 { 0xfc00, 0x4000, thumb_disasm_aluop },
819 { 0xf600, 0xb400, thumb_disasm_pushpop },
820 { 0xf000, 0xc000, thumb_disasm_blocktrans },
821 { 0xf000, 0xd000, thumb_disasm_condbranch },
822 { 0xf800, 0xe000, thumb_disasm_uncondbranch },
823 { 0xf000, 0xa000, thumb_disasm_loadadr },
824 { 0xf800, 0x4800, thumb_disasm_ldrpcrel },
825 { 0xf000, 0x9000, thumb_disasm_ldrsprel },
826 { 0xff00, 0xb000, thumb_disasm_addsprel },
827 { 0xe000, 0x6000, thumb_disasm_ldrimm },
828 { 0xf000, 0x8000, thumb_disasm_ldrhimm },
829 { 0xf200, 0x5000, thumb_disasm_ldrreg },
830 { 0xf200, 0x5200, thumb_disasm_ldrsreg },
831 { 0xe000, 0x2000, thumb_disasm_immop },
832 { 0xff00, 0xdf00, thumb_disasm_swi },
833 { 0xff00, 0xbf00, thumb_disasm_nop },
834 { 0xf800, 0x1800, thumb_disasm_addsub },
835 { 0xe000, 0x0000, thumb_disasm_movshift },
836 { 0x0000, 0x0000, NULL }
839 static const struct inst_arm tbl_thumb32[] = {
840 { 0xf800f800, 0xf000f800, thumb2_disasm_branchlinked },
841 { 0xffc0f0c0, 0xfa80f080, thumb2_disasm_misc },
842 { 0xff8000c0, 0xfb000000, thumb2_disasm_mul },
843 { 0xff8000f0, 0xfb800000, thumb2_disasm_longmuldiv },
844 { 0xff8000f0, 0xfb8000f0, thumb2_disasm_longmuldiv },
845 { 0xef100010, 0xee100010, thumb2_disasm_coprocmov1 },
846 { 0xef100010, 0xee000010, thumb2_disasm_coprocmov1 },
847 { 0x00000000, 0x00000000, NULL }
850 /***********************************************************************
851 * disasm_one_insn
853 * Disassemble instruction at 'addr'. addr is changed to point to the
854 * start of the next instruction.
856 void be_arm_disasm_one_insn(ADDRESS64 *addr, int display)
858 struct inst_arm *a_ptr = (struct inst_arm *)&tbl_arm;
859 struct inst_thumb16 *t_ptr = (struct inst_thumb16 *)&tbl_thumb16;
860 struct inst_arm *t2_ptr = (struct inst_arm *)&tbl_thumb32;
861 UINT inst;
862 WORD tinst;
863 int size;
864 int matched = 0;
866 char tmp[64];
867 DWORD_PTR* pval;
869 if (!memory_get_register(CV_ARM_CPSR, &pval, tmp, sizeof(tmp)))
870 dbg_printf("\n\tmemory_get_register failed: %s", tmp);
871 else
872 db_disasm_thumb = (*pval & 0x20) != 0;
874 db_display = display;
876 if (!db_disasm_thumb)
878 size = ARM_INSN_SIZE;
879 inst = db_get_inst( memory_to_linear_addr(addr), size );
880 while (a_ptr->func) {
881 if ((inst & a_ptr->mask) == a_ptr->pattern) {
882 matched = 1;
883 break;
885 a_ptr++;
888 if (!matched) {
889 dbg_printf("\n\tUnknown ARM Instruction: %08x", inst);
890 addr->Offset += size;
892 else
894 if (!a_ptr->func(inst, addr))
895 addr->Offset += size;
897 return;
899 else
901 WORD *taddr = memory_to_linear_addr(addr);
902 tinst = db_get_inst( taddr, THUMB_INSN_SIZE );
903 switch (tinst & 0xf800)
905 case 0xe800:
906 case 0xf000:
907 case 0xf800:
908 size = THUMB2_INSN_SIZE;
909 taddr++;
910 inst = db_get_inst( taddr, THUMB_INSN_SIZE );
911 inst |= (tinst << 16);
913 while (t2_ptr->func) {
914 if ((inst & t2_ptr->mask) == t2_ptr->pattern) {
915 matched = 1;
916 break;
918 t2_ptr++;
921 if (!matched) {
922 dbg_printf("\n\tUnknown Thumb2 Instruction: %08x", inst);
923 addr->Offset += size;
925 else
927 if (!t2_ptr->func(inst, addr))
928 addr->Offset += size;
930 return;
931 default:
932 break;
935 size = THUMB_INSN_SIZE;
936 while (t_ptr->func) {
937 if ((tinst & t_ptr->mask) == t_ptr->pattern) {
938 matched = 1;
939 break;
941 t_ptr++;
944 if (!matched) {
945 dbg_printf("\n\tUnknown Thumb Instruction: %04x", tinst);
946 addr->Offset += size;
948 else
950 if (!t_ptr->func(tinst, addr))
951 addr->Offset += size;
953 return;
957 static unsigned be_arm_get_addr(HANDLE hThread, const CONTEXT* ctx,
958 enum be_cpu_addr bca, ADDRESS64* addr)
960 switch (bca)
962 case be_cpu_addr_pc:
963 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Pc);
964 case be_cpu_addr_stack:
965 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Sp);
966 case be_cpu_addr_frame:
967 return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Fp);
969 return FALSE;
972 static unsigned be_arm_get_register_info(int regno, enum be_cpu_addr* kind)
974 switch (regno)
976 case CV_ARM_PC: *kind = be_cpu_addr_pc; return TRUE;
977 case CV_ARM_R0 + 11: *kind = be_cpu_addr_frame; return TRUE;
978 case CV_ARM_SP: *kind = be_cpu_addr_stack; return TRUE;
980 return FALSE;
983 static void be_arm_single_step(CONTEXT* ctx, unsigned enable)
985 dbg_printf("be_arm_single_step: not done\n");
988 static void be_arm_print_context(HANDLE hThread, const CONTEXT* ctx, int all_regs)
990 static const char condflags[] = "NZCV";
991 int i;
992 char buf[8];
994 switch (ctx->Cpsr & 0x1F)
996 case 0: strcpy(buf, "User26"); break;
997 case 1: strcpy(buf, "FIQ26"); break;
998 case 2: strcpy(buf, "IRQ26"); break;
999 case 3: strcpy(buf, "SVC26"); break;
1000 case 16: strcpy(buf, "User"); break;
1001 case 17: strcpy(buf, "FIQ"); break;
1002 case 18: strcpy(buf, "IRQ"); break;
1003 case 19: strcpy(buf, "SVC"); break;
1004 case 23: strcpy(buf, "ABT"); break;
1005 case 27: strcpy(buf, "UND"); break;
1006 default: strcpy(buf, "UNKNWN"); break;
1009 dbg_printf("Register dump:\n");
1010 dbg_printf("%s %s Mode\n", (ctx->Cpsr & 0x20) ? "Thumb" : "ARM", buf);
1012 strcpy(buf, condflags);
1013 for (i = 0; buf[i]; i++)
1014 if (!((ctx->Cpsr >> 26) & (1 << (sizeof(condflags) - i))))
1015 buf[i] = '-';
1017 dbg_printf(" Pc:%04x Sp:%04x Lr:%04x Cpsr:%04x(%s)\n",
1018 ctx->Pc, ctx->Sp, ctx->Lr, ctx->Cpsr, buf);
1019 dbg_printf(" r0:%04x r1:%04x r2:%04x r3:%04x\n",
1020 ctx->R0, ctx->R1, ctx->R2, ctx->R3);
1021 dbg_printf(" r4:%04x r5:%04x r6:%04x r7:%04x r8:%04x\n",
1022 ctx->R4, ctx->R5, ctx->R6, ctx->R7, ctx->R8 );
1023 dbg_printf(" r9:%04x r10:%04x Fp:%04x Ip:%04x\n",
1024 ctx->R9, ctx->R10, ctx->Fp, ctx->Ip );
1026 if (all_regs) dbg_printf( "Floating point ARM dump not implemented\n" );
1029 static void be_arm_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
1033 static struct dbg_internal_var be_arm_ctx[] =
1035 {CV_ARM_R0 + 0, "r0", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R0), dbg_itype_unsigned_int},
1036 {CV_ARM_R0 + 1, "r1", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R1), dbg_itype_unsigned_int},
1037 {CV_ARM_R0 + 2, "r2", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R2), dbg_itype_unsigned_int},
1038 {CV_ARM_R0 + 3, "r3", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R3), dbg_itype_unsigned_int},
1039 {CV_ARM_R0 + 4, "r4", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R4), dbg_itype_unsigned_int},
1040 {CV_ARM_R0 + 5, "r5", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R5), dbg_itype_unsigned_int},
1041 {CV_ARM_R0 + 6, "r6", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R6), dbg_itype_unsigned_int},
1042 {CV_ARM_R0 + 7, "r7", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R7), dbg_itype_unsigned_int},
1043 {CV_ARM_R0 + 8, "r8", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R8), dbg_itype_unsigned_int},
1044 {CV_ARM_R0 + 9, "r9", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R9), dbg_itype_unsigned_int},
1045 {CV_ARM_R0 + 10, "r10", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R10), dbg_itype_unsigned_int},
1046 {CV_ARM_R0 + 11, "r11", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Fp), dbg_itype_unsigned_int},
1047 {CV_ARM_R0 + 12, "r12", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ip), dbg_itype_unsigned_int},
1048 {CV_ARM_SP, "sp", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Sp), dbg_itype_unsigned_int},
1049 {CV_ARM_LR, "lr", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Lr), dbg_itype_unsigned_int},
1050 {CV_ARM_PC, "pc", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Pc), dbg_itype_unsigned_int},
1051 {CV_ARM_CPSR, "cpsr", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Cpsr), dbg_itype_unsigned_int},
1052 {0, NULL, 0, dbg_itype_none}
1055 static unsigned be_arm_is_step_over_insn(const void* insn)
1057 dbg_printf("be_arm_is_step_over_insn: not done\n");
1058 return FALSE;
1061 static unsigned be_arm_is_function_return(const void* insn)
1063 dbg_printf("be_arm_is_function_return: not done\n");
1064 return FALSE;
1067 static unsigned be_arm_is_break_insn(const void* insn)
1069 dbg_printf("be_arm_is_break_insn: not done\n");
1070 return FALSE;
1073 static unsigned be_arm_is_func_call(const void* insn, ADDRESS64* callee)
1075 return FALSE;
1078 static unsigned be_arm_is_jump(const void* insn, ADDRESS64* jumpee)
1080 return FALSE;
1083 static unsigned be_arm_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
1084 CONTEXT* ctx, enum be_xpoint_type type,
1085 void* addr, unsigned long* val, unsigned size)
1087 SIZE_T sz;
1089 switch (type)
1091 case be_xpoint_break:
1092 if (!size) return 0;
1093 if (!pio->read(hProcess, addr, val, 4, &sz) || sz != 4) return 0;
1094 default:
1095 dbg_printf("Unknown/unsupported bp type %c\n", type);
1096 return 0;
1098 return 1;
1101 static unsigned be_arm_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
1102 CONTEXT* ctx, enum be_xpoint_type type,
1103 void* addr, unsigned long val, unsigned size)
1105 SIZE_T sz;
1107 switch (type)
1109 case be_xpoint_break:
1110 if (!size) return 0;
1111 if (!pio->write(hProcess, addr, &val, 4, &sz) || sz == 4) return 0;
1112 break;
1113 default:
1114 dbg_printf("Unknown/unsupported bp type %c\n", type);
1115 return 0;
1117 return 1;
1120 static unsigned be_arm_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
1122 dbg_printf("be_arm_is_watchpoint_set: not done\n");
1123 return FALSE;
1126 static void be_arm_clear_watchpoint(CONTEXT* ctx, unsigned idx)
1128 dbg_printf("be_arm_clear_watchpoint: not done\n");
1131 static int be_arm_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
1133 INT step = (ctx->Cpsr & 0x20) ? 2 : 4;
1135 if (way)
1137 ctx->Pc -= step;
1138 return -step;
1140 ctx->Pc += step;
1141 return step;
1144 static int be_arm_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
1145 unsigned ext_sign, LONGLONG* ret)
1147 if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE;
1149 memset(ret, 0, sizeof(*ret)); /* clear unread bytes */
1150 /* FIXME: this assumes that debuggee and debugger use the same
1151 * integral representation
1153 if (!memory_read_value(lvalue, size, ret)) return FALSE;
1155 /* propagate sign information */
1156 if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0)
1158 ULONGLONG neg = -1;
1159 *ret |= neg << (size * 8);
1161 return TRUE;
1164 static int be_arm_fetch_float(const struct dbg_lvalue* lvalue, unsigned size,
1165 long double* ret)
1167 char tmp[sizeof(long double)];
1169 /* FIXME: this assumes that debuggee and debugger use the same
1170 * representation for reals
1172 if (!memory_read_value(lvalue, size, tmp)) return FALSE;
1174 switch (size)
1176 case sizeof(float): *ret = *(float*)tmp; break;
1177 case sizeof(double): *ret = *(double*)tmp; break;
1178 default: return FALSE;
1180 return TRUE;
1183 static int be_arm_store_integer(const struct dbg_lvalue* lvalue, unsigned size,
1184 unsigned is_signed, LONGLONG val)
1186 /* this is simple if we're on a little endian CPU */
1187 return memory_write_value(lvalue, size, &val);
1190 struct backend_cpu be_arm =
1192 IMAGE_FILE_MACHINE_ARMV7,
1194 be_cpu_linearize,
1195 be_cpu_build_addr,
1196 be_arm_get_addr,
1197 be_arm_get_register_info,
1198 be_arm_single_step,
1199 be_arm_print_context,
1200 be_arm_print_segment_info,
1201 be_arm_ctx,
1202 be_arm_is_step_over_insn,
1203 be_arm_is_function_return,
1204 be_arm_is_break_insn,
1205 be_arm_is_func_call,
1206 be_arm_is_jump,
1207 be_arm_disasm_one_insn,
1208 be_arm_insert_Xpoint,
1209 be_arm_remove_Xpoint,
1210 be_arm_is_watchpoint_set,
1211 be_arm_clear_watchpoint,
1212 be_arm_adjust_pc_for_break,
1213 be_arm_fetch_integer,
1214 be_arm_fetch_float,
1215 be_arm_store_integer,
1217 #endif