--- previous/src/cpu/cpummu030.c 2018/04/24 19:28:59 1.1 +++ previous/src/cpu/cpummu030.c 2018/04/24 19:30:13 1.1.1.2 @@ -17,20 +17,13 @@ * 16-11-2012: Improved exception handling * * - * Known Problems: - * - Special Status Word (SSW) is not handled correctly - * - * - * TODO list: - * - Correctly handle Special Status Word. Now we use 68040 values. - * This also needs to be done in m68000.c, M68000_BusError! * - Check if read-modify-write operations are correctly detected for * handling transparent access (see TT matching functions) * - If possible, test mmu030_table_search with all kinds of translations * (early termination, invalid descriptors, bus errors, indirect * descriptors, PTEST in different levels, etc). - * - Check which bits of an ATC entry should be set and which should be - * un-set, if an invalid translation occurs. + * - Check which bits of an ATC entry or the status register should be set + * and which should be un-set, if an invalid translation occurs. * - Handle cache inhibit bit when accessing ATC entries */ @@ -41,37 +34,47 @@ #include "options_cpu.h" #include "memory.h" #include "newcpu.h" -#include "main.h" -#include "m68000.h" -#include "hatari-glue.h" #include "cpummu030.h" +#include "hatari-glue.h" +#define MMU030_OP_DBG_MSG 0 +#define MMU030_ATC_DBG_MSG 0 +#define MMU030_REG_DBG_MSG 0 +#define TT_FC_MASK 0x00000007 +#define TT_FC_BASE 0x00000070 +#define TT_RWM 0x00000100 +#define TT_RW 0x00000200 +#define TT_CI 0x00000400 +#define TT_ENABLE 0x00008000 -/* Definitions for debugging */ -#define MMU030_DEBUG 1 /* enable(1) or disable(0) MMU general debugging messages */ -#define MMU030_DEBUG_ERROR 1 /* enable(1) or disable(0) MMU error debugging messages */ -#define MMU030_LOG_MEM_ACCESS 0 /* enable(1) or disable(0) ATC memory access logging */ - - -#if MMU030_DEBUG -#define debug_msg write_log -#else -#define debug_msg(...) -#endif - -#if MMU030_DEBUG_ERROR -#define error_msg write_log -#else -#define error_msg(...) -#endif - -char table_letter[4] = {'A','B','C','D'}; /* only used for debugging messages */ - +#define TT_ADDR_MASK 0x00FF0000 +#define TT_ADDR_BASE 0xFF000000 -/* Structs that hold all informations needed for address translation */ +static int bBusErrorReadWrite; +static int atcindextable[32]; +static int tt_enabled; + +int mmu030_idx; + +uae_u32 mm030_stageb_address; +bool mmu030_retry; +int mmu030_opcode; +int mmu030_opcode_stageb; +uae_u16 mmu030_state[3]; +uae_u32 mmu030_data_buffer; +uae_u32 mmu030_disp_store[2]; +uae_u32 mmu030_fmovem_store[2]; +struct mmu030_access mmu030_ad[MAX_MMU030_ACCESS]; + +/* for debugging messages */ +char table_letter[4] = {'A','B','C','D'}; + +uae_u64 srp_030, crp_030; +uae_u32 tt0_030, tt1_030, tc_030; +uae_u16 mmusr_030; -/* ATC struct for 68030 */ +/* ATC struct */ #define ATC030_NUM_ENTRIES 22 typedef struct { @@ -96,7 +99,7 @@ typedef struct { /* MMU struct for 68030 */ struct { - /* Translation tables and page */ + /* Translation tables */ struct { struct { uae_u32 mask; @@ -105,6 +108,7 @@ struct { struct { uae_u32 mask; + uae_u32 imask; uae_u8 size; } page; @@ -121,7 +125,7 @@ struct { /* Address translation cache */ MMU030_ATC_LINE atc[ATC030_NUM_ENTRIES]; - /* MMU condition */ + /* Condition */ bool enabled; uae_u16 status; } mmu030; @@ -177,82 +181,105 @@ void mmu_op30_pmove (uaecptr pc, uae_u32 int preg = (next >> 10) & 31; int rw = (next >> 9) & 1; int fd = (next >> 8) & 1; - + +#if MMU030_OP_DBG_MSG + switch (preg) { + case 0x10: + write_log(_T("PMOVE: %s TC %08X\n"), rw?"read":"write", + rw?tc_030:x_get_long(extra)); + break; + case 0x12: + write_log(_T("PMOVE: %s SRP %08X%08X\n"), rw?"read":"write", + rw?(uae_u32)(srp_030>>32)&0xFFFFFFFF:x_get_long(extra), + rw?(uae_u32)srp_030&0xFFFFFFFF:x_get_long(extra+4)); + break; + case 0x13: + write_log(_T("PMOVE: %s CRP %08X%08X\n"), rw?"read":"write", + rw?(uae_u32)(crp_030>>32)&0xFFFFFFFF:x_get_long(extra), + rw?(uae_u32)crp_030&0xFFFFFFFF:x_get_long(extra+4)); + break; + case 0x18: + write_log(_T("PMOVE: %s MMUSR %04X\n"), rw?"read":"write", + rw?mmusr_030:x_get_word(extra)); + break; + case 0x02: + write_log(_T("PMOVE: %s TT0 %08X\n"), rw?"read":"write", + rw?tt0_030:x_get_long(extra)); + break; + case 0x03: + write_log(_T("PMOVE: %s TT1 %08X\n"), rw?"read":"write", + rw?tt1_030:x_get_long(extra)); + break; + default: + break; + } + if (!fd && !rw && !(preg==0x18)) { + write_log(_T("PMOVE: flush ATC\n")); + } +#endif + switch (preg) { case 0x10: // TC - if (rw) { - debug_msg("PMOVE: read TC %08X\n", tc_030); + if (rw) x_put_long (extra, tc_030); - } else { + else { tc_030 = x_get_long (extra); - debug_msg("PMOVE: write TC %08X\n", tc_030); mmu030_decode_tc(tc_030); } break; case 0x12: // SRP if (rw) { - debug_msg("PMOVE: read SRP %08X%08X\n", srp_030>>32, srp_030); x_put_long (extra, srp_030 >> 32); x_put_long (extra + 4, srp_030); } else { srp_030 = (uae_u64)x_get_long (extra) << 32; srp_030 |= x_get_long (extra + 4); - debug_msg("PMOVE: write SRP %08X%08X\n", srp_030>>32, srp_030); mmu030_decode_rp(srp_030); } break; case 0x13: // CRP if (rw) { - debug_msg("PMOVE: read CRP %08X%08X\n", crp_030>>32, crp_030); x_put_long (extra, crp_030 >> 32); x_put_long (extra + 4, crp_030); } else { crp_030 = (uae_u64)x_get_long (extra) << 32; crp_030 |= x_get_long (extra + 4); - debug_msg("PMOVE: write CRP %08X%08X\n", crp_030>>32, crp_030); mmu030_decode_rp(crp_030); } break; case 0x18: // MMUSR - if (rw) { - debug_msg("PMOVE: read MMUSR %04X\n", mmusr_030); + if (rw) x_put_word (extra, mmusr_030); - } else { + else mmusr_030 = x_get_word (extra); - debug_msg("PMOVE: write MMUSR %04X\n", mmusr_030); - } break; case 0x02: // TT0 - if (rw) { - debug_msg("PMOVE: read TT0 %08X\n", tt0_030); + if (rw) x_put_long (extra, tt0_030); - } else { + else { tt0_030 = x_get_long (extra); - debug_msg("PMOVE: write TT0 %08X\n", tt0_030); mmu030.transparent.tt0 = mmu030_decode_tt(tt0_030); } break; case 0x03: // TT1 - if (rw) { - debug_msg("PMOVE: read TT1 %08X\n", tt1_030); + if (rw) x_put_long (extra, tt1_030); - } else { + else { tt1_030 = x_get_long (extra); - debug_msg("PMOVE: write TT1 %08X\n", tt1_030); mmu030.transparent.tt1 = mmu030_decode_tt(tt1_030); } break; default: - error_msg("Bad PMOVE at %08x\n",m68k_getpc()); + write_log (_T("Bad PMOVE at %08x\n"),m68k_getpc()); op_illg (opcode); return; - } + } if (!fd && !rw && !(preg==0x18)) { - debug_msg("PMOVE: flush ATC\n"); mmu030_flush_atc_all(); } + tt_enabled = (tt0_030 & TT_ENABLE) || (tt1_030 & TT_ENABLE); } void mmu_op30_ptest (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) @@ -274,19 +301,21 @@ void mmu_op30_ptest (uaecptr pc, uae_u32 * with an address register operand, the MC68030 takes an F-line * unimplemented instruction exception." */ - if (!level && a) { - error_msg("PTEST: Bad instruction causing F-line unimplemented instruction exception!\n"); - Exception(11, m68k_getpc(), M68000_EXC_SRC_CPU); /* F-line unimplemented instruction exception */ + if (!level && a) { /* correct ? */ + write_log(_T("PTEST: Bad instruction causing F-line unimplemented instruction exception!\n")); + Exception(11); /* F-line unimplemented instruction exception */ return; } - debug_msg("PTEST%c: addr = %08X, fc = %i, level = %i, ", +#if MMU030_OP_DBG_MSG + write_log(_T("PTEST%c: addr = %08X, fc = %i, level = %i, "), rw?'R':'W', extra, fc, level); if (a) { - debug_msg("return descriptor to register A%i\n", areg); + write_log(_T("return descriptor to register A%i\n"), areg); } else { - debug_msg("do not return descriptor\n"); + write_log(_T("do not return descriptor\n")); } +#endif if (!level) { mmu030_ptest_atc_search(extra, fc, write); @@ -298,11 +327,13 @@ void mmu_op30_ptest (uaecptr pc, uae_u32 } mmusr_030 = mmu030.status; - debug_msg("PTEST status: %04X, B = %i, L = %i, S = %i, W = %i, I = %i, M = %i, T = %i, N = %i\n", +#if MMU030_OP_DBG_MSG + write_log(_T("PTEST status: %04X, B = %i, L = %i, S = %i, W = %i, I = %i, M = %i, T = %i, N = %i\n"), mmusr_030, (mmusr_030&MMUSR_BUS_ERROR)?1:0, (mmusr_030&MMUSR_LIMIT_VIOLATION)?1:0, (mmusr_030&MMUSR_SUPER_VIOLATION)?1:0, (mmusr_030&MMUSR_WRITE_PROTECTED)?1:0, (mmusr_030&MMUSR_INVALID)?1:0, (mmusr_030&MMUSR_MODIFIED)?1:0, (mmusr_030&MMUSR_TRANSP_ACCESS)?1:0, mmusr_030&MMUSR_NUM_LEVELS_MASK); +#endif } void mmu_op30_pload (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) @@ -312,7 +343,9 @@ void mmu_op30_pload (uaecptr pc, uae_u32 bool write = rw ? false : true; - debug_msg("PLOAD%c: Create ATC entry for %08X, FC = %i\n", write?'W':'R', extra, fc); +#if 0 + write_log (_T("PLOAD%c: Create ATC entry for %08X, FC = %i\n"), write?'W':'R', extra, fc); +#endif mmu030_flush_atc_page(extra); mmu030_table_search(extra, fc, write, 0); @@ -323,26 +356,39 @@ void mmu_op30_pflush (uaecptr pc, uae_u3 uae_u16 mode = (next&0x1C00)>>10; uae_u32 fc_mask = (uae_u32)(next&0x00E0)>>5; uae_u32 fc_base = mmu_op30_helper_get_fc(next); - + +#if 0 + switch (mode) { + case 0x1: + write_log(_T("PFLUSH: Flush all entries\n")); + break; + case 0x4: + write_log(_T("PFLUSH: Flush by function code only\n")); + write_log(_T("PFLUSH: function code: base = %08X, mask = %08X\n"), fc_base, fc_mask); + break; + case 0x6: + write_log(_T("PFLUSH: Flush by function code and effective address\n")); + write_log(_T("PFLUSH: function code: base = %08X, mask = %08X\n"), fc_base, fc_mask); + write_log(_T("PFLUSH: effective address = %08X\n"), extra); + break; + default: + break; + } +#endif + switch (mode) { case 0x1: - debug_msg("PFLUSH: Flush all entries\n"); mmu030_flush_atc_all(); break; case 0x4: - debug_msg("PFLUSH: Flush by function code only\n"); - debug_msg("PFLUSH: function code: base = %08X, mask = %08X\n", fc_base, fc_mask); mmu030_flush_atc_fc(fc_base, fc_mask); break; case 0x6: - debug_msg("PFLUSH: Flush by function code and effective address\n"); - debug_msg("PFLUSH: function code: base = %08X, mask = %08X\n", fc_base, fc_mask); - debug_msg("PFLUSH: effective address = %08X\n", extra); mmu030_flush_atc_page_fc(extra, fc_base, fc_mask); break; default: - error_msg("PFLUSH ERROR: bad mode! (%i)\n",mode); + write_log(_T("PFLUSH ERROR: bad mode! (%i)\n"),mode); break; } } @@ -356,12 +402,12 @@ uae_u32 mmu_op30_helper_get_fc(uae_u16 n return (m68k_dreg(regs, next&0x7)&0x7); case 0x0000: if (next&1) { - return (regs.dfc&0x7); + return (regs.dfc); } else { - return (regs.sfc&0x7); + return (regs.sfc); } default: - error_msg("MMU_OP30 ERROR: bad fc source! (%04X)\n",next&0x0018); + write_log(_T("MMU_OP30 ERROR: bad fc source! (%04X)\n"),next&0x0018); return 0; } } @@ -376,8 +422,10 @@ void mmu030_flush_atc_fc(uae_u32 fc_base if (((fc_base&fc_mask)==(mmu030.atc[i].logical.fc&fc_mask)) && mmu030.atc[i].logical.valid) { mmu030.atc[i].logical.valid = false; - debug_msg("ATC: Flushing %08X\n", mmu030.atc[i].physical.addr); - } +#if MMU030_OP_DBG_MSG + write_log(_T("ATC: Flushing %08X\n"), mmu030.atc[i].physical.addr); +#endif + } } } @@ -385,32 +433,40 @@ void mmu030_flush_atc_fc(uae_u32 fc_base * and their function code */ void mmu030_flush_atc_page_fc(uaecptr logical_addr, uae_u32 fc_base, uae_u32 fc_mask) { int i; + logical_addr &= mmu030.translation.page.imask; for (i=0; i> 20; mmu030.translation.init_shift = (TC & TC_IS_MASK) >> 16; + regs.mmu_page_size = 1 << mmu030.translation.page.size; + - if (mmu030.translation.page.size<8) { - error_msg("MMU Configuration Exception: Bad value in TC register! (bad page size: %i byte)\n", + write_log(_T("68030 MMU enabled. Page size = %d\n"), regs.mmu_page_size); + + if (mmu030.translation.page.size<8) { + write_log(_T("MMU Configuration Exception: Bad value in TC register! (bad page size: %i byte)\n"), 1<>shift */ @@ -683,52 +770,51 @@ void mmu030_decode_tc(uae_u32 TC) { mmu030.translation.last_table = i; } - - /* TI fields are summed up until a zero field is reached (see above - * loop). The sum of all TI field values plus page size and initial - * shift has to be 32: IS + PS + TIA + TIB + TIC + TID = 32 */ - if ((shift-mmu030.translation.page.size)!=0) { - error_msg("MMU Configuration Exception: Bad value in TC register! (bad sum)\n"); - Exception(56, m68k_getpc(), M68000_EXC_SRC_CPU); /* MMU Configuration Exception */ - return; - } - -#if MMU030_DEBUG_ERROR +#if MMU030_REG_DBG_MSG /* At least one table has to be defined using at least - * 1 bit for the index. At least 2 bits are necessary + * 1 bit for the index. At least 2 bits are necessary * if there is no second table. If these conditions are * not met, it will automatically lead to a sum <32 - * and cause an exception (see above). */ + * and cause an exception (see below). */ if (!TI_bits[0]) { - write_log("MMU Configuration Exception: Bad value in TC register! (no first table index defined)\n"); + write_log(_T("MMU Configuration Exception: Bad value in TC register! (no first table index defined)\n")); } else if ((TI_bits[0]<2) && !TI_bits[1]) { - write_log("MMU Configuration Exception: Bad value in TC register! (no second table index defined and)\n"); - write_log("MMU Configuration Exception: Bad value in TC register! (only 1 bit for first table index)\n"); + write_log(_T("MMU Configuration Exception: Bad value in TC register! (no second table index defined and)\n")); + write_log(_T("MMU Configuration Exception: Bad value in TC register! (only 1 bit for first table index)\n")); } #endif -#if MMU030_DEBUG - write_log("\n"); - write_log("TRANSLATION CONTROL: %08X\n", TC); - write_log("\n"); - write_log("TC: translation %s\n", (TC&TC_ENABLE_TRANSLATION ? "enabled" : "disabled")); - write_log("TC: supervisor root pointer %s\n", (TC&TC_ENABLE_SUPERVISOR ? "enabled" : "disabled")); - write_log("TC: function code lookup %s\n", (TC&TC_ENABLE_FCL ? "enabled" : "disabled")); - write_log("\n"); - - write_log("TC: Initial Shift: %i\n", mmu030.translation.init_shift); - write_log("TC: Page Size: %i byte\n", (1<> 32; if (!descriptor_type) { /* If descriptor type is invalid */ - error_msg("MMU Configuration Exception: Root Pointer is invalid!\n"); - Exception(56, m68k_getpc(), M68000_EXC_SRC_CPU); /* MMU Configuration Exception */ + write_log(_T("MMU Configuration Exception: Root Pointer is invalid!\n")); + Exception(56); /* MMU Configuration Exception */ } -#if MMU030_DEBUG_ERROR - if (RP&RP_ZERO_BITS) { - write_log("MMU Warning: Root pointer reserved bits are non-zero!\n"); - } -#endif - -#if MMU030_DEBUG +#if MMU030_REG_DBG_MSG /* enable or disable debugging output */ uae_u32 table_limit = (RP & RP_LIMIT_MASK) >> 48; uae_u32 first_addr = (RP & RP_ADDR_MASK); - write_log("\n"); - write_log("ROOT POINTER: %08X%08X\n", (uae_u32)(RP>>32)&0xFFFFFFFF, (uae_u32)(RP&0xFFFFFFFF)); - write_log("\n"); + write_log(_T("\n")); + write_log(_T("ROOT POINTER: %08X%08X\n"), (uae_u32)(RP>>32)&0xFFFFFFFF, (uae_u32)(RP&0xFFFFFFFF)); + write_log(_T("\n")); - write_log("RP: descriptor type = %i ", descriptor_type); + write_log(_T("RP: descriptor type = %i "), descriptor_type); switch (descriptor_type) { case 0: - write_log("(invalid descriptor)\n"); + write_log(_T("(invalid descriptor)\n")); break; case 1: - write_log("(early termination page descriptor)\n"); + write_log(_T("(early termination page descriptor)\n")); break; case 2: - write_log("(valid 4 byte descriptor)\n"); + write_log(_T("(valid 4 byte descriptor)\n")); break; case 3: - write_log("(valid 8 byte descriptor)\n"); + write_log(_T("(valid 8 byte descriptor)\n")); break; } - write_log("RP: %s limit = %i\n", (RP&RP_LOWER_MASK) ? "lower" : "upper", table_limit); + write_log(_T("RP: %s limit = %i\n"), (RP&RP_LOWER_MASK) ? _T("lower") : _T("upper"), table_limit); - write_log("RP: first table address = %08X\n", first_addr); - write_log("\n"); + write_log(_T("RP: first table address = %08X\n"), first_addr); + write_log(_T("\n")); #endif } @@ -980,7 +1061,8 @@ uae_u32 mmu030_table_search(uaecptr addr uae_u32 limit = 0; uae_u32 unused_fields_mask = 0; bool super = (fc&4) ? true : false; - bool write_protect = false; + bool super_violation = false; + bool write_protected = false; bool cache_inhibit = false; bool descr_modified = false; @@ -1003,22 +1085,33 @@ uae_u32 mmu030_table_search(uaecptr addr if ((tc_030&TC_ENABLE_SUPERVISOR) && super) { descr[0] = (srp_030>>32)&0xFFFFFFFF; descr[1] = srp_030&0xFFFFFFFF; - debug_msg("Supervisor Root Pointer: %08X%08X\n",descr[0],descr[1]); +#if MMU030_REG_DBG_MSG + write_log(_T("Supervisor Root Pointer: %08X%08X\n"),descr[0],descr[1]); +#endif // MMU030_REG_DBG_MSG } else { descr[0] = (crp_030>>32)&0xFFFFFFFF; descr[1] = crp_030&0xFFFFFFFF; - debug_msg("CPU Root Pointer: %08X%08X\n",descr[0],descr[1]); +#if MMU030_REG_DBG_MSG + write_log(_T("CPU Root Pointer: %08X%08X\n"),descr[0],descr[1]); +#endif + } + + if (descr[0]&RP_ZERO_BITS) { +#if MMU030_REG_DBG_MSG + write_log(_T("MMU Warning: Root pointer reserved bits are non-zero! %08X\n"), descr[0]); +#endif + descr[0] &= (~RP_ZERO_BITS); } /* Check descriptor type of root pointer */ descr_type = descr[0]&DESCR_TYPE_MASK; switch (descr_type) { case DESCR_TYPE_INVALID: - error_msg("Fatal error: Root pointer is invalid descriptor!\n"); + write_log(_T("Fatal error: Root pointer is invalid descriptor!\n")); mmu030.status |= MMUSR_INVALID; goto stop_search; case DESCR_TYPE_EARLY_TERM: - debug_msg("Root pointer is early termination page descriptor.\n"); + write_log(_T("Root pointer is early termination page descriptor.\n")); early_termination = true; goto handle_page_descriptor; case DESCR_TYPE_VALID4: @@ -1033,12 +1126,12 @@ uae_u32 mmu030_table_search(uaecptr addr * index for top level table, limit check not required */ if (tc_030&TC_ENABLE_FCL) { - debug_msg("Function code lookup enabled, FC = %i\n", fc); + write_log(_T("Function code lookup enabled, FC = %i\n"), fc); addr_position = (descr_size==4) ? 0 : 1; table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; table_index = fc; /* table index is function code */ - debug_msg("Table FCL at %08X: index = %i, ",table_addr,table_index); + write_log(_T("Table FCL at %08X: index = %i, "),table_addr,table_index); /* Fetch next descriptor */ descr_num++; @@ -1046,11 +1139,15 @@ uae_u32 mmu030_table_search(uaecptr addr if (next_size==4) { descr[0] = phys_get_long(descr_addr[descr_num]); - debug_msg("Next descriptor: %08X\n",descr[0]); +#if MMU030_REG_DBG_MSG + write_log(_T("Next descriptor: %08X\n"),descr[0]); +#endif } else { descr[0] = phys_get_long(descr_addr[descr_num]); descr[1] = phys_get_long(descr_addr[descr_num]+4); - debug_msg("Next descriptor: %08X%08X\n",descr[0],descr[1]); +#if MMU030_REG_DBG_MSG + write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]); +#endif } descr_size = next_size; @@ -1059,13 +1156,15 @@ uae_u32 mmu030_table_search(uaecptr addr descr_type = descr[0]&DESCR_TYPE_MASK; switch (descr_type) { case DESCR_TYPE_INVALID: - debug_msg("Invalid descriptor!\n"); + write_log(_T("Invalid descriptor!\n")); /* stop table walk */ mmu030.status |= MMUSR_INVALID; goto stop_search; case DESCR_TYPE_EARLY_TERM: - debug_msg("Early termination page descriptor!\n"); - early_termination = true; +#if MMU030_REG_DBG_MSG + write_log(_T("Early termination page descriptor!\n")); +#endif + early_termination = true; goto handle_page_descriptor; case DESCR_TYPE_VALID4: next_size = 4; @@ -1080,20 +1179,23 @@ uae_u32 mmu030_table_search(uaecptr addr /* Upper level tables */ do { if (descr_num) { /* if not root pointer */ + /* Check protection */ + if ((descr_size==8) && (descr[0]&DESCR_S) && !super) { + super_violation = true; + } + if (descr[0]&DESCR_WP) { + write_protected = true; + } + /* Set the updated bit */ - if (!level && !(descr[0]&DESCR_U) && !(mmu030.status&MMUSR_SUPER_VIOLATION)) { + if (!level && !(descr[0]&DESCR_U) && !super_violation) { descr[0] |= DESCR_U; phys_put_long(descr_addr[descr_num], descr[0]); } + /* Update status bits */ - if (descr_size==8) { - if (descr[0]&DESCR_S) - mmu030.status |= super ? 0 : MMUSR_SUPER_VIOLATION; - } - if (descr[0]&DESCR_WP) { - mmu030.status |= (descr[0]&DESCR_WP) ? MMUSR_WRITE_PROTECTED : 0; - write_protect = true; - } + mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0; + mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0; /* Check if ptest level is reached */ if (level && (level==descr_num)) { @@ -1104,7 +1206,9 @@ uae_u32 mmu030_table_search(uaecptr addr addr_position = (descr_size==4) ? 0 : 1; table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; table_index = (addr&mmu030.translation.table[t].mask)>>mmu030.translation.table[t].shift; - debug_msg("Table %c at %08X: index = %i, ",table_letter[t],table_addr,table_index); +#if MMU030_REG_DBG_MSG + write_log(_T("Table %c at %08X: index = %i, "),table_letter[t],table_addr,table_index); +#endif // MMU030_REG_DBG_MSG t++; /* Proceed to the next table */ /* Perform limit check */ @@ -1112,12 +1216,16 @@ uae_u32 mmu030_table_search(uaecptr addr limit = (descr[0]&DESCR_LIMIT_MASK)>>16; if ((descr[0]&DESCR_LOWER_MASK) && (table_indexlimit)) { mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); - debug_msg("limit violation (upper limit %i)\n",limit); +#if MMU030_REG_DBG_MSG + write_log(_T("limit violation (upper limit %i)\n"),limit); +#endif goto stop_search; } } @@ -1128,11 +1236,15 @@ uae_u32 mmu030_table_search(uaecptr addr if (next_size==4) { descr[0] = phys_get_long(descr_addr[descr_num]); - debug_msg("Next descriptor: %08X\n",descr[0]); +#if MMU030_REG_DBG_MSG + write_log(_T("Next descriptor: %08X\n"),descr[0]); +#endif } else { descr[0] = phys_get_long(descr_addr[descr_num]); descr[1] = phys_get_long(descr_addr[descr_num]+4); - debug_msg("Next descriptor: %08X%08X\n",descr[0],descr[1]); +#if MMU030_REG_DBG_MSG + write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]); +#endif } descr_size = next_size; @@ -1141,15 +1253,19 @@ uae_u32 mmu030_table_search(uaecptr addr descr_type = descr[0]&DESCR_TYPE_MASK; switch (descr_type) { case DESCR_TYPE_INVALID: - debug_msg("Invalid descriptor!\n"); - /* stop table walk */ +#if MMU030_REG_DBG_MSG + write_log(_T("Invalid descriptor!\n")); +#endif + /* stop table walk */ mmu030.status |= MMUSR_INVALID; goto stop_search; case DESCR_TYPE_EARLY_TERM: /* go to last level table handling code */ if (t<=mmu030.translation.last_table) { - debug_msg("Early termination page descriptor!\n"); - early_termination = true; +#if MMU030_REG_DBG_MSG + write_log(_T("Early termination page descriptor!\n")); +#endif + early_termination = true; } goto handle_page_descriptor; case DESCR_TYPE_VALID4: @@ -1171,20 +1287,26 @@ uae_u32 mmu030_table_search(uaecptr addr addr_position = (descr_size==4) ? 0 : 1; indirect_addr = descr[addr_position]&DESCR_ID_ADDR_MASK; - debug_msg("Page indirect descriptor at %08X: ",indirect_addr); - +#if MMU030_REG_DBG_MSG + write_log(_T("Page indirect descriptor at %08X: "),indirect_addr); +#endif + /* Fetch indirect descriptor */ descr_num++; descr_addr[descr_num] = indirect_addr; if (next_size==4) { descr[0] = phys_get_long(descr_addr[descr_num]); - debug_msg("descr = %08X\n",descr[0]); - } else { +#if MMU030_REG_DBG_MSG + write_log(_T("descr = %08X\n"),descr[0]); +#endif + } else { descr[0] = phys_get_long(descr_addr[descr_num]); descr[1] = phys_get_long(descr_addr[descr_num]+4); - debug_msg("descr = %08X%08X",descr[0],descr[1]); - } +#if MMU030_REG_DBG_MSG + write_log(_T("descr = %08X%08X"),descr[0],descr[1]); +#endif + } descr_size = next_size; @@ -1198,9 +1320,17 @@ uae_u32 mmu030_table_search(uaecptr addr handle_page_descriptor: if (descr_num) { /* if not root pointer */ - if (!level && !(mmu030.status&MMUSR_SUPER_VIOLATION)) { + /* check protection */ + if ((descr_size==8) && (descr[0]&DESCR_S) && !super) { + super_violation = true; + } + if (descr[0]&DESCR_WP) { + write_protected = true; + } + + if (!level && !super_violation) { /* set modified bit */ - if (!(descr[0]&DESCR_M) && write && !(mmu030.status&MMUSR_WRITE_PROTECTED)) { + if (!(descr[0]&DESCR_M) && write && !write_protected) { descr[0] |= DESCR_M; descr_modified = true; } @@ -1215,19 +1345,14 @@ uae_u32 mmu030_table_search(uaecptr addr } } - if ((descr_size==8) && (descr[0]&DESCR_S)) { - mmu030.status |= super ? 0 : MMUSR_SUPER_VIOLATION; - } + /* update status bits */ + mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0; + mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0; /* check if caching is inhibited */ cache_inhibit = descr[0]&DESCR_CI ? true : false; - /* check write protection */ - if (descr[0]&DESCR_WP) { - mmu030.status |= (descr[0]&DESCR_WP) ? MMUSR_WRITE_PROTECTED : 0; - write_protect = true; - } - /* TODO: check if this is handled at correct point (maybe before updating descr?) */ + /* check for the modified bit and set it in the status register */ mmu030.status |= (descr[0]&DESCR_M) ? MMUSR_MODIFIED : 0; } @@ -1242,32 +1367,40 @@ uae_u32 mmu030_table_search(uaecptr addr limit = (descr[0]&DESCR_LIMIT_MASK)>>16; if ((descr[0]&DESCR_LOWER_MASK) && (table_indexlimit)) { mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); - debug_msg("Limit violation (upper limit %i)\n",limit); +#if MMU030_REG_DBG_MSG + write_log(_T("Limit violation (upper limit %i)\n"),limit); +#endif goto stop_search; } } } /* Get all unused bits of the logical address table index field. * they are added to the page address */ - /* TODO: They should be added via "signed addition". How to? */ + /* TODO: They should be added via "unsigned addition". How to? */ do { unused_fields_mask |= mmu030.translation.table[t].mask; t++; } while (t<=mmu030.translation.last_table); page_addr = addr&unused_fields_mask; - debug_msg("Logical address unused bits: %08X (mask = %08X)\n", +#if MMU030_REG_DBG_MSG + write_log(_T("Logical address unused bits: %08X (mask = %08X)\n"), page_addr,unused_fields_mask); - } - +#endif + } + /* Get page address */ addr_position = (descr_size==4) ? 0 : 1; page_addr += (descr[addr_position]&DESCR_PD_ADDR_MASK); - debug_msg("Page at %08X\n",page_addr); +#if MMU030_REG_DBG_MSG + write_log(_T("Page at %08X\n"),page_addr); +#endif // MMU030_REG_DBG_MSG stop_search: ; /* Make compiler happy */ @@ -1278,16 +1411,13 @@ uae_u32 mmu030_table_search(uaecptr addr descr_num--; } mmu030.status |= (MMUSR_BUS_ERROR|MMUSR_INVALID); - debug_msg("MMU: Bus error while %s descriptor!\n", - bBusErrorReadWrite?"reading":"writing"); + write_log(_T("MMU: Bus error while %s descriptor!\n"), + bBusErrorReadWrite?_T("reading"):_T("writing")); } ENDTRY /* check if we have to handle ptest */ if (level) { - if (mmu030.status&MMUSR_INVALID) { - /* these bits are undefined, if the I bit is set: */ - mmu030.status &= ~(MMUSR_WRITE_PROTECTED|MMUSR_MODIFIED|MMUSR_SUPER_VIOLATION); - } + /* Note: wp, m and sv bits are undefined if the invalid bit is set */ mmu030.status = (mmu030.status&~MMUSR_NUM_LEVELS_MASK) | descr_num; /* If root pointer is page descriptor (descr_num 0), return 0 */ @@ -1309,36 +1439,41 @@ uae_u32 mmu030_table_search(uaecptr addr break; } } - debug_msg("ATC is full. Replacing entry %i\n", i); - } +#if MMU030_REG_DBG_MSG + write_log(_T("ATC is full. Replacing entry %i\n"), i); +#endif + } + if (i >= ATC030_NUM_ENTRIES) { + i = 0; + write_log (_T("ATC entry not found!!!\n")); + } + mmu030_atc_handle_history_bit(i); /* Create ATC entry */ - mmu030.atc[i].logical.addr = addr & (~mmu030.translation.page.mask); /* delete page index bits */ + mmu030.atc[i].logical.addr = addr & mmu030.translation.page.imask; /* delete page index bits */ mmu030.atc[i].logical.fc = fc; mmu030.atc[i].logical.valid = true; - mmu030.atc[i].physical.addr = page_addr & (~mmu030.translation.page.mask); /* delete page index bits */ + mmu030.atc[i].physical.addr = page_addr & mmu030.translation.page.imask; /* delete page index bits */ if ((mmu030.status&MMUSR_INVALID) || (mmu030.status&MMUSR_SUPER_VIOLATION)) { mmu030.atc[i].physical.bus_error = true; } else { mmu030.atc[i].physical.bus_error = false; } - if (write && !(mmu030.status&MMUSR_SUPER_VIOLATION) && !(mmu030.status&MMUSR_LIMIT_VIOLATION)) { - mmu030.atc[i].physical.modified = true; - } else { - mmu030.atc[i].physical.modified = false; - } mmu030.atc[i].physical.cache_inhibit = cache_inhibit; - mmu030.atc[i].physical.write_protect = write_protect; + mmu030.atc[i].physical.modified = (mmu030.status&MMUSR_MODIFIED) ? true : false; + mmu030.atc[i].physical.write_protect = (mmu030.status&MMUSR_WRITE_PROTECTED) ? true : false; - debug_msg("ATC create entry(%i): logical = %08X, physical = %08X, FC = %i\n", i, +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC create entry(%i): logical = %08X, physical = %08X, FC = %i\n"), i, mmu030.atc[i].logical.addr, mmu030.atc[i].physical.addr, mmu030.atc[i].logical.fc); - debug_msg("ATC create entry(%i): B = %i, CI = %i, WP = %i, M = %i\n", i, + write_log(_T("ATC create entry(%i): B = %i, CI = %i, WP = %i, M = %i\n"), i, mmu030.atc[i].physical.bus_error?1:0, mmu030.atc[i].physical.cache_inhibit?1:0, mmu030.atc[i].physical.write_protect?1:0, mmu030.atc[i].physical.modified?1:0); +#endif // MMU030_ATC_DBG_MSG return 0; } @@ -1367,11 +1502,9 @@ void mmu030_ptest_atc_search(uaecptr log } mmu030.status |= mmu030.atc[i].physical.bus_error ? (MMUSR_BUS_ERROR|MMUSR_INVALID) : 0; + /* Note: write protect and modified bits are undefined if the invalid bit is set */ mmu030.status |= mmu030.atc[i].physical.write_protect ? MMUSR_WRITE_PROTECTED : 0; mmu030.status |= mmu030.atc[i].physical.modified ? MMUSR_MODIFIED : 0; - if (mmu030.status&MMUSR_INVALID) { - mmu030.status &= ~(MMUSR_WRITE_PROTECTED|MMUSR_MODIFIED); - } } /* This function is used for PTEST level 1 - 7. */ @@ -1387,7 +1520,7 @@ uae_u32 mmu030_ptest_table_search(uaecpt /* Address Translation Cache * * The ATC uses a pseudo-least-recently-used algorithm to keep track of - * least recently used entries. Entries are replaced if the cache is full. + * least recently used entries. They are replaced if the cache is full. * An internal history-bit (MRU-bit) is used to identify these entries. * If an entry is accessed, its history-bit is set to 1. If after that * there are no more entries with zero-bits, all other history-bits are @@ -1435,157 +1568,223 @@ uae_u32 mmu030_ptest_table_search(uaecpt #define ATC030_PHYS_CI 0x04000000 #define ATC030_PHYS_BE 0x08000000 -void mmu030_page_fault(uaecptr addr, bool read) { - write_log("MMU: page fault (logical addr = %08X)\n", addr); - regs.mmu_fault_addr = addr; - bBusErrorReadWrite = read; - THROW(2); +void mmu030_page_fault(uaecptr addr, bool read, int flags, uae_u32 fc) { + regs.mmu_fault_addr = addr; + regs.mmu_ssw = (fc & 1) ? MMU030_SSW_DF | (MMU030_SSW_DF << 1) : (MMU030_SSW_FB | MMU030_SSW_RB); + regs.mmu_ssw |= read ? MMU030_SSW_RW : 0; + regs.mmu_ssw |= flags; + regs.mmu_ssw |= fc; + bBusErrorReadWrite = read; + mm030_stageb_address = addr; +#if MMUDEBUG + write_log(_T("MMU: page fault (logical addr=%08X SSW=%04x read=%d size=%d fc=%d pc=%08x ob=%08x ins=%04X)\n"), + addr, regs.mmu_ssw, read, (flags & MMU030_SSW_SIZE_B) ? 1 : (flags & MMU030_SSW_SIZE_W) ? 2 : 4, fc, + regs.instruction_pc, (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) ? mmu030_data_buffer : mmu030_ad[mmu030_idx].val, mmu030_opcode & 0xffff); +#endif + +// extern void activate_debugger(void); +// activate_debugger (); + + THROW(2); } -void mmu030_put_long_atc(uaecptr addr, uae_u32 val, int l) { +void mmu030_put_long_atc(uaecptr addr, uae_u32 val, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (lput %08X)\n", +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (lput %08X)\n"), l, physical_addr, page_index, val); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_L, fc); return; } phys_put_long(physical_addr, val); } -void mmu030_put_word_atc(uaecptr addr, uae_u16 val, int l) { +void mmu030_put_word_atc(uaecptr addr, uae_u16 val, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (wput %04X)\n", +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (wput %04X)\n"), l, physical_addr, page_index, val); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_W, fc); return; } phys_put_word(physical_addr, val); } -void mmu030_put_byte_atc(uaecptr addr, uae_u8 val, int l) { +void mmu030_put_byte_atc(uaecptr addr, uae_u8 val, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n", +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n"), l, physical_addr, page_index, val); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_B, fc); return; } phys_put_byte(physical_addr, val); } -uae_u32 mmu030_get_long_atc(uaecptr addr, int l) { +uae_u32 mmu030_get_long_atc(uaecptr addr, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (lget %08X)\n", l, +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (lget %08X)\n"), l, physical_addr, page_index, phys_get_long(physical_addr+page_index)); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_L, fc); return 0; } return phys_get_long(physical_addr); } -uae_u16 mmu030_get_word_atc(uaecptr addr, int l) { +uae_u16 mmu030_get_word_atc(uaecptr addr, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (wget %04X)\n", l, +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (wget %04X)\n"), l, physical_addr, page_index, phys_get_word(physical_addr+page_index)); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_W, fc); return 0; } return phys_get_word(physical_addr); } -uae_u8 mmu030_get_byte_atc(uaecptr addr, int l) { +uae_u8 mmu030_get_byte_atc(uaecptr addr, int l, uae_u32 fc) { uae_u32 page_index = addr & mmu030.translation.page.mask; - uae_u32 addr_mask = ~mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; -#if MMU030_LOG_MEM_ACCESS - write_log("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n", l, +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n"), l, physical_addr, page_index, phys_get_byte(physical_addr+page_index)); #endif physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_B, fc); return 0; } return phys_get_byte(physical_addr); } +/* Generic versions of above */ +void mmu030_put_atc_generic(uaecptr addr, uae_u32 val, int l, uae_u32 fc, int size, int flags) { + uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + + uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n"), + l, physical_addr, page_index, val); +#endif + physical_addr += page_index; + + if (mmu030.atc[l].physical.write_protect || mmu030.atc[l].physical.bus_error) { + mmu030_page_fault(addr, false, flags, fc); + return; + } + if (size == sz_byte) + phys_put_byte(physical_addr, val); + else if (size == sz_word) + phys_put_word(physical_addr, val); + else + phys_put_long(physical_addr, val); + +} +uae_u32 mmu030_get_atc_generic(uaecptr addr, int l, uae_u32 fc, int size, int flags, bool checkwrite) { + uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + + uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n"), l, + physical_addr, page_index, phys_get_byte(physical_addr+page_index)); +#endif + physical_addr += page_index; + + if (mmu030.atc[l].physical.bus_error || (checkwrite && mmu030.atc[l].physical.write_protect)) { + mmu030_page_fault(addr, true, flags, fc); + return 0; + } + if (size == sz_byte) + return phys_get_byte(physical_addr); + else if (size == sz_word) + return phys_get_word(physical_addr); + return phys_get_long(physical_addr); +} + /* This function checks if a certain logical address is in the ATC * by comparing the logical address and function code to the values * stored in the ATC entries. If a matching entry is found it sets * the history bit and returns the cache index of the entry. */ int mmu030_logical_is_in_atc(uaecptr addr, uae_u32 fc, bool write) { - uaecptr physical_addr = 0; uaecptr logical_addr = 0; - uae_u32 addr_mask = ~mmu030.translation.page.mask; - uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + uae_u32 maddr = addr & addr_mask; + int offset = (maddr >> mmu030.translation.page.size) & 0x1f; - int i; + int i, index; + index = atcindextable[offset]; for (i=0; i= ATC030_NUM_ENTRIES) + index = 0; } - return ATC030_NUM_ENTRIES; + return -1; } void mmu030_atc_handle_history_bit(int entry_num) { @@ -1601,8 +1800,10 @@ void mmu030_atc_handle_history_bit(int e mmu030.atc[j].mru = 0; } mmu030.atc[entry_num].mru = 1; - debug_msg("ATC: No more history zero-bits. Reset all.\n"); - } +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC: No more history zero-bits. Reset all.\n")); +#endif + } } @@ -1613,161 +1814,269 @@ void mmu030_atc_handle_history_bit(int e * create a new ATC entry and then look up the physical address. */ -void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc, int size) { +void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,true)) || (fc==7)) { phys_put_long(addr,val); return; } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); - if (atc_line_num=0) { + mmu030_put_long_atc(addr, val, atc_line_num, fc); } else { mmu030_table_search(addr,fc,true,0); - mmu030_put_long_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); + mmu030_put_long_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); } } -void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc, int size) { +void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,true)) || (fc==7)) { phys_put_word(addr,val); return; } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); - if (atc_line_num=0) { + mmu030_put_word_atc(addr, val, atc_line_num, fc); } else { mmu030_table_search(addr, fc, true, 0); - mmu030_put_word_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); + mmu030_put_word_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); } } -void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc, int size) { +void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr, fc, true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr, fc, true)) || (fc==7)) { phys_put_byte(addr,val); return; } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); - if (atc_line_num=0) { + mmu030_put_byte_atc(addr, val, atc_line_num, fc); } else { mmu030_table_search(addr, fc, true, 0); - mmu030_put_byte_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); + mmu030_put_byte_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); } } -uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc, int size) { +uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_long(addr); } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); - if (atc_line_num=0) { + return mmu030_get_long_atc(addr, atc_line_num, fc); } else { mmu030_table_search(addr, fc, false, 0); - return mmu030_get_long_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); + return mmu030_get_long_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); } } -uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc, int size) { +uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_word(addr); } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); - if (atc_line_num=0) { + return mmu030_get_word_atc(addr, atc_line_num, fc); } else { mmu030_table_search(addr, fc, false, 0); - return mmu030_get_word_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); + return mmu030_get_word_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); } } -uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc, int size) { +uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_byte(addr); } int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); - if (atc_line_num=0) { + return mmu030_get_byte_atc(addr, atc_line_num, fc); } else { mmu030_table_search(addr, fc, false, 0); - return mmu030_get_byte_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); + return mmu030_get_byte_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); } } -uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc) +/* Not commonly used access function */ +void mmu030_put_generic(uaecptr addr, uae_u32 val, uae_u32 fc, int size, int accesssize, int flags) { + + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr, fc, true)) || (fc==7)) { + if (size == sz_byte) + phys_put_byte(addr, val); + else if (size == sz_word) + phys_put_word(addr, val); + else + phys_put_long(addr, val); + return; + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (atc_line_num>=0) { + mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); + } else { + mmu030_table_search(addr, fc, true, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); + } +} +static uae_u32 mmu030_get_generic_lrmw(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { + + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_lrmw_ttr_access(addr,fc)) || (fc==7)) { + if (size == sz_byte) + return phys_get_byte(addr); + else if (size == sz_word) + return phys_get_word(addr); + return phys_get_long(addr); + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (atc_line_num>=0) { + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); + } else { + mmu030_table_search(addr, fc, true, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); + } +} +uae_u32 mmu030_get_generic(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { + if (flags & MMU030_SSW_RM) { + return mmu030_get_generic_lrmw(addr, fc, size, accesssize, flags); + } + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { + if (size == sz_byte) + return phys_get_byte(addr); + else if (size == sz_word) + return phys_get_word(addr); + return phys_get_long(addr); + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); + if (atc_line_num>=0) { + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); + } else { + mmu030_table_search(addr, fc, false, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); + } +} + + +/* Locked RMW is rarely used */ +uae_u32 uae_mmu030_get_lrmw(uaecptr addr, int size) +{ + uae_u32 fc = (regs.s ? 4 : 0) | 1; + if (size == sz_byte) { + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) + return mmu030_get_word_unaligned(addr, fc, MMU030_SSW_RM); + else + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } else { + if (unlikely(is_unaligned(addr, 4))) + return mmu030_get_long_unaligned(addr, fc, MMU030_SSW_RM); + else + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } +} +void uae_mmu030_put_lrmw(uaecptr addr, uae_u32 val, int size) +{ + uae_u32 fc = (regs.s ? 4 : 0) | 1; + if (size == sz_byte) { + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) + mmu030_put_word_unaligned(addr, val, fc, MMU030_SSW_RM); + else + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } else { + if (unlikely(is_unaligned(addr, 4))) + mmu030_put_long_unaligned(addr, val, fc, MMU030_SSW_RM); + else + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } +} +uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc, int flags) { uae_u16 res; - res = (uae_u16)mmu030_get_byte(addr, fc, sz_word) << 8; + res = (uae_u16)mmu030_get_generic(addr, fc, sz_byte, sz_word, flags) << 8; SAVE_EXCEPTION; TRY(prb) { - res |= mmu030_get_byte(addr + 1, fc, sz_word); + res |= mmu030_get_generic(addr + 1, fc, sz_byte, sz_word, flags); RESTORE_EXCEPTION; } CATCH(prb) { RESTORE_EXCEPTION; -// regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; THROW_AGAIN(prb); } ENDTRY return res; } -uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) +uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc, int flags) { uae_u32 res; if (likely(!(addr & 1))) { - res = (uae_u32)mmu030_get_word(addr, fc, sz_long) << 16; + res = (uae_u32)mmu030_get_generic(addr, fc, sz_word, sz_long, flags) << 16; SAVE_EXCEPTION; TRY(prb) { - res |= mmu030_get_word(addr + 2, fc, sz_long); + res |= mmu030_get_generic(addr + 2, fc, sz_word, sz_long, flags); RESTORE_EXCEPTION; } CATCH(prb) { RESTORE_EXCEPTION; -// regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; THROW_AGAIN(prb); } ENDTRY } else { - res = (uae_u32)mmu030_get_byte(addr, fc, sz_long) << 8; + res = (uae_u32)mmu030_get_generic(addr, fc, sz_byte, sz_long, flags) << 8; SAVE_EXCEPTION; TRY(prb) { - res = (res | mmu030_get_byte(addr + 1, fc, sz_long)) << 8; - res = (res | mmu030_get_byte(addr + 2, fc, sz_long)) << 8; - res |= mmu030_get_byte(addr + 3, fc, sz_long); + res = (res | mmu030_get_generic(addr + 1, fc, sz_byte, sz_long, flags)) << 8; + res = (res | mmu030_get_generic(addr + 2, fc, sz_byte, sz_long, flags)) << 8; + res |= mmu030_get_generic(addr + 3, fc, sz_byte, sz_long, flags); RESTORE_EXCEPTION; } CATCH(prb) { RESTORE_EXCEPTION; -// regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; THROW_AGAIN(prb); } ENDTRY } @@ -1775,57 +2084,81 @@ uae_u32 REGPARAM2 mmu030_get_long_unalig } -void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc) +void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc, int flags) { SAVE_EXCEPTION; TRY(prb) { if (likely(!(addr & 1))) { - mmu030_put_word(addr, val >> 16, fc, sz_long); - mmu030_put_word(addr + 2, val, fc, sz_long); + mmu030_put_generic(addr, val >> 16, fc, sz_word, sz_long, flags); + mmu030_put_generic(addr + 2, val, fc, sz_word, sz_long, flags); } else { - mmu030_put_byte(addr, val >> 24, fc, sz_long); - mmu030_put_byte(addr + 1, val >> 16, fc, sz_long); - mmu030_put_byte(addr + 2, val >> 8, fc, sz_long); - mmu030_put_byte(addr + 3, val, fc, sz_long); + mmu030_put_generic(addr, val >> 24, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 1, val >> 16, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 2, val >> 8, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 3, val, fc, sz_byte, sz_long, flags); } RESTORE_EXCEPTION; } CATCH(prb) { RESTORE_EXCEPTION; regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { -// regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } THROW_AGAIN(prb); } ENDTRY } -void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc) +void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc, int flags) { SAVE_EXCEPTION; TRY(prb) { - mmu030_put_byte(addr, val >> 8, fc, sz_word); - mmu030_put_byte(addr + 1, val, fc, sz_word); + mmu030_put_generic(addr, val >> 8, fc, sz_byte, sz_word, flags); + mmu030_put_generic(addr + 1, val, fc, sz_byte, sz_word, flags); RESTORE_EXCEPTION; } CATCH(prb) { RESTORE_EXCEPTION; regs.wb3_data = val; - if (regs.mmu_fault_addr != addr) { -// regs.mmu_fault_addr = addr; - regs.mmu_ssw |= MMU_SSW_MA; - } THROW_AGAIN(prb); } ENDTRY } +/* Used by debugger */ +static uaecptr mmu030_get_addr_atc(uaecptr addr, int l, uae_u32 fc, bool write) { + uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + + uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; + physical_addr += page_index; + + if (mmu030.atc[l].physical.bus_error || (write && mmu030.atc[l].physical.write_protect)) { + mmu030_page_fault(addr, write == 0, MMU030_SSW_SIZE_B, fc); + return 0; + } + + return physical_addr; +} +uaecptr mmu030_translate(uaecptr addr, bool super, bool data, bool write) +{ + int fc = (super ? 4 : 0) | (data ? 1 : 2); + if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,write)&TT_OK_MATCH) || (fc==7)) { + return addr; + } + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, write); + + if (atc_line_num>=0) { + return mmu030_get_addr_atc(addr, atc_line_num, fc, write); + } else { + mmu030_table_search(addr, fc, false, 0); + return mmu030_get_addr_atc(addr, mmu030_logical_is_in_atc(addr,fc,write), fc, write); + } +} + /* MMU Reset */ void mmu030_reset(int hardreset) { /* A CPU reset causes the E-bits of TC and TT registers to be zeroed. */ mmu030.enabled = false; + regs.mmu_page_size = 0; tc_030 &= ~TC_ENABLE_TRANSLATION; tt0_030 &= ~TT_ENABLE; tt1_030 &= ~TT_ENABLE; @@ -1840,14 +2173,76 @@ void mmu030_reset(int hardreset) void m68k_do_rte_mmu030 (uaecptr a7) { - uae_u16 ssr = get_word_mmu030 (a7 + 8 + 4); - if (ssr & MMU_SSW_CT) { - uaecptr src_a7 = a7 + 8 - 8; - uaecptr dst_a7 = a7 + 8 + 52; - put_word_mmu030 (dst_a7 + 0, get_word_mmu030 (src_a7 + 0)); - put_long_mmu030 (dst_a7 + 2, get_long_mmu030 (src_a7 + 2)); - // skip this word - put_long_mmu030 (dst_a7 + 8, get_long_mmu030 (src_a7 + 8)); + // Restore access error exception state + + uae_u16 format = get_word_mmu030 (a7 + 6); + uae_u16 frame = format >> 12; + uae_u16 ssw = get_word_mmu030 (a7 + 10); + + // Fetch last word, real CPU does it to allow OS bus handler to map + // the page if frame crosses pages and following page is not resident. + if (frame == 0xb) + get_word_mmu030(a7 + 92 - 2); + else + get_word_mmu030(a7 + 32 - 2); + + // Internal register, our opcode storage area + mmu030_opcode = get_long_mmu030 (a7 + 0x14); + // Misc state data + mmu030_state[0] = get_word_mmu030 (a7 + 0x30); + mmu030_state[1] = get_word_mmu030 (a7 + 0x32); + mmu030_state[2] = get_word_mmu030 (a7 + 0x34); + mmu030_disp_store[0] = get_long_mmu030 (a7 + 0x1c); + mmu030_disp_store[1] = get_long_mmu030 (a7 + 0x1c + 4); + if (mmu030_state[1] & MMU030_STATEFLAG1_FMOVEM) { + mmu030_fmovem_store[0] = get_long_mmu030 (a7 + 0x5c - (7 + 1) * 4); + mmu030_fmovem_store[1] = get_long_mmu030 (a7 + 0x5c - (8 + 1) * 4); + } + // Rerun "mmu030_opcode" using restored state. + mmu030_retry = true; + + if (frame == 0xb) { + uae_u16 idxsize = get_word_mmu030 (a7 + 0x36); + for (int i = 0; i < idxsize + 1; i++) { + mmu030_ad[i].done = i < idxsize; + mmu030_ad[i].val = get_long_mmu030 (a7 + 0x5c - (i + 1) * 4); + } + mmu030_ad[idxsize + 1].done = false; + // did we have data fault but DF bit cleared? + if (ssw & (MMU030_SSW_DF << 1) && !(ssw & MMU030_SSW_DF)) { + // DF not set: mark access as done + if (ssw & MMU030_SSW_RM) { + // Read-Modify-Write: whole instruction is considered done + write_log (_T("Read-Modify-Write and DF bit cleared! PC=%08x\n"), regs.instruction_pc); + mmu030_retry = false; + } else if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) { + // if movem, skip next move + mmu030_data_buffer = get_long_mmu030 (a7 + 0x2c); + mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM2; + } else { + mmu030_ad[idxsize].done = true; + if (ssw & MMU030_SSW_RW) { + // Read and no DF: use value in data input buffer + mmu030_data_buffer = get_long_mmu030 (a7 + 0x2c); + mmu030_ad[idxsize].val = mmu030_data_buffer; + } + } + } + // did we have ins fault and RB bit cleared? + if ((ssw & MMU030_SSW_FB) && !(ssw & MMU030_SSW_RB)) { + uae_u16 stageb = get_word_mmu030 (a7 + 0x0e); + if (mmu030_opcode == -1) { + mmu030_opcode_stageb = stageb; + write_log (_T("Software fixed stage B! opcode = %04x\n"), stageb); + } else { + mmu030_ad[idxsize].done = true; + mmu030_ad[idxsize].val = stageb; + write_log (_T("Software fixed stage B! opcode = %04X, opword = %04x\n"), mmu030_opcode, stageb); + } + } + m68k_areg (regs, 7) += 92; + } else { + m68k_areg (regs, 7) += 32; } } @@ -1857,13 +2252,87 @@ void flush_mmu030 (uaecptr addr, int n) void m68k_do_rts_mmu030 (void) { - m68k_setpc (get_long_mmu030 (m68k_areg (regs, 7))); + m68k_setpc (get_long_mmu030_state (m68k_areg (regs, 7))); m68k_areg (regs, 7) += 4; } void m68k_do_bsr_mmu030 (uaecptr oldpc, uae_s32 offset) { - put_long_mmu030 (m68k_areg (regs, 7) - 4, oldpc); + put_long_mmu030_state (m68k_areg (regs, 7) - 4, oldpc); m68k_areg (regs, 7) -= 4; m68k_incpci (offset); } + +uae_u32 REGPARAM2 get_disp_ea_020_mmu030 (uae_u32 base, int idx) +{ + uae_u16 dp; + int reg; + uae_u32 v; + int oldidx; + int pcadd = 0; + + // we need to do this hack here because in worst case we don't have enough + // stack frame space to store two very large 020 addressing mode access state + // + whatever the instruction itself does. + + if (mmu030_state[1] & (1 << idx)) { + m68k_incpci (((mmu030_state[2] >> (idx * 4)) & 15) * 2); + return mmu030_disp_store[idx]; + } + + oldidx = mmu030_idx; + dp = next_iword_mmu030_state (); + pcadd += 1; + + reg = (dp >> 12) & 15; + uae_s32 regd = regs.regs[reg]; + if ((dp & 0x800) == 0) + regd = (uae_s32)(uae_s16)regd; + regd <<= (dp >> 9) & 3; + if (dp & 0x100) { + uae_s32 outer = 0; + if (dp & 0x80) + base = 0; + if (dp & 0x40) + regd = 0; + + if ((dp & 0x30) == 0x20) { + base += (uae_s32)(uae_s16) next_iword_mmu030_state (); + pcadd += 1; + } + if ((dp & 0x30) == 0x30) { + base += next_ilong_mmu030_state (); + pcadd += 2; + } + + if ((dp & 0x3) == 0x2) { + outer = (uae_s32)(uae_s16) next_iword_mmu030_state (); + pcadd += 1; + } + if ((dp & 0x3) == 0x3) { + outer = next_ilong_mmu030_state (); + pcadd += 2; + } + + if ((dp & 0x4) == 0) { + base += regd; + } + if (dp & 0x3) { + base = get_long_mmu030_state (base); + } + if (dp & 0x4) { + base += regd; + } + v = base + outer; + } else { + v = base + (uae_s32)((uae_s8)dp) + regd; + } + + mmu030_state[1] |= 1 << idx; + mmu030_state[2] |= pcadd << (idx * 4); + mmu030_disp_store[idx] = v; + mmu030_idx = oldidx; + mmu030_ad[mmu030_idx].done = false; + + return v; +}