winedbg: Use better register names for ARM disassembling.
[wine] / programs / winedbg / be_arm.c
1 /*
2  * Debugger ARM specific functions
3  *
4  * Copyright 2000-2003 Marcus Meissner
5  *                2004 Eric Pouech
6  *           2010-2012 AndrĂ© Hentschel
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "debugger.h"
24
25 #if defined(__arm__) && !defined(__ARMEB__)
26
27 /*
28  * Switch to disassemble Thumb code.
29  */
30 static BOOL db_disasm_thumb = FALSE;
31
32 /*
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.
36  */
37 static BOOL db_display = FALSE;
38
39 #define ARM_INSN_SIZE    4
40 #define THUMB_INSN_SIZE  2
41
42 #define ROR32(n, r) (((n) >> (r)) | ((n) << (32 - (r))))
43
44 #define get_cond(ins)           tbl_cond[(ins >> 28) & 0x0f]
45 #define get_nibble(ins, num)    ((ins >> (num * 4)) & 0x0f)
46
47 static char const tbl_regs[][4] = {
48     "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
49     "fp", "ip", "sp", "lr", "pc", "cpsr"
50 };
51
52 static char const tbl_addrmode[][3] = {
53     "da", "ia", "db", "ib"
54 };
55
56 static char const tbl_cond[][3] = {
57     "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", ""
58 };
59
60 static char const tbl_dataops[][4] = {
61     "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr",
62     "mov", "bic", "mvn"
63 };
64
65 static char const tbl_hiops_t[][4] = {
66 "add", "cmp", "mov", "bx"
67 };
68
69 static char const tbl_immops_t[][4] = {
70 "mov", "cmp", "add", "sub"
71 };
72
73 static UINT db_get_inst(void* addr, int size)
74 {
75     UINT result = 0;
76     char buffer[4];
77
78     if (dbg_read_memory(addr, buffer, size))
79     {
80         switch (size)
81         {
82         case 4:
83             result = *(UINT*)buffer;
84             break;
85         case 2:
86             result = *(WORD*)buffer;
87             break;
88         }
89     }
90     return result;
91 }
92
93 static UINT arm_disasm_branch(UINT inst)
94 {
95     short link = (inst >> 24) & 0x01;
96     int offset = (inst << 2) & 0x03ffffff;
97
98     if (offset & 0x02000000) offset |= 0xfc000000;
99     offset += 8;
100
101     dbg_printf("\n\tb%s%s\t#%d", link ? "l" : "", get_cond(inst), offset);
102     return 0;
103 }
104
105 static UINT arm_disasm_dataprocessing(UINT inst)
106 {
107     short condcodes = (inst >> 20) & 0x01;
108     short opcode    = (inst >> 21) & 0x0f;
109     short immediate = (inst >> 25) & 0x01;
110     short no_op1    = (opcode & 0x0d) == 0x0d;
111
112     /* check for nop */
113     if (get_nibble(inst, 3) == 15 /* r15 */ && condcodes == 0 &&
114         opcode >= 8 /* tst */ && opcode <= 11 /* cmn */)
115     {
116         dbg_printf("\n\tnop");
117         return 0;
118     }
119
120     dbg_printf("\n\t%s%s%s", tbl_dataops[opcode], condcodes ? "s" : "", get_cond(inst));
121     dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
122     if (no_op1)
123     {
124         if (immediate)
125             dbg_printf("#%u", ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
126         else
127             dbg_printf("%s", tbl_regs[get_nibble(inst, 0)]);
128     }
129     else
130     {
131         if (immediate)
132             dbg_printf("%s, #%u", tbl_regs[get_nibble(inst, 4)],
133                        ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
134         else
135             dbg_printf("%s, %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
136     }
137     return 0;
138 }
139
140 static UINT arm_disasm_singletrans(UINT inst)
141 {
142     short load      = (inst >> 20) & 0x01;
143     short writeback = (inst >> 21) & 0x01;
144     short byte      = (inst >> 22) & 0x01;
145     short direction = (inst >> 23) & 0x01;
146     short indexing  = (inst >> 24) & 0x01;
147     short immediate = !((inst >> 25) & 0x01);
148     short offset    = inst & 0x0fff;
149
150     if (!direction) offset *= -1;
151
152     dbg_printf("\n\t%s%s%s%s", load ? "ldr" : "str", byte ? "b" : "", writeback ? "t" : "",
153                get_cond(inst));
154     dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
155     if (indexing)
156     {
157         if (immediate)
158             dbg_printf("[%s, #%d]", tbl_regs[get_nibble(inst, 4)], offset);
159         else
160             dbg_printf("[%s, %s]", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
161     }
162     else
163     {
164         if (immediate)
165             dbg_printf("[%s], #%d", tbl_regs[get_nibble(inst, 4)], offset);
166         else
167             dbg_printf("[%s], %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
168     }
169     return 0;
170 }
171
172 static UINT arm_disasm_halfwordtrans(UINT inst)
173 {
174     short halfword  = (inst >> 5)  & 0x01;
175     short sign      = (inst >> 6)  & 0x01;
176     short load      = (inst >> 20) & 0x01;
177     short writeback = (inst >> 21) & 0x01;
178     short immediate = (inst >> 22) & 0x01;
179     short direction = (inst >> 23) & 0x01;
180     short indexing  = (inst >> 24) & 0x01;
181     short offset    = ((inst >> 4) & 0xf0) + (inst & 0x0f);
182
183     if (!direction) offset *= -1;
184
185     dbg_printf("\n\t%s%s%s%s%s", load ? "ldr" : "str", sign ? "s" : "",
186                halfword ? "h" : (sign ? "b" : ""), writeback ? "t" : "", get_cond(inst));
187     dbg_printf("\t%s, ", tbl_regs[get_nibble(inst, 3)]);
188     if (indexing)
189     {
190         if (immediate)
191             dbg_printf("[%s, #%d]", tbl_regs[get_nibble(inst, 4)], offset);
192         else
193             dbg_printf("[%s, %s]", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
194     }
195     else
196     {
197         if (immediate)
198             dbg_printf("[%s], #%d", tbl_regs[get_nibble(inst, 4)], offset);
199         else
200             dbg_printf("[%s], %s", tbl_regs[get_nibble(inst, 4)], tbl_regs[get_nibble(inst, 0)]);
201     }
202     return 0;
203 }
204
205 static UINT arm_disasm_blocktrans(UINT inst)
206 {
207     short load      = (inst >> 20) & 0x01;
208     short writeback = (inst >> 21) & 0x01;
209     short psr       = (inst >> 22) & 0x01;
210     short addrmode  = (inst >> 23) & 0x03;
211     short i;
212     short last=15;
213     for (i=15;i>=0;i--)
214         if ((inst>>i) & 1)
215         {
216             last = i;
217             break;
218         }
219
220     dbg_printf("\n\t%s%s%s\t%s%s, {", load ? "ldm" : "stm", tbl_addrmode[addrmode], get_cond(inst),
221                tbl_regs[get_nibble(inst, 4)], writeback ? "!" : "");
222     for (i=0;i<=15;i++)
223         if ((inst>>i) & 1)
224         {
225             if (i == last) dbg_printf("%s", tbl_regs[i]);
226             else dbg_printf("%s, ", tbl_regs[i]);
227         }
228     dbg_printf("}%s", psr ? "^" : "");
229     return 0;
230 }
231
232 static UINT arm_disasm_swi(UINT inst)
233 {
234     UINT comment = inst & 0x00ffffff;
235     dbg_printf("\n\tswi%s\t#%d", get_cond(inst), comment);
236     return 0;
237 }
238
239 static UINT arm_disasm_coproctrans(UINT inst)
240 {
241     WORD CRm    = inst & 0x0f;
242     WORD CP     = (inst >> 5)  & 0x07;
243     WORD CPnum  = (inst >> 8)  & 0x0f;
244     WORD CRn    = (inst >> 16) & 0x0f;
245     WORD load   = (inst >> 20) & 0x01;
246     WORD CP_Opc = (inst >> 21) & 0x07;
247
248     dbg_printf("\n\t%s%s\t%u, %u, %s, cr%u, cr%u, {%u}", load ? "mrc" : "mcr", get_cond(inst), CPnum,
249                CP, tbl_regs[get_nibble(inst, 3)], CRn, CRm, CP_Opc);
250     return 0;
251 }
252
253 static UINT arm_disasm_coprocdataop(UINT inst)
254 {
255     WORD CRm    = inst & 0x0f;
256     WORD CP     = (inst >> 5)  & 0x07;
257     WORD CPnum  = (inst >> 8)  & 0x0f;
258     WORD CRd    = (inst >> 12) & 0x0f;
259     WORD CRn    = (inst >> 16) & 0x0f;
260     WORD CP_Opc = (inst >> 20) & 0x0f;
261
262     dbg_printf("\n\tcdp%s\t%u, %u, cr%u, cr%u, cr%u, {%u}", get_cond(inst),
263                CPnum, CP, CRd, CRn, CRm, CP_Opc);
264     return 0;
265 }
266
267 static UINT arm_disasm_coprocdatatrans(UINT inst)
268 {
269     WORD CPnum  = (inst >> 8)  & 0x0f;
270     WORD CRd    = (inst >> 12) & 0x0f;
271     WORD load      = (inst >> 20) & 0x01;
272     WORD writeback = (inst >> 21) & 0x01;
273     WORD translen  = (inst >> 22) & 0x01;
274     WORD direction = (inst >> 23) & 0x01;
275     WORD indexing  = (inst >> 24) & 0x01;
276     short offset    = (inst & 0xff) << 2;
277
278     if (!direction) offset *= -1;
279
280     dbg_printf("\n\t%s%s%s", load ? "ldc" : "stc", translen ? "l" : "", get_cond(inst));
281     if (indexing)
282         dbg_printf("\t%u, cr%u, [%s, #%d]%s", CPnum, CRd, tbl_regs[get_nibble(inst, 4)], offset, writeback?"!":"");
283     else
284         dbg_printf("\t%u, cr%u, [%s], #%d", CPnum, CRd, tbl_regs[get_nibble(inst, 4)], offset);
285     return 0;
286 }
287
288 static WORD thumb_disasm_hireg(WORD inst, ADDRESS64 *addr)
289 {
290     short dst = inst & 0x07;
291     short src = (inst >> 3) & 0x07;
292     short h2  = (inst >> 6) & 0x01;
293     short h1  = (inst >> 7) & 0x01;
294     short op  = (inst >> 8) & 0x03;
295
296     if (h1) dst += 8;
297     if (h2) src += 8;
298
299     if (op == 3)
300         dbg_printf("\n\tb%sx\t%s", h1?"l":"", tbl_regs[src]);
301     else
302         dbg_printf("\n\t%s\t%s, %s", tbl_hiops_t[op], tbl_regs[dst], tbl_regs[src]);
303
304     return 0;
305 }
306
307 static WORD thumb_disasm_blocktrans(WORD inst, ADDRESS64 *addr)
308 {
309     short lrpc = (inst >> 8)  & 0x01;
310     short load = (inst >> 11) & 0x01;
311     short i;
312     short last;
313
314     for (i=7;i>=0;i--)
315         if ((inst>>i) & 1) break;
316     last = i;
317
318     dbg_printf("\n\t%s\t{", load ? "pop" : "push");
319
320     for (i=0;i<=7;i++)
321         if ((inst>>i) & 1)
322         {
323             if (i == last) dbg_printf("%s", tbl_regs[i]);
324             else dbg_printf("%s, ", tbl_regs[i]);
325         }
326     if (lrpc)
327         dbg_printf(", %s", load ? "pc" : "lr");
328
329     dbg_printf("}");
330     return 0;
331 }
332
333 static WORD thumb_disasm_longbl(WORD inst, ADDRESS64 *addr)
334 {
335     WORD inst2;
336     UINT offset = (inst & 0x07ff) << 12;
337
338     addr->Offset += 2;
339     inst2 = db_get_inst( memory_to_linear_addr(addr), 2 );
340     if (!((inst2 & 0xf800) == 0xf800)) return inst;
341
342     offset += (inst2 & 0x07ff) << 1;
343     dbg_printf("\tbl\t%08x", offset);
344     return 0;
345 }
346
347 static WORD thumb_disasm_swi(WORD inst, ADDRESS64 *addr)
348 {
349     WORD comment = inst & 0x00ff;
350     dbg_printf("\n\tswi\t#%d", comment);
351     return 0;
352 }
353
354 static WORD thumb_disasm_nop(WORD inst, ADDRESS64 *addr)
355 {
356     dbg_printf("\n\tnop");
357     return 0;
358 }
359
360 static WORD thumb_disasm_ldrpcrel(WORD inst, ADDRESS64 *addr)
361 {
362     WORD offset = (inst & 0xff) << 2;
363     dbg_printf("\n\tldr\t%s, [pc, #%u]", tbl_regs[(inst >> 8) & 0x07], offset);
364     return 0;
365 }
366
367 static WORD thumb_disasm_ldrsprel(WORD inst, ADDRESS64 *addr)
368 {
369     WORD offset = (inst & 0xff) << 2;
370     dbg_printf("\n\t%s\t%s, [sp, #%u]", (inst & 0x0800)?"ldr":"str", tbl_regs[(inst >> 8) & 0x07], offset);
371     return 0;
372 }
373
374 static WORD thumb_disasm_ldrimm(WORD inst, ADDRESS64 *addr)
375 {
376     WORD offset = (inst & 0x07c0) >> 6;
377     dbg_printf("\n\t%s%s\t%s, [%s, #%u]", (inst & 0x0800)?"ldr":"str", (inst & 0x1000)?"b":"",
378                tbl_regs[inst & 0x07], tbl_regs[(inst >> 3) & 0x07], (inst & 0x1000)?offset:(offset << 2));
379     return 0;
380 }
381
382 static WORD thumb_disasm_immop(WORD inst, ADDRESS64 *addr)
383 {
384     WORD op = (inst >> 11) & 0x03;
385     dbg_printf("\n\t%s\t%s, #%u", tbl_immops_t[op], tbl_regs[(inst >> 8) & 0x07], inst & 0xff);
386     return 0;
387 }
388
389 struct inst_arm
390 {
391         UINT mask;
392         UINT pattern;
393         UINT (*func)(UINT);
394 };
395
396 static const struct inst_arm tbl_arm[] = {
397     { 0x0e000000, 0x0a000000, arm_disasm_branch },
398     { 0x0c000000, 0x00000000, arm_disasm_dataprocessing },
399     { 0x0c000000, 0x04000000, arm_disasm_singletrans },
400     { 0x0e000090, 0x00000090, arm_disasm_halfwordtrans },
401     { 0x0e000000, 0x08000000, arm_disasm_blocktrans },
402     { 0x0f000000, 0x0f000000, arm_disasm_swi },
403     { 0x0f000010, 0x0e000010, arm_disasm_coproctrans },
404     { 0x0f000010, 0x0e000000, arm_disasm_coprocdataop },
405     { 0x0e000000, 0x0c000000, arm_disasm_coprocdatatrans },
406     { 0x00000000, 0x00000000, NULL }
407 };
408
409 struct inst_thumb16
410 {
411         WORD mask;
412         WORD pattern;
413         WORD (*func)(WORD, ADDRESS64*);
414 };
415
416 static const struct inst_thumb16 tbl_thumb16[] = {
417     { 0xfc00, 0x4400, thumb_disasm_hireg },
418     { 0xf600, 0xb400, thumb_disasm_blocktrans },
419     { 0xf800, 0xf000, thumb_disasm_longbl },
420     { 0xf800, 0x4800, thumb_disasm_ldrpcrel },
421     { 0xf000, 0x9000, thumb_disasm_ldrsprel },
422     { 0xe000, 0x6000, thumb_disasm_ldrimm },
423     { 0xe000, 0x2000, thumb_disasm_immop },
424     { 0xff00, 0xdf00, thumb_disasm_swi },
425     { 0xff00, 0xbf00, thumb_disasm_nop },
426     { 0x0000, 0x0000, NULL }
427 };
428
429 /***********************************************************************
430  *              disasm_one_insn
431  *
432  * Disassemble instruction at 'addr'. addr is changed to point to the
433  * start of the next instruction.
434  */
435 void be_arm_disasm_one_insn(ADDRESS64 *addr, int display)
436 {
437     struct inst_arm *a_ptr = (struct inst_arm *)&tbl_arm;
438     struct inst_thumb16 *t_ptr = (struct inst_thumb16 *)&tbl_thumb16;
439     UINT inst;
440     WORD tinst;
441     int size;
442     int matched = 0;
443
444     char tmp[64];
445     DWORD_PTR* pval;
446
447     if (!memory_get_register(CV_ARM_CPSR, &pval, tmp, sizeof(tmp)))
448         dbg_printf("\n\tmemory_get_register failed: %s\n",tmp);
449     else
450         db_disasm_thumb=(*pval & 0x20)?TRUE:FALSE;
451
452     if (db_disasm_thumb) size = THUMB_INSN_SIZE;
453     else size = ARM_INSN_SIZE;
454
455     db_display = display;
456     inst = db_get_inst( memory_to_linear_addr(addr), size );
457
458     if (!db_disasm_thumb)
459     {
460         while (a_ptr->func) {
461                 if ((inst & a_ptr->mask) ==  a_ptr->pattern) {
462                         matched = 1;
463                         break;
464                 }
465                 a_ptr++;
466         }
467
468         if (!matched) {
469                 dbg_printf("\n\tUnknown Instruction: %08x\n", inst);
470                 addr->Offset += size;
471                 return;
472         }
473         else
474         {
475             if (!a_ptr->func(inst))
476             {
477                 dbg_printf("\n");
478                 addr->Offset += size;
479             }
480             return;
481         }
482     }
483     else
484     {
485         tinst = inst;
486         while (t_ptr->func) {
487                 if ((tinst & t_ptr->mask) ==  t_ptr->pattern) {
488                         matched = 1;
489                         break;
490                 }
491                 t_ptr++;
492         }
493
494         if (!matched) {
495                 dbg_printf("\n\tUnknown Instruction: %08x\n", tinst);
496                 addr->Offset += size;
497                 return;
498         }
499         else
500         {
501             if (!t_ptr->func(tinst, addr))
502             {
503                 dbg_printf("\n");
504                 addr->Offset += size;
505             }
506         }
507         return;
508     }
509 }
510
511 static unsigned be_arm_get_addr(HANDLE hThread, const CONTEXT* ctx,
512                                 enum be_cpu_addr bca, ADDRESS64* addr)
513 {
514     switch (bca)
515     {
516     case be_cpu_addr_pc:
517         return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Pc);
518     case be_cpu_addr_stack:
519         return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Sp);
520     case be_cpu_addr_frame:
521         return be_cpu_build_addr(hThread, ctx, addr, 0, ctx->Fp);
522     }
523     return FALSE;
524 }
525
526 static unsigned be_arm_get_register_info(int regno, enum be_cpu_addr* kind)
527 {
528     switch (regno)
529     {
530     case CV_ARM_PC:  *kind = be_cpu_addr_pc; return TRUE;
531     case CV_ARM_R0 + 11: *kind = be_cpu_addr_frame; return TRUE;
532     case CV_ARM_SP:  *kind = be_cpu_addr_stack; return TRUE;
533     }
534     return FALSE;
535 }
536
537 static void be_arm_single_step(CONTEXT* ctx, unsigned enable)
538 {
539     dbg_printf("be_arm_single_step: not done\n");
540 }
541
542 static void be_arm_print_context(HANDLE hThread, const CONTEXT* ctx, int all_regs)
543 {
544     static const char condflags[] = "NZCV";
545     int i;
546     char        buf[8];
547
548     switch (ctx->Cpsr & 0x1F)
549     {
550     case 0:  strcpy(buf, "User26"); break;
551     case 1:  strcpy(buf, "FIQ26"); break;
552     case 2:  strcpy(buf, "IRQ26"); break;
553     case 3:  strcpy(buf, "SVC26"); break;
554     case 16: strcpy(buf, "User"); break;
555     case 17: strcpy(buf, "FIQ"); break;
556     case 18: strcpy(buf, "IRQ"); break;
557     case 19: strcpy(buf, "SVC"); break;
558     case 23: strcpy(buf, "ABT"); break;
559     case 27: strcpy(buf, "UND"); break;
560     default: strcpy(buf, "UNKNWN"); break;
561     }
562
563     dbg_printf("Register dump:\n");
564     dbg_printf("%s %s Mode\n", (ctx->Cpsr & 0x20) ? "Thumb" : "ARM", buf);
565
566     strcpy(buf, condflags);
567     for (i = 0; buf[i]; i++)
568         if (!((ctx->Cpsr >> 26) & (1 << (sizeof(condflags) - i))))
569             buf[i] = '-';
570
571     dbg_printf(" Pc:%04x Sp:%04x Lr:%04x Cpsr:%04x(%s)\n",
572                ctx->Pc, ctx->Sp, ctx->Lr, ctx->Cpsr, buf);
573     dbg_printf(" r0:%04x r1:%04x r2:%04x r3:%04x\n",
574                ctx->R0, ctx->R1, ctx->R2, ctx->R3);
575     dbg_printf(" r4:%04x r5:%04x  r6:%04x  r7:%04x r8:%04x\n",
576                ctx->R4, ctx->R5, ctx->R6, ctx->R7, ctx->R8 );
577     dbg_printf(" r9:%04x r10:%04x Fp:%04x Ip:%04x\n",
578                ctx->R9, ctx->R10, ctx->Fp, ctx->Ip );
579
580     if (all_regs) dbg_printf( "Floating point ARM dump not implemented\n" );
581 }
582
583 static void be_arm_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
584 {
585 }
586
587 static struct dbg_internal_var be_arm_ctx[] =
588 {
589     {CV_ARM_R0 +  0,    "r0",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R0),     dbg_itype_unsigned_int},
590     {CV_ARM_R0 +  1,    "r1",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R1),     dbg_itype_unsigned_int},
591     {CV_ARM_R0 +  2,    "r2",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R2),     dbg_itype_unsigned_int},
592     {CV_ARM_R0 +  3,    "r3",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R3),     dbg_itype_unsigned_int},
593     {CV_ARM_R0 +  4,    "r4",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R4),     dbg_itype_unsigned_int},
594     {CV_ARM_R0 +  5,    "r5",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R5),     dbg_itype_unsigned_int},
595     {CV_ARM_R0 +  6,    "r6",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R6),     dbg_itype_unsigned_int},
596     {CV_ARM_R0 +  7,    "r7",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R7),     dbg_itype_unsigned_int},
597     {CV_ARM_R0 +  8,    "r8",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R8),     dbg_itype_unsigned_int},
598     {CV_ARM_R0 +  9,    "r9",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R9),     dbg_itype_unsigned_int},
599     {CV_ARM_R0 +  10,   "r10",          (DWORD_PTR*)FIELD_OFFSET(CONTEXT, R10),    dbg_itype_unsigned_int},
600     {CV_ARM_R0 +  11,   "r11",          (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Fp),     dbg_itype_unsigned_int},
601     {CV_ARM_R0 +  12,   "r12",          (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ip),     dbg_itype_unsigned_int},
602     {CV_ARM_SP,         "sp",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Sp),     dbg_itype_unsigned_int},
603     {CV_ARM_LR,         "lr",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Lr),     dbg_itype_unsigned_int},
604     {CV_ARM_PC,         "pc",           (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Pc),     dbg_itype_unsigned_int},
605     {CV_ARM_CPSR,       "cpsr",         (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Cpsr),   dbg_itype_unsigned_int},
606     {0,                 NULL,           0,                                         dbg_itype_none}
607 };
608
609 static unsigned be_arm_is_step_over_insn(const void* insn)
610 {
611     dbg_printf("be_arm_is_step_over_insn: not done\n");
612     return FALSE;
613 }
614
615 static unsigned be_arm_is_function_return(const void* insn)
616 {
617     dbg_printf("be_arm_is_function_return: not done\n");
618     return FALSE;
619 }
620
621 static unsigned be_arm_is_break_insn(const void* insn)
622 {
623     dbg_printf("be_arm_is_break_insn: not done\n");
624     return FALSE;
625 }
626
627 static unsigned be_arm_is_func_call(const void* insn, ADDRESS64* callee)
628 {
629     return FALSE;
630 }
631
632 static unsigned be_arm_is_jump(const void* insn, ADDRESS64* jumpee)
633 {
634     return FALSE;
635 }
636
637 static unsigned be_arm_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
638                                      CONTEXT* ctx, enum be_xpoint_type type,
639                                      void* addr, unsigned long* val, unsigned size)
640 {
641     SIZE_T              sz;
642
643     switch (type)
644     {
645     case be_xpoint_break:
646         if (!size) return 0;
647         if (!pio->read(hProcess, addr, val, 4, &sz) || sz != 4) return 0;
648     default:
649         dbg_printf("Unknown/unsupported bp type %c\n", type);
650         return 0;
651     }
652     return 1;
653 }
654
655 static unsigned be_arm_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
656                                      CONTEXT* ctx, enum be_xpoint_type type,
657                                      void* addr, unsigned long val, unsigned size)
658 {
659     SIZE_T              sz;
660
661     switch (type)
662     {
663     case be_xpoint_break:
664         if (!size) return 0;
665         if (!pio->write(hProcess, addr, &val, 4, &sz) || sz == 4) return 0;
666         break;
667     default:
668         dbg_printf("Unknown/unsupported bp type %c\n", type);
669         return 0;
670     }
671     return 1;
672 }
673
674 static unsigned be_arm_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
675 {
676     dbg_printf("be_arm_is_watchpoint_set: not done\n");
677     return FALSE;
678 }
679
680 static void be_arm_clear_watchpoint(CONTEXT* ctx, unsigned idx)
681 {
682     dbg_printf("be_arm_clear_watchpoint: not done\n");
683 }
684
685 static int be_arm_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
686 {
687     INT step = (ctx->Cpsr & 0x20) ? 2 : 4;
688
689     if (way)
690     {
691         ctx->Pc -= step;
692         return -step;
693     }
694     ctx->Pc += step;
695     return step;
696 }
697
698 static int be_arm_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
699                                 unsigned ext_sign, LONGLONG* ret)
700 {
701     if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE;
702
703     memset(ret, 0, sizeof(*ret)); /* clear unread bytes */
704     /* FIXME: this assumes that debuggee and debugger use the same
705      * integral representation
706      */
707     if (!memory_read_value(lvalue, size, ret)) return FALSE;
708
709     /* propagate sign information */
710     if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0)
711     {
712         ULONGLONG neg = -1;
713         *ret |= neg << (size * 8);
714     }
715     return TRUE;
716 }
717
718 static int be_arm_fetch_float(const struct dbg_lvalue* lvalue, unsigned size,
719                               long double* ret)
720 {
721     char        tmp[sizeof(long double)];
722
723     /* FIXME: this assumes that debuggee and debugger use the same
724      * representation for reals
725      */
726     if (!memory_read_value(lvalue, size, tmp)) return FALSE;
727
728     switch (size)
729     {
730     case sizeof(float):         *ret = *(float*)tmp;            break;
731     case sizeof(double):        *ret = *(double*)tmp;           break;
732     default:                    return FALSE;
733     }
734     return TRUE;
735 }
736
737 static int be_arm_store_integer(const struct dbg_lvalue* lvalue, unsigned size,
738                                 unsigned is_signed, LONGLONG val)
739 {
740     /* this is simple if we're on a little endian CPU */
741     return memory_write_value(lvalue, size, &val);
742 }
743
744 struct backend_cpu be_arm =
745 {
746     IMAGE_FILE_MACHINE_ARMV7,
747     4,
748     be_cpu_linearize,
749     be_cpu_build_addr,
750     be_arm_get_addr,
751     be_arm_get_register_info,
752     be_arm_single_step,
753     be_arm_print_context,
754     be_arm_print_segment_info,
755     be_arm_ctx,
756     be_arm_is_step_over_insn,
757     be_arm_is_function_return,
758     be_arm_is_break_insn,
759     be_arm_is_func_call,
760     be_arm_is_jump,
761     be_arm_disasm_one_insn,
762     be_arm_insert_Xpoint,
763     be_arm_remove_Xpoint,
764     be_arm_is_watchpoint_set,
765     be_arm_clear_watchpoint,
766     be_arm_adjust_pc_for_break,
767     be_arm_fetch_integer,
768     be_arm_fetch_float,
769     be_arm_store_integer,
770 };
771 #endif