--- trunk/src/cpus/cpu_sparc.c 2007/10/08 16:19:37 22 +++ trunk/src/cpus/cpu_sparc.c 2007/10/08 16:20:26 28 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2006 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_sparc.c,v 1.11 2005/12/11 21:34:43 debug Exp $ + * $Id: cpu_sparc.c,v 1.34 2006/07/16 13:32:26 debug Exp $ * * SPARC CPU emulation. */ @@ -43,10 +43,12 @@ #define DYNTRANS_DUALMODE_32 +#define DYNTRANS_DELAYSLOT #include "tmp_sparc_head.c" static char *sparc_regnames[N_SPARC_REG] = SPARC_REG_NAMES; +static char *sparc_pregnames[N_SPARC_PREG] = SPARC_PREG_NAMES; static char *sparc_regbranch_names[N_SPARC_REGBRANCH_TYPES] = SPARC_REGBRANCH_NAMES; static char *sparc_branch_names[N_SPARC_BRANCH_TYPES] = SPARC_BRANCH_NAMES; @@ -86,7 +88,10 @@ cpu->byte_order = EMUL_BIG_ENDIAN; cpu->is_32bit = (cpu->cd.sparc.cpu_type.bits == 32)? 1 : 0; + cpu->instruction_has_delayslot = sparc_cpu_instruction_has_delayslot; + if (cpu->is_32bit) { + cpu->run_instr = sparc32_run_instr; cpu->update_translation_table = sparc32_update_translation_table; cpu->invalidate_translation_caches = @@ -94,6 +99,7 @@ cpu->invalidate_code_translation = sparc32_invalidate_code_translation; } else { + cpu->run_instr = sparc_run_instr; cpu->update_translation_table = sparc_update_translation_table; cpu->invalidate_translation_caches = sparc_invalidate_translation_caches; @@ -125,6 +131,22 @@ } } + /* After a reset, the Tick register is not readable by user code: */ + cpu->cd.sparc.tick |= SPARC_TICK_NPT; + + /* Insert number of Windows and Trap levels into the version reg.: */ + cpu->cd.sparc.ver |= MAXWIN | (MAXTL << SPARC_VER_MAXTL_SHIFT); + + /* Misc. initial settings suitable for userland emulation: */ + cpu->cd.sparc.cansave = cpu->cd.sparc.cpu_type.nwindows - 1; + cpu->cd.sparc.cleanwin = cpu->cd.sparc.cpu_type.nwindows / 2; + + if (cpu->cd.sparc.cpu_type.nwindows >= MAXWIN) { + fatal("Fatal internal error: nwindows = %1 is more than %i\n", + cpu->cd.sparc.cpu_type.nwindows, MAXWIN); + exit(1); + } + return 1; } @@ -142,10 +164,10 @@ i = 0; while (tdefs[i].name != NULL) { debug("%s", tdefs[i].name); - for (j=10 - strlen(tdefs[i].name); j>0; j--) + for (j=16 - strlen(tdefs[i].name); j>0; j--) debug(" "); i++; - if ((i % 6) == 0 || tdefs[i].name == NULL) + if ((i % 4) == 0 || tdefs[i].name == NULL) debug("\n"); } } @@ -182,17 +204,44 @@ debug("cpu%i: pc = 0x", x); if (bits32) - debug("%08x", (int)cpu->pc); + debug("%08"PRIx32, (uint32_t) cpu->pc); else - debug("%016llx", (long long)cpu->pc); + debug("%016"PRIx64, (uint64_t) cpu->pc); debug(" <%s>\n", symbol != NULL? symbol : " no symbol "); + debug("cpu%i: y = 0x%08"PRIx32" ", + x, (uint32_t)cpu->cd.sparc.y); + debug("icc = "); + debug(cpu->cd.sparc.ccr & SPARC_CCR_N? "N" : "n"); + debug(cpu->cd.sparc.ccr & SPARC_CCR_Z? "Z" : "z"); + debug(cpu->cd.sparc.ccr & SPARC_CCR_V? "V" : "v"); + debug(cpu->cd.sparc.ccr & SPARC_CCR_C? "C" : "c"); + if (!bits32) { + debug(" xcc = "); + debug((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) + & SPARC_CCR_N? "N" : "n"); + debug((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) + & SPARC_CCR_Z? "Z" : "z"); + debug((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) + & SPARC_CCR_V? "V" : "v"); + debug((cpu->cd.sparc.ccr >> SPARC_CCR_XCC_SHIFT) + & SPARC_CCR_C? "C" : "c"); + } + debug("\n"); + + if (bits32) + debug("cpu%i: psr = 0x%08"PRIx32"\n", + x, (uint32_t) cpu->cd.sparc.psr); + else + debug("cpu%i: pstate = 0x%016"PRIx64"\n", + x, (uint64_t) cpu->cd.sparc.pstate); + if (bits32) { for (i=0; i> 1) & 15) | ((i&1) << 4); if ((i & 1) == 0) debug("cpu%i: ", x); + /* Skip the zero register: */ - if (r==0) { + if (i == SPARC_ZEROREG) { debug(" "); continue; } + debug("%s = ", sparc_regnames[r]); - debug("0x%016llx", (long long) + debug("0x%016"PRIx64, (uint64_t) cpu->cd.sparc.r[r]); + if ((i & 1) < 1) debug(" "); else @@ -232,20 +284,142 @@ void sparc_cpu_register_match(struct machine *m, char *name, int writeflag, uint64_t *valuep, int *match_register) { - int cpunr = 0; + int i, cpunr = 0; /* CPU number: */ - /* TODO */ - /* Register name: */ + for (i=0; icpus[cpunr]->cd.sparc.r[i] = *valuep; + else + *valuep = m->cpus[cpunr]->cd.sparc.r[i]; + *match_register = 1; + } + } + if (strcasecmp(name, "pc") == 0) { if (writeflag) { m->cpus[cpunr]->pc = *valuep; - } else + } else { *valuep = m->cpus[cpunr]->pc; + } *match_register = 1; } + + if (strcasecmp(name, "y") == 0) { + if (writeflag) { + m->cpus[cpunr]->cd.sparc.y = (uint32_t) *valuep; + } else { + *valuep = (uint32_t) m->cpus[cpunr]->cd.sparc.y; + } + *match_register = 1; + } + + if (*match_register && m->cpus[cpunr]->is_32bit) + (*valuep) &= 0xffffffffULL; +} + + +/* + * sparc_cpu_tlbdump(): + * + * Called from the debugger to dump the TLB in a readable format. + * x is the cpu number to dump, or -1 to dump all CPUs. + * + * If rawflag is nonzero, then the TLB contents isn't formated nicely, + * just dumped. + */ +void sparc_cpu_tlbdump(struct machine *m, int x, int rawflag) +{ +} + + +static void add_response_word(struct cpu *cpu, char *r, uint64_t value, + size_t maxlen, int len) +{ + char *format = (len == 4)? "%08"PRIx64 : "%016"PRIx64; + if (len == 4) + value &= 0xffffffffULL; + if (cpu->byte_order == EMUL_LITTLE_ENDIAN) { + if (len == 4) { + value = ((value & 0xff) << 24) + + ((value & 0xff00) << 8) + + ((value & 0xff0000) >> 8) + + ((value & 0xff000000) >> 24); + } else { + value = ((value & 0xff) << 56) + + ((value & 0xff00) << 40) + + ((value & 0xff0000) << 24) + + ((value & 0xff000000ULL) << 8) + + ((value & 0xff00000000ULL) >> 8) + + ((value & 0xff0000000000ULL) >> 24) + + ((value & 0xff000000000000ULL) >> 40) + + ((value & 0xff00000000000000ULL) >> 56); + } + } + snprintf(r + strlen(r), maxlen - strlen(r), format, (uint64_t)value); +} + + +/* + * sparc_cpu_gdb_stub(): + * + * Execute a "remote GDB" command. Returns a newly allocated response string + * on success, NULL on failure. + */ +char *sparc_cpu_gdb_stub(struct cpu *cpu, char *cmd) +{ + if (strcmp(cmd, "g") == 0) { + int i; + char *r; + size_t wlen = cpu->is_32bit? + sizeof(uint32_t) : sizeof(uint64_t); + size_t len = 1 + 76 * wlen; + r = malloc(len); + if (r == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + r[0] = '\0'; + /* TODO */ + for (i=0; i<128; i++) + add_response_word(cpu, r, i, len, wlen); + return r; + } + + if (cmd[0] == 'p') { + int regnr = strtol(cmd + 1, NULL, 16); + size_t wlen = sizeof(uint32_t); + /* TODO: cpu->is_32bit? sizeof(uint32_t) : sizeof(uint64_t); */ + size_t len = 2 * wlen + 1; + char *r = malloc(len); + r[0] = '\0'; + if (regnr >= 0 && regnr < N_SPARC_REG) { + add_response_word(cpu, r, + cpu->cd.sparc.r[regnr], len, wlen); + } else if (regnr == 0x44) { + add_response_word(cpu, r, cpu->pc, len, wlen); +/* TODO: +20..3f = f0..f31 +40 = y +41 = psr +42 = wim +43 = tbr +45 = npc +46 = fsr +47 = csr +*/ + } else { + /* Unimplemented: */ + add_response_word(cpu, r, 0xcc000 + regnr, len, wlen); + } + return r; + } + + fatal("sparc_cpu_gdb_stub(): TODO\n"); + return NULL; } @@ -270,6 +444,43 @@ /* + * sparc_cpu_instruction_has_delayslot(): + * + * Return 1 if an opcode is a branch, 0 otherwise. + */ +int sparc_cpu_instruction_has_delayslot(struct cpu *cpu, unsigned char *ib) +{ + uint32_t iword = *((uint32_t *)&ib[0]); + int hi2, op2; + + iword = BE32_TO_HOST(iword); + + hi2 = iword >> 30; + op2 = (hi2 == 0)? ((iword >> 22) & 7) : ((iword >> 19) & 0x3f); + + switch (hi2) { + case 0: /* conditional branch */ + switch (op2) { + case 1: + case 2: + case 3: return 1; + } + break; + case 1: /* call */ + return 1; + case 2: /* misc alu instructions */ + switch (op2) { + case 56:/* jump and link */ + return 1; + } + break; + } + + return 0; +} + + +/* * sparc_cpu_disassemble_instr(): * * Convert an instruction word into human readable format, for instruction @@ -282,13 +493,13 @@ * cpu->pc for relative addresses. */ int sparc_cpu_disassemble_instr(struct cpu *cpu, unsigned char *instr, - int running, uint64_t dumpaddr, int bintrans) + int running, uint64_t dumpaddr) { uint64_t offset, tmp; uint32_t iword; int hi2, op2, rd, rs1, rs2, siconst, btype, tmps, no_rd = 0; int asi, no_rs1 = 0, no_rs2 = 0, jmpl = 0, shift_x = 0, cc, p; - char *symbol, *mnem; + char *symbol, *mnem, *rd_name, *rs_name; if (running) dumpaddr = cpu->pc; @@ -302,14 +513,20 @@ debug("cpu%i: ", cpu->cpu_id); if (cpu->is_32bit) - debug("%08x", (int)dumpaddr); + debug("%08"PRIx32, (uint32_t) dumpaddr); else - debug("%016llx", (long long)dumpaddr); + debug("%016"PRIx64, (uint64_t) dumpaddr); iword = *(uint32_t *)&instr[0]; iword = BE32_TO_HOST(iword); - debug(": %08x\t", iword); + debug(": %08x", iword); + + if (running && cpu->delay_slot) + debug(" (d)"); + + debug("\t"); + /* * Decode the instruction: @@ -370,7 +587,7 @@ } tmp = (int64_t)(int32_t)tmps; tmp += dumpaddr; - debug("0x%llx", (long long)tmp); + debug("0x%"PRIx64, (uint64_t) tmp); symbol = get_symbol_name(&cpu->machine-> symbol_context, tmp, &offset); if (symbol != NULL) @@ -391,7 +608,7 @@ case 1: tmp = (int32_t)iword << 2; tmp += dumpaddr; - debug("call\t0x%llx", (long long)tmp); + debug("call\t0x%"PRIx64, (uint64_t) tmp); symbol = get_symbol_name(&cpu->machine->symbol_context, tmp, &offset); if (symbol != NULL) @@ -399,6 +616,8 @@ break; case 2: mnem = sparc_alu_names[op2]; + rs_name = sparc_regnames[rs1]; + rd_name = sparc_regnames[rd]; switch (op2) { case 0: /* add */ if (rd == rs1 && (iword & 0x3fff) == 0x2001) { @@ -433,18 +652,63 @@ } else siconst &= 0x1f; break; + case 40:/* rd on pre-sparcv9, membar etc on sparcv9 */ + no_rs2 = 1; + rs_name = "UNIMPLEMENTED"; + switch (rs1) { + case 0: rs_name = "y"; break; + case 2: rs_name = "ccr"; break; + case 3: rs_name = "asi"; break; + case 4: rs_name = "tick"; break; + case 5: rs_name = "pc"; break; + case 6: rs_name = "fprs"; break; + case 15:/* membar etc. */ + if ((iword >> 13) & 1) { + no_rd = 1; + mnem = "membar"; + rs_name = "#TODO"; + } + break; + case 23:rs_name = "tick_cmpr"; break; /* v9 ? */ + } + break; + case 41:rs_name = "psr"; + no_rs2 = 1; + break; + case 42:rs_name = "wim"; + no_rs2 = 1; + break; case 43:/* ? */ + /* TODO: pre-sparcv9: rd, rs_name = "tbr"; */ if (iword == 0x81580000) { mnem = "flushw"; no_rs1 = no_rs2 = no_rd = 1; } break; + case 48:/* wr* (SPARCv8) */ + mnem = "wr"; + if (rs1 == SPARC_ZEROREG) + no_rs1 = 1; + switch (rd) { + case 0: rd_name = "y"; break; + case 2: rd_name = "ccr"; break; + case 3: rd_name = "asi"; break; + case 6: rd_name = "fprs"; break; + case 23:rd_name = "tick_cmpr"; break; /* v9 ? */ + default:rd_name = "UNIMPLEMENTED"; + } + break; case 49:/* ? */ if (iword == 0x83880000) { mnem = "restored"; no_rs1 = no_rs2 = no_rd = 1; } break; + case 50:/* wrpr */ + rd_name = sparc_pregnames[rd]; + if (rs1 == SPARC_ZEROREG) + no_rs1 = 1; + break; case 56:/* jmpl */ jmpl = 1; if (iword == 0x81c7e008) { @@ -471,7 +735,7 @@ debug("x"); debug("\t"); if (!no_rs1) - debug("%%%s", sparc_regnames[rs1]); + debug("%%%s", rs_name); if (!no_rs1 && !no_rs2) { if (jmpl) debug("+"); @@ -482,6 +746,9 @@ if ((iword >> 13) & 1) { if (siconst >= -9 && siconst <= 9) debug("%i", siconst); + else if (siconst < 0 && (op2 == 0 || + op2 == 4 || op2 == 20 || op2 == 60)) + debug("-0x%x", -siconst); else debug("0x%x", siconst); } else { @@ -491,10 +758,17 @@ if ((!no_rs1 || !no_rs2) && !no_rd) debug(","); if (!no_rd) - debug("%%%s", sparc_regnames[rd]); + debug("%%%s", rd_name); break; - case 3: debug("%s\t", sparc_loadstore_names[op2]); + case 3: mnem = sparc_loadstore_names[op2]; + switch (op2) { + case 0: /* 'lduw' was called only 'ld' in pre-v9 */ + if (cpu->cd.sparc.cpu_type.v < 9) + mnem = "ld"; + break; + } + debug("%s\t", mnem); if (op2 & 4) debug("%%%s,", sparc_regnames[rd]); debug("[%%%s", sparc_regnames[rs1]); @@ -508,7 +782,7 @@ debug("+%%%s", sparc_regnames[rs2]); } debug("]"); - if (asi != 0) + if ((op2 & 0x30) == 0x10) debug("(%i)", asi); if (!(op2 & 4)) debug(",%%%s", sparc_regnames[rd]); @@ -520,5 +794,20 @@ } +/* + * sparc_update_pstate(): + * + * Update the pstate register (64-bit sparcs). + */ +static void sparc_update_pstate(struct cpu *cpu, uint64_t new_pstate) +{ + /* uint64_t old_pstate = cpu->cd.sparc.pstate; */ + + /* TODO: Check individual bits. */ + + cpu->cd.sparc.pstate = new_pstate; +} + + #include "tmp_sparc_tail.c"