As test runs are not invoked from the option parsing loop anymore, we
[wine] / programs / winedbg / be_i386.c
1 /*
2  * Debugger i386 specific functions
3  *
4  * Copyright 2004 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "debugger.h"
22 #include "wine/debug.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
25
26 #ifdef __i386__
27
28   /* debugger/db_disasm.c */
29 extern void             be_i386_disasm_one_insn(ADDRESS* addr, int display);
30
31 #define STEP_FLAG 0x00000100 /* single step flag */
32 #define V86_FLAG  0x00020000
33
34 #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
35
36 static ADDRESS_MODE get_selector_type(HANDLE hThread, const CONTEXT* ctx, WORD sel)
37 {
38     LDT_ENTRY   le;
39
40     if (IS_VM86_MODE(ctx)) return AddrModeReal;
41     /* null or system selector */
42     if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat;
43     if (GetThreadSelectorEntry(hThread, sel, &le))
44         return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616;
45     /* selector doesn't exist */
46     return -1;
47 }
48
49 static void* be_i386_linearize(HANDLE hThread, const ADDRESS* addr)
50 {
51     LDT_ENTRY   le;
52
53     switch (addr->Mode)
54     {
55     case AddrModeReal:
56         return (void*)((DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset);
57     case AddrMode1632:
58         if (!(addr->Segment & 4) || ((addr->Segment >> 3) < 17))
59             return (void*)addr->Offset;
60         /* fall through */
61     case AddrMode1616:
62         if (!GetThreadSelectorEntry(hThread, addr->Segment, &le)) return NULL;
63         return (void*)((le.HighWord.Bits.BaseHi << 24) + 
64                        (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset);
65         break;
66     case AddrModeFlat:
67         return (void*)addr->Offset;
68     }
69     return NULL;
70 }
71
72 static unsigned be_i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr,
73                                    unsigned seg, unsigned long offset)
74 {
75     addr->Mode    = AddrModeFlat;
76     addr->Segment = seg;
77     addr->Offset  = offset;
78     if (seg)
79     {
80         addr->Mode = get_selector_type(hThread, ctx, seg);
81         switch (addr->Mode)
82         {
83         case AddrModeReal:
84         case AddrMode1616:
85             addr->Offset &= 0xffff;
86             break;
87         case AddrModeFlat:
88         case AddrMode1632:
89             break;
90         default:
91             addr->Mode = -1;
92             return FALSE;
93         }            
94     }
95     return TRUE;
96 }
97
98 static unsigned be_i386_get_addr(HANDLE hThread, const CONTEXT* ctx, 
99                                  enum be_cpu_addr bca, ADDRESS* addr)
100 {
101     switch (bca)
102     {
103     case be_cpu_addr_pc:
104         return be_i386_build_addr(hThread, ctx, addr, ctx->SegCs, ctx->Eip);
105     case be_cpu_addr_stack:
106         return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Esp);
107     case be_cpu_addr_frame:
108         return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Ebp);
109     }
110     return FALSE;
111 }
112
113 static void be_i386_single_step(CONTEXT* ctx, unsigned enable)
114 {
115     if (enable) ctx->EFlags |= STEP_FLAG;
116     else ctx->EFlags &= ~STEP_FLAG;
117 }
118
119 static void be_i386_print_context(HANDLE hThread, const CONTEXT* ctx)
120 {
121     char        buf[33];
122     char*       pt;
123
124     dbg_printf("Register dump:\n");
125
126     /* First get the segment registers out of the way */
127     dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x",
128                (WORD)ctx->SegCs, (WORD)ctx->SegSs,
129                (WORD)ctx->SegDs, (WORD)ctx->SegEs,
130                (WORD)ctx->SegFs, (WORD)ctx->SegGs);
131
132     strcpy(buf, "   - 00      - - - ");
133     pt = buf + strlen(buf) - 1;
134     if (ctx->EFlags & 0x00000001) *pt-- = 'C'; /* Carry Flag */
135     if (ctx->EFlags & 0x00000002) *pt-- = '1';
136     if (ctx->EFlags & 0x00000004) *pt-- = 'P'; /* Parity Flag */
137     if (ctx->EFlags & 0x00000008) *pt-- = '-';
138     if (ctx->EFlags & 0x00000010) *pt-- = 'A'; /* Auxiliary Carry Flag */
139     if (ctx->EFlags & 0x00000020) *pt-- = '-';
140     if (ctx->EFlags & 0x00000040) *pt-- = 'Z'; /* Zero Flag */
141     if (ctx->EFlags & 0x00000080) *pt-- = 'S'; /* Sign Flag */
142     if (ctx->EFlags & 0x00000100) *pt-- = 'T'; /* Trap/Trace Flag */
143     if (ctx->EFlags & 0x00000200) *pt-- = 'I'; /* Interupt Enable Flag */
144     if (ctx->EFlags & 0x00000400) *pt-- = 'D'; /* Direction Indicator */
145     if (ctx->EFlags & 0x00000800) *pt-- = 'O'; /* Overflow flags */
146     if (ctx->EFlags & 0x00001000) *pt-- = '1'; /* I/O Privilege Level */
147     if (ctx->EFlags & 0x00002000) *pt-- = '1'; /* I/O Privilege Level */
148     if (ctx->EFlags & 0x00004000) *pt-- = 'N'; /* Nested Task Flag */
149     if (ctx->EFlags & 0x00008000) *pt-- = '-';
150     if (ctx->EFlags & 0x00010000) *pt-- = 'R'; /* Resume Flag */
151     if (ctx->EFlags & 0x00020000) *pt-- = 'V'; /* Vritual Mode Flag */
152     if (ctx->EFlags & 0x00040000) *pt-- = 'a'; /* Alignment Check Flag */
153     
154     switch (get_selector_type(hThread, ctx, ctx->SegCs))
155     {
156     case AddrMode1616:
157     case AddrModeReal:
158         dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n",
159                    LOWORD(ctx->Eip), LOWORD(ctx->Esp),
160                    LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf);
161         dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n",
162                    LOWORD(ctx->Eax), LOWORD(ctx->Ebx),
163                    LOWORD(ctx->Ecx), LOWORD(ctx->Edx),
164                    LOWORD(ctx->Esi), LOWORD(ctx->Edi));
165         break;
166     case AddrModeFlat:
167     case AddrMode1632:
168         dbg_printf("\n EIP:%08lx ESP:%08lx EBP:%08lx EFLAGS:%08lx(%s)\n",
169                    ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf);
170         dbg_printf(" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n",
171                    ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx);
172         dbg_printf(" ESI:%08lx EDI:%08lx\n",
173                    ctx->Esi, ctx->Edi);
174         break;
175     }
176 }
177
178 static void be_i386_print_segment_info(HANDLE hThread, const CONTEXT* ctx)
179 {
180     if (get_selector_type(hThread, ctx, ctx->SegCs) == AddrMode1616)
181     {
182         info_win32_segments(ctx->SegDs >> 3, 1);
183         if (ctx->SegEs != ctx->SegDs) info_win32_segments(ctx->SegEs >> 3, 1);
184     }
185     info_win32_segments(ctx->SegFs >> 3, 1);
186 }
187
188 static struct dbg_internal_var be_i386_ctx[] =
189 {
190     {CV_REG_AL,         "AL",           (DWORD*)FIELD_OFFSET(CONTEXT, Eax),     dbg_itype_unsigned_char_int},
191     {CV_REG_CL,         "CL",           (DWORD*)FIELD_OFFSET(CONTEXT, Ecx),     dbg_itype_unsigned_char_int},
192     {CV_REG_DL,         "DL",           (DWORD*)FIELD_OFFSET(CONTEXT, Edx),     dbg_itype_unsigned_char_int},
193     {CV_REG_BL,         "BL",           (DWORD*)FIELD_OFFSET(CONTEXT, Ebx),     dbg_itype_unsigned_char_int},
194     {CV_REG_AH,         "AH",           (DWORD*)(FIELD_OFFSET(CONTEXT, Eax)+1), dbg_itype_unsigned_char_int},
195     {CV_REG_CH,         "CH",           (DWORD*)(FIELD_OFFSET(CONTEXT, Ecx)+1), dbg_itype_unsigned_char_int},
196     {CV_REG_DH,         "DH",           (DWORD*)(FIELD_OFFSET(CONTEXT, Edx)+1), dbg_itype_unsigned_char_int},
197     {CV_REG_BH,         "BH",           (DWORD*)(FIELD_OFFSET(CONTEXT, Ebx)+1), dbg_itype_unsigned_char_int},
198     {CV_REG_AX,         "AX",           (DWORD*)FIELD_OFFSET(CONTEXT, Eax),     dbg_itype_unsigned_short_int},
199     {CV_REG_CX,         "CX",           (DWORD*)FIELD_OFFSET(CONTEXT, Ecx),     dbg_itype_unsigned_short_int},
200     {CV_REG_DX,         "DX",           (DWORD*)FIELD_OFFSET(CONTEXT, Edx),     dbg_itype_unsigned_short_int},
201     {CV_REG_BX,         "BX",           (DWORD*)FIELD_OFFSET(CONTEXT, Ebx),     dbg_itype_unsigned_short_int},
202     {CV_REG_SP,         "SP",           (DWORD*)FIELD_OFFSET(CONTEXT, Esp),     dbg_itype_unsigned_short_int},
203     {CV_REG_BP,         "BP",           (DWORD*)FIELD_OFFSET(CONTEXT, Ebp),     dbg_itype_unsigned_short_int},
204     {CV_REG_SI,         "SI",           (DWORD*)FIELD_OFFSET(CONTEXT, Esi),     dbg_itype_unsigned_short_int},
205     {CV_REG_DI,         "DI",           (DWORD*)FIELD_OFFSET(CONTEXT, Edi),     dbg_itype_unsigned_short_int},
206     {CV_REG_EAX,        "EAX",          (DWORD*)FIELD_OFFSET(CONTEXT, Eax),     dbg_itype_unsigned_int},
207     {CV_REG_ECX,        "ECX",          (DWORD*)FIELD_OFFSET(CONTEXT, Ecx),     dbg_itype_unsigned_int},
208     {CV_REG_EDX,        "EDX",          (DWORD*)FIELD_OFFSET(CONTEXT, Edx),     dbg_itype_unsigned_int},
209     {CV_REG_EBX,        "EBX",          (DWORD*)FIELD_OFFSET(CONTEXT, Ebx),     dbg_itype_unsigned_int},
210     {CV_REG_ESP,        "ESP",          (DWORD*)FIELD_OFFSET(CONTEXT, Esp),     dbg_itype_unsigned_int},
211     {CV_REG_EBP,        "EBP",          (DWORD*)FIELD_OFFSET(CONTEXT, Ebp),     dbg_itype_unsigned_int},
212     {CV_REG_ESI,        "ESI",          (DWORD*)FIELD_OFFSET(CONTEXT, Esi),     dbg_itype_unsigned_int},
213     {CV_REG_EDI,        "EDI",          (DWORD*)FIELD_OFFSET(CONTEXT, Edi),     dbg_itype_unsigned_int},
214     {CV_REG_ES,         "ES",           (DWORD*)FIELD_OFFSET(CONTEXT, SegEs),   dbg_itype_unsigned_short_int},
215     {CV_REG_CS,         "CS",           (DWORD*)FIELD_OFFSET(CONTEXT, SegCs),   dbg_itype_unsigned_short_int},
216     {CV_REG_SS,         "SS",           (DWORD*)FIELD_OFFSET(CONTEXT, SegSs),   dbg_itype_unsigned_short_int},
217     {CV_REG_DS,         "DS",           (DWORD*)FIELD_OFFSET(CONTEXT, SegDs),   dbg_itype_unsigned_short_int},
218     {CV_REG_FS,         "FS",           (DWORD*)FIELD_OFFSET(CONTEXT, SegFs),   dbg_itype_unsigned_short_int},
219     {CV_REG_GS,         "GS",           (DWORD*)FIELD_OFFSET(CONTEXT, SegGs),   dbg_itype_unsigned_short_int},
220     {CV_REG_IP,         "IP",           (DWORD*)FIELD_OFFSET(CONTEXT, Eip),     dbg_itype_unsigned_short_int},
221     {CV_REG_FLAGS,      "FLAGS",        (DWORD*)FIELD_OFFSET(CONTEXT, EFlags),  dbg_itype_unsigned_short_int},
222     {CV_REG_EIP,        "EIP",          (DWORD*)FIELD_OFFSET(CONTEXT, Eip),     dbg_itype_unsigned_int},
223     {CV_REG_EFLAGS,     "EFLAGS",       (DWORD*)FIELD_OFFSET(CONTEXT, EFlags),  dbg_itype_unsigned_int},
224     {0,                 NULL,           0,                                      dbg_itype_none}
225 };
226
227 static const struct dbg_internal_var* be_i386_init_registers(CONTEXT* ctx)
228 {
229     struct dbg_internal_var*    div;
230
231     for (div = be_i386_ctx; div->name; div++) 
232         div->pval = (DWORD*)((char*)ctx + (DWORD)div->pval);
233     return be_i386_ctx;
234 }
235
236 static unsigned be_i386_is_step_over_insn(const void* insn)
237 {
238     BYTE        ch;
239
240     for (;;)
241     {
242         if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
243
244         switch (ch)
245         {
246         /* Skip all prefixes */
247         case 0x2e:  /* cs: */
248         case 0x36:  /* ss: */
249         case 0x3e:  /* ds: */
250         case 0x26:  /* es: */
251         case 0x64:  /* fs: */
252         case 0x65:  /* gs: */
253         case 0x66:  /* opcode size prefix */
254         case 0x67:  /* addr size prefix */
255         case 0xf0:  /* lock */
256         case 0xf2:  /* repne */
257         case 0xf3:  /* repe */
258             insn = (const char*)insn + 1;
259             continue;
260
261         /* Handle call instructions */
262         case 0xcd:  /* int <intno> */
263         case 0xe8:  /* call <offset> */
264         case 0x9a:  /* lcall <seg>:<off> */
265             return TRUE;
266
267         case 0xff:  /* call <regmodrm> */
268             if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
269                 return FALSE;
270             return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18));
271
272         /* Handle string instructions */
273         case 0x6c:  /* insb */
274         case 0x6d:  /* insw */
275         case 0x6e:  /* outsb */
276         case 0x6f:  /* outsw */
277         case 0xa4:  /* movsb */
278         case 0xa5:  /* movsw */
279         case 0xa6:  /* cmpsb */
280         case 0xa7:  /* cmpsw */
281         case 0xaa:  /* stosb */
282         case 0xab:  /* stosw */
283         case 0xac:  /* lodsb */
284         case 0xad:  /* lodsw */
285         case 0xae:  /* scasb */
286         case 0xaf:  /* scasw */
287             return TRUE;
288
289         default:
290             return FALSE;
291         }
292     }
293 }
294
295 static unsigned be_i386_is_function_return(const void* insn)
296 {
297     BYTE ch;
298
299     if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
300     return (ch == 0xC2) || (ch == 0xC3);
301 }
302
303 static unsigned be_i386_is_break_insn(const void* insn)
304 {
305     BYTE        c;
306
307     if (!dbg_read_memory(insn, &c, 1)) return FALSE;
308     return c == 0xCC;
309 }
310
311 static unsigned be_i386_is_func_call(const void* insn, ADDRESS* callee)
312 {
313     BYTE        ch;
314     int         delta;
315
316     dbg_read_memory(insn, &ch, sizeof(ch));
317     if (ch == 0xe8)
318     {
319         dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta));
320         
321         callee->Mode = AddrModeFlat;
322         callee->Offset = (DWORD)insn;
323         be_i386_disasm_one_insn(callee, FALSE);
324         callee->Offset += delta;
325
326         return TRUE;
327     }
328     return FALSE;
329 }
330
331 #define DR7_CONTROL_SHIFT       16
332 #define DR7_CONTROL_SIZE        4
333
334 #define DR7_RW_EXECUTE          (0x0)
335 #define DR7_RW_WRITE            (0x1)
336 #define DR7_RW_READ             (0x3)
337
338 #define DR7_LEN_1               (0x0)
339 #define DR7_LEN_2               (0x4)
340 #define DR7_LEN_4               (0xC)
341
342 #define DR7_LOCAL_ENABLE_SHIFT  0
343 #define DR7_GLOBAL_ENABLE_SHIFT 1
344 #define DR7_ENABLE_SIZE         2
345
346 #define DR7_LOCAL_ENABLE_MASK   (0x55)
347 #define DR7_GLOBAL_ENABLE_MASK  (0xAA)
348
349 #define DR7_CONTROL_RESERVED    (0xFC00)
350 #define DR7_LOCAL_SLOWDOWN      (0x100)
351 #define DR7_GLOBAL_SLOWDOWN     (0x200)
352
353 #define DR7_ENABLE_MASK(dr)     (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr)))
354 #define IS_DR7_SET(ctrl,dr)     ((ctrl)&DR7_ENABLE_MASK(dr))
355
356 static inline int be_i386_get_unused_DR(CONTEXT* ctx, unsigned long** r)
357 {
358     if (!IS_DR7_SET(ctx->Dr7, 0))
359     {
360         *r = &ctx->Dr0;
361         return 0;
362     }
363     if (!IS_DR7_SET(ctx->Dr7, 1))
364     {
365         *r = &ctx->Dr1;
366         return 1;
367     }
368     if (!IS_DR7_SET(ctx->Dr7, 2))
369     {
370         *r = &ctx->Dr2;
371         return 2;
372     }
373     if (!IS_DR7_SET(ctx->Dr7, 3))
374     {
375         *r = &ctx->Dr3;
376         return 3;
377     }
378     dbg_printf("All hardware registers have been used\n");
379
380     return -1;
381 }
382
383 static unsigned be_i386_insert_Xpoint(HANDLE hProcess, CONTEXT* ctx,
384                                       enum be_xpoint_type type, void* addr,
385                                       unsigned long* val, unsigned size)
386 {
387     unsigned char       ch;
388     unsigned long       sz;
389     unsigned long*      pr;
390     int                 reg;
391     unsigned long       bits;
392
393     switch (type)
394     {
395     case be_xpoint_break:
396         if (size != 0) return 0;
397         if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
398         *val = ch;
399         ch = 0xcc;
400         if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
401         break;
402     case be_xpoint_watch_exec:
403         bits = DR7_RW_EXECUTE;
404         goto hw_bp;
405     case be_xpoint_watch_read:
406         bits = DR7_RW_READ;
407         goto hw_bp;
408     case be_xpoint_watch_write:
409         bits = DR7_RW_WRITE;
410     hw_bp:
411         if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return 0;
412         *pr = (unsigned long)addr;
413         if (type != be_xpoint_watch_exec) switch (size)
414         {
415         case 4: bits |= DR7_LEN_4; break;
416         case 2: bits |= DR7_LEN_2; break;
417         case 1: bits |= DR7_LEN_1; break;
418         default: return 0;
419         }
420         *val = reg;
421         /* clear old values */
422         ctx->Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg));
423         /* set the correct ones */
424         ctx->Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg);
425         ctx->Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN;
426         break;
427     default:
428         dbg_printf("Unknown bp type %c\n", type);
429         return 0;
430     }
431     return 1;
432 }
433
434 static unsigned be_i386_remove_Xpoint(HANDLE hProcess, CONTEXT* ctx, 
435                                       enum be_xpoint_type type, void* addr, 
436                                       unsigned long val, unsigned size)
437 {
438     unsigned long       sz;
439     unsigned char       ch;
440
441     switch (type)
442     {
443     case be_xpoint_break:
444         if (size != 0) return 0;
445         if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
446         if (ch != (unsigned char)0xCC)
447             WINE_FIXME("Cannot get back %02x instead of 0xCC at %08lx\n",
448                        ch, (unsigned long)addr);
449         ch = (unsigned char)val;
450         if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0;
451         break;
452     case be_xpoint_watch_exec:
453     case be_xpoint_watch_read:
454     case be_xpoint_watch_write:
455         /* simply disable the entry */
456         ctx->Dr7 &= ~DR7_ENABLE_MASK(val);
457         break;
458     default:
459         dbg_printf("Unknown bp type %c\n", type);
460         return 0;
461     }
462     return 1;
463 }
464
465 static unsigned be_i386_is_watchpoint_set(const CONTEXT* ctx, unsigned idx)
466 {
467     return ctx->Dr6 & (1 << idx);
468 }
469
470 static void be_i386_clear_watchpoint(CONTEXT* ctx, unsigned idx)
471 {
472     ctx->Dr6 &= ~(1 << idx);
473 }
474
475 static int be_i386_adjust_pc_for_break(CONTEXT* ctx, BOOL way)
476 {
477     if (way)
478     {
479         ctx->Eip--;
480         return -1;
481     }
482     ctx->Eip++;
483     return 1;
484 }
485
486 static int be_i386_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size,
487                                  unsigned ext_sign, long long int* ret)
488 {
489     if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE;
490
491     memset(ret, 0, sizeof(*ret)); /* clear unread bytes */
492     /* FIXME: this assumes that debuggee and debugger use the same
493      * integral representation
494      */
495     if (!memory_read_value(lvalue, size, ret)) return FALSE;
496
497     /* propagate sign information */
498     if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0)
499     {
500         long long unsigned int neg = -1;
501         *ret |= neg << (size * 8);
502     }
503     return TRUE;
504 }
505
506 static int be_i386_fetch_float(const struct dbg_lvalue* lvalue, unsigned size, 
507                                long double* ret)
508 {
509     char        tmp[12];
510
511     /* FIXME: this assumes that debuggee and debugger use the same 
512      * representation for reals
513      */
514     if (!memory_read_value(lvalue, size, tmp)) return FALSE;
515
516     /* float & double types have to be promoted to a long double */
517     switch (size)
518     {
519     case sizeof(float):         *ret = *(float*)tmp;            break;
520     case sizeof(double):        *ret = *(double*)tmp;           break;
521     case sizeof(long double):   *ret = *(long double*)tmp;      break;
522     default:                    return FALSE;
523     }
524     return TRUE;
525 }
526
527 struct backend_cpu be_i386 =
528 {
529     be_i386_linearize,
530     be_i386_build_addr,
531     be_i386_get_addr,
532     be_i386_single_step,
533     be_i386_print_context,
534     be_i386_print_segment_info,
535     be_i386_init_registers,
536     be_i386_is_step_over_insn,
537     be_i386_is_function_return,
538     be_i386_is_break_insn,
539     be_i386_is_func_call,
540     be_i386_disasm_one_insn,
541     be_i386_insert_Xpoint,
542     be_i386_remove_Xpoint,
543     be_i386_is_watchpoint_set,
544     be_i386_clear_watchpoint,
545     be_i386_adjust_pc_for_break,
546     be_i386_fetch_integer,
547     be_i386_fetch_float,
548 };
549 #endif