From 97361dd8cf80ec2685b04ab27547da8f129740ad Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 1 Oct 2022 19:51:54 +0300 Subject: [PATCH] Accurate 68000 IPL emulation updates --- gencpu.cpp | 85 +++++++++++++++++++++++++++++++++++++++++------- include/newcpu.h | 5 ++- newcpu.cpp | 34 ++++++++++++++++--- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/gencpu.cpp b/gencpu.cpp index 3b835b4d..440f8713 100644 --- a/gencpu.cpp +++ b/gencpu.cpp @@ -67,6 +67,7 @@ static const char *mmu_postfix, *xfc_postfix; static int memory_cycle_cnt; static int did_prefetch; static int ipl_fetched; +static int pre_ipl; static int opcode_nextcopy; static int disable_noflags; static int do_always_dynamic_cycles; @@ -248,6 +249,27 @@ static int get_current_cycles(void) return (count_readw + count_writew) * 4 + (count_readl + count_writel) * 8 + count_cycles; } +static void set_ipl_pre(void) +{ + if (using_ce) { + pre_ipl = 1; + out("ipl_fetch_pre();\n"); + } +} + +static void set_ipl(void) +{ + last_access_offset_ipl = strlen(outbuffer); + ipl_fetch_cycles = get_current_cycles(); + ipl_fetched = 2; +} + +static void set_ipl_now(void) +{ + set_ipl(); + ipl_fetched = 3; +} + static void set_last_access_ipl(void) { if (ipl_fetched) @@ -506,6 +528,9 @@ static bool isprefetch020(void) static void check_ipl(void) { + if (ipl_fetched == 2) { + return; + } // So far it seems 68000 IPL fetch happens when CPU is doing // memory cycle data part followed by prefetch cycle. It must // happen after possible bus error has been detected but before @@ -1430,7 +1455,6 @@ static void fill_prefetch_full_000_special(int pctype, const char *format, ...) } check_prefetch_bus_error(-1, -1, -1); irc2ir(); - check_ipl_always(); if (using_bus_error) { copy_opcode(); if (cpu_level == 0) { @@ -1450,6 +1474,7 @@ static void fill_prefetch_full_000_special(int pctype, const char *format, ...) va_end(parms); out(outbuf); } + set_ipl(); out("%s(%d);\n", prefetch_word, 2); count_readw++; if (pctype > 0) { @@ -4548,13 +4573,13 @@ static void genmovemel_ce(uae_u16 opcode) int size = table68k[opcode].size == sz_long ? 4 : 2; amodes mode = table68k[opcode].dmode; out("uae_u16 mask = %s;\n", gen_nextiword(mode < Ad16 ? GF_PCM2 : 0)); - ipl_fetched = -1; do_instruction_buserror(); out("uae_u32 dmask = mask & 0xff, amask = (mask >> 8) & 0xff;\n"); if (mode == Ad8r || mode == PC8r) { addcycles000(2); } genamode(NULL, mode, "dstreg", table68k[opcode].size, "src", 2, -1, GF_AA | GF_MOVE | GF_PCM2); + set_ipl(); movem_ex3(0); if (table68k[opcode].size == sz_long) { out("while (dmask) {\n"); @@ -4855,12 +4880,12 @@ static const char *cmask (wordsizes size) } } -static int source_is_imm1_8 (struct instr *i) +static int source_is_imm1_8(struct instr *i) { return i->stype == 3; } -static void shift_ce (amodes dmode, int size) +static void shift_ce(amodes dmode, int size) { if (isreg (dmode)) { int c = size == sz_long ? 4 : 2; @@ -4882,7 +4907,7 @@ static void shift_ce (amodes dmode, int size) } // BCHG/BSET/BCLR Dx,Dx or #xx,Dx adds 2 cycles if bit number > 15 -static void bsetcycles (struct instr *curi) +static void bsetcycles(struct instr *curi) { if (curi->size == sz_byte) { out("src &= 7;\n"); @@ -4906,7 +4931,7 @@ static void bsetcycles (struct instr *curi) } } -static int islongimm (struct instr *curi) +static int islongimm(struct instr *curi) { return (curi->size == sz_long && (curi->smode == Dreg || curi->smode == imm || curi->smode == Areg)); } @@ -4937,6 +4962,9 @@ static void resetvars (void) bus_error_cycles = 0; exception_pc_offset = 0; exception_pc_offset_extra_000 = 0; + did_prefetch = 0; + ipl_fetched = 0; + pre_ipl = 0; ir2irc = 0; mmufixupcnt = 0; @@ -5516,6 +5544,7 @@ static void gen_opcode (unsigned int opcode) "SET_VFLG((bflgs ^ bflgo) & (bflgn ^ bflgo));\n"); } if (c > 0) { + set_ipl(); addcycles000(c); } genastore_rev("newv", curi->dmode, "dstreg", curi->size, "dst"); @@ -5557,6 +5586,7 @@ static void gen_opcode (unsigned int opcode) } fill_prefetch_next_after(0, "areg_68000_long_replace_low(dstreg, newv);\n"); if (c > 0) { + set_ipl(); addcycles000(c); } genastore("newv", curi->dmode, "dstreg", sz_long, "dst"); @@ -5662,9 +5692,14 @@ static void gen_opcode (unsigned int opcode) } else { out("SET_VFLG((tmp_newv & 0x80) != 0 && (newv & 0x80) == 0);\n"); } + if (isreg(curi->smode)) { + set_ipl_pre(); + } fill_prefetch_next_after(1, NULL); if (isreg (curi->smode)) { addcycles000(2); + } else { + set_ipl(); } exception_pc_offset_extra_000 = 0; genastore("newv", curi->dmode, "dstreg", curi->size, "dst"); @@ -5713,8 +5748,10 @@ static void gen_opcode (unsigned int opcode) "SET_XFLG(GET_CFLG());\n" "SET_VFLG((bflgs ^ bflgn) & (bflgo ^ bflgn));\n"); } - if (c > 0) + if (c > 0) { + set_ipl(); addcycles000(c); + } genastore_rev("newv", curi->dmode, "dstreg", curi->size, "dst"); } else { if (curi->dmode == Dreg) { @@ -5753,6 +5790,7 @@ static void gen_opcode (unsigned int opcode) } fill_prefetch_next_after(1, "areg_68000_long_replace_low(dstreg, newv);\n"); if (c > 0) { + set_ipl(); addcycles000(c); } genastore("newv", curi->dmode, "dstreg", sz_long, "dst"); @@ -5860,9 +5898,14 @@ static void gen_opcode (unsigned int opcode) } else { out("SET_VFLG((tmp_newv & 0x80) == 0 && (newv & 0x80) != 0);\n"); } + if (isreg(curi->smode)) { + set_ipl_pre(); + } fill_prefetch_next_after(1, NULL); if (isreg (curi->smode)) { addcycles000(2); + } else { + set_ipl(); } exception_pc_offset_extra_000 = 0; genastore("newv", curi->dmode, "dstreg", curi->size, "dst"); @@ -5874,6 +5917,7 @@ static void gen_opcode (unsigned int opcode) if (curi->size == sz_long) { // prefetch bus error and long register: only low word is updated fill_prefetch_next_after(1, "dreg_68000_long_replace_low(srcreg, dst);\n"); + set_ipl(); genastore_rev("dst", curi->smode, "srcreg", curi->size, "src"); } else { genastore_rev("dst", curi->smode, "srcreg", curi->size, "src"); @@ -5908,6 +5952,7 @@ static void gen_opcode (unsigned int opcode) if (curi->size == sz_long) { // prefetch bus error and long register: only low word is updated fill_prefetch_next_after(1, "dreg_68000_long_replace_low(srcreg, newv);\n"); + set_ipl(); genastore_rev("newv", curi->smode, "srcreg", curi->size, "src"); } else { genastore_rev("newv", curi->smode, "srcreg", curi->size, "src"); @@ -5963,9 +6008,11 @@ static void gen_opcode (unsigned int opcode) out("SET_VFLG((tmp_newv & 0x80) != 0 && (newv & 0x80) == 0);\n"); } if (isreg(curi->smode)) { + set_ipl_pre(); fill_prefetch_next_after(1, NULL); addcycles000(2); } else { + set_ipl(); fill_prefetch_next_after(1, NULL); } genastore("newv", curi->smode, "srcreg", curi->size, "src"); @@ -5981,6 +6028,7 @@ static void gen_opcode (unsigned int opcode) fill_prefetch_next_after(1, "m68k_dreg(regs, srcreg) = (src & 0xffff0000);\n" "SET_VFLG(0);SET_ZFLG(1);SET_NFLG(0);SET_CFLG(0);\n"); + set_ipl(); genastore_rev("0", curi->smode, "srcreg", curi->size, "src"); } else { genastore_rev("0", curi->smode, "srcreg", curi->size, "src"); @@ -6058,6 +6106,7 @@ static void gen_opcode (unsigned int opcode) "SET_VFLG(0);SET_ZFLG(!dst);\n" "SET_NFLG(dst & 0x80000000);\n" "SET_CFLG(0);\n"); + set_ipl(); genastore_rev("dst", curi->smode, "srcreg", curi->size, "src"); } else { genastore_rev("dst", curi->smode, "srcreg", curi->size, "src"); @@ -6091,6 +6140,7 @@ static void gen_opcode (unsigned int opcode) curi->dmode, "dstreg", curi->size, "dst", 1, 0); if (curi->size == sz_long) { fill_prefetch_next_after(1, NULL); + set_ipl(); bsetcycles(curi); out("SET_ZFLG(1 ^ ((dst >> src) & 1));\n"); } else { @@ -6098,6 +6148,7 @@ static void gen_opcode (unsigned int opcode) if (curi->dmode == imm) { // btst dn,#x fill_prefetch_next_after(1, NULL); + set_ipl(); addcycles000(2); out("SET_ZFLG(1 ^ ((dst >> src) & 1));\n"); } else { @@ -6119,6 +6170,7 @@ static void gen_opcode (unsigned int opcode) curi->dmode, "dstreg", curi->size, "dst", 1, GF_RMW); if (curi->size == sz_long) { fill_prefetch_next_after(1, NULL); + set_ipl(); } else { if (curi->smode == Dreg || curi->smode >= imm) { fill_prefetch_next_after(1, NULL); @@ -6160,6 +6212,7 @@ static void gen_opcode (unsigned int opcode) curi->smode, "srcreg", curi->size, "src", 1, GF_AA, curi->dmode, "dstreg", curi->size, "dst", 1, GF_AA | GF_NOLIPL); genflags (flag_cmp, curi->size, "newv", "src", "dst"); + set_ipl(); fill_prefetch_next_t(); break; case i_CMP: @@ -6170,6 +6223,7 @@ static void gen_opcode (unsigned int opcode) genflags(flag_cmp, curi->size, "newv", "src", "dst"); if (curi->dmode == Dreg && curi->size == sz_long) { fill_prefetch_next_after(1, NULL); + set_ipl(); addcycles000(2); } else { fill_prefetch_next_t(); @@ -6186,6 +6240,9 @@ static void gen_opcode (unsigned int opcode) } else { fill_prefetch_next_t(); } + if (curi->smode == imm || curi->smode == Dreg || curi->smode == Areg) { + set_ipl(); + } addcycles000(2); break; /* The next two are coded a little unconventional, but they are doing @@ -6526,6 +6583,7 @@ static void gen_opcode (unsigned int opcode) fill_prefetch_next_after(1, "MakeSR();\n" "m68k_dreg(regs, srcreg) = (m68k_dreg(regs, srcreg) & ~0xffff) | ((regs.sr) & 0xffff);\n"); + set_ipl(); } else { fill_prefetch_next_after(1, NULL); } @@ -6600,6 +6658,7 @@ static void gen_opcode (unsigned int opcode) curi->dmode, "dstreg", curi->size, "dst", 1, 0); genastore("dst", curi->smode, "srcreg", curi->size, "src"); genastore("src", curi->dmode, "dstreg", curi->size, "dst"); + set_ipl_pre(); fill_prefetch_next_after(1, NULL); addcycles000(2); break; @@ -7038,9 +7097,9 @@ static void gen_opcode (unsigned int opcode) write_return_cycles(0); out("}\n"); } - set_last_access_ipl(); ipl_fetched = 1; genastore_2("src", Apdi, "7", sz_long, "old", 0, GF_NOLIPL); + set_ipl(); genastore("m68k_areg(regs, 7)", curi->smode, "srcreg", sz_long, "src"); out("m68k_areg(regs, 7) += offs;\n"); fill_prefetch_next_t(); @@ -7062,6 +7121,7 @@ static void gen_opcode (unsigned int opcode) out("m68k_areg(regs, 7) = src + 4;\n"); m68k_pc_offset = 2; genastore("old", curi->smode, "srcreg", curi->size, "src"); + set_ipl(); fill_prefetch_next_t(); } break; @@ -8114,6 +8174,7 @@ bccl_not68020: } else { fill_prefetch_next_noopcodecopy("SET_ZFLG(!(val & %s));\nSET_NFLG(val & %s);\n", bit_mask(curi->size), cmask(curi->size)); } + set_ipl(); out("int ccnt = cnt & 63;\n"); out("cnt &= 63;\n"); out("if (cnt >= %d) {\n", bit_size (curi->size)); @@ -9159,20 +9220,20 @@ end: if (last_access_offset_ipl > 0) { char iplfetch[100]; int tc = get_current_cycles(); - if (tc - ipl_fetch_cycles > 4) { + if (tc - ipl_fetch_cycles > 4 || ipl_fetched == 3) { strcpy(iplfetch, "ipl_fetch_now();\n"); //sprintf(iplfetch, "ipl_fetch_now(); // %d %d\n", tc, ipl_fetch_cycles); } else { strcpy(iplfetch, "ipl_fetch_next();\n"); //sprintf(iplfetch, "ipl_fetch_next(); // %d %d\n", tc, ipl_fetch_cycles); } - insertstring(iplfetch, last_access_offset_ipl); + if (!pre_ipl) { + insertstring(iplfetch, last_access_offset_ipl); + } } else { out("// MISSING\n"); } } - did_prefetch = 0; - ipl_fetched = 0; if (cpu_level >= 2 && !using_ce && !using_ce020) { int v = curi->clocks; if (v < 4) diff --git a/include/newcpu.h b/include/newcpu.h index 7eab6b7a..1782cfc9 100644 --- a/include/newcpu.h +++ b/include/newcpu.h @@ -202,6 +202,7 @@ struct regstruct int exception; int intmask; int ipl[2], ipl_pin; + evt_t ipl_evt_pre; uae_u32 vbr, sfc, dfc; @@ -693,6 +694,7 @@ extern void prepare_interrupt (uae_u32); extern void doint(void); extern void checkint(void); extern void intlev_load(void); +extern void ipl_fetch_pre(void); extern void ipl_fetch_now(void); extern void ipl_fetch_next(void); extern void dump_counts (void); @@ -703,7 +705,8 @@ extern int m68k_mull (uae_u32, uae_u32, uae_u16); extern void init_m68k (void); extern void m68k_go (int); extern void m68k_dumpstate(uaecptr *, uaecptr); -extern void m68k_dumpcache (bool); +extern void m68k_dumpcache(bool); +extern bool m68k_readcache(uaecptr memaddr, bool dc, uae_u32* valp); extern int getMulu68kCycles(uae_u16 src); extern int getMuls68kCycles(uae_u16 src); extern int getDivu68kCycles (uae_u32 dividend, uae_u16 divisor); diff --git a/newcpu.cpp b/newcpu.cpp index 0e0162f5..2d4370cc 100644 --- a/newcpu.cpp +++ b/newcpu.cpp @@ -2371,7 +2371,14 @@ static void MakeFromSR_x(int t0trace) regs.m = (regs.sr >> 12) & 1; if (regs.intmask != ((regs.sr >> 8) & 7)) { - regs.intmask = (regs.sr >> 8) & 7; + int newimask = (regs.sr >> 8) & 7; + // STOP intmask change enabling already active interrupt: delay it by 1 STOP round + if (t0trace < 0 && regs.ipl[0] <= regs.intmask && regs.ipl[0] > newimask && regs.ipl[0] < 7) { + regs.ipl[0] = 0; + unset_special(SPCFLAG_INT); + } + regs.intmask = newimask; + if (m68k_interrupt_delay && (regs.ipl[0] > 0 || regs.ipl[1] > 0)) { set_special(SPCFLAG_INT); } @@ -4368,10 +4375,20 @@ static bool uae_ppc_poll_check_halt(void) // check if interrupt active -static bool time_for_interrupt(void) +static int time_for_interrupt(void) { int ipl = get_ipl(); - return ipl > regs.intmask || ipl == 7; + if (ipl > regs.intmask || ipl == 7) { + return ipl; + } + return 0; +} + +// ipl check mid next memory cycle +void ipl_fetch_pre(void) +{ + ipl_fetch_next(); + regs.ipl_evt_pre = get_cycles(); } // ipl check was early enough, interrupt possible after current instruction @@ -4394,6 +4411,7 @@ void ipl_fetch_next(void) void intlev_load(void) { + ipl_fetch_now(); doint(); } @@ -4407,6 +4425,11 @@ void doint(void) #endif int il = intlev(); regs.ipl_pin = il; + // check if 68000/010 interrupt was detected mid memory access, + // 2 cycles from start of memory cycle (CYCLE_UNIT == 2 CPU clocks) + if (il > 0 && get_cycles() == regs.ipl_evt_pre + CYCLE_UNIT) { + ipl_fetch_next(); + } #ifdef DEBUGGER if (debug_dma) { record_dma_ipl(current_hpos(), vpos); @@ -4557,9 +4580,10 @@ static int do_specialties (int cycles) } if (m68k_interrupt_delay) { - if (time_for_interrupt()) { + int ipl = time_for_interrupt(); + if (ipl) { unset_special(SPCFLAG_INT); - do_interrupt(get_ipl()); + do_interrupt(ipl); } else { if (regs.ipl[0] == regs.ipl[1]) { unset_special(SPCFLAG_INT); -- 2.47.3