+ return;
+ printf("D: %"PRIu64" WD: %"PRIu64" U: %"PRIu64"\n",
+ regs[i].dirty, regs[i].wasdirty, unneeded_reg[i]);
+ print_regmap("pre: ", regmap_pre[i]);
+ print_regmap("entry: ", regs[i].regmap_entry);
+ print_regmap("map: ", regs[i].regmap);
+ if (dops[i].is_jump) {
+ print_regmap("bentry:", branch_regs[i].regmap_entry);
+ print_regmap("bmap: ", branch_regs[i].regmap);
+ }
+}
+#else
+#define set_mnemonic(i_, n_)
+static void disassemble_inst(int i) {}
+#endif // DISASM
+
+#define DRC_TEST_VAL 0x74657374
+
+static void new_dynarec_test(void)
+{
+ int (*testfunc)(void);
+ void *beginning;
+ int ret[2];
+ size_t i;
+
+ // check structure linkage
+ if ((u_char *)rcnts - (u_char *)&psxRegs != sizeof(psxRegs))
+ {
+ SysPrintf("linkage_arm* miscompilation/breakage detected.\n");
+ }
+
+ SysPrintf("testing if we can run recompiled code @%p...\n", out);
+ ((volatile u_int *)out)[0]++; // make cache dirty
+
+ for (i = 0; i < ARRAY_SIZE(ret); i++) {
+ out = ndrc->translation_cache;
+ beginning = start_block();
+ emit_movimm(DRC_TEST_VAL + i, 0); // test
+ emit_ret();
+ literal_pool(0);
+ end_block(beginning);
+ testfunc = beginning;
+ ret[i] = testfunc();
+ }
+
+ if (ret[0] == DRC_TEST_VAL && ret[1] == DRC_TEST_VAL + 1)
+ SysPrintf("test passed.\n");
+ else
+ SysPrintf("test failed, will likely crash soon (r=%08x %08x)\n", ret[0], ret[1]);
+ out = ndrc->translation_cache;
+}
+
+// clear the state completely, instead of just marking
+// things invalid like invalidate_all_pages() does
+void new_dynarec_clear_full(void)
+{
+ int n;
+ out = ndrc->translation_cache;
+ memset(invalid_code,1,sizeof(invalid_code));
+ memset(hash_table,0xff,sizeof(hash_table));
+ memset(mini_ht,-1,sizeof(mini_ht));
+ memset(restore_candidate,0,sizeof(restore_candidate));
+ memset(shadow,0,sizeof(shadow));
+ copy=shadow;
+ expirep=16384; // Expiry pointer, +2 blocks
+ pending_exception=0;
+ literalcount=0;
+ stop_after_jal=0;
+ inv_code_start=inv_code_end=~0;
+ hack_addr=0;
+ f1_hack=0;
+ // TLB
+ for(n=0;n<4096;n++) ll_clear(jump_in+n);
+ for(n=0;n<4096;n++) ll_clear(jump_out+n);
+ for(n=0;n<4096;n++) ll_clear(jump_dirty+n);
+
+ cycle_multiplier_old = cycle_multiplier;
+ new_dynarec_hacks_old = new_dynarec_hacks;
+}
+
+void new_dynarec_init(void)
+{
+ SysPrintf("Init new dynarec, ndrc size %x\n", (int)sizeof(*ndrc));
+
+#ifdef _3DS
+ check_rosalina();
+#endif
+#ifdef BASE_ADDR_DYNAMIC
+ #ifdef VITA
+ sceBlock = getVMBlock(); //sceKernelAllocMemBlockForVM("code", sizeof(*ndrc));
+ if (sceBlock <= 0)
+ SysPrintf("sceKernelAllocMemBlockForVM failed: %x\n", sceBlock);
+ int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&ndrc);
+ if (ret < 0)
+ SysPrintf("sceKernelGetMemBlockBase failed: %x\n", ret);
+ sceKernelOpenVMDomain();
+ sceClibPrintf("translation_cache = 0x%08lx\n ", (long)ndrc->translation_cache);
+ #elif defined(_MSC_VER)
+ ndrc = VirtualAlloc(NULL, sizeof(*ndrc), MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE);
+ #else
+ uintptr_t desired_addr = 0;
+ #ifdef __ELF__
+ extern char _end;
+ desired_addr = ((uintptr_t)&_end + 0xffffff) & ~0xffffffl;
+ #endif
+ ndrc = mmap((void *)desired_addr, sizeof(*ndrc),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ndrc == MAP_FAILED) {
+ SysPrintf("mmap() failed: %s\n", strerror(errno));
+ abort();
+ }
+ #endif
+#else
+ #ifndef NO_WRITE_EXEC
+ // not all systems allow execute in data segment by default
+ // size must be 4K aligned for 3DS?
+ if (mprotect(ndrc, sizeof(*ndrc),
+ PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
+ SysPrintf("mprotect() failed: %s\n", strerror(errno));
+ #endif
+#endif
+ out = ndrc->translation_cache;
+ cycle_multiplier=200;
+ new_dynarec_clear_full();
+#ifdef HOST_IMM8
+ // Copy this into local area so we don't have to put it in every literal pool
+ invc_ptr=invalid_code;
+#endif
+ arch_init();
+ new_dynarec_test();
+ ram_offset=(uintptr_t)rdram-0x80000000;
+ if (ram_offset!=0)
+ SysPrintf("warning: RAM is not directly mapped, performance will suffer\n");
+}
+
+void new_dynarec_cleanup(void)
+{
+ int n;
+#ifdef BASE_ADDR_DYNAMIC
+ #ifdef VITA
+ // sceBlock is managed by retroarch's bootstrap code
+ //sceKernelFreeMemBlock(sceBlock);
+ //sceBlock = -1;
+ #else
+ if (munmap(ndrc, sizeof(*ndrc)) < 0)
+ SysPrintf("munmap() failed\n");
+ #endif
+#endif
+ for(n=0;n<4096;n++) ll_clear(jump_in+n);
+ for(n=0;n<4096;n++) ll_clear(jump_out+n);
+ for(n=0;n<4096;n++) ll_clear(jump_dirty+n);
+ #ifdef ROM_COPY
+ if (munmap (ROM_COPY, 67108864) < 0) {SysPrintf("munmap() failed\n");}
+ #endif
+}
+
+static u_int *get_source_start(u_int addr, u_int *limit)
+{
+ if (addr < 0x00200000 ||
+ (0xa0000000 <= addr && addr < 0xa0200000))
+ {
+ // used for BIOS calls mostly?
+ *limit = (addr&0xa0000000)|0x00200000;
+ return (u_int *)(rdram + (addr&0x1fffff));
+ }
+ else if (!Config.HLE && (
+ /* (0x9fc00000 <= addr && addr < 0x9fc80000) ||*/
+ (0xbfc00000 <= addr && addr < 0xbfc80000)))
+ {
+ // BIOS. The multiplier should be much higher as it's uncached 8bit mem,
+ // but timings in PCSX are too tied to the interpreter's BIAS
+ if (!HACK_ENABLED(NDHACK_OVERRIDE_CYCLE_M))
+ cycle_multiplier_active = 200;
+
+ *limit = (addr & 0xfff00000) | 0x80000;
+ return (u_int *)((u_char *)psxR + (addr&0x7ffff));
+ }
+ else if (addr >= 0x80000000 && addr < 0x80000000+RAM_SIZE) {
+ *limit = (addr & 0x80600000) + 0x00200000;
+ return (u_int *)(rdram + (addr&0x1fffff));
+ }
+ return NULL;
+}
+
+static u_int scan_for_ret(u_int addr)
+{
+ u_int limit = 0;
+ u_int *mem;
+
+ mem = get_source_start(addr, &limit);
+ if (mem == NULL)
+ return addr;
+
+ if (limit > addr + 0x1000)
+ limit = addr + 0x1000;
+ for (; addr < limit; addr += 4, mem++) {
+ if (*mem == 0x03e00008) // jr $ra
+ return addr + 8;
+ }
+ return addr;
+}
+
+struct savestate_block {
+ uint32_t addr;
+ uint32_t regflags;
+};
+
+static int addr_cmp(const void *p1_, const void *p2_)
+{
+ const struct savestate_block *p1 = p1_, *p2 = p2_;
+ return p1->addr - p2->addr;
+}
+
+int new_dynarec_save_blocks(void *save, int size)
+{
+ struct savestate_block *blocks = save;
+ int maxcount = size / sizeof(blocks[0]);
+ struct savestate_block tmp_blocks[1024];
+ struct ll_entry *head;
+ int p, s, d, o, bcnt;
+ u_int addr;
+
+ o = 0;
+ for (p = 0; p < ARRAY_SIZE(jump_in); p++) {
+ bcnt = 0;
+ for (head = jump_in[p]; head != NULL; head = head->next) {
+ tmp_blocks[bcnt].addr = head->vaddr;
+ tmp_blocks[bcnt].regflags = head->reg_sv_flags;
+ bcnt++;
+ }
+ if (bcnt < 1)
+ continue;
+ qsort(tmp_blocks, bcnt, sizeof(tmp_blocks[0]), addr_cmp);
+
+ addr = tmp_blocks[0].addr;
+ for (s = d = 0; s < bcnt; s++) {
+ if (tmp_blocks[s].addr < addr)
+ continue;
+ if (d == 0 || tmp_blocks[d-1].addr != tmp_blocks[s].addr)
+ tmp_blocks[d++] = tmp_blocks[s];
+ addr = scan_for_ret(tmp_blocks[s].addr);
+ }
+
+ if (o + d > maxcount)
+ d = maxcount - o;
+ memcpy(&blocks[o], tmp_blocks, d * sizeof(blocks[0]));
+ o += d;
+ }
+
+ return o * sizeof(blocks[0]);
+}
+
+void new_dynarec_load_blocks(const void *save, int size)
+{
+ const struct savestate_block *blocks = save;
+ int count = size / sizeof(blocks[0]);
+ u_int regs_save[32];
+ uint32_t f;
+ int i, b;
+
+ get_addr(psxRegs.pc);
+
+ // change GPRs for speculation to at least partially work..
+ memcpy(regs_save, &psxRegs.GPR, sizeof(regs_save));
+ for (i = 1; i < 32; i++)
+ psxRegs.GPR.r[i] = 0x80000000;
+
+ for (b = 0; b < count; b++) {
+ for (f = blocks[b].regflags, i = 0; f; f >>= 1, i++) {
+ if (f & 1)
+ psxRegs.GPR.r[i] = 0x1f800000;
+ }
+
+ get_addr(blocks[b].addr);
+
+ for (f = blocks[b].regflags, i = 0; f; f >>= 1, i++) {
+ if (f & 1)
+ psxRegs.GPR.r[i] = 0x80000000;
+ }
+ }