summaryrefslogtreecommitdiff
path: root/exec-i386.c
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-03-06 23:23:54 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-03-06 23:23:54 +0000
commit7d13299d07a9c3c42277207ae7a691f0501a70b2 (patch)
tree981b791299c674712bacc00292de4afc6cc436ec /exec-i386.c
parent1017ebe9cb38ae034b0e7c6c449abe2c9b5284fb (diff)
downloadqemu-7d13299d07a9c3c42277207ae7a691f0501a70b2.tar.gz
added translation cache
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@25 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'exec-i386.c')
-rw-r--r--exec-i386.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/exec-i386.c b/exec-i386.c
new file mode 100644
index 0000000000..c067685095
--- /dev/null
+++ b/exec-i386.c
@@ -0,0 +1,213 @@
+/*
+ * i386 emulator main execution loop
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "exec-i386.h"
+
+#define DEBUG_EXEC
+#define DEBUG_FLUSH
+
+/* main execution loop */
+
+/* maximum total translate dcode allocated */
+#define CODE_GEN_BUFFER_SIZE (2048 * 1024)
+//#define CODE_GEN_BUFFER_SIZE (128 * 1024)
+#define CODE_GEN_MAX_SIZE 65536
+#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
+
+/* threshold to flush the translated code buffer */
+#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
+
+#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64)
+#define CODE_GEN_HASH_BITS 15
+#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS)
+typedef struct TranslationBlock {
+ unsigned long pc; /* simulated PC corresponding to this block */
+ uint8_t *tc_ptr; /* pointer to the translated code */
+ struct TranslationBlock *hash_next; /* next matching block */
+} TranslationBlock;
+
+TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
+TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
+int nb_tbs;
+
+uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
+uint8_t *code_gen_ptr;
+
+#ifdef DEBUG_EXEC
+static const char *cc_op_str[] = {
+ "DYNAMIC",
+ "EFLAGS",
+ "MUL",
+ "ADDB",
+ "ADDW",
+ "ADDL",
+ "ADCB",
+ "ADCW",
+ "ADCL",
+ "SUBB",
+ "SUBW",
+ "SUBL",
+ "SBBB",
+ "SBBW",
+ "SBBL",
+ "LOGICB",
+ "LOGICW",
+ "LOGICL",
+ "INCB",
+ "INCW",
+ "INCL",
+ "DECB",
+ "DECW",
+ "DECL",
+ "SHLB",
+ "SHLW",
+ "SHLL",
+ "SARB",
+ "SARW",
+ "SARL",
+};
+
+static void cpu_x86_dump_state(void)
+{
+ int eflags;
+ eflags = cc_table[CC_OP].compute_all();
+ eflags |= (DF & DIRECTION_FLAG);
+ fprintf(logfile,
+ "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
+ "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
+ "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n",
+ env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX],
+ env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP],
+ env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
+ eflags & DIRECTION_FLAG ? 'D' : '-',
+ eflags & CC_O ? 'O' : '-',
+ eflags & CC_S ? 'S' : '-',
+ eflags & CC_Z ? 'Z' : '-',
+ eflags & CC_A ? 'A' : '-',
+ eflags & CC_P ? 'P' : '-',
+ eflags & CC_C ? 'C' : '-'
+ );
+#if 1
+ fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n",
+ (double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
+#endif
+}
+
+#endif
+
+void cpu_x86_tblocks_init(void)
+{
+ if (!code_gen_ptr) {
+ code_gen_ptr = code_gen_buffer;
+ }
+}
+
+/* flush all the translation blocks */
+static void tb_flush(void)
+{
+ int i;
+#ifdef DEBUG_FLUSH
+ printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
+ code_gen_ptr - code_gen_buffer,
+ nb_tbs,
+ (code_gen_ptr - code_gen_buffer) / nb_tbs);
+#endif
+ nb_tbs = 0;
+ for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
+ tb_hash[i] = NULL;
+ code_gen_ptr = code_gen_buffer;
+ /* XXX: flush processor icache at this point */
+}
+
+/* find a translation block in the translation cache. If not found,
+ allocate a new one */
+static inline TranslationBlock *tb_find_and_alloc(unsigned long pc)
+{
+ TranslationBlock **ptb, *tb;
+ unsigned int h;
+
+ h = pc & (CODE_GEN_HASH_SIZE - 1);
+ ptb = &tb_hash[h];
+ for(;;) {
+ tb = *ptb;
+ if (!tb)
+ break;
+ if (tb->pc == pc)
+ return tb;
+ ptb = &tb->hash_next;
+ }
+ if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
+ (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
+ tb_flush();
+ tb = &tbs[nb_tbs++];
+ *ptb = tb;
+ tb->pc = pc;
+ tb->tc_ptr = NULL;
+ tb->hash_next = NULL;
+ return tb;
+}
+
+int cpu_x86_exec(CPUX86State *env1)
+{
+ int saved_T0, saved_T1, saved_A0;
+ CPUX86State *saved_env;
+ int code_gen_size, ret;
+ void (*gen_func)(void);
+ TranslationBlock *tb;
+ uint8_t *tc_ptr;
+
+ /* first we save global registers */
+ saved_T0 = T0;
+ saved_T1 = T1;
+ saved_A0 = A0;
+ saved_env = env;
+ env = env1;
+
+ /* prepare setjmp context for exception handling */
+ if (setjmp(env->jmp_env) == 0) {
+ for(;;) {
+#ifdef DEBUG_EXEC
+ if (loglevel) {
+ cpu_x86_dump_state();
+ }
+#endif
+ tb = tb_find_and_alloc((unsigned long)env->pc);
+ tc_ptr = tb->tc_ptr;
+ if (!tb->tc_ptr) {
+ /* if no translated code available, then translate it now */
+ tc_ptr = code_gen_ptr;
+ cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
+ &code_gen_size, (uint8_t *)env->pc);
+ tb->tc_ptr = tc_ptr;
+ code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+ }
+ /* execute the generated code */
+ gen_func = (void *)tc_ptr;
+ gen_func();
+ }
+ }
+ ret = env->exception_index;
+
+ /* restore global registers */
+ T0 = saved_T0;
+ T1 = saved_T1;
+ A0 = saved_A0;
+ env = saved_env;
+ return ret;
+}