|
|
1.1 ! root 1: /* Emulation of MC68030 MMU ! 2: * This code has been written for Previous - a NeXT Computer emulator ! 3: * ! 4: * This file is distributed under the GNU General Public License, version 2 ! 5: * or at your option any later version. Read the file gpl.txt for details. ! 6: * ! 7: * ! 8: * Written by Andreas Grabher ! 9: * ! 10: * Many thanks go to Thomas Huth and the Hatari community for helping ! 11: * to test and debug this code! ! 12: * ! 13: * ! 14: * Release notes: ! 15: * 01-09-2012: First release ! 16: * 29-09-2012: Improved function code handling ! 17: * 16-11-2012: Improved exception handling ! 18: * ! 19: * ! 20: * - Check if read-modify-write operations are correctly detected for ! 21: * handling transparent access (see TT matching functions) ! 22: * - If possible, test mmu030_table_search with all kinds of translations ! 23: * (early termination, invalid descriptors, bus errors, indirect ! 24: * descriptors, PTEST in different levels, etc). ! 25: * - Check which bits of an ATC entry or the status register should be set ! 26: * and which should be un-set, if an invalid translation occurs. ! 27: * - Handle cache inhibit bit when accessing ATC entries ! 28: */ ! 29: ! 30: ! 31: #include "sysconfig.h" ! 32: #include "sysdeps.h" ! 33: ! 34: #include "main.h" ! 35: #include "hatari-glue.h" ! 36: #include "host.h" ! 37: ! 38: #include "options_cpu.h" ! 39: #include "memory.h" ! 40: #include "newcpu.h" ! 41: #include "cpummu030.h" ! 42: ! 43: #define MMU030_OP_DBG_MSG 0 ! 44: #define MMU030_ATC_DBG_MSG 0 ! 45: #define MMU030_REG_DBG_MSG 0 ! 46: ! 47: #define TT_FC_MASK 0x00000007 ! 48: #define TT_FC_BASE 0x00000070 ! 49: #define TT_RWM 0x00000100 ! 50: #define TT_RW 0x00000200 ! 51: #define TT_CI 0x00000400 ! 52: #define TT_ENABLE 0x00008000 ! 53: ! 54: #define TT_ADDR_MASK 0x00FF0000 ! 55: #define TT_ADDR_BASE 0xFF000000 ! 56: ! 57: static int bBusErrorReadWrite; ! 58: static int atcindextable[32]; ! 59: static int tt_enabled; ! 60: ! 61: int mmu030_idx; ! 62: ! 63: uae_u32 mm030_stageb_address; ! 64: bool mmu030_retry; ! 65: int mmu030_opcode; ! 66: int mmu030_opcode_stageb; ! 67: ! 68: int mmu030_fake_prefetch; ! 69: uaecptr mmu030_fake_prefetch_addr; ! 70: ! 71: uae_u16 mmu030_state[3]; ! 72: uae_u32 mmu030_data_buffer; ! 73: uae_u32 mmu030_disp_store[2]; ! 74: uae_u32 mmu030_fmovem_store[2]; ! 75: struct mmu030_access mmu030_ad[MAX_MMU030_ACCESS]; ! 76: ! 77: /* for debugging messages */ ! 78: char table_letter[4] = {'A','B','C','D'}; ! 79: ! 80: uae_u64 srp_030, crp_030; ! 81: uae_u32 tt0_030, tt1_030, tc_030; ! 82: uae_u16 mmusr_030; ! 83: ! 84: /* ATC struct */ ! 85: #define ATC030_NUM_ENTRIES 22 ! 86: ! 87: typedef struct { ! 88: struct { ! 89: uaecptr addr; ! 90: bool modified; ! 91: bool write_protect; ! 92: bool cache_inhibit; ! 93: bool bus_error; ! 94: } physical; ! 95: ! 96: struct { ! 97: uaecptr addr; ! 98: uae_u32 fc; ! 99: bool valid; ! 100: } logical; ! 101: /* history bit */ ! 102: int mru; ! 103: } MMU030_ATC_LINE; ! 104: ! 105: ! 106: /* MMU struct for 68030 */ ! 107: static struct { ! 108: ! 109: /* Translation tables */ ! 110: struct { ! 111: struct { ! 112: uae_u32 mask; ! 113: uae_u8 shift; ! 114: } table[4]; ! 115: ! 116: struct { ! 117: uae_u32 mask; ! 118: uae_u32 imask; ! 119: uae_u8 size; ! 120: } page; ! 121: ! 122: uae_u8 init_shift; ! 123: uae_u8 last_table; ! 124: } translation; ! 125: ! 126: /* Transparent translation */ ! 127: struct { ! 128: TT_info tt0; ! 129: TT_info tt1; ! 130: } transparent; ! 131: ! 132: /* Address translation cache */ ! 133: MMU030_ATC_LINE atc[ATC030_NUM_ENTRIES]; ! 134: ! 135: /* Condition */ ! 136: bool enabled; ! 137: uae_u16 status; ! 138: } mmu030; ! 139: ! 140: ! 141: ! 142: /* MMU Status Register ! 143: * ! 144: * ---x ---x x-xx x--- ! 145: * reserved (all 0) ! 146: * ! 147: * x--- ---- ---- ---- ! 148: * bus error ! 149: * ! 150: * -x-- ---- ---- ---- ! 151: * limit violation ! 152: * ! 153: * --x- ---- ---- ---- ! 154: * supervisor only ! 155: * ! 156: * ---- x--- ---- ---- ! 157: * write protected ! 158: * ! 159: * ---- -x-- ---- ---- ! 160: * invalid ! 161: * ! 162: * ---- --x- ---- ---- ! 163: * modified ! 164: * ! 165: * ---- ---- -x-- ---- ! 166: * transparent access ! 167: * ! 168: * ---- ---- ---- -xxx ! 169: * number of levels (number of tables accessed during search) ! 170: * ! 171: */ ! 172: ! 173: #define MMUSR_BUS_ERROR 0x8000 ! 174: #define MMUSR_LIMIT_VIOLATION 0x4000 ! 175: #define MMUSR_SUPER_VIOLATION 0x2000 ! 176: #define MMUSR_WRITE_PROTECTED 0x0800 ! 177: #define MMUSR_INVALID 0x0400 ! 178: #define MMUSR_MODIFIED 0x0200 ! 179: #define MMUSR_TRANSP_ACCESS 0x0040 ! 180: #define MMUSR_NUM_LEVELS_MASK 0x0007 ! 181: ! 182: ! 183: ! 184: /* -- MMU instructions -- */ ! 185: ! 186: static bool mmu_op30_invea(uae_u32 opcode) ! 187: { ! 188: int eamode = (opcode >> 3) & 7; ! 189: int rreg = opcode & 7; ! 190: ! 191: // Dn, An, (An)+, -(An), immediate and PC-relative not allowed ! 192: if (eamode == 0 || eamode == 1 || eamode == 3 || eamode == 4 || eamode == 6 || (eamode == 7 && rreg > 1)) ! 193: return true; ! 194: return false; ! 195: } ! 196: ! 197: bool mmu_op30_pmove (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 198: { ! 199: int preg = (next >> 10) & 31; ! 200: int rw = (next >> 9) & 1; ! 201: int fd = (next >> 8) & 1; ! 202: int unused = (next & 0xff); ! 203: ! 204: if (mmu_op30_invea(opcode)) ! 205: return true; ! 206: // unused low 8 bits must be zeroed ! 207: if (unused) ! 208: return true; ! 209: // read and fd set? ! 210: if (rw && fd) ! 211: return true; ! 212: ! 213: #if MMU030_OP_DBG_MSG ! 214: switch (preg) { ! 215: case 0x10: ! 216: write_log(_T("PMOVE: %s TC %08X\n"), rw?"read":"write", ! 217: rw?tc_030:x_get_long(extra)); ! 218: break; ! 219: case 0x12: ! 220: write_log(_T("PMOVE: %s SRP %08X%08X\n"), rw?"read":"write", ! 221: rw?(uae_u32)(srp_030>>32)&0xFFFFFFFF:x_get_long(extra), ! 222: rw?(uae_u32)srp_030&0xFFFFFFFF:x_get_long(extra+4)); ! 223: break; ! 224: case 0x13: ! 225: write_log(_T("PMOVE: %s CRP %08X%08X\n"), rw?"read":"write", ! 226: rw?(uae_u32)(crp_030>>32)&0xFFFFFFFF:x_get_long(extra), ! 227: rw?(uae_u32)crp_030&0xFFFFFFFF:x_get_long(extra+4)); ! 228: break; ! 229: case 0x18: ! 230: write_log(_T("PMOVE: %s MMUSR %04X\n"), rw?"read":"write", ! 231: rw?mmusr_030:x_get_word(extra)); ! 232: break; ! 233: case 0x02: ! 234: write_log(_T("PMOVE: %s TT0 %08X\n"), rw?"read":"write", ! 235: rw?tt0_030:x_get_long(extra)); ! 236: break; ! 237: case 0x03: ! 238: write_log(_T("PMOVE: %s TT1 %08X\n"), rw?"read":"write", ! 239: rw?tt1_030:x_get_long(extra)); ! 240: break; ! 241: default: ! 242: break; ! 243: } ! 244: if (!fd && !rw && !(preg==0x18)) { ! 245: write_log(_T("PMOVE: flush ATC\n")); ! 246: } ! 247: #endif ! 248: ! 249: switch (preg) ! 250: { ! 251: case 0x10: // TC ! 252: if (rw) ! 253: x_put_long (extra, tc_030); ! 254: else { ! 255: tc_030 = x_get_long (extra); ! 256: if (mmu030_decode_tc(tc_030)) ! 257: return true; ! 258: } ! 259: break; ! 260: case 0x12: // SRP ! 261: if (rw) { ! 262: x_put_long (extra, srp_030 >> 32); ! 263: x_put_long (extra + 4, srp_030); ! 264: } else { ! 265: srp_030 = (uae_u64)x_get_long (extra) << 32; ! 266: srp_030 |= x_get_long (extra + 4); ! 267: host_darkmatter(srp_030 == crp_030); ! 268: if (mmu030_decode_rp(srp_030)) ! 269: return true; ! 270: } ! 271: break; ! 272: case 0x13: // CRP ! 273: if (rw) { ! 274: x_put_long (extra, crp_030 >> 32); ! 275: x_put_long (extra + 4, crp_030); ! 276: } else { ! 277: crp_030 = (uae_u64)x_get_long (extra) << 32; ! 278: crp_030 |= x_get_long (extra + 4); ! 279: if (mmu030_decode_rp(crp_030)) ! 280: return true; ! 281: } ! 282: break; ! 283: case 0x18: // MMUSR ! 284: if (fd) { ! 285: // FD must be always zero when MMUSR read or write ! 286: return true; ! 287: } ! 288: if (rw) ! 289: x_put_word (extra, mmusr_030); ! 290: else ! 291: mmusr_030 = x_get_word (extra); ! 292: break; ! 293: case 0x02: // TT0 ! 294: if (rw) ! 295: x_put_long (extra, tt0_030); ! 296: else { ! 297: tt0_030 = x_get_long (extra); ! 298: mmu030.transparent.tt0 = mmu030_decode_tt(tt0_030); ! 299: } ! 300: break; ! 301: case 0x03: // TT1 ! 302: if (rw) ! 303: x_put_long (extra, tt1_030); ! 304: else { ! 305: tt1_030 = x_get_long (extra); ! 306: mmu030.transparent.tt1 = mmu030_decode_tt(tt1_030); ! 307: } ! 308: break; ! 309: default: ! 310: write_log (_T("Bad PMOVE at %08x\n"),m68k_getpc()); ! 311: return true; ! 312: } ! 313: ! 314: if (!fd && !rw && preg != 0x18) { ! 315: mmu030_flush_atc_all(); ! 316: } ! 317: tt_enabled = (tt0_030 & TT_ENABLE) || (tt1_030 & TT_ENABLE); ! 318: return false; ! 319: } ! 320: ! 321: bool mmu_op30_ptest (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 322: { ! 323: mmu030.status = mmusr_030 = 0; ! 324: ! 325: int level = (next&0x1C00)>>10; ! 326: int rw = (next >> 9) & 1; ! 327: int a = (next >> 8) & 1; ! 328: int areg = (next&0xE0)>>5; ! 329: uae_u32 fc = mmu_op30_helper_get_fc(next); ! 330: ! 331: bool write = rw ? false : true; ! 332: ! 333: uae_u32 ret = 0; ! 334: ! 335: if (mmu_op30_invea(opcode)) ! 336: return true; ! 337: ! 338: if (!level && a) { ! 339: write_log(_T("PTEST: Bad instruction causing F-line unimplemented instruction exception!\n")); ! 340: return true; ! 341: } ! 342: ! 343: #if MMU030_OP_DBG_MSG ! 344: write_log(_T("PTEST%c: addr = %08X, fc = %i, level = %i, "), ! 345: rw?'R':'W', extra, fc, level); ! 346: if (a) { ! 347: write_log(_T("return descriptor to register A%i\n"), areg); ! 348: } else { ! 349: write_log(_T("do not return descriptor\n")); ! 350: } ! 351: #endif ! 352: ! 353: if (!level) { ! 354: mmu030_ptest_atc_search(extra, fc, write); ! 355: } else { ! 356: ret = mmu030_ptest_table_search(extra, fc, write, level); ! 357: if (a) { ! 358: m68k_areg (regs, areg) = ret; ! 359: } ! 360: } ! 361: mmusr_030 = mmu030.status; ! 362: ! 363: #if MMU030_OP_DBG_MSG ! 364: write_log(_T("PTEST status: %04X, B = %i, L = %i, S = %i, W = %i, I = %i, M = %i, T = %i, N = %i\n"), ! 365: mmusr_030, (mmusr_030&MMUSR_BUS_ERROR)?1:0, (mmusr_030&MMUSR_LIMIT_VIOLATION)?1:0, ! 366: (mmusr_030&MMUSR_SUPER_VIOLATION)?1:0, (mmusr_030&MMUSR_WRITE_PROTECTED)?1:0, ! 367: (mmusr_030&MMUSR_INVALID)?1:0, (mmusr_030&MMUSR_MODIFIED)?1:0, ! 368: (mmusr_030&MMUSR_TRANSP_ACCESS)?1:0, mmusr_030&MMUSR_NUM_LEVELS_MASK); ! 369: #endif ! 370: return false; ! 371: } ! 372: ! 373: bool mmu_op30_pload (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 374: { ! 375: int rw = (next >> 9) & 1; ! 376: int unused = (next & (0x100 | 0x80 | 0x40 | 0x20)); ! 377: uae_u32 fc = mmu_op30_helper_get_fc(next); ! 378: bool write = rw ? false : true; ! 379: ! 380: if (mmu_op30_invea(opcode)) ! 381: return true; ! 382: if (unused) ! 383: return true; ! 384: ! 385: #if 0 ! 386: write_log (_T("PLOAD%c: Create ATC entry for %08X, FC = %i\n"), write?'W':'R', extra, fc); ! 387: #endif ! 388: ! 389: mmu030_flush_atc_page(extra); ! 390: mmu030_table_search(extra, fc, write, 0); ! 391: return false; ! 392: } ! 393: ! 394: bool mmu_op30_pflush (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 395: { ! 396: uae_u16 mode = (next >> 10) & 0x7; ! 397: uae_u32 fc_mask = (next >> 5) & 0x7; ! 398: uae_u32 fc_base = mmu_op30_helper_get_fc(next); ! 399: uae_u32 fc_bits = next & 0x1f; ! 400: ! 401: #if 0 ! 402: switch (mode) { ! 403: case 0x1: ! 404: write_log(_T("PFLUSH: Flush all entries\n")); ! 405: break; ! 406: case 0x4: ! 407: write_log(_T("PFLUSH: Flush by function code only\n")); ! 408: write_log(_T("PFLUSH: function code: base = %08X, mask = %08X\n"), fc_base, fc_mask); ! 409: break; ! 410: case 0x6: ! 411: write_log(_T("PFLUSH: Flush by function code and effective address\n")); ! 412: write_log(_T("PFLUSH: function code: base = %08X, mask = %08X\n"), fc_base, fc_mask); ! 413: write_log(_T("PFLUSH: effective address = %08X\n"), extra); ! 414: break; ! 415: default: ! 416: break; ! 417: } ! 418: #endif ! 419: ! 420: switch (mode) { ! 421: case 0x1: ! 422: if (fc_bits) ! 423: return true; ! 424: mmu030_flush_atc_all(); ! 425: break; ! 426: case 0x4: ! 427: mmu030_flush_atc_fc(fc_base, fc_mask); ! 428: break; ! 429: case 0x6: ! 430: if (mmu_op30_invea(opcode)) ! 431: return true; ! 432: mmu030_flush_atc_page_fc(extra, fc_base, fc_mask); ! 433: break; ! 434: ! 435: default: ! 436: write_log(_T("PFLUSH ERROR: bad mode! (%i)\n"),mode); ! 437: return true; ! 438: } ! 439: return false; ! 440: } ! 441: ! 442: /* -- Helper function for MMU instructions -- */ ! 443: uae_u32 mmu_op30_helper_get_fc(uae_u16 next) { ! 444: switch (next&0x0018) { ! 445: case 0x0010: ! 446: return (next&0x7); ! 447: case 0x0008: ! 448: return (m68k_dreg(regs, next&0x7)&0x7); ! 449: case 0x0000: ! 450: if (next&1) { ! 451: return (regs.dfc); ! 452: } else { ! 453: return (regs.sfc); ! 454: } ! 455: default: ! 456: write_log(_T("MMU_OP30 ERROR: bad fc source! (%04X)\n"),next&0x0018); ! 457: return 0; ! 458: } ! 459: } ! 460: ! 461: ! 462: /* -- ATC flushing functions -- */ ! 463: ! 464: /* This function flushes ATC entries depending on their function code */ ! 465: void mmu030_flush_atc_fc(uae_u32 fc_base, uae_u32 fc_mask) { ! 466: int i; ! 467: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 468: if (((fc_base&fc_mask)==(mmu030.atc[i].logical.fc&fc_mask)) && ! 469: mmu030.atc[i].logical.valid) { ! 470: mmu030.atc[i].logical.valid = false; ! 471: #if MMU030_OP_DBG_MSG ! 472: write_log(_T("ATC: Flushing %08X\n"), mmu030.atc[i].physical.addr); ! 473: #endif ! 474: } ! 475: } ! 476: } ! 477: ! 478: /* This function flushes ATC entries depending on their logical address ! 479: * and their function code */ ! 480: void mmu030_flush_atc_page_fc(uaecptr logical_addr, uae_u32 fc_base, uae_u32 fc_mask) { ! 481: int i; ! 482: logical_addr &= mmu030.translation.page.imask; ! 483: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 484: if (((fc_base&fc_mask)==(mmu030.atc[i].logical.fc&fc_mask)) && ! 485: (mmu030.atc[i].logical.addr == logical_addr) && ! 486: mmu030.atc[i].logical.valid) { ! 487: mmu030.atc[i].logical.valid = false; ! 488: #if MMU030_OP_DBG_MSG ! 489: write_log(_T("ATC: Flushing %08X\n"), mmu030.atc[i].physical.addr); ! 490: #endif ! 491: } ! 492: } ! 493: } ! 494: ! 495: /* This function flushes ATC entries depending on their logical address */ ! 496: void mmu030_flush_atc_page(uaecptr logical_addr) { ! 497: int i; ! 498: logical_addr &= mmu030.translation.page.imask; ! 499: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 500: if ((mmu030.atc[i].logical.addr == logical_addr) && ! 501: mmu030.atc[i].logical.valid) { ! 502: mmu030.atc[i].logical.valid = false; ! 503: #if MMU030_OP_DBG_MSG ! 504: write_log(_T("ATC: Flushing %08X\n"), mmu030.atc[i].physical.addr); ! 505: #endif ! 506: } ! 507: } ! 508: } ! 509: ! 510: /* This function flushes all ATC entries */ ! 511: void mmu030_flush_atc_all(void) { ! 512: #if MMU030_OP_DBG_MSG ! 513: write_log(_T("ATC: Flushing all entries\n")); ! 514: #endif ! 515: int i; ! 516: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 517: mmu030.atc[i].logical.valid = false; ! 518: } ! 519: } ! 520: ! 521: ! 522: /* Transparent Translation Registers (TT0 and TT1) ! 523: * ! 524: * ---- ---- ---- ---- -xxx x--- x--- x--- ! 525: * reserved, must be 0 ! 526: * ! 527: * ---- ---- ---- ---- ---- ---- ---- -xxx ! 528: * function code mask (FC bits to be ignored) ! 529: * ! 530: * ---- ---- ---- ---- ---- ---- -xxx ---- ! 531: * function code base (FC value for transparent block) ! 532: * ! 533: * ---- ---- ---- ---- ---- ---x ---- ---- ! 534: * 0 = r/w field used, 1 = read and write is transparently translated ! 535: * ! 536: * ---- ---- ---- ---- ---- --x- ---- ---- ! 537: * r/w field: 0 = write ..., 1 = read access transparent ! 538: * ! 539: * ---- ---- ---- ---- ---- -x-- ---- ---- ! 540: * cache inhibit: 0 = caching allowed, 1 = caching inhibited ! 541: * ! 542: * ---- ---- ---- ---- x--- ---- ---- ---- ! 543: * 0 = transparent translation enabled disabled, 1 = enabled ! 544: * ! 545: * ---- ---- xxxx xxxx ---- ---- ---- ---- ! 546: * logical address mask ! 547: * ! 548: * xxxx xxxx ---- ---- ---- ---- ---- ---- ! 549: * logical address base ! 550: * ! 551: */ ! 552: ! 553: /* TT comparision results */ ! 554: #define TT_NO_MATCH 0x1 ! 555: #define TT_OK_MATCH 0x2 ! 556: #define TT_NO_READ 0x4 ! 557: #define TT_NO_WRITE 0x8 ! 558: ! 559: TT_info mmu030_decode_tt(uae_u32 TT) { ! 560: ! 561: TT_info ret; ! 562: ! 563: ret.fc_mask = ~((TT&TT_FC_MASK)|0xFFFFFFF8); ! 564: ret.fc_base = (TT&TT_FC_BASE)>>4; ! 565: ret.addr_base = TT & TT_ADDR_BASE; ! 566: ret.addr_mask = ~(((TT&TT_ADDR_MASK)<<8)|0x00FFFFFF); ! 567: ! 568: #if 0 ! 569: if ((TT&TT_ENABLE) && !(TT&TT_RWM)) { ! 570: write_log(_T("MMU Warning: Transparent translation of read-modify-write cycle is not correctly handled!\n")); ! 571: } ! 572: #endif ! 573: ! 574: #if MMU030_REG_DBG_MSG /* enable or disable debugging messages */ ! 575: write_log(_T("\n")); ! 576: write_log(_T("TRANSPARENT TRANSLATION: %08X\n"), TT); ! 577: write_log(_T("\n")); ! 578: ! 579: write_log(_T("TT: transparent translation ")); ! 580: if (TT&TT_ENABLE) { ! 581: write_log(_T("enabled\n")); ! 582: } else { ! 583: write_log(_T("disabled\n")); ! 584: return ret; ! 585: } ! 586: ! 587: write_log(_T("TT: caching %s\n"), (TT&TT_CI) ? _T("inhibited") : _T("enabled")); ! 588: write_log(_T("TT: read-modify-write ")); ! 589: if (TT&TT_RWM) { ! 590: write_log(_T("enabled\n")); ! 591: } else { ! 592: write_log(_T("disabled (%s only)\n"), (TT&TT_RW) ? _T("read") : _T("write")); ! 593: } ! 594: write_log(_T("\n")); ! 595: write_log(_T("TT: function code base: %08X\n"), ret.fc_base); ! 596: write_log(_T("TT: function code mask: %08X\n"), ret.fc_mask); ! 597: write_log(_T("\n")); ! 598: write_log(_T("TT: address base: %08X\n"), ret.addr_base); ! 599: write_log(_T("TT: address mask: %08X\n"), ret.addr_mask); ! 600: write_log(_T("\n")); ! 601: #endif ! 602: ! 603: return ret; ! 604: } ! 605: ! 606: /* This function compares the address with both transparent ! 607: * translation registers and returns the result */ ! 608: int mmu030_match_ttr(uaecptr addr, uae_u32 fc, bool write) ! 609: { ! 610: int tt0, tt1; ! 611: ! 612: bool cache_inhibit = false; /* TODO: pass to memory access function */ ! 613: ! 614: tt0 = mmu030_do_match_ttr(tt0_030, mmu030.transparent.tt0, addr, fc, write); ! 615: if (tt0&TT_OK_MATCH) { ! 616: cache_inhibit = (tt0_030&TT_CI) ? true : false; ! 617: } ! 618: tt1 = mmu030_do_match_ttr(tt1_030, mmu030.transparent.tt1, addr, fc, write); ! 619: if (tt1&TT_OK_MATCH) { ! 620: if (!cache_inhibit) { ! 621: cache_inhibit = (tt1_030&TT_CI) ? true : false; ! 622: } ! 623: } ! 624: ! 625: return (tt0|tt1); ! 626: } ! 627: int mmu030_match_ttr_access(uaecptr addr, uae_u32 fc, bool write) ! 628: { ! 629: int tt0, tt1; ! 630: if (!tt_enabled) ! 631: return 0; ! 632: tt0 = mmu030_do_match_ttr(tt0_030, mmu030.transparent.tt0, addr, fc, write); ! 633: tt1 = mmu030_do_match_ttr(tt1_030, mmu030.transparent.tt1, addr, fc, write); ! 634: return (tt0|tt1) & TT_OK_MATCH; ! 635: } ! 636: ! 637: /* Locked Read-Modify-Write */ ! 638: int mmu030_match_lrmw_ttr_access(uaecptr addr, uae_u32 fc) ! 639: { ! 640: int tt0, tt1; ! 641: ! 642: if (!tt_enabled) ! 643: return 0; ! 644: tt0 = mmu030_do_match_lrmw_ttr(tt0_030, mmu030.transparent.tt0, addr, fc); ! 645: tt1 = mmu030_do_match_lrmw_ttr(tt1_030, mmu030.transparent.tt1, addr, fc); ! 646: return (tt0|tt1) & TT_OK_MATCH; ! 647: } ! 648: ! 649: /* This function checks if an address matches a transparent ! 650: * translation register */ ! 651: ! 652: /* FIXME: ! 653: * If !(tt&TT_RMW) neither the read nor the write portion ! 654: * of a read-modify-write cycle is transparently translated! */ ! 655: ! 656: int mmu030_do_match_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc, bool write) ! 657: { ! 658: if (tt & TT_ENABLE) { /* transparent translation enabled */ ! 659: ! 660: /* Compare actual function code with function code base using mask */ ! 661: if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) { ! 662: ! 663: /* Compare actual address with address base using mask */ ! 664: if ((comp.addr_base&comp.addr_mask)==(addr&comp.addr_mask)) { ! 665: ! 666: if (tt&TT_RWM) { /* r/w field disabled */ ! 667: return TT_OK_MATCH; ! 668: } else { ! 669: if (tt&TT_RW) { /* read access transparent */ ! 670: return write ? TT_NO_WRITE : TT_OK_MATCH; ! 671: } else { /* write access transparent */ ! 672: return write ? TT_OK_MATCH : TT_NO_READ; /* TODO: check this! */ ! 673: } ! 674: } ! 675: } ! 676: } ! 677: } ! 678: return TT_NO_MATCH; ! 679: } ! 680: ! 681: int mmu030_do_match_lrmw_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc) ! 682: { ! 683: if ((tt & TT_ENABLE) && (tt & TT_RWM)) { /* transparent translation enabled */ ! 684: ! 685: /* Compare actual function code with function code base using mask */ ! 686: if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) { ! 687: ! 688: /* Compare actual address with address base using mask */ ! 689: if ((comp.addr_base&comp.addr_mask)==(addr&comp.addr_mask)) { ! 690: ! 691: return TT_OK_MATCH; ! 692: } ! 693: } ! 694: } ! 695: return TT_NO_MATCH; ! 696: } ! 697: ! 698: ! 699: ! 700: /* Translation Control Register: ! 701: * ! 702: * x--- ---- ---- ---- ---- ---- ---- ---- ! 703: * translation: 1 = enable, 0 = disable ! 704: * ! 705: * ---- --x- ---- ---- ---- ---- ---- ---- ! 706: * supervisor root: 1 = enable, 0 = disable ! 707: * ! 708: * ---- ---x ---- ---- ---- ---- ---- ---- ! 709: * function code lookup: 1 = enable, 0 = disable ! 710: * ! 711: * ---- ---- xxxx ---- ---- ---- ---- ---- ! 712: * page size: ! 713: * 1000 = 256 bytes ! 714: * 1001 = 512 bytes ! 715: * 1010 = 1 kB ! 716: * 1011 = 2 kB ! 717: * 1100 = 4 kB ! 718: * 1101 = 8 kB ! 719: * 1110 = 16 kB ! 720: * 1111 = 32 kB ! 721: * ! 722: * ---- ---- ---- xxxx ---- ---- ---- ---- ! 723: * initial shift ! 724: * ! 725: * ---- ---- ---- ---- xxxx ---- ---- ---- ! 726: * number of bits for table index A ! 727: * ! 728: * ---- ---- ---- ---- ---- xxxx ---- ---- ! 729: * number of bits for table index B ! 730: * ! 731: * ---- ---- ---- ---- ---- ---- xxxx ---- ! 732: * number of bits for table index C ! 733: * ! 734: * ---- ---- ---- ---- ---- ----- ---- xxxx ! 735: * number of bits for table index D ! 736: * ! 737: */ ! 738: ! 739: ! 740: #define TC_ENABLE_TRANSLATION 0x80000000 ! 741: #define TC_ENABLE_SUPERVISOR 0x02000000 ! 742: #define TC_ENABLE_FCL 0x01000000 ! 743: ! 744: #define TC_PS_MASK 0x00F00000 ! 745: #define TC_IS_MASK 0x000F0000 ! 746: ! 747: #define TC_TIA_MASK 0x0000F000 ! 748: #define TC_TIB_MASK 0x00000F00 ! 749: #define TC_TIC_MASK 0x000000F0 ! 750: #define TC_TID_MASK 0x0000000F ! 751: ! 752: static void mmu030_do_fake_prefetch(void) ! 753: { ! 754: // fetch next opcode before MMU state switches. ! 755: // There are programs that do following: ! 756: // - enable MMU ! 757: // - JMP (An) ! 758: // "enable MMU" unmaps memory under us. ! 759: TRY (prb) { ! 760: uaecptr pc = m68k_getpci(); ! 761: mmu030_fake_prefetch = -1; ! 762: mmu030_fake_prefetch_addr = mmu030_translate(pc, regs.s != 0, false, false); ! 763: mmu030_fake_prefetch = x_prefetch(0); ! 764: // A26x0 ROM code switches off rom ! 765: // NOP ! 766: // JMP (a0) ! 767: if (mmu030_fake_prefetch == 0x4e71) ! 768: mmu030_fake_prefetch = x_prefetch(2); ! 769: } CATCH (prb) { ! 770: // didn't work, oh well.. ! 771: mmu030_fake_prefetch = -1; ! 772: } ENDTRY ! 773: } ! 774: ! 775: bool mmu030_decode_tc(uae_u32 TC) ! 776: { ! 777: /* Set MMU condition */ ! 778: if (TC & TC_ENABLE_TRANSLATION) { ! 779: if (!mmu030.enabled) ! 780: mmu030_do_fake_prefetch(); ! 781: mmu030.enabled = true; ! 782: } else { ! 783: if (mmu030.enabled) { ! 784: mmu030_do_fake_prefetch(); ! 785: write_log(_T("MMU disabled PC=%08x\n"), M68K_GETPC); ! 786: } ! 787: mmu030.enabled = false; ! 788: return false; ! 789: } ! 790: ! 791: /* Note: 0 = Table A, 1 = Table B, 2 = Table C, 3 = Table D */ ! 792: int i, j; ! 793: uae_u8 TI_bits[4] = {0,0,0,0}; ! 794: ! 795: /* Reset variables before extracting new values from TC */ ! 796: for (i = 0; i < 4; i++) { ! 797: mmu030.translation.table[i].mask = 0; ! 798: mmu030.translation.table[i].shift = 0; ! 799: } ! 800: ! 801: ! 802: /* Extract initial shift and page size values from TC register */ ! 803: mmu030.translation.page.size = (TC & TC_PS_MASK) >> 20; ! 804: mmu030.translation.init_shift = (TC & TC_IS_MASK) >> 16; ! 805: regs.mmu_page_size = 1 << mmu030.translation.page.size; ! 806: ! 807: ! 808: write_log(_T("68030 MMU enabled. Page size = %d PC=%08x\n"), regs.mmu_page_size, M68K_GETPC); ! 809: ! 810: if (mmu030.translation.page.size<8) { ! 811: write_log(_T("MMU Configuration Exception: Bad value in TC register! (bad page size: %i byte)\n"), ! 812: 1<<mmu030.translation.page.size); ! 813: Exception(56); /* MMU Configuration Exception */ ! 814: return true; ! 815: } ! 816: mmu030.translation.page.mask = regs.mmu_page_size - 1; ! 817: mmu030.translation.page.imask = ~mmu030.translation.page.mask; ! 818: ! 819: /* Calculate masks and shifts for later extracting table indices ! 820: * from logical addresses using: index = (addr&mask)>>shift */ ! 821: ! 822: /* Get number of bits for each table index */ ! 823: for (i = 0; i < 4; i++) { ! 824: j = (3-i)*4; ! 825: TI_bits[i] = (TC >> j) & 0xF; ! 826: } ! 827: ! 828: /* Calculate masks and shifts for each table */ ! 829: mmu030.translation.last_table = 0; ! 830: uae_u8 shift = 32 - mmu030.translation.init_shift; ! 831: for (i = 0; (i < 4) && TI_bits[i]; i++) { ! 832: /* Get the shift */ ! 833: shift -= TI_bits[i]; ! 834: mmu030.translation.table[i].shift = shift; ! 835: /* Build the mask */ ! 836: for (j = 0; j < TI_bits[i]; j++) { ! 837: mmu030.translation.table[i].mask |= (1<<(mmu030.translation.table[i].shift + j)); ! 838: } ! 839: /* Update until reaching the last table */ ! 840: mmu030.translation.last_table = i; ! 841: } ! 842: ! 843: #if MMU030_REG_DBG_MSG ! 844: /* At least one table has to be defined using at least ! 845: * 1 bit for the index. At least 2 bits are necessary ! 846: * if there is no second table. If these conditions are ! 847: * not met, it will automatically lead to a sum <32 ! 848: * and cause an exception (see below). */ ! 849: if (!TI_bits[0]) { ! 850: write_log(_T("MMU Configuration Exception: Bad value in TC register! (no first table index defined)\n")); ! 851: } else if ((TI_bits[0]<2) && !TI_bits[1]) { ! 852: write_log(_T("MMU Configuration Exception: Bad value in TC register! (no second table index defined and)\n")); ! 853: write_log(_T("MMU Configuration Exception: Bad value in TC register! (only 1 bit for first table index)\n")); ! 854: } ! 855: #endif ! 856: ! 857: /* TI fields are summed up until a zero field is reached (see above ! 858: * loop). The sum of all TI field values plus page size and initial ! 859: * shift has to be 32: IS + PS + TIA + TIB + TIC + TID = 32 */ ! 860: if ((shift-mmu030.translation.page.size)!=0) { ! 861: write_log(_T("MMU Configuration Exception: Bad value in TC register! (bad sum)\n")); ! 862: Exception(56); /* MMU Configuration Exception */ ! 863: return true; ! 864: } ! 865: ! 866: #if MMU030_REG_DBG_MSG /* enable or disable debugging output */ ! 867: write_log(_T("\n")); ! 868: write_log(_T("TRANSLATION CONTROL: %08X\n"), TC); ! 869: write_log(_T("\n")); ! 870: write_log(_T("TC: translation %s\n"), (TC&TC_ENABLE_TRANSLATION ? _T("enabled") : _T("disabled"))); ! 871: write_log(_T("TC: supervisor root pointer %s\n"), (TC&TC_ENABLE_SUPERVISOR ? _T("enabled") : _T("disabled"))); ! 872: write_log(_T("TC: function code lookup %s\n"), (TC&TC_ENABLE_FCL ? _T("enabled") : _T("disabled"))); ! 873: write_log(_T("\n")); ! 874: ! 875: write_log(_T("TC: Initial Shift: %i\n"), mmu030.translation.init_shift); ! 876: write_log(_T("TC: Page Size: %i byte\n"), (1<<mmu030.translation.page.size)); ! 877: write_log(_T("\n")); ! 878: ! 879: for (i = 0; i <= mmu030.translation.last_table; i++) { ! 880: write_log(_T("TC: Table %c: mask = %08X, shift = %i\n"), table_letter[i], mmu030.translation.table[i].mask, mmu030.translation.table[i].shift); ! 881: } ! 882: ! 883: write_log(_T("TC: Page: mask = %08X\n"), mmu030.translation.page.mask); ! 884: write_log(_T("\n")); ! 885: ! 886: write_log(_T("TC: Last Table: %c\n"), table_letter[mmu030.translation.last_table]); ! 887: write_log(_T("\n")); ! 888: #endif ! 889: return false; ! 890: } ! 891: ! 892: ! 893: ! 894: /* Root Pointer Registers (SRP and CRP) ! 895: * ! 896: * ---- ---- ---- ---- xxxx xxxx xxxx xx-- ---- ---- ---- ---- ---- ---- ---- xxxx ! 897: * reserved, must be 0 ! 898: * ! 899: * ---- ---- ---- ---- ---- ---- ---- ---- xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 900: * table A address ! 901: * ! 902: * ---- ---- ---- ---- ---- ---- ---- --xx ---- ---- ---- ---- ---- ---- ---- ---- ! 903: * descriptor type ! 904: * ! 905: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ! 906: * limit ! 907: * ! 908: * x--- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ! 909: * 0 = upper limit, 1 = lower limit ! 910: * ! 911: */ ! 912: ! 913: ! 914: #define RP_ADDR_MASK (UVAL64(0x00000000FFFFFFF0)) ! 915: #define RP_DESCR_MASK (UVAL64(0x0000000300000000)) ! 916: #define RP_LIMIT_MASK (UVAL64(0x7FFF000000000000)) ! 917: #define RP_LOWER_MASK (UVAL64(0x8000000000000000)) ! 918: ! 919: #define RP_ZERO_BITS 0x0000FFFC /* These bits in upper longword of RP must be 0 */ ! 920: ! 921: bool mmu030_decode_rp(uae_u64 RP) { ! 922: ! 923: uae_u8 descriptor_type = (RP & RP_DESCR_MASK) >> 32; ! 924: if (!descriptor_type) { /* If descriptor type is invalid */ ! 925: write_log(_T("MMU Configuration Exception: Root Pointer is invalid!\n")); ! 926: Exception(56); /* MMU Configuration Exception */ ! 927: return true; ! 928: } ! 929: return false; ! 930: ! 931: #if MMU030_REG_DBG_MSG /* enable or disable debugging output */ ! 932: uae_u32 table_limit = (RP & RP_LIMIT_MASK) >> 48; ! 933: uae_u32 first_addr = (RP & RP_ADDR_MASK); ! 934: ! 935: write_log(_T("\n")); ! 936: write_log(_T("ROOT POINTER: %08X%08X\n"), (uae_u32)(RP>>32)&0xFFFFFFFF, (uae_u32)(RP&0xFFFFFFFF)); ! 937: write_log(_T("\n")); ! 938: ! 939: write_log(_T("RP: descriptor type = %i "), descriptor_type); ! 940: switch (descriptor_type) { ! 941: case 0: ! 942: write_log(_T("(invalid descriptor)\n")); ! 943: break; ! 944: case 1: ! 945: write_log(_T("(early termination page descriptor)\n")); ! 946: break; ! 947: case 2: ! 948: write_log(_T("(valid 4 byte descriptor)\n")); ! 949: break; ! 950: case 3: ! 951: write_log(_T("(valid 8 byte descriptor)\n")); ! 952: break; ! 953: } ! 954: ! 955: write_log(_T("RP: %s limit = %i\n"), (RP&RP_LOWER_MASK) ? _T("lower") : _T("upper"), table_limit); ! 956: ! 957: write_log(_T("RP: first table address = %08X\n"), first_addr); ! 958: write_log(_T("\n")); ! 959: #endif ! 960: } ! 961: ! 962: ! 963: ! 964: /* Descriptors */ ! 965: ! 966: #define DESCR_TYPE_MASK 0x00000003 ! 967: ! 968: #define DESCR_TYPE_INVALID 0 /* all tables */ ! 969: ! 970: #define DESCR_TYPE_EARLY_TERM 1 /* all but lowest level table */ ! 971: #define DESCR_TYPE_PAGE 1 /* only lowest level table */ ! 972: #define DESCR_TYPE_VALID4 2 /* all but lowest level table */ ! 973: #define DESCR_TYPE_INDIRECT4 2 /* only lowest level table */ ! 974: #define DESCR_TYPE_VALID8 3 /* all but lowest level table */ ! 975: #define DESCR_TYPE_INDIRECT8 3 /* only lowest level table */ ! 976: ! 977: #define DESCR_TYPE_VALID_MASK 0x2 /* all but lowest level table */ ! 978: #define DESCR_TYPE_INDIRECT_MASK 0x2 /* only lowest level table */ ! 979: ! 980: ! 981: /* Short format (4 byte): ! 982: * ! 983: * ---- ---- ---- ---- ---- ---- ---- --xx ! 984: * descriptor type: ! 985: * 0 = invalid ! 986: * 1 = page descriptor (early termination) ! 987: * 2 = valid (4 byte) ! 988: * 3 = valid (8 byte) ! 989: * ! 990: * ! 991: * table descriptor: ! 992: * ---- ---- ---- ---- ---- ---- ---- -x-- ! 993: * write protect ! 994: * ! 995: * ---- ---- ---- ---- ---- ---- ---- x--- ! 996: * update ! 997: * ! 998: * xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 999: * table address ! 1000: * ! 1001: * ! 1002: * (early termination) page descriptor: ! 1003: * ---- ---- ---- ---- ---- ---- ---- -x-- ! 1004: * write protect ! 1005: * ! 1006: * ---- ---- ---- ---- ---- ---- ---- x--- ! 1007: * update ! 1008: * ! 1009: * ---- ---- ---- ---- ---- ---- ---x ---- ! 1010: * modified ! 1011: * ! 1012: * ---- ---- ---- ---- ---- ---- -x-- ---- ! 1013: * cache inhibit ! 1014: * ! 1015: * ---- ---- ---- ---- ---- ---- x-x- ---- ! 1016: * reserved (must be 0) ! 1017: * ! 1018: * xxxx xxxx xxxx xxxx xxxx xxxx ---- ---- ! 1019: * page address ! 1020: * ! 1021: * ! 1022: * indirect descriptor: ! 1023: * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx-- ! 1024: * descriptor address ! 1025: * ! 1026: */ ! 1027: ! 1028: #define DESCR_WP 0x00000004 ! 1029: #define DESCR_U 0x00000008 ! 1030: #define DESCR_M 0x00000010 /* only last level table */ ! 1031: #define DESCR_CI 0x00000040 /* only last level table */ ! 1032: ! 1033: #define DESCR_TD_ADDR_MASK 0xFFFFFFF0 ! 1034: #define DESCR_PD_ADDR_MASK 0xFFFFFF00 ! 1035: #define DESCR_ID_ADDR_MASK 0xFFFFFFFC ! 1036: ! 1037: ! 1038: /* Long format (8 byte): ! 1039: * ! 1040: * ---- ---- ---- ---- ---- ---- ---- --xx | ---- ---- ---- ---- ---- ---- ---- ---- ! 1041: * descriptor type: ! 1042: * 0 = invalid ! 1043: * 1 = page descriptor (early termination) ! 1044: * 2 = valid (4 byte) ! 1045: * 3 = valid (8 byte) ! 1046: * ! 1047: * ! 1048: * table desctriptor: ! 1049: * ---- ---- ---- ---- ---- ---- ---- -x-- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1050: * write protect ! 1051: * ! 1052: * ---- ---- ---- ---- ---- ---- ---- x--- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1053: * update ! 1054: * ! 1055: * ---- ---- ---- ---- ---- ---- xxxx ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1056: * reserved (must be 0) ! 1057: * ! 1058: * ---- ---- ---- ---- ---- ---x ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1059: * supervisor ! 1060: * ! 1061: * ---- ---- ---- ---- xxxx xxx- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1062: * reserved (must be 1111 110) ! 1063: * ! 1064: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1065: * limit ! 1066: * ! 1067: * x--- ---- ---- ---- ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1068: * 0 = upper limit, 1 = lower limit ! 1069: * ! 1070: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 1071: * table address ! 1072: * ! 1073: * ! 1074: * (early termination) page descriptor: ! 1075: * ---- ---- ---- ---- ---- ---- ---- -x-- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1076: * write protect ! 1077: * ! 1078: * ---- ---- ---- ---- ---- ---- ---- x--- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1079: * update ! 1080: * ! 1081: * ---- ---- ---- ---- ---- ---- ---x ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1082: * modified ! 1083: * ! 1084: * ---- ---- ---- ---- ---- ---- -x-- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1085: * cache inhibit ! 1086: * ! 1087: * ---- ---- ---- ---- ---- ---x ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1088: * supervisor ! 1089: * ! 1090: * ---- ---- ---- ---- ---- ---- x-x- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1091: * reserved (must be 0) ! 1092: * ! 1093: * ---- ---- ---- ---- xxxx xxx- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1094: * reserved (must be 1111 110) ! 1095: * ! 1096: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1097: * limit (only used with early termination page descriptor) ! 1098: * ! 1099: * x--- ---- ---- ---- ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 1100: * 0 = upper limit, 1 = lower limit (only used with early termination page descriptor) ! 1101: * ! 1102: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx ---- ---- ! 1103: * page address ! 1104: * ! 1105: * ! 1106: * indirect descriptor: ! 1107: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx-- ! 1108: * descriptor address ! 1109: * ! 1110: */ ! 1111: ! 1112: /* only for long descriptors */ ! 1113: #define DESCR_S 0x00000100 ! 1114: ! 1115: #define DESCR_LIMIT_MASK 0x7FFF0000 ! 1116: #define DESCR_LOWER_MASK 0x80000000 ! 1117: ! 1118: ! 1119: ! 1120: /* This functions searches through the translation tables. It can be used ! 1121: * for PTEST (levels 1 to 7). Using level 0 creates an ATC entry. */ ! 1122: ! 1123: uae_u32 mmu030_table_search(uaecptr addr, uae_u32 fc, bool write, int level) { ! 1124: /* During table walk up to 7 different descriptors are used: ! 1125: * root pointer, descriptors fetched from function code lookup table, ! 1126: * tables A, B, C and D and one indirect descriptor */ ! 1127: uae_u32 descr[2]; ! 1128: uae_u32 descr_type; ! 1129: uaecptr descr_addr[7]; ! 1130: uaecptr table_addr = 0; ! 1131: uaecptr page_addr = 0; ! 1132: uaecptr indirect_addr = 0; ! 1133: uae_u32 table_index = 0; ! 1134: uae_u32 limit = 0; ! 1135: uae_u32 unused_fields_mask = 0; ! 1136: bool super = (fc&4) ? true : false; ! 1137: bool super_violation = false; ! 1138: bool write_protected = false; ! 1139: bool cache_inhibit = false; ! 1140: bool descr_modified = false; ! 1141: ! 1142: mmu030.status = 0; /* Reset status */ ! 1143: ! 1144: /* Initial values for condition variables. ! 1145: * Note: Root pointer is long descriptor. */ ! 1146: int t = 0; ! 1147: int addr_position = 1; ! 1148: int next_size = 0; ! 1149: int descr_size = 8; ! 1150: int descr_num = 0; ! 1151: bool early_termination = false; ! 1152: ! 1153: int i; ! 1154: ! 1155: TRY(prb) { ! 1156: /* Use super user root pointer if enabled in TC register and access is in ! 1157: * super user mode, else use cpu root pointer. */ ! 1158: if ((tc_030&TC_ENABLE_SUPERVISOR) && super) { ! 1159: descr[0] = (srp_030>>32)&0xFFFFFFFF; ! 1160: descr[1] = srp_030&0xFFFFFFFF; ! 1161: #if MMU030_REG_DBG_MSG ! 1162: write_log(_T("Supervisor Root Pointer: %08X%08X\n"),descr[0],descr[1]); ! 1163: #endif // MMU030_REG_DBG_MSG ! 1164: } else { ! 1165: descr[0] = (crp_030>>32)&0xFFFFFFFF; ! 1166: descr[1] = crp_030&0xFFFFFFFF; ! 1167: #if MMU030_REG_DBG_MSG ! 1168: write_log(_T("CPU Root Pointer: %08X%08X\n"),descr[0],descr[1]); ! 1169: #endif ! 1170: } ! 1171: ! 1172: if (descr[0]&RP_ZERO_BITS) { ! 1173: #if MMU030_REG_DBG_MSG ! 1174: write_log(_T("MMU Warning: Root pointer reserved bits are non-zero! %08X\n"), descr[0]); ! 1175: #endif ! 1176: descr[0] &= (~RP_ZERO_BITS); ! 1177: } ! 1178: ! 1179: /* Check descriptor type of root pointer */ ! 1180: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1181: switch (descr_type) { ! 1182: case DESCR_TYPE_INVALID: ! 1183: write_log(_T("Fatal error: Root pointer is invalid descriptor!\n")); ! 1184: mmu030.status |= MMUSR_INVALID; ! 1185: goto stop_search; ! 1186: case DESCR_TYPE_EARLY_TERM: ! 1187: write_log(_T("Root pointer is early termination page descriptor.\n")); ! 1188: early_termination = true; ! 1189: goto handle_page_descriptor; ! 1190: case DESCR_TYPE_VALID4: ! 1191: next_size = 4; ! 1192: break; ! 1193: case DESCR_TYPE_VALID8: ! 1194: next_size = 8; ! 1195: break; ! 1196: } ! 1197: ! 1198: /* If function code lookup is enabled in TC register use function code as ! 1199: * index for top level table, limit check not required */ ! 1200: ! 1201: if (tc_030&TC_ENABLE_FCL) { ! 1202: write_log(_T("Function code lookup enabled, FC = %i\n"), fc); ! 1203: ! 1204: addr_position = (descr_size==4) ? 0 : 1; ! 1205: table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; ! 1206: table_index = fc; /* table index is function code */ ! 1207: write_log(_T("Table FCL at %08X: index = %i, "),table_addr,table_index); ! 1208: ! 1209: /* Fetch next descriptor */ ! 1210: descr_num++; ! 1211: descr_addr[descr_num] = table_addr+(table_index*next_size); ! 1212: ! 1213: if (next_size==4) { ! 1214: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1215: #if MMU030_REG_DBG_MSG ! 1216: write_log(_T("Next descriptor: %08X\n"),descr[0]); ! 1217: #endif ! 1218: } else { ! 1219: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1220: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1221: #if MMU030_REG_DBG_MSG ! 1222: write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]); ! 1223: #endif ! 1224: } ! 1225: ! 1226: descr_size = next_size; ! 1227: ! 1228: /* Check descriptor type */ ! 1229: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1230: switch (descr_type) { ! 1231: case DESCR_TYPE_INVALID: ! 1232: write_log(_T("Invalid descriptor!\n")); ! 1233: /* stop table walk */ ! 1234: mmu030.status |= MMUSR_INVALID; ! 1235: goto stop_search; ! 1236: case DESCR_TYPE_EARLY_TERM: ! 1237: #if MMU030_REG_DBG_MSG ! 1238: write_log(_T("Early termination page descriptor!\n")); ! 1239: #endif ! 1240: early_termination = true; ! 1241: goto handle_page_descriptor; ! 1242: case DESCR_TYPE_VALID4: ! 1243: next_size = 4; ! 1244: break; ! 1245: case DESCR_TYPE_VALID8: ! 1246: next_size = 8; ! 1247: break; ! 1248: } ! 1249: } ! 1250: ! 1251: ! 1252: /* Upper level tables */ ! 1253: do { ! 1254: if (descr_num) { /* if not root pointer */ ! 1255: /* Check protection */ ! 1256: if ((descr_size==8) && (descr[0]&DESCR_S) && !super) { ! 1257: super_violation = true; ! 1258: } ! 1259: if (descr[0]&DESCR_WP) { ! 1260: write_protected = true; ! 1261: } ! 1262: ! 1263: /* Set the updated bit */ ! 1264: if (!level && !(descr[0]&DESCR_U) && !super_violation) { ! 1265: descr[0] |= DESCR_U; ! 1266: phys_put_long(descr_addr[descr_num], descr[0]); ! 1267: } ! 1268: ! 1269: /* Update status bits */ ! 1270: mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0; ! 1271: mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0; ! 1272: ! 1273: /* Check if ptest level is reached */ ! 1274: if (level && (level==descr_num)) { ! 1275: goto stop_search; ! 1276: } ! 1277: } ! 1278: ! 1279: addr_position = (descr_size==4) ? 0 : 1; ! 1280: table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; ! 1281: table_index = (addr&mmu030.translation.table[t].mask)>>mmu030.translation.table[t].shift; ! 1282: #if MMU030_REG_DBG_MSG ! 1283: write_log(_T("Table %c at %08X: index = %i, "),table_letter[t],table_addr,table_index); ! 1284: #endif // MMU030_REG_DBG_MSG ! 1285: t++; /* Proceed to the next table */ ! 1286: ! 1287: /* Perform limit check */ ! 1288: if (descr_size==8) { ! 1289: limit = (descr[0]&DESCR_LIMIT_MASK)>>16; ! 1290: if ((descr[0]&DESCR_LOWER_MASK) && (table_index<limit)) { ! 1291: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1292: #if MMU030_REG_DBG_MSG ! 1293: write_log(_T("limit violation (lower limit %i)\n"),limit); ! 1294: #endif ! 1295: goto stop_search; ! 1296: } ! 1297: if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) { ! 1298: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1299: #if MMU030_REG_DBG_MSG ! 1300: write_log(_T("limit violation (upper limit %i)\n"),limit); ! 1301: #endif ! 1302: goto stop_search; ! 1303: } ! 1304: } ! 1305: ! 1306: /* Fetch next descriptor */ ! 1307: descr_num++; ! 1308: descr_addr[descr_num] = table_addr+(table_index*next_size); ! 1309: ! 1310: if (next_size==4) { ! 1311: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1312: #if MMU030_REG_DBG_MSG ! 1313: write_log(_T("Next descriptor: %08X\n"),descr[0]); ! 1314: #endif ! 1315: } else { ! 1316: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1317: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1318: #if MMU030_REG_DBG_MSG ! 1319: write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]); ! 1320: #endif ! 1321: } ! 1322: ! 1323: descr_size = next_size; ! 1324: ! 1325: /* Check descriptor type */ ! 1326: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1327: switch (descr_type) { ! 1328: case DESCR_TYPE_INVALID: ! 1329: #if MMU030_REG_DBG_MSG ! 1330: write_log(_T("Invalid descriptor!\n")); ! 1331: #endif ! 1332: /* stop table walk */ ! 1333: mmu030.status |= MMUSR_INVALID; ! 1334: goto stop_search; ! 1335: case DESCR_TYPE_EARLY_TERM: ! 1336: /* go to last level table handling code */ ! 1337: if (t<=mmu030.translation.last_table) { ! 1338: #if MMU030_REG_DBG_MSG ! 1339: write_log(_T("Early termination page descriptor!\n")); ! 1340: #endif ! 1341: early_termination = true; ! 1342: } ! 1343: goto handle_page_descriptor; ! 1344: case DESCR_TYPE_VALID4: ! 1345: next_size = 4; ! 1346: break; ! 1347: case DESCR_TYPE_VALID8: ! 1348: next_size = 8; ! 1349: break; ! 1350: } ! 1351: } while (t<=mmu030.translation.last_table); ! 1352: ! 1353: ! 1354: /* Handle indirect descriptor */ ! 1355: ! 1356: /* Check if ptest level is reached */ ! 1357: if (level && (level==descr_num)) { ! 1358: goto stop_search; ! 1359: } ! 1360: ! 1361: addr_position = (descr_size==4) ? 0 : 1; ! 1362: indirect_addr = descr[addr_position]&DESCR_ID_ADDR_MASK; ! 1363: #if MMU030_REG_DBG_MSG ! 1364: write_log(_T("Page indirect descriptor at %08X: "),indirect_addr); ! 1365: #endif ! 1366: ! 1367: /* Fetch indirect descriptor */ ! 1368: descr_num++; ! 1369: descr_addr[descr_num] = indirect_addr; ! 1370: ! 1371: if (next_size==4) { ! 1372: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1373: #if MMU030_REG_DBG_MSG ! 1374: write_log(_T("descr = %08X\n"),descr[0]); ! 1375: #endif ! 1376: } else { ! 1377: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1378: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1379: #if MMU030_REG_DBG_MSG ! 1380: write_log(_T("descr = %08X%08X"),descr[0],descr[1]); ! 1381: #endif ! 1382: } ! 1383: ! 1384: descr_size = next_size; ! 1385: ! 1386: /* Check descriptor type, only page descriptor is valid */ ! 1387: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1388: if (descr_type!=DESCR_TYPE_PAGE) { ! 1389: mmu030.status |= MMUSR_INVALID; ! 1390: goto stop_search; ! 1391: } ! 1392: ! 1393: handle_page_descriptor: ! 1394: ! 1395: if (descr_num) { /* if not root pointer */ ! 1396: /* check protection */ ! 1397: if ((descr_size==8) && (descr[0]&DESCR_S) && !super) { ! 1398: super_violation = true; ! 1399: } ! 1400: if (descr[0]&DESCR_WP) { ! 1401: write_protected = true; ! 1402: } ! 1403: ! 1404: if (!level && !super_violation) { ! 1405: /* set modified bit */ ! 1406: if (!(descr[0]&DESCR_M) && write && !write_protected) { ! 1407: descr[0] |= DESCR_M; ! 1408: descr_modified = true; ! 1409: } ! 1410: /* set updated bit */ ! 1411: if (!(descr[0]&DESCR_U)) { ! 1412: descr[0] |= DESCR_U; ! 1413: descr_modified = true; ! 1414: } ! 1415: /* write modified descriptor if neccessary */ ! 1416: if (descr_modified) { ! 1417: phys_put_long(descr_addr[descr_num], descr[0]); ! 1418: } ! 1419: } ! 1420: ! 1421: /* update status bits */ ! 1422: mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0; ! 1423: mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0; ! 1424: ! 1425: /* check if caching is inhibited */ ! 1426: cache_inhibit = descr[0]&DESCR_CI ? true : false; ! 1427: ! 1428: /* check for the modified bit and set it in the status register */ ! 1429: mmu030.status |= (descr[0]&DESCR_M) ? MMUSR_MODIFIED : 0; ! 1430: } ! 1431: ! 1432: /* Check limit using next index field of logical address. ! 1433: * Limit is only checked on early termination. If we are ! 1434: * still at root pointer level, only check limit, if FCL ! 1435: * is disabled. */ ! 1436: if (early_termination) { ! 1437: if (descr_num || !(tc_030&TC_ENABLE_FCL)) { ! 1438: if (descr_size==8) { ! 1439: table_index = (addr&mmu030.translation.table[t].mask)>>mmu030.translation.table[t].shift; ! 1440: limit = (descr[0]&DESCR_LIMIT_MASK)>>16; ! 1441: if ((descr[0]&DESCR_LOWER_MASK) && (table_index<limit)) { ! 1442: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1443: #if MMU030_REG_DBG_MSG ! 1444: write_log(_T("Limit violation (lower limit %i)\n"),limit); ! 1445: #endif ! 1446: goto stop_search; ! 1447: } ! 1448: if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) { ! 1449: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1450: #if MMU030_REG_DBG_MSG ! 1451: write_log(_T("Limit violation (upper limit %i)\n"),limit); ! 1452: #endif ! 1453: goto stop_search; ! 1454: } ! 1455: } ! 1456: } ! 1457: /* Get all unused bits of the logical address table index field. ! 1458: * they are added to the page address */ ! 1459: /* TODO: They should be added via "unsigned addition". How to? */ ! 1460: do { ! 1461: unused_fields_mask |= mmu030.translation.table[t].mask; ! 1462: t++; ! 1463: } while (t<=mmu030.translation.last_table); ! 1464: page_addr = addr&unused_fields_mask; ! 1465: #if MMU030_REG_DBG_MSG ! 1466: write_log(_T("Logical address unused bits: %08X (mask = %08X)\n"), ! 1467: page_addr,unused_fields_mask); ! 1468: #endif ! 1469: } ! 1470: ! 1471: /* Get page address */ ! 1472: addr_position = (descr_size==4) ? 0 : 1; ! 1473: page_addr += (descr[addr_position]&DESCR_PD_ADDR_MASK); ! 1474: #if MMU030_REG_DBG_MSG ! 1475: write_log(_T("Page at %08X\n"),page_addr); ! 1476: #endif // MMU030_REG_DBG_MSG ! 1477: ! 1478: stop_search: ! 1479: ; /* Make compiler happy */ ! 1480: } CATCH(prb) { ! 1481: /* We jump to this place, if a bus error occured during table search. ! 1482: * bBusErrorReadWrite is set in m68000.c, M68000_BusError: read = 1 */ ! 1483: if (bBusErrorReadWrite) { ! 1484: descr_num--; ! 1485: } ! 1486: mmu030.status |= (MMUSR_BUS_ERROR|MMUSR_INVALID); ! 1487: write_log(_T("MMU: Bus error while %s descriptor!\n"), ! 1488: bBusErrorReadWrite?_T("reading"):_T("writing")); ! 1489: } ENDTRY ! 1490: ! 1491: /* check if we have to handle ptest */ ! 1492: if (level) { ! 1493: /* Note: wp, m and sv bits are undefined if the invalid bit is set */ ! 1494: mmu030.status = (mmu030.status&~MMUSR_NUM_LEVELS_MASK) | descr_num; ! 1495: ! 1496: /* If root pointer is page descriptor (descr_num 0), return 0 */ ! 1497: return descr_num ? descr_addr[descr_num] : 0; ! 1498: } ! 1499: ! 1500: /* Find an ATC entry to replace */ ! 1501: /* Search for invalid entry */ ! 1502: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1503: if (!mmu030.atc[i].logical.valid) { ! 1504: break; ! 1505: } ! 1506: } ! 1507: /* If there are no invalid entries, replace first entry ! 1508: * with history bit not set */ ! 1509: if (i == ATC030_NUM_ENTRIES) { ! 1510: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1511: if (!mmu030.atc[i].mru) { ! 1512: break; ! 1513: } ! 1514: } ! 1515: #if MMU030_REG_DBG_MSG ! 1516: write_log(_T("ATC is full. Replacing entry %i\n"), i); ! 1517: #endif ! 1518: } ! 1519: if (i >= ATC030_NUM_ENTRIES) { ! 1520: i = 0; ! 1521: write_log (_T("ATC entry not found!!!\n")); ! 1522: } ! 1523: ! 1524: mmu030_atc_handle_history_bit(i); ! 1525: ! 1526: /* Create ATC entry */ ! 1527: mmu030.atc[i].logical.addr = addr & mmu030.translation.page.imask; /* delete page index bits */ ! 1528: mmu030.atc[i].logical.fc = fc; ! 1529: mmu030.atc[i].logical.valid = true; ! 1530: mmu030.atc[i].physical.addr = page_addr & mmu030.translation.page.imask; /* delete page index bits */ ! 1531: if ((mmu030.status&MMUSR_INVALID) || (mmu030.status&MMUSR_SUPER_VIOLATION)) { ! 1532: mmu030.atc[i].physical.bus_error = true; ! 1533: } else { ! 1534: mmu030.atc[i].physical.bus_error = false; ! 1535: } ! 1536: mmu030.atc[i].physical.cache_inhibit = cache_inhibit; ! 1537: mmu030.atc[i].physical.modified = (mmu030.status&MMUSR_MODIFIED) ? true : false; ! 1538: mmu030.atc[i].physical.write_protect = (mmu030.status&MMUSR_WRITE_PROTECTED) ? true : false; ! 1539: ! 1540: #if MMU030_ATC_DBG_MSG ! 1541: write_log(_T("ATC create entry(%i): logical = %08X, physical = %08X, FC = %i\n"), i, ! 1542: mmu030.atc[i].logical.addr, mmu030.atc[i].physical.addr, ! 1543: mmu030.atc[i].logical.fc); ! 1544: write_log(_T("ATC create entry(%i): B = %i, CI = %i, WP = %i, M = %i\n"), i, ! 1545: mmu030.atc[i].physical.bus_error?1:0, ! 1546: mmu030.atc[i].physical.cache_inhibit?1:0, ! 1547: mmu030.atc[i].physical.write_protect?1:0, ! 1548: mmu030.atc[i].physical.modified?1:0); ! 1549: #endif // MMU030_ATC_DBG_MSG ! 1550: ! 1551: return 0; ! 1552: } ! 1553: ! 1554: /* This function is used for PTEST level 0. */ ! 1555: void mmu030_ptest_atc_search(uaecptr logical_addr, uae_u32 fc, bool write) { ! 1556: int i; ! 1557: mmu030.status = 0; ! 1558: ! 1559: if (mmu030_match_ttr(logical_addr, fc, write)&TT_OK_MATCH) { ! 1560: mmu030.status |= MMUSR_TRANSP_ACCESS; ! 1561: return; ! 1562: } ! 1563: ! 1564: for (i = 0; i < ATC030_NUM_ENTRIES; i++) { ! 1565: if ((mmu030.atc[i].logical.fc == fc) && ! 1566: (mmu030.atc[i].logical.addr == logical_addr) && ! 1567: mmu030.atc[i].logical.valid) { ! 1568: break; ! 1569: } ! 1570: } ! 1571: ! 1572: if (i==ATC030_NUM_ENTRIES) { ! 1573: mmu030.status |= MMUSR_INVALID; ! 1574: return; ! 1575: } ! 1576: ! 1577: mmu030.status |= mmu030.atc[i].physical.bus_error ? (MMUSR_BUS_ERROR|MMUSR_INVALID) : 0; ! 1578: /* Note: write protect and modified bits are undefined if the invalid bit is set */ ! 1579: mmu030.status |= mmu030.atc[i].physical.write_protect ? MMUSR_WRITE_PROTECTED : 0; ! 1580: mmu030.status |= mmu030.atc[i].physical.modified ? MMUSR_MODIFIED : 0; ! 1581: } ! 1582: ! 1583: /* This function is used for PTEST level 1 - 7. */ ! 1584: uae_u32 mmu030_ptest_table_search(uaecptr logical_addr, uae_u32 fc, bool write, int level) { ! 1585: if (mmu030_match_ttr(logical_addr, fc, write)&TT_OK_MATCH) { ! 1586: return 0; ! 1587: } else { ! 1588: return mmu030_table_search(logical_addr, fc, write, level); ! 1589: } ! 1590: } ! 1591: ! 1592: ! 1593: /* Address Translation Cache ! 1594: * ! 1595: * The ATC uses a pseudo-least-recently-used algorithm to keep track of ! 1596: * least recently used entries. They are replaced if the cache is full. ! 1597: * An internal history-bit (MRU-bit) is used to identify these entries. ! 1598: * If an entry is accessed, its history-bit is set to 1. If after that ! 1599: * there are no more entries with zero-bits, all other history-bits are ! 1600: * set to 0. When no more invalid entries are in the ATC, the first entry ! 1601: * with a zero-bit is replaced. ! 1602: * ! 1603: * ! 1604: * Logical Portion (28 bit): ! 1605: * oooo ---- xxxx xxxx xxxx xxxx xxxx xxxx ! 1606: * logical address (most significant 24 bit) ! 1607: * ! 1608: * oooo -xxx ---- ---- ---- ---- ---- ---- ! 1609: * function code ! 1610: * ! 1611: * oooo x--- ---- ---- ---- ---- ---- ---- ! 1612: * valid ! 1613: * ! 1614: * ! 1615: * Physical Portion (28 bit): ! 1616: * oooo ---- xxxx xxxx xxxx xxxx xxxx xxxx ! 1617: * physical address ! 1618: * ! 1619: * oooo ---x ---- ---- ---- ---- ---- ---- ! 1620: * modified ! 1621: * ! 1622: * oooo --x- ---- ---- ---- ---- ---- ---- ! 1623: * write protect ! 1624: * ! 1625: * oooo -x-- ---- ---- ---- ---- ---- ---- ! 1626: * cache inhibit ! 1627: * ! 1628: * oooo x--- ---- ---- ---- ---- ---- ---- ! 1629: * bus error ! 1630: * ! 1631: */ ! 1632: ! 1633: #define ATC030_MASK 0x0FFFFFFF ! 1634: #define ATC030_ADDR_MASK 0x00FFFFFF /* after masking shift 8 (<< 8) */ ! 1635: ! 1636: #define ATC030_LOG_FC 0x07000000 ! 1637: #define ATC030_LOG_V 0x08000000 ! 1638: ! 1639: #define ATC030_PHYS_M 0x01000000 ! 1640: #define ATC030_PHYS_WP 0x02000000 ! 1641: #define ATC030_PHYS_CI 0x04000000 ! 1642: #define ATC030_PHYS_BE 0x08000000 ! 1643: ! 1644: void mmu030_page_fault(uaecptr addr, bool read, int flags, uae_u32 fc) { ! 1645: regs.mmu_fault_addr = addr; ! 1646: regs.mmu_ssw = (fc & 1) ? MMU030_SSW_DF | (MMU030_SSW_DF << 1) : (MMU030_SSW_FB | MMU030_SSW_RB); ! 1647: regs.mmu_ssw |= read ? MMU030_SSW_RW : 0; ! 1648: regs.mmu_ssw |= flags; ! 1649: regs.mmu_ssw |= fc; ! 1650: bBusErrorReadWrite = read; ! 1651: mm030_stageb_address = addr; ! 1652: #if MMUDEBUG ! 1653: write_log(_T("MMU: page fault (logical addr=%08X SSW=%04x read=%d size=%d fc=%d pc=%08x ob=%08x ins=%04X)\n"), ! 1654: addr, regs.mmu_ssw, read, (flags & MMU030_SSW_SIZE_B) ? 1 : (flags & MMU030_SSW_SIZE_W) ? 2 : 4, fc, ! 1655: regs.instruction_pc, (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) ? mmu030_data_buffer : mmu030_ad[mmu030_idx].val, mmu030_opcode & 0xffff); ! 1656: #endif ! 1657: ! 1658: // extern void activate_debugger(void); ! 1659: // activate_debugger (); ! 1660: ! 1661: THROW(2); ! 1662: } ! 1663: ! 1664: void mmu030_put_long_atc(uaecptr addr, uae_u32 val, int l, uae_u32 fc) { ! 1665: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1666: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1667: ! 1668: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1669: #if MMU030_ATC_DBG_MSG ! 1670: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (lput %08X)\n"), ! 1671: l, physical_addr, page_index, val); ! 1672: #endif ! 1673: physical_addr += page_index; ! 1674: ! 1675: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1676: mmu030_page_fault(addr, false, MMU030_SSW_SIZE_L, fc); ! 1677: return; ! 1678: } ! 1679: ! 1680: phys_put_long(physical_addr, val); ! 1681: } ! 1682: ! 1683: void mmu030_put_word_atc(uaecptr addr, uae_u16 val, int l, uae_u32 fc) { ! 1684: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1685: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1686: ! 1687: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1688: #if MMU030_ATC_DBG_MSG ! 1689: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (wput %04X)\n"), ! 1690: l, physical_addr, page_index, val); ! 1691: #endif ! 1692: physical_addr += page_index; ! 1693: ! 1694: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1695: mmu030_page_fault(addr, false, MMU030_SSW_SIZE_W, fc); ! 1696: return; ! 1697: } ! 1698: ! 1699: phys_put_word(physical_addr, val); ! 1700: } ! 1701: ! 1702: void mmu030_put_byte_atc(uaecptr addr, uae_u8 val, int l, uae_u32 fc) { ! 1703: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1704: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1705: ! 1706: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1707: #if MMU030_ATC_DBG_MSG ! 1708: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n"), ! 1709: l, physical_addr, page_index, val); ! 1710: #endif ! 1711: physical_addr += page_index; ! 1712: ! 1713: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1714: mmu030_page_fault(addr, false, MMU030_SSW_SIZE_B, fc); ! 1715: return; ! 1716: } ! 1717: ! 1718: phys_put_byte(physical_addr, val); ! 1719: } ! 1720: ! 1721: uae_u32 mmu030_get_long_atc(uaecptr addr, int l, uae_u32 fc) { ! 1722: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1723: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1724: ! 1725: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1726: #if MMU030_ATC_DBG_MSG ! 1727: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (lget %08X)\n"), l, ! 1728: physical_addr, page_index, phys_get_long(physical_addr+page_index)); ! 1729: #endif ! 1730: physical_addr += page_index; ! 1731: ! 1732: if (mmu030.atc[l].physical.bus_error) { ! 1733: mmu030_page_fault(addr, true, MMU030_SSW_SIZE_L, fc); ! 1734: return 0; ! 1735: } ! 1736: ! 1737: return phys_get_long(physical_addr); ! 1738: } ! 1739: ! 1740: static uae_u32 mmu030_get_ilong_atc(uaecptr addr, int l, uae_u32 fc) { ! 1741: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1742: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1743: ! 1744: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1745: #if MMU030_ATC_DBG_MSG ! 1746: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (lget %08X)\n"), l, ! 1747: physical_addr, page_index, phys_get_long(physical_addr + page_index)); ! 1748: #endif ! 1749: physical_addr += page_index; ! 1750: ! 1751: if (mmu030.atc[l].physical.bus_error) { ! 1752: mmu030_page_fault(addr, true, MMU030_SSW_SIZE_L, fc); ! 1753: return 0; ! 1754: } ! 1755: ! 1756: return phys_get_long(physical_addr); ! 1757: } ! 1758: ! 1759: uae_u16 mmu030_get_word_atc(uaecptr addr, int l, uae_u32 fc) { ! 1760: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1761: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1762: ! 1763: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1764: #if MMU030_ATC_DBG_MSG ! 1765: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (wget %04X)\n"), l, ! 1766: physical_addr, page_index, phys_get_word(physical_addr+page_index)); ! 1767: #endif ! 1768: physical_addr += page_index; ! 1769: ! 1770: if (mmu030.atc[l].physical.bus_error) { ! 1771: mmu030_page_fault(addr, true, MMU030_SSW_SIZE_W, fc); ! 1772: return 0; ! 1773: } ! 1774: ! 1775: return phys_get_word(physical_addr); ! 1776: } ! 1777: ! 1778: static uae_u16 mmu030_get_iword_atc(uaecptr addr, int l, uae_u32 fc) { ! 1779: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1780: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1781: ! 1782: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1783: #if MMU030_ATC_DBG_MSG ! 1784: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (wget %04X)\n"), l, ! 1785: physical_addr, page_index, phys_get_word(physical_addr + page_index)); ! 1786: #endif ! 1787: physical_addr += page_index; ! 1788: ! 1789: if (mmu030.atc[l].physical.bus_error) { ! 1790: mmu030_page_fault(addr, true, MMU030_SSW_SIZE_W, fc); ! 1791: return 0; ! 1792: } ! 1793: ! 1794: return phys_get_word(physical_addr); ! 1795: } ! 1796: ! 1797: uae_u8 mmu030_get_byte_atc(uaecptr addr, int l, uae_u32 fc) { ! 1798: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1799: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1800: ! 1801: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1802: #if MMU030_ATC_DBG_MSG ! 1803: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n"), l, ! 1804: physical_addr, page_index, phys_get_byte(physical_addr+page_index)); ! 1805: #endif ! 1806: physical_addr += page_index; ! 1807: ! 1808: if (mmu030.atc[l].physical.bus_error) { ! 1809: mmu030_page_fault(addr, true, MMU030_SSW_SIZE_B, fc); ! 1810: return 0; ! 1811: } ! 1812: ! 1813: return phys_get_byte(physical_addr); ! 1814: } ! 1815: ! 1816: /* Generic versions of above */ ! 1817: void mmu030_put_atc_generic(uaecptr addr, uae_u32 val, int l, uae_u32 fc, int size, int flags) { ! 1818: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1819: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1820: ! 1821: uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; ! 1822: #if MMU030_ATC_DBG_MSG ! 1823: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n"), ! 1824: l, physical_addr, page_index, val); ! 1825: #endif ! 1826: physical_addr += page_index; ! 1827: ! 1828: if (mmu030.atc[l].physical.write_protect || mmu030.atc[l].physical.bus_error) { ! 1829: mmu030_page_fault(addr, false, flags, fc); ! 1830: return; ! 1831: } ! 1832: if (size == sz_byte) ! 1833: phys_put_byte(physical_addr, val); ! 1834: else if (size == sz_word) ! 1835: phys_put_word(physical_addr, val); ! 1836: else ! 1837: phys_put_long(physical_addr, val); ! 1838: ! 1839: } ! 1840: uae_u32 mmu030_get_atc_generic(uaecptr addr, int l, uae_u32 fc, int size, int flags, bool checkwrite) { ! 1841: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1842: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1843: ! 1844: uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; ! 1845: #if MMU030_ATC_DBG_MSG ! 1846: write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n"), l, ! 1847: physical_addr, page_index, phys_get_byte(physical_addr+page_index)); ! 1848: #endif ! 1849: physical_addr += page_index; ! 1850: ! 1851: if (mmu030.atc[l].physical.bus_error || (checkwrite && mmu030.atc[l].physical.write_protect)) { ! 1852: mmu030_page_fault(addr, true, flags, fc); ! 1853: return 0; ! 1854: } ! 1855: if (size == sz_byte) ! 1856: return phys_get_byte(physical_addr); ! 1857: else if (size == sz_word) ! 1858: return phys_get_word(physical_addr); ! 1859: return phys_get_long(physical_addr); ! 1860: } ! 1861: ! 1862: ! 1863: /* This function checks if a certain logical address is in the ATC ! 1864: * by comparing the logical address and function code to the values ! 1865: * stored in the ATC entries. If a matching entry is found it sets ! 1866: * the history bit and returns the cache index of the entry. */ ! 1867: int mmu030_logical_is_in_atc(uaecptr addr, uae_u32 fc, bool write) { ! 1868: uaecptr logical_addr = 0; ! 1869: uae_u32 addr_mask = mmu030.translation.page.imask; ! 1870: uae_u32 maddr = addr & addr_mask; ! 1871: int offset = (maddr >> mmu030.translation.page.size) & 0x1f; ! 1872: ! 1873: int i, index; ! 1874: index = atcindextable[offset]; ! 1875: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1876: logical_addr = mmu030.atc[index].logical.addr; ! 1877: /* If actual address matches address in ATC */ ! 1878: if (maddr==(logical_addr&addr_mask) && ! 1879: (mmu030.atc[index].logical.fc==fc) && ! 1880: mmu030.atc[index].logical.valid) { ! 1881: /* If access is valid write and M bit is not set, invalidate entry ! 1882: * else return index */ ! 1883: if (!write || mmu030.atc[index].physical.modified || ! 1884: mmu030.atc[index].physical.write_protect || ! 1885: mmu030.atc[index].physical.bus_error) { ! 1886: /* Maintain history bit */ ! 1887: mmu030_atc_handle_history_bit(index); ! 1888: atcindextable[offset] = index; ! 1889: return index; ! 1890: } else { ! 1891: mmu030.atc[index].logical.valid = false; ! 1892: } ! 1893: } ! 1894: index++; ! 1895: if (index >= ATC030_NUM_ENTRIES) ! 1896: index = 0; ! 1897: } ! 1898: return -1; ! 1899: } ! 1900: ! 1901: void mmu030_atc_handle_history_bit(int entry_num) { ! 1902: int j; ! 1903: mmu030.atc[entry_num].mru = 1; ! 1904: for (j=0; j<ATC030_NUM_ENTRIES; j++) { ! 1905: if (!mmu030.atc[j].mru) ! 1906: break; ! 1907: } ! 1908: /* If there are no more zero-bits, reset all */ ! 1909: if (j==ATC030_NUM_ENTRIES) { ! 1910: for (j=0; j<ATC030_NUM_ENTRIES; j++) { ! 1911: mmu030.atc[j].mru = 0; ! 1912: } ! 1913: mmu030.atc[entry_num].mru = 1; ! 1914: #if MMU030_ATC_DBG_MSG ! 1915: write_log(_T("ATC: No more history zero-bits. Reset all.\n")); ! 1916: #endif ! 1917: } ! 1918: } ! 1919: ! 1920: ! 1921: /* Memory access functions: ! 1922: * If the address matches one of the transparent translation registers ! 1923: * use it directly as physical address, else check ATC for the ! 1924: * logical address. If the logical address is not resident in the ATC ! 1925: * create a new ATC entry and then look up the physical address. ! 1926: */ ! 1927: ! 1928: void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc) { ! 1929: ! 1930: // addr,fc,write ! 1931: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,true)) || (!mmu030.enabled)) { ! 1932: phys_put_long(addr,val); ! 1933: return; ! 1934: } ! 1935: ! 1936: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1937: ! 1938: if (atc_line_num>=0) { ! 1939: mmu030_put_long_atc(addr, val, atc_line_num, fc); ! 1940: } else { ! 1941: mmu030_table_search(addr,fc,true,0); ! 1942: mmu030_put_long_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); ! 1943: } ! 1944: } ! 1945: ! 1946: void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc) { ! 1947: ! 1948: // addr,fc,write ! 1949: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,true)) || (!mmu030.enabled)) { ! 1950: phys_put_word(addr,val); ! 1951: return; ! 1952: } ! 1953: ! 1954: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1955: ! 1956: if (atc_line_num>=0) { ! 1957: mmu030_put_word_atc(addr, val, atc_line_num, fc); ! 1958: } else { ! 1959: mmu030_table_search(addr, fc, true, 0); ! 1960: mmu030_put_word_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); ! 1961: } ! 1962: } ! 1963: ! 1964: void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc) { ! 1965: ! 1966: // addr,fc,write ! 1967: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,true)) || (!mmu030.enabled)) { ! 1968: phys_put_byte(addr,val); ! 1969: return; ! 1970: } ! 1971: ! 1972: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1973: ! 1974: if (atc_line_num>=0) { ! 1975: mmu030_put_byte_atc(addr, val, atc_line_num, fc); ! 1976: } else { ! 1977: mmu030_table_search(addr, fc, true, 0); ! 1978: mmu030_put_byte_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true), fc); ! 1979: } ! 1980: } ! 1981: ! 1982: uae_u32 mmu030_get_ilong(uaecptr addr, uae_u32 fc) { ! 1983: ! 1984: // addr,fc,write ! 1985: if ((fc == 7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 1986: return phys_get_long(addr); ! 1987: } ! 1988: ! 1989: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 1990: ! 1991: if (atc_line_num >= 0) { ! 1992: return mmu030_get_ilong_atc(addr, atc_line_num, fc); ! 1993: } ! 1994: else { ! 1995: mmu030_table_search(addr, fc, false, 0); ! 1996: return mmu030_get_ilong_atc(addr, mmu030_logical_is_in_atc(addr, fc, false), fc); ! 1997: } ! 1998: } ! 1999: uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc) { ! 2000: ! 2001: // addr,fc,write ! 2002: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 2003: return phys_get_long(addr); ! 2004: } ! 2005: ! 2006: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2007: ! 2008: if (atc_line_num>=0) { ! 2009: return mmu030_get_long_atc(addr, atc_line_num, fc); ! 2010: } else { ! 2011: mmu030_table_search(addr, fc, false, 0); ! 2012: return mmu030_get_long_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); ! 2013: } ! 2014: } ! 2015: ! 2016: uae_u16 mmu030_get_iword(uaecptr addr, uae_u32 fc) { ! 2017: ! 2018: // addr,fc,write ! 2019: if ((fc == 7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 2020: return phys_get_word(addr); ! 2021: } ! 2022: ! 2023: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2024: ! 2025: if (atc_line_num >= 0) { ! 2026: return mmu030_get_iword_atc(addr, atc_line_num, fc); ! 2027: } else { ! 2028: mmu030_table_search(addr, fc, false, 0); ! 2029: return mmu030_get_iword_atc(addr, mmu030_logical_is_in_atc(addr, fc, false), fc); ! 2030: } ! 2031: } ! 2032: uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc) { ! 2033: ! 2034: // addr,fc,write ! 2035: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 2036: return phys_get_word(addr); ! 2037: } ! 2038: ! 2039: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2040: ! 2041: if (atc_line_num>=0) { ! 2042: return mmu030_get_word_atc(addr, atc_line_num, fc); ! 2043: } else { ! 2044: mmu030_table_search(addr, fc, false, 0); ! 2045: return mmu030_get_word_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); ! 2046: } ! 2047: } ! 2048: ! 2049: uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc) { ! 2050: ! 2051: // addr,fc,write ! 2052: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 2053: return phys_get_byte(addr); ! 2054: } ! 2055: ! 2056: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2057: ! 2058: if (atc_line_num>=0) { ! 2059: return mmu030_get_byte_atc(addr, atc_line_num, fc); ! 2060: } else { ! 2061: mmu030_table_search(addr, fc, false, 0); ! 2062: return mmu030_get_byte_atc(addr, mmu030_logical_is_in_atc(addr,fc,false), fc); ! 2063: } ! 2064: } ! 2065: ! 2066: ! 2067: /* Not commonly used access function */ ! 2068: void mmu030_put_generic(uaecptr addr, uae_u32 val, uae_u32 fc, int size, int accesssize, int flags) { ! 2069: ! 2070: // addr,fc,write ! 2071: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,true)) || (!mmu030.enabled)) { ! 2072: if (size == sz_byte) ! 2073: phys_put_byte(addr, val); ! 2074: else if (size == sz_word) ! 2075: phys_put_word(addr, val); ! 2076: else ! 2077: phys_put_long(addr, val); ! 2078: return; ! 2079: } ! 2080: ! 2081: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 2082: if (atc_line_num>=0) { ! 2083: mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); ! 2084: } else { ! 2085: mmu030_table_search(addr, fc, true, 0); ! 2086: atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 2087: if (accesssize == sz_byte) ! 2088: flags |= MMU030_SSW_SIZE_B; ! 2089: else if (accesssize == sz_word) ! 2090: flags |= MMU030_SSW_SIZE_W; ! 2091: mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); ! 2092: } ! 2093: } ! 2094: static uae_u32 mmu030_get_generic_lrmw(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { ! 2095: ! 2096: // addr,fc,write ! 2097: if ((fc==7) || (mmu030_match_lrmw_ttr_access(addr,fc)) || (!mmu030.enabled)) { ! 2098: if (size == sz_byte) ! 2099: return phys_get_byte(addr); ! 2100: else if (size == sz_word) ! 2101: return phys_get_word(addr); ! 2102: return phys_get_long(addr); ! 2103: } ! 2104: ! 2105: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 2106: if (atc_line_num>=0) { ! 2107: return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); ! 2108: } else { ! 2109: mmu030_table_search(addr, fc, true, 0); ! 2110: atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 2111: if (accesssize == sz_byte) ! 2112: flags |= MMU030_SSW_SIZE_B; ! 2113: else if (accesssize == sz_word) ! 2114: flags |= MMU030_SSW_SIZE_W; ! 2115: return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); ! 2116: } ! 2117: } ! 2118: uae_u32 mmu030_get_generic(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { ! 2119: if (flags & MMU030_SSW_RM) { ! 2120: return mmu030_get_generic_lrmw(addr, fc, size, accesssize, flags); ! 2121: } ! 2122: // addr,fc,write ! 2123: if ((fc==7) || (mmu030_match_ttr_access(addr,fc,false)) || (!mmu030.enabled)) { ! 2124: if (size == sz_byte) ! 2125: return phys_get_byte(addr); ! 2126: else if (size == sz_word) ! 2127: return phys_get_word(addr); ! 2128: return phys_get_long(addr); ! 2129: } ! 2130: ! 2131: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2132: if (atc_line_num>=0) { ! 2133: return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); ! 2134: } else { ! 2135: mmu030_table_search(addr, fc, false, 0); ! 2136: atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 2137: if (accesssize == sz_byte) ! 2138: flags |= MMU030_SSW_SIZE_B; ! 2139: else if (accesssize == sz_word) ! 2140: flags |= MMU030_SSW_SIZE_W; ! 2141: return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); ! 2142: } ! 2143: } ! 2144: ! 2145: ! 2146: /* Locked RMW is rarely used */ ! 2147: uae_u32 uae_mmu030_get_lrmw(uaecptr addr, int size) ! 2148: { ! 2149: uae_u32 fc = (regs.s ? 4 : 0) | 1; ! 2150: if (size == sz_byte) { ! 2151: return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); ! 2152: } else if (size == sz_word) { ! 2153: if (unlikely(is_unaligned(addr, 2))) ! 2154: return mmu030_get_word_unaligned(addr, fc, MMU030_SSW_RM); ! 2155: else ! 2156: return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); ! 2157: } else { ! 2158: if (unlikely(is_unaligned(addr, 4))) ! 2159: return mmu030_get_long_unaligned(addr, fc, MMU030_SSW_RM); ! 2160: else ! 2161: return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); ! 2162: } ! 2163: } ! 2164: void uae_mmu030_put_lrmw(uaecptr addr, uae_u32 val, int size) ! 2165: { ! 2166: uae_u32 fc = (regs.s ? 4 : 0) | 1; ! 2167: if (size == sz_byte) { ! 2168: mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); ! 2169: } else if (size == sz_word) { ! 2170: if (unlikely(is_unaligned(addr, 2))) ! 2171: mmu030_put_word_unaligned(addr, val, fc, MMU030_SSW_RM); ! 2172: else ! 2173: mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); ! 2174: } else { ! 2175: if (unlikely(is_unaligned(addr, 4))) ! 2176: mmu030_put_long_unaligned(addr, val, fc, MMU030_SSW_RM); ! 2177: else ! 2178: mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); ! 2179: } ! 2180: } ! 2181: uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc, int flags) ! 2182: { ! 2183: uae_u16 res; ! 2184: ! 2185: res = (uae_u16)mmu030_get_generic(addr, fc, sz_byte, sz_word, flags) << 8; ! 2186: SAVE_EXCEPTION; ! 2187: TRY(prb) { ! 2188: res |= mmu030_get_generic(addr + 1, fc, sz_byte, sz_word, flags); ! 2189: RESTORE_EXCEPTION; ! 2190: } ! 2191: CATCH(prb) { ! 2192: RESTORE_EXCEPTION; ! 2193: THROW_AGAIN(prb); ! 2194: } ENDTRY ! 2195: return res; ! 2196: } ! 2197: ! 2198: uae_u32 REGPARAM2 mmu030_get_ilong_unaligned(uaecptr addr, uae_u32 fc, int flags) ! 2199: { ! 2200: uae_u32 res; ! 2201: ! 2202: res = (uae_u32)mmu030_get_iword(addr, fc) << 16; ! 2203: SAVE_EXCEPTION; ! 2204: TRY(prb) { ! 2205: res |= mmu030_get_iword(addr + 2, fc); ! 2206: RESTORE_EXCEPTION; ! 2207: } ! 2208: CATCH(prb) { ! 2209: RESTORE_EXCEPTION; ! 2210: THROW_AGAIN(prb); ! 2211: } ENDTRY ! 2212: return res; ! 2213: } ! 2214: ! 2215: uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc, int flags) ! 2216: { ! 2217: uae_u32 res; ! 2218: ! 2219: if (likely(!(addr & 1))) { ! 2220: res = (uae_u32)mmu030_get_generic(addr, fc, sz_word, sz_long, flags) << 16; ! 2221: SAVE_EXCEPTION; ! 2222: TRY(prb) { ! 2223: res |= mmu030_get_generic(addr + 2, fc, sz_word, sz_long, flags); ! 2224: RESTORE_EXCEPTION; ! 2225: } ! 2226: CATCH(prb) { ! 2227: RESTORE_EXCEPTION; ! 2228: THROW_AGAIN(prb); ! 2229: } ENDTRY ! 2230: } else { ! 2231: res = (uae_u32)mmu030_get_generic(addr, fc, sz_byte, sz_long, flags) << 8; ! 2232: SAVE_EXCEPTION; ! 2233: TRY(prb) { ! 2234: res = (res | mmu030_get_generic(addr + 1, fc, sz_byte, sz_long, flags)) << 8; ! 2235: res = (res | mmu030_get_generic(addr + 2, fc, sz_byte, sz_long, flags)) << 8; ! 2236: res |= mmu030_get_generic(addr + 3, fc, sz_byte, sz_long, flags); ! 2237: RESTORE_EXCEPTION; ! 2238: } ! 2239: CATCH(prb) { ! 2240: RESTORE_EXCEPTION; ! 2241: THROW_AGAIN(prb); ! 2242: } ENDTRY ! 2243: } ! 2244: return res; ! 2245: } ! 2246: ! 2247: ! 2248: void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc, int flags) ! 2249: { ! 2250: SAVE_EXCEPTION; ! 2251: TRY(prb) { ! 2252: if (likely(!(addr & 1))) { ! 2253: mmu030_put_generic(addr, val >> 16, fc, sz_word, sz_long, flags); ! 2254: mmu030_put_generic(addr + 2, val, fc, sz_word, sz_long, flags); ! 2255: } else { ! 2256: mmu030_put_generic(addr, val >> 24, fc, sz_byte, sz_long, flags); ! 2257: mmu030_put_generic(addr + 1, val >> 16, fc, sz_byte, sz_long, flags); ! 2258: mmu030_put_generic(addr + 2, val >> 8, fc, sz_byte, sz_long, flags); ! 2259: mmu030_put_generic(addr + 3, val, fc, sz_byte, sz_long, flags); ! 2260: } ! 2261: RESTORE_EXCEPTION; ! 2262: } ! 2263: CATCH(prb) { ! 2264: RESTORE_EXCEPTION; ! 2265: regs.wb3_data = val; ! 2266: THROW_AGAIN(prb); ! 2267: } ENDTRY ! 2268: } ! 2269: ! 2270: void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc, int flags) ! 2271: { ! 2272: SAVE_EXCEPTION; ! 2273: TRY(prb) { ! 2274: mmu030_put_generic(addr, val >> 8, fc, sz_byte, sz_word, flags); ! 2275: mmu030_put_generic(addr + 1, val, fc, sz_byte, sz_word, flags); ! 2276: RESTORE_EXCEPTION; ! 2277: } ! 2278: CATCH(prb) { ! 2279: RESTORE_EXCEPTION; ! 2280: regs.wb3_data = val; ! 2281: THROW_AGAIN(prb); ! 2282: } ENDTRY ! 2283: } ! 2284: ! 2285: ! 2286: /* Used by debugger */ ! 2287: static uaecptr mmu030_get_addr_atc(uaecptr addr, int l, uae_u32 fc, bool write) { ! 2288: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 2289: uae_u32 addr_mask = mmu030.translation.page.imask; ! 2290: ! 2291: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 2292: physical_addr += page_index; ! 2293: ! 2294: if (mmu030.atc[l].physical.bus_error || (write && mmu030.atc[l].physical.write_protect)) { ! 2295: mmu030_page_fault(addr, write == 0, MMU030_SSW_SIZE_B, fc); ! 2296: return 0; ! 2297: } ! 2298: ! 2299: return physical_addr; ! 2300: } ! 2301: uaecptr mmu030_translate(uaecptr addr, bool super, bool data, bool write) ! 2302: { ! 2303: int fc = (super ? 4 : 0) | (data ? 1 : 2); ! 2304: if ((fc==7) || (mmu030_match_ttr(addr,fc,write)&TT_OK_MATCH) || (!mmu030.enabled)) { ! 2305: return addr; ! 2306: } ! 2307: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, write); ! 2308: ! 2309: if (atc_line_num>=0) { ! 2310: return mmu030_get_addr_atc(addr, atc_line_num, fc, write); ! 2311: } else { ! 2312: mmu030_table_search(addr, fc, false, 0); ! 2313: return mmu030_get_addr_atc(addr, mmu030_logical_is_in_atc(addr,fc,write), fc, write); ! 2314: } ! 2315: } ! 2316: ! 2317: /* MMU Reset */ ! 2318: void mmu030_reset(int hardreset) ! 2319: { ! 2320: /* A CPU reset causes the E-bits of TC and TT registers to be zeroed. */ ! 2321: mmu030.enabled = false; ! 2322: regs.mmu_page_size = 0; ! 2323: tc_030 &= ~TC_ENABLE_TRANSLATION; ! 2324: tt0_030 &= ~TT_ENABLE; ! 2325: tt1_030 &= ~TT_ENABLE; ! 2326: if (hardreset) { ! 2327: srp_030 = crp_030 = 0; ! 2328: tt0_030 = tt1_030 = tc_030 = 0; ! 2329: mmusr_030 = 0; ! 2330: mmu030_flush_atc_all(); ! 2331: } ! 2332: mmu030_set_funcs(); ! 2333: } ! 2334: ! 2335: void mmu030_set_funcs(void) ! 2336: { ! 2337: if (currprefs.mmu_model != 68030) ! 2338: return; ! 2339: } ! 2340: ! 2341: ! 2342: void m68k_do_rte_mmu030 (uaecptr a7) ! 2343: { ! 2344: // Restore access error exception state ! 2345: ! 2346: uae_u16 format = get_word_mmu030 (a7 + 6); ! 2347: uae_u16 frame = format >> 12; ! 2348: uae_u16 ssw = get_word_mmu030 (a7 + 10); ! 2349: ! 2350: // Fetch last word, real CPU does it to allow OS bus handler to map ! 2351: // the page if frame crosses pages and following page is not resident. ! 2352: if (frame == 0xb) ! 2353: get_word_mmu030(a7 + 92 - 2); ! 2354: else ! 2355: get_word_mmu030(a7 + 32 - 2); ! 2356: ! 2357: // Internal register, our opcode storage area ! 2358: mmu030_opcode = get_long_mmu030 (a7 + 0x14); ! 2359: // Misc state data ! 2360: mmu030_state[0] = get_word_mmu030 (a7 + 0x30); ! 2361: mmu030_state[1] = get_word_mmu030 (a7 + 0x32); ! 2362: mmu030_state[2] = get_word_mmu030 (a7 + 0x34); ! 2363: mmu030_disp_store[0] = get_long_mmu030 (a7 + 0x1c); ! 2364: mmu030_disp_store[1] = get_long_mmu030 (a7 + 0x1c + 4); ! 2365: if (mmu030_state[1] & MMU030_STATEFLAG1_FMOVEM) { ! 2366: mmu030_fmovem_store[0] = get_long_mmu030 (a7 + 0x5c - (7 + 1) * 4); ! 2367: mmu030_fmovem_store[1] = get_long_mmu030 (a7 + 0x5c - (8 + 1) * 4); ! 2368: } ! 2369: // Rerun "mmu030_opcode" using restored state. ! 2370: mmu030_retry = true; ! 2371: ! 2372: if (frame == 0xb) { ! 2373: uae_u16 idxsize = get_word_mmu030 (a7 + 0x36); ! 2374: int i; ! 2375: for (i = 0; i < idxsize + 1; i++) { ! 2376: mmu030_ad[i].done = i < idxsize; ! 2377: mmu030_ad[i].val = get_long_mmu030 (a7 + 0x5c - (i + 1) * 4); ! 2378: } ! 2379: mmu030_ad[idxsize + 1].done = false; ! 2380: // did we have data fault but DF bit cleared? ! 2381: if (ssw & (MMU030_SSW_DF << 1) && !(ssw & MMU030_SSW_DF)) { ! 2382: // DF not set: mark access as done ! 2383: if (ssw & MMU030_SSW_RM) { ! 2384: // Read-Modify-Write: whole instruction is considered done ! 2385: write_log (_T("Read-Modify-Write and DF bit cleared! PC=%08x\n"), regs.instruction_pc); ! 2386: mmu030_retry = false; ! 2387: } else if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) { ! 2388: // if movem, skip next move ! 2389: mmu030_data_buffer = get_long_mmu030 (a7 + 0x2c); ! 2390: mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM2; ! 2391: } else { ! 2392: mmu030_ad[idxsize].done = true; ! 2393: if (ssw & MMU030_SSW_RW) { ! 2394: // Read and no DF: use value in data input buffer ! 2395: mmu030_data_buffer = get_long_mmu030 (a7 + 0x2c); ! 2396: mmu030_ad[idxsize].val = mmu030_data_buffer; ! 2397: } ! 2398: } ! 2399: } ! 2400: // did we have ins fault and RB bit cleared? ! 2401: if ((ssw & MMU030_SSW_FB) && !(ssw & MMU030_SSW_RB)) { ! 2402: uae_u16 stageb = get_word_mmu030 (a7 + 0x0e); ! 2403: if (mmu030_opcode == -1) { ! 2404: mmu030_opcode_stageb = stageb; ! 2405: write_log (_T("Software fixed stage B! opcode = %04x\n"), stageb); ! 2406: } else { ! 2407: mmu030_ad[idxsize].done = true; ! 2408: mmu030_ad[idxsize].val = stageb; ! 2409: write_log (_T("Software fixed stage B! opcode = %04X, opword = %04x\n"), mmu030_opcode, stageb); ! 2410: } ! 2411: } ! 2412: m68k_areg (regs, 7) += 92; ! 2413: } else { ! 2414: m68k_areg (regs, 7) += 32; ! 2415: } ! 2416: } ! 2417: ! 2418: void flush_mmu030 (uaecptr addr, int n) ! 2419: { ! 2420: } ! 2421: ! 2422: void m68k_do_rts_mmu030 (void) ! 2423: { ! 2424: m68k_setpc (get_long_mmu030_state (m68k_areg (regs, 7))); ! 2425: m68k_areg (regs, 7) += 4; ! 2426: } ! 2427: ! 2428: void m68k_do_bsr_mmu030 (uaecptr oldpc, uae_s32 offset) ! 2429: { ! 2430: put_long_mmu030_state (m68k_areg (regs, 7) - 4, oldpc); ! 2431: m68k_areg (regs, 7) -= 4; ! 2432: m68k_incpci (offset); ! 2433: } ! 2434: ! 2435: uae_u32 REGPARAM2 get_disp_ea_020_mmu030 (uae_u32 base, int idx) ! 2436: { ! 2437: uae_u16 dp; ! 2438: int reg; ! 2439: uae_u32 v; ! 2440: int oldidx; ! 2441: int pcadd = 0; ! 2442: ! 2443: // we need to do this hack here because in worst case we don't have enough ! 2444: // stack frame space to store two very large 020 addressing mode access state ! 2445: // + whatever the instruction itself does. ! 2446: ! 2447: if (mmu030_state[1] & (1 << idx)) { ! 2448: m68k_incpci (((mmu030_state[2] >> (idx * 4)) & 15) * 2); ! 2449: return mmu030_disp_store[idx]; ! 2450: } ! 2451: ! 2452: oldidx = mmu030_idx; ! 2453: dp = next_iword_mmu030_state (); ! 2454: pcadd += 1; ! 2455: ! 2456: reg = (dp >> 12) & 15; ! 2457: uae_s32 regd = regs.regs[reg]; ! 2458: if ((dp & 0x800) == 0) ! 2459: regd = (uae_s32)(uae_s16)regd; ! 2460: regd <<= (dp >> 9) & 3; ! 2461: if (dp & 0x100) { ! 2462: uae_s32 outer = 0; ! 2463: if (dp & 0x80) ! 2464: base = 0; ! 2465: if (dp & 0x40) ! 2466: regd = 0; ! 2467: ! 2468: if ((dp & 0x30) == 0x20) { ! 2469: base += (uae_s32)(uae_s16) next_iword_mmu030_state (); ! 2470: pcadd += 1; ! 2471: } ! 2472: if ((dp & 0x30) == 0x30) { ! 2473: base += next_ilong_mmu030_state (); ! 2474: pcadd += 2; ! 2475: } ! 2476: ! 2477: if ((dp & 0x3) == 0x2) { ! 2478: outer = (uae_s32)(uae_s16) next_iword_mmu030_state (); ! 2479: pcadd += 1; ! 2480: } ! 2481: if ((dp & 0x3) == 0x3) { ! 2482: outer = next_ilong_mmu030_state (); ! 2483: pcadd += 2; ! 2484: } ! 2485: ! 2486: if ((dp & 0x4) == 0) { ! 2487: base += regd; ! 2488: } ! 2489: if (dp & 0x3) { ! 2490: base = get_long_mmu030_state (base); ! 2491: } ! 2492: if (dp & 0x4) { ! 2493: base += regd; ! 2494: } ! 2495: v = base + outer; ! 2496: } else { ! 2497: v = base + (uae_s32)((uae_s8)dp) + regd; ! 2498: } ! 2499: ! 2500: mmu030_state[1] |= 1 << idx; ! 2501: mmu030_state[2] |= pcadd << (idx * 4); ! 2502: mmu030_disp_store[idx] = v; ! 2503: mmu030_idx = oldidx; ! 2504: mmu030_ad[mmu030_idx].done = false; ! 2505: ! 2506: return v; ! 2507: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.