--- trunk/src/cpu_arm.c 2007/10/08 16:18:27 10 +++ trunk/src/cpu_arm.c 2007/10/08 16:18:38 12 @@ -25,12 +25,13 @@ * SUCH DAMAGE. * * - * $Id: cpu_arm.c,v 1.19 2005/06/27 16:44:54 debug Exp $ + * $Id: cpu_arm.c,v 1.57 2005/08/12 20:20:28 debug Exp $ * * ARM CPU emulation. * - * Whenever there is a reference to "(1)", that means - * "http://www.pinknoise.demon.co.uk/ARMinstrs/ARMinstrs.html". + * Sources of information refered to in cpu_arm*.c: + * + * (1) http://www.pinknoise.demon.co.uk/ARMinstrs/ARMinstrs.html */ #include @@ -67,39 +68,24 @@ #include "memory.h" #include "symbol.h" +#define DYNTRANS_32 +#include "tmp_arm_head.c" -/* instr uses the same names as in cpu_arm_instr.c */ -#define instr(n) arm_instr_ ## n -static char *arm_condition_string[16] = { - "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", ""/*Always*/, "(INVALID)" }; - -/* ARM symbolic register names: */ -static char *arm_regname[N_ARM_REGS] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }; - -/* Data processing instructions: */ -static char *arm_dpiname[16] = { - "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", - "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn" }; -static int arm_dpi_uses_d[16] = { - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 }; -static int arm_dpi_uses_n[16] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 }; - -extern volatile int single_step; -extern int old_show_trace_tree; -extern int old_instruction_trace; -extern int old_quiet_mode; -extern int quiet_mode; +/* ARM symbolic register names and condition strings: */ +static char *arm_regname[N_ARM_REGS] = ARM_REG_NAMES; +static char *arm_condition_string[16] = ARM_CONDITION_STRINGS; + +/* Data Processing Instructions: */ +static char *arm_dpiname[16] = ARM_DPI_NAMES; +static int arm_dpi_uses_d[16] = { 1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1 }; +static int arm_dpi_uses_n[16] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0 }; /* * arm_cpu_new(): * - * Create a new ARM cpu object by filling in the CPU struct. + * Create a new ARM cpu object by filling the CPU struct. * Return 1 on success, 0 if cpu_type_name isn't a valid ARM processor. */ int arm_cpu_new(struct cpu *cpu, struct memory *mem, @@ -109,6 +95,12 @@ return 0; cpu->memory_rw = arm_memory_rw; + cpu->update_translation_table = arm_update_translation_table; + cpu->invalidate_translation_caches_paddr = + arm_invalidate_translation_caches_paddr; + cpu->invalidate_code_translation_caches = + arm_invalidate_code_translation_caches; + cpu->is_32bit = 1; cpu->cd.arm.flags = ARM_FLAG_I | ARM_FLAG_F | ARM_MODE_USR32; @@ -126,8 +118,8 @@ */ void arm_cpu_dumpinfo(struct cpu *cpu) { - debug(" (%i MB translation cache)\n", - (int)(ARM_TRANSLATION_CACHE_SIZE / 1048576)); + /* TODO */ + debug("\n"); } @@ -183,6 +175,7 @@ { char *symbol; uint64_t offset; + int mode = cpu->cd.arm.flags & ARM_FLAG_MODE; int i, x = cpu->cpu_id; if (gprs) { @@ -196,7 +189,11 @@ (cpu->cd.arm.flags & ARM_FLAG_V)? "V" : "v", (cpu->cd.arm.flags & ARM_FLAG_I)? "I" : "i", (cpu->cd.arm.flags & ARM_FLAG_F)? "F" : "f"); - debug(" pc = 0x%08x", (int)cpu->cd.arm.r[ARM_PC]); + if (mode < ARM_MODE_USR32) + debug(" pc = 0x%07x", + (int)(cpu->cd.arm.r[ARM_PC] & 0x03ffffff)); + else + debug(" pc = 0x%08x", (int)cpu->cd.arm.r[ARM_PC]); /* TODO: Flags */ @@ -216,6 +213,52 @@ /* + * arm_cpu_show_full_statistics(): + * + * Show detailed statistics on opcode usage on each cpu. + */ +void arm_cpu_show_full_statistics(struct machine *m) +{ + fatal("arm_cpu_show_full_statistics(): TODO\n"); +} + + +/* + * arm_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 arm_cpu_tlbdump(struct machine *m, int x, int rawflag) +{ + fatal("arm_cpu_tlbdump(): TODO\n"); +} + + +/* + * arm_cpu_interrupt(): + */ +int arm_cpu_interrupt(struct cpu *cpu, uint64_t irq_nr) +{ + fatal("arm_cpu_interrupt(): TODO\n"); + return 0; +} + + +/* + * arm_cpu_interrupt_ack(): + */ +int arm_cpu_interrupt_ack(struct cpu *cpu, uint64_t irq_nr) +{ + /* fatal("arm_cpu_interrupt_ack(): TODO\n"); */ + return 0; +} + + +/* * arm_cpu_disassemble_instr(): * * Convert an instruction word into human readable format, for instruction @@ -232,7 +275,7 @@ { uint32_t iw, tmp; int main_opcode, secondary_opcode, s_bit, r16, r12, r8; - int p_bit, u_bit, b_bit, w_bit, l_bit; + int i, n, p_bit, u_bit, b_bit, w_bit, l_bit; char *symbol, *condition; uint64_t offset; @@ -276,8 +319,8 @@ * xxxx000a aaaSnnnn ddddcccc ctttmmmm Register form * xxxx001a aaaSnnnn ddddrrrr bbbbbbbb Immediate form */ - if (iw & 0x80 && !(main_opcode & 2)) { - debug("UNIMPLEMENTED reg (c!=0)\n"); + if (iw & 0x80 && !(main_opcode & 2) && iw & 0x10) { + debug("UNIMPLEMENTED reg (c!=0), t odd\n"); break; } @@ -305,24 +348,24 @@ debug("%s", arm_regname[iw & 15]); switch (t) { case 0: if (c != 0) - debug(" LSL #%i", c); + debug(", lsl #%i", c); break; - case 1: debug(" LSL %s", arm_regname[c >> 1]); + case 1: debug(", lsl %s", arm_regname[c >> 1]); break; - case 2: debug(" LSR #%i", c? c : 32); + case 2: debug(", lsr #%i", c? c : 32); break; - case 3: debug(" LSR %s", arm_regname[c >> 1]); + case 3: debug(", lsr %s", arm_regname[c >> 1]); break; - case 4: debug(" ASR #%i", c? c : 32); + case 4: debug(", asr #%i", c? c : 32); break; - case 5: debug(" ASR %s", arm_regname[c >> 1]); + case 5: debug(", asr %s", arm_regname[c >> 1]); break; case 6: if (c != 0) - debug(" ROR #%i", c); + debug(", ror #%i", c); else - debug(" RRX"); + debug(", rrx"); break; - case 7: debug(" ROR %s", arm_regname[c >> 1]); + case 7: debug(", ror %s", arm_regname[c >> 1]); break; } } @@ -363,7 +406,30 @@ break; case 0x8: /* Block Data Transfer */ case 0x9: - debug("TODO: block data transfer\n"); + /* See (1): xxxx100P USWLnnnn llllllll llllllll */ + p_bit = main_opcode & 1; + s_bit = b_bit; + debug("%s%s", l_bit? "ldm" : "stm", condition); + switch (u_bit * 2 + p_bit) { + case 0: debug("da"); break; + case 1: debug("db"); break; + case 2: debug("ia"); break; + case 3: debug("ib"); break; + } + debug("\t%s", arm_regname[r16]); + if (w_bit) + debug("!"); + debug(",{"); + n = 0; + for (i=0; i<16; i++) + if ((iw >> i) & 1) { + debug("%s%s", (n > 0)? ",":"", arm_regname[i]); + n++; + } + debug("}"); + if (s_bit) + debug("^"); + debug("\n"); break; case 0xa: /* B: branch */ case 0xb: /* BL: branch and link */ @@ -421,267 +487,7 @@ } -/* - * arm_create_or_reset_tc(): - * - * Create the translation cache in memory (ie allocate memory for it), if - * necessary, and then reset it to an initial state. - */ -static void arm_create_or_reset_tc(struct cpu *cpu) -{ - if (cpu->cd.arm.translation_cache == NULL) { - cpu->cd.arm.translation_cache = malloc( - ARM_TRANSLATION_CACHE_SIZE + ARM_TRANSLATION_CACHE_MARGIN); - if (cpu->cd.arm.translation_cache == NULL) { - fprintf(stderr, "arm_create_or_reset_tc(): out of " - "memory when allocating the translation cache\n"); - exit(1); - } - } - - /* Create an empty table at the beginning of the translation cache: */ - memset(cpu->cd.arm.translation_cache, 0, sizeof(uint32_t) * - N_BASE_TABLE_ENTRIES); - - cpu->cd.arm.translation_cache_cur_ofs = - N_BASE_TABLE_ENTRIES * sizeof(uint32_t); -} - - -/* - * arm_tc_allocate_default_page(): - * - * Create a default page (with just pointers to instr(to_be_translated) - * at cpu->cd.arm.translation_cache_cur_ofs. - */ -/* forward declaration of to_be_translated and end_of_page: */ -static void instr(to_be_translated)(struct cpu *,struct arm_instr_call *); -static void instr(end_of_page)(struct cpu *,struct arm_instr_call *); -static void arm_tc_allocate_default_page(struct cpu *cpu, uint32_t physaddr) -{ - struct arm_tc_physpage *ppp; - int i; - - /* Create the physpage header: */ - ppp = (struct arm_tc_physpage *)(cpu->cd.arm.translation_cache - + cpu->cd.arm.translation_cache_cur_ofs); - ppp->next_ofs = 0; - ppp->physaddr = physaddr; - - for (i=0; iics[i].f = instr(to_be_translated); - - ppp->ics[IC_ENTRIES_PER_PAGE].f = instr(end_of_page); - - cpu->cd.arm.translation_cache_cur_ofs += - sizeof(struct arm_tc_physpage); -} - - -#define MEMORY_RW arm_memory_rw -#define MEM_ARM -#include "memory_rw.c" -#undef MEM_ARM -#undef MEMORY_RW - - -#include "cpu_arm_instr.c" - - -/* - * arm_cpu_run_instr(): - * - * Execute one or more instructions on a specific CPU. - * - * Return value is the number of instructions executed during this call, - * 0 if no instructions were executed. - */ -int arm_cpu_run_instr(struct emul *emul, struct cpu *cpu) -{ - /* - * Find the correct translated page in the translation cache, - * and start running code on that page. - */ - - uint32_t cached_pc, physaddr, physpage_ofs; - int pagenr, table_index, n_instrs, low_pc; - uint32_t *physpage_entryp; - struct arm_tc_physpage *ppp; - - if (cpu->cd.arm.translation_cache == NULL || cpu->cd. - arm.translation_cache_cur_ofs >= ARM_TRANSLATION_CACHE_SIZE) - arm_create_or_reset_tc(cpu); - - cached_pc = cpu->cd.arm.r[ARM_PC]; - - physaddr = cached_pc & ~(((IC_ENTRIES_PER_PAGE-1) << 2) | 3); - /* TODO: virtual to physical */ - - pagenr = ADDR_TO_PAGENR(physaddr); - table_index = PAGENR_TO_TABLE_INDEX(pagenr); - - physpage_entryp = &(((uint32_t *) - cpu->cd.arm.translation_cache)[table_index]); - physpage_ofs = *physpage_entryp; - ppp = NULL; - - /* Traverse the physical page chain: */ - while (physpage_ofs != 0) { - ppp = (struct arm_tc_physpage *)(cpu->cd.arm.translation_cache - + physpage_ofs); - /* If we found the page in the cache, then we're done: */ - if (ppp->physaddr == physaddr) - break; - /* Try the next page in the chain: */ - physpage_ofs = ppp->next_ofs; - } - - /* If the offset is 0 (or ppp is NULL), then we need to create a - new "default" empty translation page. */ - - if (ppp == NULL) { - fatal("CREATING page %i (physaddr 0x%08x), table index = %i\n", - pagenr, physaddr, table_index); - *physpage_entryp = physpage_ofs = - cpu->cd.arm.translation_cache_cur_ofs; - - arm_tc_allocate_default_page(cpu, physaddr); - - ppp = (struct arm_tc_physpage *)(cpu->cd.arm.translation_cache - + physpage_ofs); - } - - cpu->cd.arm.cur_physpage = ppp; - cpu->cd.arm.cur_ic_page = &ppp->ics[0]; - cpu->cd.arm.next_ic = cpu->cd.arm.cur_ic_page + - PC_TO_IC_ENTRY(cached_pc); - - /* printf("cached_pc = 0x%08x pagenr = %i table_index = %i, " - "physpage_ofs = 0x%08x\n", (int)cached_pc, (int)pagenr, - (int)table_index, (int)physpage_ofs); */ - - cpu->cd.arm.n_translated_instrs = 0; - cpu->cd.arm.running_translated = 1; - - if (single_step || cpu->machine->instruction_trace) { - /* - * Single-step: - */ - struct arm_instr_call *ic = cpu->cd.arm.next_ic ++; - if (cpu->machine->instruction_trace) { - unsigned char instr[4]; - if (!cpu->memory_rw(cpu, cpu->mem, cpu->pc, &instr[0], - sizeof(instr), MEM_READ, CACHE_INSTRUCTION)) { - fatal("arm_cpu_run_instr(): could not read " - "the instruction\n"); - } else - arm_cpu_disassemble_instr(cpu, instr, 1, 0, 0); - } - - /* When single-stepping, multiple instruction calls cannot - be combined into one. This clears all translations: */ - if (ppp->flags & ARM_COMBINATIONS) { - int i; - for (i=0; iics[i].f = instr(to_be_translated); - debug("[ Note: The translation of physical page 0x%08x" - " contained combinations of instructions; these " - "are now flushed because we are single-stepping." - " ]\n", ppp->physaddr); - ppp->flags &= ~ARM_COMBINATIONS; - } - - /* Execute just one instruction: */ - ic->f(cpu, ic); - n_instrs = 1; - } else { - /* Execute multiple instructions: */ - n_instrs = 0; - for (;;) { - struct arm_instr_call *ic; - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - ic = cpu->cd.arm.next_ic ++; ic->f(cpu, ic); - - n_instrs += 24; - if (!cpu->cd.arm.running_translated || single_step || - n_instrs + cpu->cd.arm.n_translated_instrs >= 8192) - break; - } - } - - - /* - * Update the program counter and return the correct number of - * executed instructions: - */ - low_pc = ((size_t)cpu->cd.arm.next_ic - (size_t) - cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call); - - if (low_pc >= 0 && low_pc < IC_ENTRIES_PER_PAGE) { - cpu->cd.arm.r[ARM_PC] &= ~((IC_ENTRIES_PER_PAGE-1) << 2); - cpu->cd.arm.r[ARM_PC] += (low_pc << 2); - cpu->pc = cpu->cd.arm.r[ARM_PC]; - } else { - fatal("Outside a page (This is actually ok)\n"); - } - - return n_instrs + cpu->cd.arm.n_translated_instrs; -} - - -#define CPU_RUN arm_cpu_run -#define CPU_RINSTR arm_cpu_run_instr -#define CPU_RUN_ARM -#include "cpu_run.c" -#undef CPU_RINSTR -#undef CPU_RUN_ARM -#undef CPU_RUN +#include "tmp_arm_tail.c" -/* - * arm_cpu_family_init(): - * - * This function fills the cpu_family struct with valid data. - */ -int arm_cpu_family_init(struct cpu_family *fp) -{ - fp->name = "ARM"; - fp->cpu_new = arm_cpu_new; - fp->list_available_types = arm_cpu_list_available_types; - fp->register_match = arm_cpu_register_match; - fp->disassemble_instr = arm_cpu_disassemble_instr; - fp->register_dump = arm_cpu_register_dump; - fp->run = arm_cpu_run; - fp->dumpinfo = arm_cpu_dumpinfo; - /* fp->show_full_statistics = arm_cpu_show_full_statistics; */ - /* fp->tlbdump = arm_cpu_tlbdump; */ - /* fp->interrupt = arm_cpu_interrupt; */ - /* fp->interrupt_ack = arm_cpu_interrupt_ack; */ - return 1; -} - #endif /* ENABLE_ARM */