sparc support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@119 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/cpu-i386.h b/cpu-i386.h
index ca33791..4b8aef1 100644
--- a/cpu-i386.h
+++ b/cpu-i386.h
@@ -180,6 +180,12 @@
 
     /* emulator internal variables */
     CPU86_LDouble ft0;
+    union {
+	float f;
+        double d;
+	int i32;
+        int64_t i64;
+    } fp_convert;
     
     /* segments */
     uint32_t segs[6]; /* selector values */
diff --git a/dyngen.c b/dyngen.c
index 66f735b..6d06c73 100644
--- a/dyngen.c
+++ b/dyngen.c
@@ -65,6 +65,20 @@
 #define elf_check_arch(x) ((x) == EM_IA_64)
 #define ELF_USES_RELOCA
 
+#elif defined(HOST_SPARC)
+
+#define ELF_CLASS	ELFCLASS32
+#define ELF_ARCH	EM_SPARC
+#define elf_check_arch(x) ((x) == EM_SPARC || (x) == EM_SPARC32PLUS)
+#define ELF_USES_RELOCA
+
+#elif defined(HOST_SPARC64)
+
+#define ELF_CLASS	ELFCLASS64
+#define ELF_ARCH	EM_SPARCV9
+#define elf_check_arch(x) ((x) == EM_SPARCV9)
+#define ELF_USES_RELOCA
+
 #else
 #error unsupported CPU - please update the code
 #endif
@@ -326,6 +340,47 @@
             copy_size = p - p_start;
 	}
         break;
+    case EM_SPARC:
+    case EM_SPARC32PLUS:
+	{
+            uint8_t *p;
+            p = (void *)(p_end - 8);
+            if (p <= p_start)
+                error("empty code for %s", name);
+	    if (get32((uint32_t *)(p_start + 0x0)) != 0x9de3bf98)
+                error("save %%sp,-104,%%sp expected at the start of %s "
+		      "found [%08x]",
+		      name, get32((uint32_t *)(p_start + 0x0)));
+            if (get32((uint32_t *)(p + 0x0)) != 0x81c7e008 ||
+		get32((uint32_t *)(p + 0x4)) != 0x81e80000)
+                error("ret; restore; expected at the end of %s found [%08x:%08x]",
+		      name,
+		      get32((uint32_t *)(p + 0x0)),
+		      get32((uint32_t *)(p + 0x4)));
+
+            copy_size = p - p_start;
+	}
+	break;
+    case EM_SPARCV9:
+	{
+            uint8_t *p;
+            p = (void *)(p_end - 8);
+            if (p <= p_start)
+                error("empty code for %s", name);
+	    if (get32((uint32_t *)(p_start + 0x0)) != 0x9de3bf40)
+                error("save %%sp,-192,%%sp expected at the start of %s "
+		      "found [%08x]",
+		      name, get32((uint32_t *)(p_start + 0x0)));
+            if (get32((uint32_t *)(p + 0x0)) != 0x81cfe008 ||
+		get32((uint32_t *)(p + 0x4)) != 0x01000000)
+                error("rett %%i7+8; nop; expected at the end of %s "
+		      "found [%08x:%08x]",
+		      name,
+		      get32((uint32_t *)(p + 0x0)),
+		      get32((uint32_t *)(p + 0x4)));
+            copy_size = p - p_start;
+	}
+	break;
     default:
 	error("unknown ELF architecture");
     }
@@ -375,6 +430,14 @@
             if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
                 sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
                 if (!strstart(sym_name, "__op_param", &p)) {
+#if defined(HOST_SPARC)
+		    if (sym_name[0] == '.') {
+			fprintf(outfile,
+				"extern char __dot_%s __asm__(\"%s\");\n",
+				sym_name+1, sym_name);
+			continue;
+		    }
+#endif
                     fprintf(outfile, "extern char %s;\n", sym_name);
                 }
             }
@@ -554,6 +617,126 @@
                     }
                 }
             }
+#elif defined(HOST_SPARC)
+            {
+                char name[256];
+                int type;
+                int addend;
+                for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+                    if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
+                        sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
+                        if (strstart(sym_name, "__op_param", &p)) {
+                            snprintf(name, sizeof(name), "param%s", p);
+                        } else {
+				if (sym_name[0] == '.')
+					snprintf(name, sizeof(name),
+						 "(long)(&__dot_%s)",
+						 sym_name + 1);
+				else
+					snprintf(name, sizeof(name),
+						 "(long)(&%s)", sym_name);
+                        }
+                        type = ELF32_R_TYPE(rel->r_info);
+                        addend = rel->r_addend;
+                        switch(type) {
+                        case R_SPARC_32:
+                            fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
+                                    rel->r_offset - offset, name, addend);
+			    break;
+			case R_SPARC_HI22:
+                            fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3fffff) "
+				    " | ((%s + %d) & 0x3fffff);\n",
+                                    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+			case R_SPARC_LO10:
+                            fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3ff) "
+				    " | ((%s + %d) & 0x3ff);\n",
+                                    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+			case R_SPARC_WDISP30:
+			    fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3fffffff) "
+				    " | ((((%s + %d) - (long)gen_code_ptr)>>2) "
+				    "    & 0x3fffffff);\n",
+				    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+                        default:
+                            error("unsupported sparc relocation (%d)", type);
+                        }
+                    }
+                }
+            }
+#elif defined(HOST_SPARC64)
+            {
+                char name[256];
+                int type;
+                int addend;
+                for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+                    if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
+                        sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
+                        if (strstart(sym_name, "__op_param", &p)) {
+                            snprintf(name, sizeof(name), "param%s", p);
+                        } else {
+                            snprintf(name, sizeof(name), "(long)(&%s)", sym_name);
+                        }
+                        type = ELF64_R_TYPE(rel->r_info);
+                        addend = rel->r_addend;
+                        switch(type) {
+                        case R_SPARC_32:
+                            fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
+                                    rel->r_offset - offset, name, addend);
+			    break;
+			case R_SPARC_HI22:
+                            fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3fffff) "
+				    " | ((%s + %d) & 0x3fffff);\n",
+                                    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+			case R_SPARC_LO10:
+                            fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3ff) "
+				    " | ((%s + %d) & 0x3ff);\n",
+                                    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+			case R_SPARC_WDISP30:
+			    fprintf(outfile,
+				    "    *(uint32_t *)(gen_code_ptr + %d) = "
+				    "((*(uint32_t *)(gen_code_ptr + %d)) "
+				    " & ~0x3fffffff) "
+				    " | ((((%s + %d) - (long)gen_code_ptr)>>2) "
+				    "    & 0x3fffffff);\n",
+				    rel->r_offset - offset,
+				    rel->r_offset - offset,
+				    name, addend);
+			    break;
+                        default:
+			    error("unsupported sparc64 relocation (%d)", type);
+                        }
+                    }
+                }
+            }
 #else
 #error unsupported CPU
 #endif
@@ -759,6 +942,14 @@
     case EM_IA_64:
         fprintf(outfile, "*((uint32_t *)gen_code_ptr)++ = 0x00840008; /* br.ret.sptk.many b0;; */\n");
         break;
+    case EM_SPARC:
+    case EM_SPARC32PLUS:
+    case EM_SPARCV9:
+	/* Fill the delay slot. */
+	fprintf(outfile, "*((uint32_t *)gen_code_ptr) = *((uint32_t *)gen_code_ptr - 1); /* delay slot */\n");
+	fprintf(outfile, "*((uint32_t *)gen_code_ptr - 1) = 0x81c3e008; /* retl */\n");
+	fprintf(outfile, "gen_code_ptr++;\n");
+        break;
     default:
 	error("unknown ELF architecture");
     }
diff --git a/exec-i386.c b/exec-i386.c
index 508d068..ecb7adc 100644
--- a/exec-i386.c
+++ b/exec-i386.c
@@ -121,6 +121,20 @@
 }
 #endif
 
+#ifdef __sparc__
+static inline int testandset (int *p)
+{
+	int ret;
+
+	__asm__ __volatile__("ldstub	[%1], %0"
+			     : "=r" (ret)
+			     : "r" (p)
+			     : "memory");
+
+	return (ret ? 1 : 0);
+}
+#endif
+
 int global_cpu_lock = 0;
 
 void cpu_lock(void)
diff --git a/exec-i386.h b/exec-i386.h
index b4f5a00..4f7f4ce 100644
--- a/exec-i386.h
+++ b/exec-i386.h
@@ -93,6 +93,7 @@
 register unsigned int T1 asm("l1");
 register unsigned int A0 asm("l2");
 register struct CPUX86State *env asm("l3");
+#define USE_FP_CONVERT
 #endif
 #ifdef __s390__
 register unsigned int T0 asm("r7");
@@ -160,6 +161,10 @@
 #define ST(n)  (env->fpregs[(env->fpstt + (n)) & 7])
 #define ST1    ST(1)
 
+#ifdef USE_FP_CONVERT
+#define FP_CONVERT  (env->fp_convert)
+#endif
+
 extern int __op_param1, __op_param2, __op_param3;
 #define PARAM1 ((long)(&__op_param1))
 #define PARAM2 ((long)(&__op_param2))
diff --git a/op-i386.c b/op-i386.c
index 323fce3..ede63ea 100644
--- a/op-i386.c
+++ b/op-i386.c
@@ -1605,12 +1605,22 @@
 
 void OPPROTO op_flds_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldl((void *)A0);
+    FT0 = FP_CONVERT.f;
+#else
     FT0 = ldfl((void *)A0);
+#endif
 }
 
 void OPPROTO op_fldl_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = ldq((void *)A0);
+    FT0 = FP_CONVERT.d;
+#else
     FT0 = ldfq((void *)A0);
+#endif
 }
 
 /* helpers are needed to avoid static constant reference. XXX: find a better way */
@@ -1650,17 +1660,32 @@
 
 void OPPROTO op_fild_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldsw((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     FT0 = (CPU86_LDouble)ldsw((void *)A0);
+#endif
 }
 
 void OPPROTO op_fildl_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = (int32_t) ldl((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     FT0 = (CPU86_LDouble)((int32_t)ldl((void *)A0));
+#endif
 }
 
 void OPPROTO op_fildll_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = (int64_t) ldq((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i64;
+#else
     FT0 = (CPU86_LDouble)((int64_t)ldq((void *)A0));
+#endif
 }
 #endif
 
@@ -1668,12 +1693,22 @@
 
 void OPPROTO op_flds_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldl((void *)A0);
+    ST0 = FP_CONVERT.f;
+#else
     ST0 = ldfl((void *)A0);
+#endif
 }
 
 void OPPROTO op_fldl_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = ldq((void *)A0);
+    ST0 = FP_CONVERT.d;
+#else
     ST0 = ldfq((void *)A0);
+#endif
 }
 
 #ifdef USE_X86LDOUBLE
@@ -1738,17 +1773,32 @@
 
 void OPPROTO op_fild_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldsw((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     ST0 = (CPU86_LDouble)ldsw((void *)A0);
+#endif
 }
 
 void OPPROTO op_fildl_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = (int32_t) ldl((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     ST0 = (CPU86_LDouble)((int32_t)ldl((void *)A0));
+#endif
 }
 
 void OPPROTO op_fildll_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = (int64_t) ldq((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i64;
+#else
     ST0 = (CPU86_LDouble)((int64_t)ldq((void *)A0));
+#endif
 }
 
 #endif
@@ -1757,7 +1807,12 @@
 
 void OPPROTO op_fsts_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.d = ST0;
+    stfl((void *)A0, FP_CONVERT.f);
+#else
     stfl((void *)A0, (float)ST0);
+#endif
 }
 
 void OPPROTO op_fstl_ST0_A0(void)
@@ -1792,22 +1847,43 @@
 
 void OPPROTO op_fist_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int val;
-    val = lrint(ST0);
+
+    d = ST0;
+    val = lrint(d);
     stw((void *)A0, val);
 }
 
 void OPPROTO op_fistl_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int val;
-    val = lrint(ST0);
+
+    d = ST0;
+    val = lrint(d);
     stl((void *)A0, val);
 }
 
 void OPPROTO op_fistll_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int64_t val;
-    val = llrint(ST0);
+
+    d = ST0;
+    val = llrint(d);
     stq((void *)A0, val);
 }
 
diff --git a/translate-i386.c b/translate-i386.c
index e23ff67..1cdad2d 100644
--- a/translate-i386.c
+++ b/translate-i386.c
@@ -89,6 +89,21 @@
 }
 #endif
 
+#ifdef __sparc__
+
+static void inline flush_icache_range(unsigned long start, unsigned long stop)
+{
+	unsigned long p;
+
+	p = start & ~(8UL - 1UL);
+	stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
+
+	for (; p < stop; p += 8)
+		__asm__ __volatile__("flush\t%0" : : "r" (p));
+}
+
+#endif
+
 extern FILE *logfile;
 extern int loglevel;