/[gxemul]/trunk/src/cpu_x86.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/cpu_x86.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Mon Oct 8 16:18:00 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 23561 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.707 2005/04/27 16:37:33 debug Exp $
20050408	Some minor updates to the wdc. Linux now doesn't complain
		anymore if a disk is non-present.
20050409	Various minor fixes (a bintrans bug, and some other things).
		The wdc seems to work with Playstation2 emulation, but there
		is a _long_ annoying delay when disks are detected.
		Fixing a really important bintrans bug (when devices and RAM
		are mixed within 4KB pages), which was triggered with
		NetBSD/playstation2 kernels.
20050410	Adding a dummy dev_ps2_ether (just so that NetBSD doesn't
		complain as much during bootup).
		Symbols starting with '$' are now ignored.
		Renaming dev_ps2_ohci.c to dev_ohci.c, etc.
20050411	Moving the bintrans-cache-isolation check from cpu_mips.c to
		cpu_mips_coproc.c. (I thought this would give a speedup, but
		it's not noticable.)
		Better playstation2 sbus interrupt code.
		Skip ahead many ticks if the count register is read manually.
		(This increases the speed of delay-loops that simply read
		the count register.)
20050412	Updates to the playstation2 timer/interrupt code.
		Some other minor updates.
20050413	NetBSD/cobalt runs from a disk image :-) including userland;
		updating the documentation on how to install NetBSD/cobalt
		using NetBSD/pmax (!).
		Some minor bintrans updates (no real speed improvement) and
		other minor updates (playstation2 now uses the -o options).
20050414	Adding a dummy x86 (and AMD64) mode.
20050415	Adding some (32-bit and 16-bit) x86 instructions.
		Adding some initial support for non-SCSI, non-IDE floppy
		images. (The x86 mode can boot from these, more or less.)
		Moving the devices/ and include/ directories to src/devices/
		and src/include/, respectively.
20050416	Continuing on the x86 stuff. (Adding pc_bios.c and some simple
		support for software interrupts in 16-bit mode.)
20050417	Ripping out most of the x86 instruction decoding stuff, trying
		to rewrite it in a cleaner way.
		Disabling some of the least working CPU families in the
		configure script (sparc, x86, alpha, hppa), so that they are
		not enabled by default.
20050418	Trying to fix the bug which caused problems when turning on
		and off bintrans interactively, by flushing the bintrans cache
		whenever bintrans is manually (re)enabled.
20050419	Adding the 'lswi' ppc instruction.
		Minor updates to the x86 instruction decoding.
20050420	Renaming x86 register name indices from R_xx to X86_R_xx (this
		makes building on Tru64 nicer).
20050422	Adding a check for duplicate MIPS TLB entries on tlbwr/tlbwi.
20050427	Adding screenshots to guestoses.html.
		Some minor fixes and testing for the next release.

==============  RELEASE 0.3.2  ==============


1 /*
2 * Copyright (C) 2005 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: cpu_x86.c,v 1.24 2005/04/20 02:05:56 debug Exp $
29 *
30 * x86 (and amd64) CPU emulation.
31 *
32 *
33 * TODO: Pretty much everything.
34 *
35 * See http://www.amd.com/us-en/Processors/DevelopWithAMD/
36 * 0,,30_2252_875_7044,00.html for more info on AMD64.
37 *
38 * http://www.cs.ucla.edu/~kohler/class/04f-aos/ref/i386/appa.htm has a
39 * nice overview of the standard i386 opcodes.
40 */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46
47 #include "misc.h"
48
49
50 #ifndef ENABLE_X86
51
52
53 #include "cpu_x86.h"
54
55
56 /*
57 * x86_cpu_family_init():
58 *
59 * Bogus, when ENABLE_X86 isn't defined.
60 */
61 int x86_cpu_family_init(struct cpu_family *fp)
62 {
63 return 0;
64 }
65
66
67 #else /* ENABLE_X86 */
68
69
70 #include "cpu.h"
71 #include "cpu_x86.h"
72 #include "machine.h"
73 #include "memory.h"
74 #include "symbol.h"
75
76
77 extern volatile int single_step;
78 extern int old_show_trace_tree;
79 extern int old_instruction_trace;
80 extern int old_quiet_mode;
81 extern int quiet_mode;
82
83
84 static struct x86_model models[] = x86_models;
85 static char *reg_names[N_X86_REGS] = x86_reg_names;
86 static char *seg_names[N_X86_SEGS] = x86_seg_names;
87 static char *cond_names[N_X86_CONDS] = x86_cond_names;
88
89
90 /*
91 * x86_cpu_new():
92 *
93 * Create a new x86 cpu object.
94 */
95 struct cpu *x86_cpu_new(struct memory *mem, struct machine *machine,
96 int cpu_id, char *cpu_type_name)
97 {
98 int i = 0;
99 struct cpu *cpu;
100
101 if (cpu_type_name == NULL)
102 return NULL;
103
104 /* Try to find a match: */
105 while (models[i].model_number != 0) {
106 if (strcasecmp(cpu_type_name, models[i].name) == 0)
107 break;
108 i++;
109 }
110
111 if (models[i].name == NULL)
112 return NULL;
113
114 cpu = malloc(sizeof(struct cpu));
115 if (cpu == NULL) {
116 fprintf(stderr, "out of memory\n");
117 exit(1);
118 }
119
120 memset(cpu, 0, sizeof(struct cpu));
121 cpu->memory_rw = x86_memory_rw;
122 cpu->name = cpu_type_name;
123 cpu->mem = mem;
124 cpu->machine = machine;
125 cpu->cpu_id = cpu_id;
126 cpu->byte_order = EMUL_LITTLE_ENDIAN;
127 cpu->bootstrap_cpu_flag = 0;
128 cpu->running = 0;
129
130 cpu->cd.x86.model = models[i];
131 cpu->cd.x86.mode = 32;
132 cpu->cd.x86.bits = 32;
133
134 if (cpu->cd.x86.model.model_number == X86_MODEL_AMD64)
135 cpu->cd.x86.bits = 64;
136
137 cpu->cd.x86.r[X86_R_SP] = 0xff0;
138
139 /* Only show name and caches etc for CPU nr 0 (in SMP machines): */
140 if (cpu_id == 0) {
141 debug("%s", cpu->name);
142 }
143
144 return cpu;
145 }
146
147
148 /*
149 * x86_cpu_dumpinfo():
150 */
151 void x86_cpu_dumpinfo(struct cpu *cpu)
152 {
153 debug(" (%i-bit)", cpu->cd.x86.bits);
154 debug(", currently in %i-bit mode", cpu->cd.x86.mode);
155 debug("\n");
156 }
157
158
159 /*
160 * x86_cpu_list_available_types():
161 *
162 * Print a list of available x86 CPU types.
163 */
164 void x86_cpu_list_available_types(void)
165 {
166 int i = 0, j;
167
168 while (models[i].model_number != 0) {
169 debug("%s", models[i].name);
170
171 for (j=0; j<10-strlen(models[i].name); j++)
172 debug(" ");
173 i++;
174 if ((i % 6) == 0 || models[i].name == NULL)
175 debug("\n");
176 }
177 }
178
179
180 /*
181 * x86_cpu_register_dump():
182 *
183 * Dump cpu registers in a relatively readable format.
184 * (gprs and coprocs are mostly useful for the MIPS version of this function.)
185 */
186 void x86_cpu_register_dump(struct cpu *cpu, int gprs, int coprocs)
187 {
188 char *symbol;
189 uint64_t offset;
190 int i, x = cpu->cpu_id;
191
192 if (cpu->cd.x86.mode == 16) {
193 debug("cpu%i: cs:ip = 0x%04x:0x%04x\n", x,
194 cpu->cd.x86.s[X86_S_CS], (int)cpu->pc);
195
196 debug("cpu%i: ax = 0x%04x bx = 0x%04x cx = 0x%04x dx = "
197 "0x%04x\n", x,
198 (int)cpu->cd.x86.r[X86_R_AX], (int)cpu->cd.x86.r[X86_R_BX],
199 (int)cpu->cd.x86.r[X86_R_CX], (int)cpu->cd.x86.r[X86_R_DX]);
200 debug("cpu%i: si = 0x%04x di = 0x%04x bp = 0x%04x sp = "
201 "0x%04x\n", x,
202 (int)cpu->cd.x86.r[X86_R_SI], (int)cpu->cd.x86.r[X86_R_DI],
203 (int)cpu->cd.x86.r[X86_R_BP], (int)cpu->cd.x86.r[X86_R_SP]);
204
205 debug("cpu%i: ds = 0x%04x es = 0x%04x ss = 0x%04x flags "
206 "= 0x%04x\n", x,
207 (int)cpu->cd.x86.s[X86_S_DS], (int)cpu->cd.x86.s[X86_S_ES],
208 (int)cpu->cd.x86.s[X86_S_SS], (int)cpu->cd.x86.rflags);
209 } else if (cpu->cd.x86.mode == 32) {
210 symbol = get_symbol_name(&cpu->machine->symbol_context,
211 cpu->pc, &offset);
212
213 debug("cpu%i: eip=0x", x);
214 debug("%08x", (int)cpu->pc);
215 debug(" <%s>\n", symbol != NULL? symbol : " no symbol ");
216
217 debug("cpu%i: eax=0x%08x ebx=0x%08x ecx=0x%08x edx="
218 "0x%08x\n", x,
219 (int)cpu->cd.x86.r[X86_R_AX], (int)cpu->cd.x86.r[X86_R_BX],
220 (int)cpu->cd.x86.r[X86_R_CX], (int)cpu->cd.x86.r[X86_R_DX]);
221 debug("cpu%i: esi=0x%08x edi=0x%08x ebp=0x%08x esp="
222 "0x%08x\n", x,
223 (int)cpu->cd.x86.r[X86_R_SI], (int)cpu->cd.x86.r[X86_R_DI],
224 (int)cpu->cd.x86.r[X86_R_BP], (int)cpu->cd.x86.r[X86_R_SP]);
225 } else {
226 /* 64-bit */
227 symbol = get_symbol_name(&cpu->machine->symbol_context,
228 cpu->pc, &offset);
229
230 debug("cpu%i: rip = 0x", x);
231 debug("%016llx", (long long)cpu->pc);
232 debug(" <%s>\n", symbol != NULL? symbol : " no symbol ");
233
234 for (i=0; i<N_X86_REGS; i++) {
235 if ((i & 1) == 0)
236 debug("cpu%i:", x);
237 debug(" r%s = 0x%016llx", reg_names[i],
238 (long long)cpu->cd.x86.r[i]);
239 if ((i & 1) == 1)
240 debug("\n");
241 }
242 }
243
244 if (cpu->cd.x86.mode >= 32) {
245 debug("cpu%i: cs=0x%04x ds=0x%04x es=0x%04x "
246 "fs=0x%04x gs=0x%04x ss=0x%04x\n", x,
247 (int)cpu->cd.x86.s[X86_S_CS], (int)cpu->cd.x86.s[X86_S_DS],
248 (int)cpu->cd.x86.s[X86_S_ES], (int)cpu->cd.x86.s[X86_S_FS],
249 (int)cpu->cd.x86.s[X86_S_GS], (int)cpu->cd.x86.s[X86_S_SS]);
250 }
251
252 if (cpu->cd.x86.mode == 32) {
253 debug("cpu%i: cr0 = 0x%08x cr3 = 0x%08x eflags = 0x%08x\n",
254 x, (int)cpu->cd.x86.cr[0],
255 (int)cpu->cd.x86.cr[3], (int)cpu->cd.x86.rflags);
256 }
257
258 if (cpu->cd.x86.mode == 64) {
259 debug("cpu%i: cr0 = 0x%016llx cr3 = 0x%016llx\n", x,
260 "0x%016llx\n", x, (long long)cpu->cd.x86.cr[0], (long long)
261 cpu->cd.x86.cr[3]);
262 debug("cpu%i: rflags = 0x%016llx\n", x,
263 (long long)cpu->cd.x86.rflags);
264 }
265 }
266
267
268 /*
269 * x86_cpu_register_match():
270 */
271 void x86_cpu_register_match(struct machine *m, char *name,
272 int writeflag, uint64_t *valuep, int *match_register)
273 {
274 int cpunr = 0;
275
276 /* CPU number: */
277
278 /* TODO */
279
280 /* Register name: */
281 if (strcasecmp(name, "pc") == 0 || strcasecmp(name, "ip") == 0
282 || strcasecmp(name, "eip") == 0) {
283 if (writeflag) {
284 m->cpus[cpunr]->pc = *valuep;
285 } else
286 *valuep = m->cpus[cpunr]->pc;
287 *match_register = 1;
288 }
289
290 #if 0
291 TODO: regmatch for 64, 32, 16, and 8 bit register names
292 #endif
293 }
294
295
296 /* Macro which modifies the lower part of a value, or the entire value,
297 depending on 'mode': */
298 #define modify(old,new) ( \
299 mode==16? ( \
300 ((old) & ~0xffff) + ((new) & 0xffff) \
301 ) : (new) )
302
303 #define HEXPRINT(x,n) { int j; for (j=0; j<(n); j++) debug("%02x",(x)[j]); }
304 #define HEXSPACES(i) { int j; for (j=0; j<10-(i);j++) debug(" "); debug(" "); }
305 #define SPACES HEXSPACES(ilen)
306
307
308 static uint32_t read_imm_common(unsigned char **instrp, int *ilenp,
309 int len, int printflag)
310 {
311 uint32_t imm;
312 unsigned char *instr = *instrp;
313
314 if (len == 8)
315 imm = instr[0];
316 else if (len == 16)
317 imm = instr[0] + (instr[1] << 8);
318 else
319 imm = instr[0] + (instr[1] << 8) +
320 (instr[2] << 16) + (instr[3] << 24);
321
322 if (printflag)
323 HEXPRINT(instr, len / 8);
324
325 (*ilenp) += len/8;
326 (*instrp) += len/8;
327 return imm;
328 }
329
330
331 static uint32_t read_imm_and_print(unsigned char **instrp, int *ilenp,
332 int mode)
333 {
334 return read_imm_common(instrp, ilenp, mode, 1);
335 }
336
337
338 static uint32_t read_imm(unsigned char **instrp, uint64_t *newpc,
339 int mode)
340 {
341 int x = 0;
342 uint32_t r = read_imm_common(instrp, &x, mode, 0);
343 (*newpc) += x;
344 return r;
345 }
346
347
348 static void print_csip(struct cpu *cpu)
349 {
350 if (cpu->cd.x86.mode < 64)
351 fatal("0x%04x:", cpu->cd.x86.s[X86_S_CS]);
352 switch (cpu->cd.x86.mode) {
353 case 16: fatal("0x%04x", (int)cpu->pc); break;
354 case 32: fatal("0x%08x", (int)cpu->pc); break;
355 case 64: fatal("0x%016llx", (long long)cpu->pc); break;
356 }
357 }
358
359
360 static char modrm_dst[65];
361 static char modrm_src[65];
362 static void read_modrm(int mode, unsigned char **instrp, int *ilenp)
363 {
364 uint32_t imm = read_imm_and_print(instrp, ilenp, 8);
365 modrm_dst[0] = modrm_dst[64] = '\0';
366 modrm_src[0] = modrm_src[64] = '\0';
367
368 fatal("read_modrm(): TODO\n");
369
370 if ((imm & 0xc0) == 0xc0) {
371
372 } else {
373 fatal("read_modrm(): unimplemented modr/m\n");
374 }
375 }
376
377
378 /*
379 * x86_cpu_disassemble_instr():
380 *
381 * Convert an instruction word into human readable format, for instruction
382 * tracing.
383 *
384 * If running is 1, cpu->pc should be the address of the instruction.
385 *
386 * If running is 0, things that depend on the runtime environment (eg.
387 * register contents) will not be shown, and addr will be used instead of
388 * cpu->pc for relative addresses.
389 */
390 int x86_cpu_disassemble_instr(struct cpu *cpu, unsigned char *instr,
391 int running, uint64_t dumpaddr, int bintrans)
392 {
393 int ilen = 0, op, rep = 0, n_prefix_bytes = 0;
394 uint64_t offset;
395 uint32_t imm=0, imm2, mode = cpu->cd.x86.mode;
396 char *symbol, *tmp = "ERROR", *mnem = "ERROR", *e = "e",
397 *prefix = NULL;
398
399 if (running)
400 dumpaddr = cpu->pc;
401
402 symbol = get_symbol_name(&cpu->machine->symbol_context,
403 dumpaddr, &offset);
404 if (symbol != NULL && offset==0)
405 debug("<%s>\n", symbol);
406
407 if (cpu->machine->ncpus > 1 && running)
408 debug("cpu%i: ", cpu->cpu_id);
409
410 if (mode == 32)
411 debug("%08x: ", (int)dumpaddr);
412 else if (mode == 64)
413 debug("%016llx: ", (long long)dumpaddr);
414 else { /* 16-bit mode */
415 if (running)
416 debug("%04x:%04x ", cpu->cd.x86.s[X86_S_CS],
417 (int)dumpaddr);
418 else
419 debug("%08x: ", (int)dumpaddr);
420 }
421
422 /*
423 * Decode the instruction:
424 */
425
426 /* All instructions are at least 1 byte long: */
427 HEXPRINT(instr,1);
428 ilen = 1;
429
430 /* Any prefix? */
431 for (;;) {
432 if (instr[0] == 0x66) {
433 if (mode == 32)
434 mode = 16;
435 else
436 mode = 32;
437 } else if (instr[0] == 0xf3) {
438 rep = 1;
439 } else
440 break;
441
442 if (++n_prefix_bytes > 4) {
443 SPACES; debug("more than 4 prefix bytes?\n");
444 return 4;
445 }
446
447 /* TODO: lock, segment overrides etc */
448 instr ++; ilen ++;
449 debug("%02x", instr[0]);
450 }
451
452 if (mode == 16)
453 e = "";
454
455 op = instr[0];
456 instr ++;
457
458 if ((op & 0xf0) <= 0x30 && (op & 7) <= 5) {
459 switch (op & 0x38) {
460 case 0x00: mnem = "add"; break;
461 case 0x08: mnem = "or"; break;
462 case 0x10: mnem = "adc"; break;
463 case 0x18: mnem = "sbb"; break;
464 case 0x20: mnem = "and"; break;
465 case 0x28: mnem = "sub"; break;
466 case 0x30: mnem = "xor"; break;
467 case 0x38: mnem = "cmp"; break;
468 }
469 switch (op & 7) {
470 case 4: imm = read_imm_and_print(&instr, &ilen, 8);
471 SPACES; debug("%s\tal,0x%02x", mnem, imm);
472 break;
473 case 5: imm = read_imm_and_print(&instr, &ilen, mode);
474 SPACES; debug("%s\t%sax,0x%x", mnem, e, imm);
475 break;
476 default:
477 read_modrm(mode, &instr, &ilen);
478 SPACES; debug("%s\t%s,%s", mnem, modrm_dst, modrm_src);
479 }
480 } else if (op == 0xf) {
481 /* "pop cs" on 8086 */
482 if (cpu->cd.x86.model.model_number == X86_MODEL_8086) {
483 SPACES; debug("pop\tcs");
484 } else {
485 SPACES; debug("UNIMPLEMENTED 0x0f");
486 }
487 } else if (op < 0x20 && (op & 7) == 6) {
488 SPACES; debug("push\t%s", seg_names[op/8]);
489 } else if (op < 0x20 && (op & 7) == 7) {
490 SPACES; debug("pop\t%s", seg_names[op/8]);
491 } else if (op >= 0x20 && op < 0x40 && (op & 7) == 7) {
492 SPACES; debug("%sa%s", op < 0x30? "d" : "a",
493 (op & 0xf)==7? "a" : "s");
494 } else if (op >= 0x40 && op <= 0x5f) {
495 switch (op & 0x38) {
496 case 0x00: mnem = "inc"; break;
497 case 0x08: mnem = "dec"; break;
498 case 0x10: mnem = "push"; break;
499 case 0x18: mnem = "pop"; break;
500 }
501 SPACES; debug("%s\t%s%s", mnem, e, reg_names[op & 7]);
502 } else if (op == 0x60) {
503 SPACES; debug("pusha");
504 } else if (op == 0x61) {
505 SPACES; debug("popa");
506 } else if ((op & 0xf0) == 0x70) {
507 imm = (signed char)read_imm_and_print(&instr, &ilen, 8);
508 imm = dumpaddr + 2 + imm;
509 SPACES; debug("j%s%s\t0x%x", op&1? "n" : "",
510 cond_names[(op/2) & 0x7], imm);
511 } else if (op == 0x90) {
512 SPACES; debug("nop");
513 } else if (op >= 0x91 && op <= 0x97) {
514 SPACES; debug("xchg\t%sax,%s%s", e, e, reg_names[op & 7]);
515 } else if (op == 0x98) {
516 SPACES; debug("cbw");
517 } else if (op == 0x99) {
518 SPACES; debug("cwd");
519 } else if (op == 0x9b) {
520 SPACES; debug("wait");
521 } else if (op == 0x9c) {
522 SPACES; debug("pushf");
523 } else if (op == 0x9d) {
524 SPACES; debug("popf");
525 } else if (op == 0x9e) {
526 SPACES; debug("sahf");
527 } else if (op == 0x9f) {
528 SPACES; debug("lahf");
529 } else if (op >= 0xb0 && op <= 0xb7) {
530 imm = read_imm_and_print(&instr, &ilen, 8);
531 switch (op & 7) {
532 case 0: tmp = "al"; break;
533 case 1: tmp = "cl"; break;
534 case 2: tmp = "dl"; break;
535 case 3: tmp = "bl"; break;
536 case 4: tmp = "ah"; break;
537 case 5: tmp = "ch"; break;
538 case 6: tmp = "dh"; break;
539 case 7: tmp = "bh"; break;
540 }
541 SPACES; debug("mov\t%s,0x%x", tmp, imm);
542 } else if (op >= 0xb8 && op <= 0xbf) {
543 imm = read_imm_and_print(&instr, &ilen, mode);
544 SPACES; debug("mov\t%s%s,0x%x", e, reg_names[op & 7], imm);
545 } else if (op == 0xc9) {
546 SPACES; debug("leave");
547 } else if (op == 0xcc) {
548 SPACES; debug("int3");
549 } else if (op == 0xcd) {
550 imm = read_imm_and_print(&instr, &ilen, 8);
551 SPACES; debug("int\t0x%x", imm);
552 } else if (op == 0xce) {
553 SPACES; debug("into");
554 } else if (op == 0xcf) {
555 SPACES; debug("iret");
556 } else if (op == 0xd4) {
557 SPACES; debug("aam");
558 } else if (op == 0xd5) {
559 SPACES; debug("aad");
560 } else if (op == 0xd7) {
561 SPACES; debug("xlat");
562 } else if (op == 0xea) {
563 imm = read_imm_and_print(&instr, &ilen, mode);
564 imm2 = read_imm_and_print(&instr, &ilen, 16);
565 SPACES; debug("jmp\t0x%04x:", imm2);
566 if (mode == 16)
567 debug("0x%04x", imm);
568 else
569 debug("0x%08x", imm);
570 } else if (op == 0xeb) {
571 imm = read_imm_and_print(&instr, &ilen, 8);
572 imm = dumpaddr + ilen + (signed char)imm;
573 SPACES; debug("jmp\t0x%x", imm);
574 } else if (op == 0xf4) {
575 SPACES; debug("hlt");
576 } else if (op == 0xf8) {
577 SPACES; debug("clc");
578 } else if (op == 0xf9) {
579 SPACES; debug("stc");
580 } else if (op == 0xfa) {
581 SPACES; debug("cli");
582 } else if (op == 0xfb) {
583 SPACES; debug("sti");
584 } else if (op == 0xfc) {
585 SPACES; debug("cld");
586 } else if (op == 0xfd) {
587 SPACES; debug("std");
588 } else {
589 SPACES; debug("UNIMPLEMENTED 0x%02x", op);
590 }
591
592 if (rep)
593 debug(" (rep)");
594 if (prefix != NULL)
595 debug(" (%s)", prefix);
596
597 debug("\n");
598 return ilen;
599 }
600
601
602 #define MEMORY_RW x86_memory_rw
603 #define MEM_X86
604 #include "memory_rw.c"
605 #undef MEM_X86
606 #undef MEMORY_RW
607
608
609 /*
610 * x86_load():
611 *
612 * Returns same error code as memory_rw().
613 */
614 static int x86_load(struct cpu *cpu, uint64_t addr, uint64_t *data, int len)
615 {
616 unsigned char databuf[8];
617 int res;
618 uint64_t d;
619
620 res = cpu->memory_rw(cpu, cpu->mem, addr, &databuf[0], len,
621 MEM_READ, CACHE_DATA);
622
623 d = databuf[0];
624 if (len > 1) {
625 d += ((uint64_t)databuf[1] << 8);
626 if (len > 2) {
627 d += ((uint64_t)databuf[2] << 16);
628 d += ((uint64_t)databuf[3] << 24);
629 if (len > 4) {
630 d += ((uint64_t)databuf[4] << 32);
631 d += ((uint64_t)databuf[5] << 40);
632 d += ((uint64_t)databuf[6] << 48);
633 d += ((uint64_t)databuf[7] << 56);
634 }
635 }
636 }
637
638 *data = d;
639 return res;
640 }
641
642
643 /*
644 * x86_store():
645 *
646 * Returns same error code as memory_rw().
647 */
648 static int x86_store(struct cpu *cpu, uint64_t addr, uint64_t data, int len)
649 {
650 unsigned char databuf[8];
651
652 /* x86 is always little-endian: */
653 databuf[0] = data;
654 if (len > 1) {
655 databuf[1] = data >> 8;
656 if (len > 2) {
657 databuf[2] = data >> 16;
658 databuf[3] = data >> 24;
659 if (len > 4) {
660 databuf[4] = data >> 32;
661 databuf[5] = data >> 40;
662 databuf[6] = data >> 48;
663 databuf[7] = data >> 56;
664 }
665 }
666 }
667
668 return cpu->memory_rw(cpu, cpu->mem, addr, &databuf[0], len,
669 MEM_WRITE, CACHE_DATA);
670 }
671
672
673 /*
674 * x86_interrupt():
675 *
676 * NOTE/TODO: Only for 16-bit mode so far.
677 */
678 static int x86_interrupt(struct cpu *cpu, int nr)
679 {
680 uint64_t seg, ofs;
681 const int len = sizeof(uint16_t);
682
683 if (cpu->cd.x86.mode != 16) {
684 fatal("x86 'int' only implemented for 16-bit so far\n");
685 exit(1);
686 }
687
688 /* Read the interrupt vector from beginning of RAM: */
689 cpu->cd.x86.cursegment = 0;
690 x86_load(cpu, nr * 4 + 0, &ofs, sizeof(uint16_t));
691 x86_load(cpu, nr * 4 + 2, &seg, sizeof(uint16_t));
692
693 /* Push flags, cs, and ip (pc): */
694 cpu->cd.x86.cursegment = cpu->cd.x86.s[X86_S_SS];
695 if (x86_store(cpu, cpu->cd.x86.r[X86_R_SP] - len * 1,
696 cpu->cd.x86.rflags, len) != MEMORY_ACCESS_OK)
697 fatal("x86_interrupt(): TODO: how to handle this\n");
698 if (x86_store(cpu, cpu->cd.x86.r[X86_R_SP] - len * 2,
699 cpu->cd.x86.s[X86_S_CS], len) != MEMORY_ACCESS_OK)
700 fatal("x86_interrupt(): TODO: how to handle this\n");
701 if (x86_store(cpu, cpu->cd.x86.r[X86_R_SP] - len * 3, cpu->pc,
702 len) != MEMORY_ACCESS_OK)
703 fatal("x86_interrupt(): TODO: how to handle this\n");
704
705 cpu->cd.x86.r[X86_R_SP] = (cpu->cd.x86.r[X86_R_SP] & ~0xffff)
706 | ((cpu->cd.x86.r[X86_R_SP] - len*3) & 0xffff);
707
708 /* TODO: clear the Interrupt Flag? */
709
710 cpu->cd.x86.s[X86_S_CS] = seg;
711 cpu->pc = ofs;
712
713 return 1;
714 }
715
716
717 /*
718 * x86_cmp():
719 */
720 static void x86_cmp(struct cpu *cpu, uint64_t a, uint64_t b)
721 {
722 if (a == b)
723 cpu->cd.x86.rflags |= X86_FLAGS_ZF;
724 else
725 cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;
726
727 if (a < b)
728 cpu->cd.x86.rflags |= X86_FLAGS_CF;
729 else
730 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
731
732 /* TODO: other bits? */
733 }
734
735
736 /*
737 * x86_test():
738 */
739 static void x86_test(struct cpu *cpu, uint64_t a, uint64_t b)
740 {
741 a &= b;
742
743 if (a == 0)
744 cpu->cd.x86.rflags |= X86_FLAGS_ZF;
745 else
746 cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;
747
748 if ((int32_t)a < 0)
749 cpu->cd.x86.rflags |= X86_FLAGS_SF;
750 else
751 cpu->cd.x86.rflags &= ~X86_FLAGS_SF;
752
753 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
754 cpu->cd.x86.rflags &= ~X86_FLAGS_OF;
755 /* TODO: PF */
756 }
757
758
759 /*
760 * x86_cpu_run_instr():
761 *
762 * Execute one instruction on a specific CPU.
763 *
764 * Return value is the number of instructions executed during this call,
765 * 0 if no instruction was executed.
766 */
767 int x86_cpu_run_instr(struct emul *emul, struct cpu *cpu)
768 {
769 int i, r, rep = 0, op, len, diff, mode = cpu->cd.x86.mode;
770 int mode_addr = mode, nprefixbytes = 0;
771 uint32_t imm, imm2, value;
772 unsigned char buf[16];
773 unsigned char *instr = buf;
774 uint64_t newpc = cpu->pc;
775 unsigned char databuf[8];
776 uint64_t tmp;
777
778 /* Check PC against breakpoints: */
779 if (!single_step)
780 for (i=0; i<cpu->machine->n_breakpoints; i++)
781 if (cpu->pc == cpu->machine->breakpoint_addr[i]) {
782 fatal("Breakpoint reached, pc=0x%llx",
783 (long long)cpu->pc);
784 single_step = 1;
785 return 0;
786 }
787
788 /* 16-bit BIOS emulation: */
789 if (mode == 16 && ((newpc + (cpu->cd.x86.s[X86_S_CS] << 4)) & 0xff000)
790 == 0xf8000 && cpu->machine->prom_emulation) {
791 pc_bios_emul(cpu);
792 return 1;
793 }
794
795 /* Read an instruction from memory: */
796 cpu->cd.x86.cursegment = cpu->cd.x86.s[X86_S_CS];
797
798 r = cpu->memory_rw(cpu, cpu->mem, cpu->pc, &buf[0], sizeof(buf),
799 MEM_READ, CACHE_INSTRUCTION);
800 if (!r)
801 return 0;
802
803 if (cpu->machine->instruction_trace)
804 x86_cpu_disassemble_instr(cpu, instr, 1, 0, 0);
805
806 /* All instructions are at least one byte long :-) */
807 newpc ++;
808
809 /* Default is to use the data segment, or the stack segment: */
810 cpu->cd.x86.cursegment = cpu->cd.x86.s[X86_S_DS];
811
812 /* Any prefix? */
813 for (;;) {
814 if (instr[0] == 0x66) {
815 if (mode == 16)
816 mode = 32;
817 else
818 mode = 16;
819 } else if (instr[0] == 0x67) {
820 if (mode_addr == 16)
821 mode_addr = 32;
822 else
823 mode_addr = 16;
824 } else if (instr[0] == 0xf3)
825 rep = 1;
826 else
827 break;
828 /* TODO: repnz, lock etc */
829 instr ++;
830 newpc ++;
831 if (++nprefixbytes > 4) {
832 fatal("x86: too many prefix bytes at ");
833 print_csip(cpu); fatal("\n");
834 cpu->running = 0;
835 return 0;
836 }
837 }
838
839 op = instr[0];
840 instr ++;
841
842 if (op >= 0x40 && op <= 0x4f) {
843 if (op < 0x48)
844 cpu->cd.x86.r[op & 7] = modify(cpu->cd.x86.r[op & 7],
845 cpu->cd.x86.r[op & 7] + 1);
846 else
847 cpu->cd.x86.r[op & 7] = modify(cpu->cd.x86.r[op & 7],
848 cpu->cd.x86.r[op & 7] - 1);
849 /* TODO: flags etc */
850 } else if (op == 0x90) { /* NOP */
851 } else if (op >= 0xb8 && op <= 0xbf) {
852 imm = read_imm(&instr, &newpc, mode);
853 cpu->cd.x86.r[op & 7] = imm;
854 } else if (op == 0xcc) { /* INT3 */
855 cpu->pc = newpc;
856 return x86_interrupt(cpu, 3);
857 } else if (op == 0xcd) { /* INT */
858 imm = read_imm(&instr, &newpc, 8);
859 cpu->pc = newpc;
860 return x86_interrupt(cpu, imm);
861 } else if (op == 0xea) { /* JMP seg:ofs */
862 imm = read_imm(&instr, &newpc, mode);
863 imm2 = read_imm(&instr, &newpc, 16);
864 cpu->cd.x86.s[X86_S_CS] = imm2;
865 newpc = modify(cpu->pc, imm);
866 } else if (op == 0xeb) { /* JMP short */
867 imm = read_imm(&instr, &newpc, 8);
868 newpc = modify(newpc, newpc + (signed char)imm);
869 } else if (op == 0xf8) { /* CLC */
870 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
871 } else if (op == 0xf9) { /* STC */
872 cpu->cd.x86.rflags |= X86_FLAGS_CF;
873 } else if (op == 0xfa) { /* CLI */
874 cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
875 } else if (op == 0xfb) { /* STI */
876 cpu->cd.x86.rflags |= X86_FLAGS_IF;
877 } else if (op == 0xfc) { /* CLD */
878 cpu->cd.x86.rflags &= ~X86_FLAGS_DF;
879 } else if (op == 0xfd) { /* STD */
880 cpu->cd.x86.rflags |= X86_FLAGS_DF;
881 } else {
882 fatal("x86_cpu_run_instr(): unimplemented opcode 0x%02x"
883 " at ", op); print_csip(cpu); fatal("\n");
884 cpu->running = 0;
885 return 0;
886 }
887
888 cpu->pc = newpc;
889
890 return 1;
891 }
892
893
894 #define CPU_RUN x86_cpu_run
895 #define CPU_RINSTR x86_cpu_run_instr
896 #define CPU_RUN_X86
897 #include "cpu_run.c"
898 #undef CPU_RINSTR
899 #undef CPU_RUN_X86
900 #undef CPU_RUN
901
902
903 /*
904 * x86_cpu_family_init():
905 *
906 * Fill in the cpu_family struct for x86.
907 */
908 int x86_cpu_family_init(struct cpu_family *fp)
909 {
910 fp->name = "x86";
911 fp->cpu_new = x86_cpu_new;
912 fp->list_available_types = x86_cpu_list_available_types;
913 fp->register_match = x86_cpu_register_match;
914 fp->disassemble_instr = x86_cpu_disassemble_instr;
915 fp->register_dump = x86_cpu_register_dump;
916 fp->run = x86_cpu_run;
917 fp->dumpinfo = x86_cpu_dumpinfo;
918 /* fp->show_full_statistics = x86_cpu_show_full_statistics; */
919 /* fp->tlbdump = x86_cpu_tlbdump; */
920 /* fp->interrupt = x86_cpu_interrupt; */
921 /* fp->interrupt_ack = x86_cpu_interrupt_ack; */
922 return 1;
923 }
924
925 #endif /* ENABLE_X86 */

  ViewVC Help
Powered by ViewVC 1.1.26