|
|
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: * Known Problems: ! 21: * - Special Status Word (SSW) is not handled correctly ! 22: * ! 23: * ! 24: * TODO list: ! 25: * - Correctly handle Special Status Word. Now we use 68040 values. ! 26: * This also needs to be done in m68000.c, M68000_BusError! ! 27: * - Check if read-modify-write operations are correctly detected for ! 28: * handling transparent access (see TT matching functions) ! 29: * - If possible, test mmu030_table_search with all kinds of translations ! 30: * (early termination, invalid descriptors, bus errors, indirect ! 31: * descriptors, PTEST in different levels, etc). ! 32: * - Check which bits of an ATC entry should be set and which should be ! 33: * un-set, if an invalid translation occurs. ! 34: * - Handle cache inhibit bit when accessing ATC entries ! 35: */ ! 36: ! 37: ! 38: #include "sysconfig.h" ! 39: #include "sysdeps.h" ! 40: ! 41: #include "options_cpu.h" ! 42: #include "memory.h" ! 43: #include "newcpu.h" ! 44: #include "main.h" ! 45: #include "m68000.h" ! 46: #include "hatari-glue.h" ! 47: #include "cpummu030.h" ! 48: ! 49: ! 50: #define MMU030_OP_DBG_MSG 0 ! 51: #define MMU030_ATC_DBG_MSG 0 ! 52: #define MMU030_REG_DBG_MSG 0 ! 53: ! 54: ! 55: /* for debugging messages */ ! 56: char table_letter[4] = {'A','B','C','D'}; ! 57: ! 58: ! 59: /* ATC struct */ ! 60: #define ATC030_NUM_ENTRIES 22 ! 61: ! 62: typedef struct { ! 63: struct { ! 64: uaecptr addr; ! 65: bool modified; ! 66: bool write_protect; ! 67: bool cache_inhibit; ! 68: bool bus_error; ! 69: } physical; ! 70: ! 71: struct { ! 72: uaecptr addr; ! 73: uae_u32 fc; ! 74: bool valid; ! 75: } logical; ! 76: /* history bit */ ! 77: int mru; ! 78: } MMU030_ATC_LINE; ! 79: ! 80: ! 81: /* MMU struct for 68030 */ ! 82: struct { ! 83: ! 84: /* Translation tables */ ! 85: struct { ! 86: struct { ! 87: uae_u32 mask; ! 88: uae_u8 shift; ! 89: } table[4]; ! 90: ! 91: struct { ! 92: uae_u32 mask; ! 93: uae_u8 size; ! 94: } page; ! 95: ! 96: uae_u8 init_shift; ! 97: uae_u8 last_table; ! 98: } translation; ! 99: ! 100: /* Transparent translation */ ! 101: struct { ! 102: TT_info tt0; ! 103: TT_info tt1; ! 104: } transparent; ! 105: ! 106: /* Address translation cache */ ! 107: MMU030_ATC_LINE atc[ATC030_NUM_ENTRIES]; ! 108: ! 109: /* Condition */ ! 110: bool enabled; ! 111: uae_u16 status; ! 112: } mmu030; ! 113: ! 114: ! 115: ! 116: /* MMU Status Register ! 117: * ! 118: * ---x ---x x-xx x--- ! 119: * reserved (all 0) ! 120: * ! 121: * x--- ---- ---- ---- ! 122: * bus error ! 123: * ! 124: * -x-- ---- ---- ---- ! 125: * limit violation ! 126: * ! 127: * --x- ---- ---- ---- ! 128: * supervisor only ! 129: * ! 130: * ---- x--- ---- ---- ! 131: * write protected ! 132: * ! 133: * ---- -x-- ---- ---- ! 134: * invalid ! 135: * ! 136: * ---- --x- ---- ---- ! 137: * modified ! 138: * ! 139: * ---- ---- -x-- ---- ! 140: * transparent access ! 141: * ! 142: * ---- ---- ---- -xxx ! 143: * number of levels (number of tables accessed during search) ! 144: * ! 145: */ ! 146: ! 147: #define MMUSR_BUS_ERROR 0x8000 ! 148: #define MMUSR_LIMIT_VIOLATION 0x4000 ! 149: #define MMUSR_SUPER_VIOLATION 0x2000 ! 150: #define MMUSR_WRITE_PROTECTED 0x0800 ! 151: #define MMUSR_INVALID 0x0400 ! 152: #define MMUSR_MODIFIED 0x0200 ! 153: #define MMUSR_TRANSP_ACCESS 0x0040 ! 154: #define MMUSR_NUM_LEVELS_MASK 0x0007 ! 155: ! 156: ! 157: ! 158: /* -- MMU instructions -- */ ! 159: ! 160: void mmu_op30_pmove (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 161: { ! 162: int preg = (next >> 10) & 31; ! 163: int rw = (next >> 9) & 1; ! 164: int fd = (next >> 8) & 1; ! 165: ! 166: #if MMU030_OP_DBG_MSG ! 167: switch (preg) { ! 168: case 0x10: ! 169: write_log("PMOVE: %s TC %08X\n", rw?"read":"write", ! 170: rw?tc_030:x_get_long(extra)); ! 171: break; ! 172: case 0x12: ! 173: write_log("PMOVE: %s SRP %08X%08X\n", rw?"read":"write", ! 174: rw?(uae_u32)(srp_030>>32)&0xFFFFFFFF:x_get_long(extra), ! 175: rw?(uae_u32)srp_030&0xFFFFFFFF:x_get_long(extra+4)); ! 176: break; ! 177: case 0x13: ! 178: write_log("PMOVE: %s CRP %08X%08X\n", rw?"read":"write", ! 179: rw?(uae_u32)(crp_030>>32)&0xFFFFFFFF:x_get_long(extra), ! 180: rw?(uae_u32)crp_030&0xFFFFFFFF:x_get_long(extra+4)); ! 181: break; ! 182: case 0x18: ! 183: write_log("PMOVE: %s MMUSR %04X\n", rw?"read":"write", ! 184: rw?mmusr_030:x_get_word(extra)); ! 185: break; ! 186: case 0x02: ! 187: write_log("PMOVE: %s TT0 %08X\n", rw?"read":"write", ! 188: rw?tt0_030:x_get_long(extra)); ! 189: break; ! 190: case 0x03: ! 191: write_log("PMOVE: %s TT1 %08X\n", rw?"read":"write", ! 192: rw?tt1_030:x_get_long(extra)); ! 193: break; ! 194: default: ! 195: break; ! 196: } ! 197: if (!fd && !rw && !(preg==0x18)) { ! 198: write_log("PMOVE: flush ATC\n"); ! 199: } ! 200: #endif ! 201: ! 202: switch (preg) ! 203: { ! 204: case 0x10: // TC ! 205: if (rw) ! 206: x_put_long (extra, tc_030); ! 207: else { ! 208: tc_030 = x_get_long (extra); ! 209: mmu030_decode_tc(tc_030); ! 210: } ! 211: break; ! 212: case 0x12: // SRP ! 213: if (rw) { ! 214: x_put_long (extra, srp_030 >> 32); ! 215: x_put_long (extra + 4, srp_030); ! 216: } else { ! 217: srp_030 = (uae_u64)x_get_long (extra) << 32; ! 218: srp_030 |= x_get_long (extra + 4); ! 219: mmu030_decode_rp(srp_030); ! 220: } ! 221: break; ! 222: case 0x13: // CRP ! 223: if (rw) { ! 224: x_put_long (extra, crp_030 >> 32); ! 225: x_put_long (extra + 4, crp_030); ! 226: } else { ! 227: crp_030 = (uae_u64)x_get_long (extra) << 32; ! 228: crp_030 |= x_get_long (extra + 4); ! 229: mmu030_decode_rp(crp_030); ! 230: } ! 231: break; ! 232: case 0x18: // MMUSR ! 233: if (rw) ! 234: x_put_word (extra, mmusr_030); ! 235: else ! 236: mmusr_030 = x_get_word (extra); ! 237: break; ! 238: case 0x02: // TT0 ! 239: if (rw) ! 240: x_put_long (extra, tt0_030); ! 241: else { ! 242: tt0_030 = x_get_long (extra); ! 243: mmu030.transparent.tt0 = mmu030_decode_tt(tt0_030); ! 244: } ! 245: break; ! 246: case 0x03: // TT1 ! 247: if (rw) ! 248: x_put_long (extra, tt1_030); ! 249: else { ! 250: tt1_030 = x_get_long (extra); ! 251: mmu030.transparent.tt1 = mmu030_decode_tt(tt1_030); ! 252: } ! 253: break; ! 254: default: ! 255: write_log ("Bad PMOVE at %08x\n",m68k_getpc()); ! 256: op_illg (opcode); ! 257: return; ! 258: } ! 259: ! 260: if (!fd && !rw && !(preg==0x18)) { ! 261: mmu030_flush_atc_all(); ! 262: } ! 263: } ! 264: ! 265: void mmu_op30_ptest (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 266: { ! 267: mmu030.status = mmusr_030 = 0; ! 268: ! 269: int level = (next&0x1C00)>>10; ! 270: int rw = (next >> 9) & 1; ! 271: int a = (next >> 8) & 1; ! 272: int areg = (next&0xE0)>>5; ! 273: uae_u32 fc = mmu_op30_helper_get_fc(next); ! 274: ! 275: bool write = rw ? false : true; ! 276: ! 277: uae_u32 ret = 0; ! 278: ! 279: /* Check this - datasheet says: ! 280: * "When the instruction specifies an address translation cache search ! 281: * with an address register operand, the MC68030 takes an F-line ! 282: * unimplemented instruction exception." ! 283: */ ! 284: if (!level && a) { /* correct ? */ ! 285: write_log("PTEST: Bad instruction causing F-line unimplemented instruction exception!\n"); ! 286: Exception(11, m68k_getpc(), M68000_EXC_SRC_CPU); /* F-line unimplemented instruction exception */ ! 287: return; ! 288: } ! 289: ! 290: #if MMU030_OP_DBG_MSG ! 291: write_log("PTEST%c: addr = %08X, fc = %i, level = %i, ", ! 292: rw?'R':'W', extra, fc, level); ! 293: if (a) { ! 294: write_log("return descriptor to register A%i\n", areg); ! 295: } else { ! 296: write_log("do not return descriptor\n"); ! 297: } ! 298: #endif ! 299: ! 300: if (!level) { ! 301: mmu030_ptest_atc_search(extra, fc, write); ! 302: } else { ! 303: ret = mmu030_ptest_table_search(extra, fc, write, level); ! 304: if (a) { ! 305: m68k_areg (regs, areg) = ret; ! 306: } ! 307: } ! 308: mmusr_030 = mmu030.status; ! 309: ! 310: #if MMU030_OP_DBG_MSG ! 311: write_log("PTEST status: %04X, B = %i, L = %i, S = %i, W = %i, I = %i, M = %i, T = %i, N = %i\n", ! 312: mmusr_030, (mmusr_030&MMUSR_BUS_ERROR)?1:0, (mmusr_030&MMUSR_LIMIT_VIOLATION)?1:0, ! 313: (mmusr_030&MMUSR_SUPER_VIOLATION)?1:0, (mmusr_030&MMUSR_WRITE_PROTECTED)?1:0, ! 314: (mmusr_030&MMUSR_INVALID)?1:0, (mmusr_030&MMUSR_MODIFIED)?1:0, ! 315: (mmusr_030&MMUSR_TRANSP_ACCESS)?1:0, mmusr_030&MMUSR_NUM_LEVELS_MASK); ! 316: #endif ! 317: } ! 318: ! 319: void mmu_op30_pload (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 320: { ! 321: int rw = (next >> 9) & 1; ! 322: uae_u32 fc = mmu_op30_helper_get_fc(next); ! 323: ! 324: bool write = rw ? false : true; ! 325: ! 326: #if MMU030_OP_DBG_MSG ! 327: write_log ("PLOAD%c: Create ATC entry for %08X, FC = %i\n", write?'W':'R', extra, fc); ! 328: #endif ! 329: ! 330: mmu030_flush_atc_page(extra); ! 331: mmu030_table_search(extra, fc, write, 0); ! 332: } ! 333: ! 334: void mmu_op30_pflush (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) ! 335: { ! 336: uae_u16 mode = (next&0x1C00)>>10; ! 337: uae_u32 fc_mask = (uae_u32)(next&0x00E0)>>5; ! 338: uae_u32 fc_base = mmu_op30_helper_get_fc(next); ! 339: ! 340: #if MMU030_OP_DBG_MSG ! 341: switch (mode) { ! 342: case 0x1: ! 343: write_log("PFLUSH: Flush all entries\n"); ! 344: break; ! 345: case 0x4: ! 346: write_log("PFLUSH: Flush by function code only\n"); ! 347: write_log("PFLUSH: function code: base = %08X, mask = %08X\n", fc_base, fc_mask); ! 348: break; ! 349: case 0x6: ! 350: write_log("PFLUSH: Flush by function code and effective address\n"); ! 351: write_log("PFLUSH: function code: base = %08X, mask = %08X\n", fc_base, fc_mask); ! 352: write_log("PFLUSH: effective address = %08X\n", extra); ! 353: break; ! 354: default: ! 355: break; ! 356: } ! 357: #endif ! 358: ! 359: switch (mode) { ! 360: case 0x1: ! 361: mmu030_flush_atc_all(); ! 362: break; ! 363: case 0x4: ! 364: mmu030_flush_atc_fc(fc_base, fc_mask); ! 365: break; ! 366: case 0x6: ! 367: mmu030_flush_atc_page_fc(extra, fc_base, fc_mask); ! 368: break; ! 369: ! 370: default: ! 371: write_log("PFLUSH ERROR: bad mode! (%i)\n",mode); ! 372: break; ! 373: } ! 374: } ! 375: ! 376: /* -- Helper function for MMU instructions -- */ ! 377: uae_u32 mmu_op30_helper_get_fc(uae_u16 next) { ! 378: switch (next&0x0018) { ! 379: case 0x0010: ! 380: return (next&0x7); ! 381: case 0x0008: ! 382: return (m68k_dreg(regs, next&0x7)&0x7); ! 383: case 0x0000: ! 384: if (next&1) { ! 385: return (regs.dfc&0x7); ! 386: } else { ! 387: return (regs.sfc&0x7); ! 388: } ! 389: default: ! 390: write_log("MMU_OP30 ERROR: bad fc source! (%04X)\n",next&0x0018); ! 391: return 0; ! 392: } ! 393: } ! 394: ! 395: ! 396: /* -- ATC flushing functions -- */ ! 397: ! 398: /* This function flushes ATC entries depending on their function code */ ! 399: void mmu030_flush_atc_fc(uae_u32 fc_base, uae_u32 fc_mask) { ! 400: int i; ! 401: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 402: if (((fc_base&fc_mask)==(mmu030.atc[i].logical.fc&fc_mask)) && ! 403: mmu030.atc[i].logical.valid) { ! 404: mmu030.atc[i].logical.valid = false; ! 405: write_log("ATC: Flushing %08X\n", mmu030.atc[i].physical.addr); ! 406: } ! 407: } ! 408: } ! 409: ! 410: /* This function flushes ATC entries depending on their logical address ! 411: * and their function code */ ! 412: void mmu030_flush_atc_page_fc(uaecptr logical_addr, uae_u32 fc_base, uae_u32 fc_mask) { ! 413: int i; ! 414: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 415: if (((fc_base&fc_mask)==(mmu030.atc[i].logical.fc&fc_mask)) && ! 416: (mmu030.atc[i].logical.addr == logical_addr) && ! 417: mmu030.atc[i].logical.valid) { ! 418: mmu030.atc[i].logical.valid = false; ! 419: write_log("ATC: Flushing %08X\n", mmu030.atc[i].physical.addr); ! 420: } ! 421: } ! 422: } ! 423: ! 424: /* This function flushes ATC entries depending on their logical address */ ! 425: void mmu030_flush_atc_page(uaecptr logical_addr) { ! 426: int i; ! 427: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 428: if ((mmu030.atc[i].logical.addr == logical_addr) && ! 429: mmu030.atc[i].logical.valid) { ! 430: mmu030.atc[i].logical.valid = false; ! 431: write_log("ATC: Flushing %08X\n", mmu030.atc[i].physical.addr); ! 432: } ! 433: } ! 434: } ! 435: ! 436: /* This function flushes all ATC entries */ ! 437: void mmu030_flush_atc_all(void) { ! 438: #if MMU030_ATC_DBG_MSG ! 439: write_log("ATC: Flushing all entries\n"); ! 440: #endif ! 441: int i; ! 442: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 443: mmu030.atc[i].logical.valid = false; ! 444: } ! 445: } ! 446: ! 447: ! 448: /* Transparent Translation Registers (TT0 and TT1) ! 449: * ! 450: * ---- ---- ---- ---- -xxx x--- x--- x--- ! 451: * reserved, must be 0 ! 452: * ! 453: * ---- ---- ---- ---- ---- ---- ---- -xxx ! 454: * function code mask (FC bits to be ignored) ! 455: * ! 456: * ---- ---- ---- ---- ---- ---- -xxx ---- ! 457: * function code base (FC value for transparent block) ! 458: * ! 459: * ---- ---- ---- ---- ---- ---x ---- ---- ! 460: * 0 = r/w field used, 1 = read and write is transparently translated ! 461: * ! 462: * ---- ---- ---- ---- ---- --x- ---- ---- ! 463: * r/w field: 0 = write ..., 1 = read access transparent ! 464: * ! 465: * ---- ---- ---- ---- ---- -x-- ---- ---- ! 466: * cache inhibit: 0 = caching allowed, 1 = caching inhibited ! 467: * ! 468: * ---- ---- ---- ---- x--- ---- ---- ---- ! 469: * 0 = transparent translation enabled disabled, 1 = enabled ! 470: * ! 471: * ---- ---- xxxx xxxx ---- ---- ---- ---- ! 472: * logical address mask ! 473: * ! 474: * xxxx xxxx ---- ---- ---- ---- ---- ---- ! 475: * logical address base ! 476: * ! 477: */ ! 478: ! 479: ! 480: #define TT_FC_MASK 0x00000007 ! 481: #define TT_FC_BASE 0x00000070 ! 482: #define TT_RWM 0x00000100 ! 483: #define TT_RW 0x00000200 ! 484: #define TT_CI 0x00000400 ! 485: #define TT_ENABLE 0x00008000 ! 486: ! 487: #define TT_ADDR_MASK 0x00FF0000 ! 488: #define TT_ADDR_BASE 0xFF000000 ! 489: ! 490: /* TT comparision results */ ! 491: #define TT_NO_MATCH 0x1 ! 492: #define TT_OK_MATCH 0x2 ! 493: #define TT_NO_READ 0x4 ! 494: #define TT_NO_WRITE 0x8 ! 495: ! 496: TT_info mmu030_decode_tt(uae_u32 TT) { ! 497: ! 498: TT_info ret; ! 499: ! 500: ret.fc_mask = ~((TT&TT_FC_MASK)|0xFFFFFFF8); ! 501: ret.fc_base = (TT&TT_FC_BASE)>>4; ! 502: ret.addr_base = TT & TT_ADDR_BASE; ! 503: ret.addr_mask = ~(((TT&TT_ADDR_MASK)<<8)|0x00FFFFFF); ! 504: ! 505: if ((TT&TT_ENABLE) && !(TT&TT_RWM)) { ! 506: write_log("MMU Warning: Transparent translation of read-modify-write cycle is not correctly handled!\n"); ! 507: } ! 508: ! 509: #if MMU030_REG_DBG_MSG /* enable or disable debugging messages */ ! 510: write_log("\n"); ! 511: write_log("TRANSPARENT TRANSLATION: %08X\n", TT); ! 512: write_log("\n"); ! 513: ! 514: write_log("TT: transparent translation "); ! 515: if (TT&TT_ENABLE) { ! 516: write_log("enabled\n"); ! 517: } else { ! 518: write_log("disabled\n"); ! 519: return ret; ! 520: } ! 521: ! 522: write_log("TT: caching %s\n", (TT&TT_CI) ? "inhibited" : "enabled"); ! 523: write_log("TT: read-modify-write "); ! 524: if (TT&TT_RWM) { ! 525: write_log("enabled\n"); ! 526: } else { ! 527: write_log("disabled (%s only)\n", (TT&TT_RW) ? "read" : "write"); ! 528: } ! 529: write_log("\n"); ! 530: write_log("TT: function code base: %08X\n", ret.fc_base); ! 531: write_log("TT: function code mask: %08X\n", ret.fc_mask); ! 532: write_log("\n"); ! 533: write_log("TT: address base: %08X\n", ret.addr_base); ! 534: write_log("TT: address mask: %08X\n", ret.addr_mask); ! 535: write_log("\n"); ! 536: #endif ! 537: ! 538: return ret; ! 539: } ! 540: ! 541: /* This function compares the address with both transparent ! 542: * translation registers and returns the result */ ! 543: int mmu030_match_ttr(uaecptr addr, uae_u32 fc, bool write) ! 544: { ! 545: int tt0, tt1; ! 546: ! 547: bool cache_inhibit = false; /* TODO: pass to memory access function */ ! 548: ! 549: tt0 = mmu030_do_match_ttr(tt0_030, mmu030.transparent.tt0, addr, fc, write); ! 550: if (tt0&TT_OK_MATCH) { ! 551: cache_inhibit = (tt0_030&TT_CI) ? true : false; ! 552: } ! 553: tt1 = mmu030_do_match_ttr(tt1_030, mmu030.transparent.tt1, addr, fc, write); ! 554: if (tt1&TT_OK_MATCH) { ! 555: if (!cache_inhibit) { ! 556: cache_inhibit = (tt1_030&TT_CI) ? true : false; ! 557: } ! 558: } ! 559: ! 560: return (tt0|tt1); ! 561: } ! 562: ! 563: /* This function checks if an address matches a transparent ! 564: * translation register */ ! 565: ! 566: /* FIXME: ! 567: * If !(tt&TT_RMW) neither the read nor the write portion ! 568: * of a read-modify-write cycle is transparently translated! */ ! 569: ! 570: int mmu030_do_match_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc, bool write) ! 571: { ! 572: if (tt & TT_ENABLE) { /* transparent translation enabled */ ! 573: ! 574: /* Compare actual function code with function code base using mask */ ! 575: if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) { ! 576: ! 577: /* Compare actual address with address base using mask */ ! 578: if ((comp.addr_base&comp.addr_mask)==(addr&comp.addr_mask)) { ! 579: ! 580: if (tt&TT_RWM) { /* r/w field disabled */ ! 581: return TT_OK_MATCH; ! 582: } else { ! 583: if (tt&TT_RW) { /* read access transparent */ ! 584: return write ? TT_NO_WRITE : TT_OK_MATCH; ! 585: } else { /* write access transparent */ ! 586: return write ? TT_OK_MATCH : TT_NO_READ; /* TODO: check this! */ ! 587: } ! 588: } ! 589: } ! 590: } ! 591: } ! 592: return TT_NO_MATCH; ! 593: } ! 594: ! 595: ! 596: ! 597: /* Translation Control Register: ! 598: * ! 599: * x--- ---- ---- ---- ---- ---- ---- ---- ! 600: * translation: 1 = enable, 0 = disable ! 601: * ! 602: * ---- --x- ---- ---- ---- ---- ---- ---- ! 603: * supervisor root: 1 = enable, 0 = disable ! 604: * ! 605: * ---- ---x ---- ---- ---- ---- ---- ---- ! 606: * function code lookup: 1 = enable, 0 = disable ! 607: * ! 608: * ---- ---- xxxx ---- ---- ---- ---- ---- ! 609: * page size: ! 610: * 1000 = 256 bytes ! 611: * 1001 = 512 bytes ! 612: * 1010 = 1 kB ! 613: * 1011 = 2 kB ! 614: * 1100 = 4 kB ! 615: * 1101 = 8 kB ! 616: * 1110 = 16 kB ! 617: * 1111 = 32 kB ! 618: * ! 619: * ---- ---- ---- xxxx ---- ---- ---- ---- ! 620: * initial shift ! 621: * ! 622: * ---- ---- ---- ---- xxxx ---- ---- ---- ! 623: * number of bits for table index A ! 624: * ! 625: * ---- ---- ---- ---- ---- xxxx ---- ---- ! 626: * number of bits for table index B ! 627: * ! 628: * ---- ---- ---- ---- ---- ---- xxxx ---- ! 629: * number of bits for table index C ! 630: * ! 631: * ---- ---- ---- ---- ---- ----- ---- xxxx ! 632: * number of bits for table index D ! 633: * ! 634: */ ! 635: ! 636: ! 637: #define TC_ENABLE_TRANSLATION 0x80000000 ! 638: #define TC_ENABLE_SUPERVISOR 0x02000000 ! 639: #define TC_ENABLE_FCL 0x01000000 ! 640: ! 641: #define TC_PS_MASK 0x00F00000 ! 642: #define TC_IS_MASK 0x000F0000 ! 643: ! 644: #define TC_TIA_MASK 0x0000F000 ! 645: #define TC_TIB_MASK 0x00000F00 ! 646: #define TC_TIC_MASK 0x000000F0 ! 647: #define TC_TID_MASK 0x0000000F ! 648: ! 649: ! 650: void mmu030_decode_tc(uae_u32 TC) { ! 651: ! 652: /* Set MMU condition */ ! 653: if (TC & TC_ENABLE_TRANSLATION) { ! 654: mmu030.enabled = true; ! 655: write_log("MMU enabled\n"); ! 656: } else { ! 657: mmu030.enabled = false; ! 658: write_log("MMU disabled\n"); ! 659: return; ! 660: } ! 661: ! 662: /* Note: 0 = Table A, 1 = Table B, 2 = Table C, 3 = Table D */ ! 663: int i, j; ! 664: uae_u8 TI_bits[4] = {0,0,0,0}; ! 665: ! 666: /* Reset variables before extracting new values from TC */ ! 667: for (i = 0; i < 4; i++) { ! 668: mmu030.translation.table[i].mask = 0; ! 669: mmu030.translation.table[i].shift = 0; ! 670: } ! 671: mmu030.translation.page.mask = 0; ! 672: ! 673: ! 674: /* Extract initial shift and page size values from TC register */ ! 675: mmu030.translation.page.size = (TC & TC_PS_MASK) >> 20; ! 676: mmu030.translation.init_shift = (TC & TC_IS_MASK) >> 16; ! 677: ! 678: if (mmu030.translation.page.size<8) { ! 679: write_log("MMU Configuration Exception: Bad value in TC register! (bad page size: %i byte)\n", ! 680: 1<<mmu030.translation.page.size); ! 681: Exception(56, m68k_getpc(), M68000_EXC_SRC_CPU); /* MMU Configuration Exception */ ! 682: return; ! 683: } ! 684: /* Build the page mask */ ! 685: for (i = 0; i < mmu030.translation.page.size; i++) { ! 686: mmu030.translation.page.mask |= (1<<i); ! 687: } ! 688: ! 689: ! 690: /* Calculate masks and shifts for later extracting table indices ! 691: * from logical addresses using: index = (addr&mask)>>shift */ ! 692: ! 693: /* Get number of bits for each table index */ ! 694: for (i = 0; i < 4; i++) { ! 695: j = (3-i)*4; ! 696: TI_bits[i] = (TC >> j) & 0xF; ! 697: } ! 698: ! 699: /* Calculate masks and shifts for each table */ ! 700: mmu030.translation.last_table = 0; ! 701: uae_u8 shift = 32 - mmu030.translation.init_shift; ! 702: for (i = 0; (i < 4) && TI_bits[i]; i++) { ! 703: /* Get the shift */ ! 704: shift -= TI_bits[i]; ! 705: mmu030.translation.table[i].shift = shift; ! 706: /* Build the mask */ ! 707: for (j = 0; j < TI_bits[i]; j++) { ! 708: mmu030.translation.table[i].mask |= (1<<(mmu030.translation.table[i].shift + j)); ! 709: } ! 710: /* Update until reaching the last table */ ! 711: mmu030.translation.last_table = i; ! 712: } ! 713: ! 714: #if MMU030_REG_DBG_MSG ! 715: /* At least one table has to be defined using at least ! 716: * 1 bit for the index. At least 2 bits are necessary ! 717: * if there is no second table. If these conditions are ! 718: * not met, it will automatically lead to a sum <32 ! 719: * and cause an exception (see below). */ ! 720: if (!TI_bits[0]) { ! 721: write_log("MMU Configuration Exception: Bad value in TC register! (no first table index defined)\n"); ! 722: } else if ((TI_bits[0]<2) && !TI_bits[1]) { ! 723: write_log("MMU Configuration Exception: Bad value in TC register! (no second table index defined and)\n"); ! 724: write_log("MMU Configuration Exception: Bad value in TC register! (only 1 bit for first table index)\n"); ! 725: } ! 726: #endif ! 727: ! 728: /* TI fields are summed up until a zero field is reached (see above ! 729: * loop). The sum of all TI field values plus page size and initial ! 730: * shift has to be 32: IS + PS + TIA + TIB + TIC + TID = 32 */ ! 731: if ((shift-mmu030.translation.page.size)!=0) { ! 732: write_log("MMU Configuration Exception: Bad value in TC register! (bad sum)\n"); ! 733: Exception(56, m68k_getpc(), M68000_EXC_SRC_CPU); /* MMU Configuration Exception */ ! 734: return; ! 735: } ! 736: ! 737: #if MMU030_REG_DBG_MSG /* enable or disable debugging output */ ! 738: write_log("\n"); ! 739: write_log("TRANSLATION CONTROL: %08X\n", TC); ! 740: write_log("\n"); ! 741: write_log("TC: translation %s\n", (TC&TC_ENABLE_TRANSLATION ? "enabled" : "disabled")); ! 742: write_log("TC: supervisor root pointer %s\n", (TC&TC_ENABLE_SUPERVISOR ? "enabled" : "disabled")); ! 743: write_log("TC: function code lookup %s\n", (TC&TC_ENABLE_FCL ? "enabled" : "disabled")); ! 744: write_log("\n"); ! 745: ! 746: write_log("TC: Initial Shift: %i\n", mmu030.translation.init_shift); ! 747: write_log("TC: Page Size: %i byte\n", (1<<mmu030.translation.page.size)); ! 748: write_log("\n"); ! 749: ! 750: for (i = 0; i <= mmu030.translation.last_table; i++) { ! 751: write_log("TC: Table %c: mask = %08X, shift = %i\n", table_letter[i], mmu030.translation.table[i].mask, mmu030.translation.table[i].shift); ! 752: } ! 753: ! 754: write_log("TC: Page: mask = %08X\n", mmu030.translation.page.mask); ! 755: write_log("\n"); ! 756: ! 757: write_log("TC: Last Table: %c\n", table_letter[mmu030.translation.last_table]); ! 758: write_log("\n"); ! 759: #endif ! 760: } ! 761: ! 762: ! 763: ! 764: /* Root Pointer Registers (SRP and CRP) ! 765: * ! 766: * ---- ---- ---- ---- xxxx xxxx xxxx xx-- ---- ---- ---- ---- ---- ---- ---- xxxx ! 767: * reserved, must be 0 ! 768: * ! 769: * ---- ---- ---- ---- ---- ---- ---- ---- xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 770: * table A address ! 771: * ! 772: * ---- ---- ---- ---- ---- ---- ---- --xx ---- ---- ---- ---- ---- ---- ---- ---- ! 773: * descriptor type ! 774: * ! 775: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ! 776: * limit ! 777: * ! 778: * x--- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ! 779: * 0 = upper limit, 1 = lower limit ! 780: * ! 781: */ ! 782: ! 783: ! 784: #define RP_ADDR_MASK (UVAL64(0x00000000FFFFFFF0)) ! 785: #define RP_DESCR_MASK (UVAL64(0x0000000300000000)) ! 786: #define RP_LIMIT_MASK (UVAL64(0x7FFF000000000000)) ! 787: #define RP_LOWER_MASK (UVAL64(0x8000000000000000)) ! 788: ! 789: #define RP_ZERO_BITS 0x0000FFFC /* These bits in upper longword of RP must be 0 */ ! 790: ! 791: void mmu030_decode_rp(uae_u64 RP) { ! 792: ! 793: uae_u8 descriptor_type = (RP & RP_DESCR_MASK) >> 32; ! 794: if (!descriptor_type) { /* If descriptor type is invalid */ ! 795: write_log("MMU Configuration Exception: Root Pointer is invalid!\n"); ! 796: Exception(56, m68k_getpc(), M68000_EXC_SRC_CPU); /* MMU Configuration Exception */ ! 797: } ! 798: ! 799: #if MMU030_REG_DBG_MSG /* enable or disable debugging output */ ! 800: uae_u32 table_limit = (RP & RP_LIMIT_MASK) >> 48; ! 801: uae_u32 first_addr = (RP & RP_ADDR_MASK); ! 802: ! 803: write_log("\n"); ! 804: write_log("ROOT POINTER: %08X%08X\n", (uae_u32)(RP>>32)&0xFFFFFFFF, (uae_u32)(RP&0xFFFFFFFF)); ! 805: write_log("\n"); ! 806: ! 807: write_log("RP: descriptor type = %i ", descriptor_type); ! 808: switch (descriptor_type) { ! 809: case 0: ! 810: write_log("(invalid descriptor)\n"); ! 811: break; ! 812: case 1: ! 813: write_log("(early termination page descriptor)\n"); ! 814: break; ! 815: case 2: ! 816: write_log("(valid 4 byte descriptor)\n"); ! 817: break; ! 818: case 3: ! 819: write_log("(valid 8 byte descriptor)\n"); ! 820: break; ! 821: } ! 822: ! 823: write_log("RP: %s limit = %i\n", (RP&RP_LOWER_MASK) ? "lower" : "upper", table_limit); ! 824: ! 825: write_log("RP: first table address = %08X\n", first_addr); ! 826: write_log("\n"); ! 827: #endif ! 828: } ! 829: ! 830: ! 831: ! 832: /* Descriptors */ ! 833: ! 834: #define DESCR_TYPE_MASK 0x00000003 ! 835: ! 836: #define DESCR_TYPE_INVALID 0 /* all tables */ ! 837: ! 838: #define DESCR_TYPE_EARLY_TERM 1 /* all but lowest level table */ ! 839: #define DESCR_TYPE_PAGE 1 /* only lowest level table */ ! 840: #define DESCR_TYPE_VALID4 2 /* all but lowest level table */ ! 841: #define DESCR_TYPE_INDIRECT4 2 /* only lowest level table */ ! 842: #define DESCR_TYPE_VALID8 3 /* all but lowest level table */ ! 843: #define DESCR_TYPE_INDIRECT8 3 /* only lowest level table */ ! 844: ! 845: #define DESCR_TYPE_VALID_MASK 0x2 /* all but lowest level table */ ! 846: #define DESCR_TYPE_INDIRECT_MASK 0x2 /* only lowest level table */ ! 847: ! 848: ! 849: /* Short format (4 byte): ! 850: * ! 851: * ---- ---- ---- ---- ---- ---- ---- --xx ! 852: * descriptor type: ! 853: * 0 = invalid ! 854: * 1 = page descriptor (early termination) ! 855: * 2 = valid (4 byte) ! 856: * 3 = valid (8 byte) ! 857: * ! 858: * ! 859: * table descriptor: ! 860: * ---- ---- ---- ---- ---- ---- ---- -x-- ! 861: * write protect ! 862: * ! 863: * ---- ---- ---- ---- ---- ---- ---- x--- ! 864: * update ! 865: * ! 866: * xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 867: * table address ! 868: * ! 869: * ! 870: * (early termination) page descriptor: ! 871: * ---- ---- ---- ---- ---- ---- ---- -x-- ! 872: * write protect ! 873: * ! 874: * ---- ---- ---- ---- ---- ---- ---- x--- ! 875: * update ! 876: * ! 877: * ---- ---- ---- ---- ---- ---- ---x ---- ! 878: * modified ! 879: * ! 880: * ---- ---- ---- ---- ---- ---- -x-- ---- ! 881: * cache inhibit ! 882: * ! 883: * ---- ---- ---- ---- ---- ---- x-x- ---- ! 884: * reserved (must be 0) ! 885: * ! 886: * xxxx xxxx xxxx xxxx xxxx xxxx ---- ---- ! 887: * page address ! 888: * ! 889: * ! 890: * indirect descriptor: ! 891: * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx-- ! 892: * descriptor address ! 893: * ! 894: */ ! 895: ! 896: #define DESCR_WP 0x00000004 ! 897: #define DESCR_U 0x00000008 ! 898: #define DESCR_M 0x00000010 /* only last level table */ ! 899: #define DESCR_CI 0x00000040 /* only last level table */ ! 900: ! 901: #define DESCR_TD_ADDR_MASK 0xFFFFFFF0 ! 902: #define DESCR_PD_ADDR_MASK 0xFFFFFF00 ! 903: #define DESCR_ID_ADDR_MASK 0xFFFFFFFC ! 904: ! 905: ! 906: /* Long format (8 byte): ! 907: * ! 908: * ---- ---- ---- ---- ---- ---- ---- --xx | ---- ---- ---- ---- ---- ---- ---- ---- ! 909: * descriptor type: ! 910: * 0 = invalid ! 911: * 1 = page descriptor (early termination) ! 912: * 2 = valid (4 byte) ! 913: * 3 = valid (8 byte) ! 914: * ! 915: * ! 916: * table desctriptor: ! 917: * ---- ---- ---- ---- ---- ---- ---- -x-- | ---- ---- ---- ---- ---- ---- ---- ---- ! 918: * write protect ! 919: * ! 920: * ---- ---- ---- ---- ---- ---- ---- x--- | ---- ---- ---- ---- ---- ---- ---- ---- ! 921: * update ! 922: * ! 923: * ---- ---- ---- ---- ---- ---- xxxx ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 924: * reserved (must be 0) ! 925: * ! 926: * ---- ---- ---- ---- ---- ---x ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 927: * supervisor ! 928: * ! 929: * ---- ---- ---- ---- xxxx xxx- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 930: * reserved (must be 1111 110) ! 931: * ! 932: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 933: * limit ! 934: * ! 935: * x--- ---- ---- ---- ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 936: * 0 = upper limit, 1 = lower limit ! 937: * ! 938: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx xxxx ---- ! 939: * table address ! 940: * ! 941: * ! 942: * (early termination) page descriptor: ! 943: * ---- ---- ---- ---- ---- ---- ---- -x-- | ---- ---- ---- ---- ---- ---- ---- ---- ! 944: * write protect ! 945: * ! 946: * ---- ---- ---- ---- ---- ---- ---- x--- | ---- ---- ---- ---- ---- ---- ---- ---- ! 947: * update ! 948: * ! 949: * ---- ---- ---- ---- ---- ---- ---x ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 950: * modified ! 951: * ! 952: * ---- ---- ---- ---- ---- ---- -x-- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 953: * cache inhibit ! 954: * ! 955: * ---- ---- ---- ---- ---- ---x ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 956: * supervisor ! 957: * ! 958: * ---- ---- ---- ---- ---- ---- x-x- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 959: * reserved (must be 0) ! 960: * ! 961: * ---- ---- ---- ---- xxxx xxx- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 962: * reserved (must be 1111 110) ! 963: * ! 964: * -xxx xxxx xxxx xxxx ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 965: * limit (only used with early termination page decriptor) ! 966: * ! 967: * x--- ---- ---- ---- ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ---- ! 968: * 0 = upper limit, 1 = lower limit (only used with early termination page descriptor) ! 969: * ! 970: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx ---- ---- ! 971: * page address ! 972: * ! 973: * ! 974: * indirect descriptor: ! 975: * ---- ---- ---- ---- ---- ---- ---- ---- | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx-- ! 976: * descriptor address ! 977: * ! 978: */ ! 979: ! 980: /* only for long descriptors */ ! 981: #define DESCR_S 0x00000100 ! 982: ! 983: #define DESCR_LIMIT_MASK 0x7FFF0000 ! 984: #define DESCR_LOWER_MASK 0x80000000 ! 985: ! 986: ! 987: ! 988: /* This functions searches through the translation tables. It can be used ! 989: * for PTEST (levels 1 to 7). Using level 0 creates an ATC entry. */ ! 990: ! 991: uae_u32 mmu030_table_search(uaecptr addr, uae_u32 fc, bool write, int level) { ! 992: /* During table walk up to 7 different descriptors are used: ! 993: * root pointer, descriptors fetched from function code lookup table, ! 994: * tables A, B, C and D and one indirect descriptor */ ! 995: uae_u32 descr[2]; ! 996: uae_u32 descr_type; ! 997: uaecptr descr_addr[7]; ! 998: uaecptr table_addr = 0; ! 999: uaecptr page_addr = 0; ! 1000: uaecptr indirect_addr = 0; ! 1001: uae_u32 table_index = 0; ! 1002: uae_u32 limit = 0; ! 1003: uae_u32 unused_fields_mask = 0; ! 1004: bool super = (fc&4) ? true : false; ! 1005: bool write_protect = false; ! 1006: bool cache_inhibit = false; ! 1007: bool descr_modified = false; ! 1008: ! 1009: mmu030.status = 0; /* Reset status */ ! 1010: ! 1011: /* Initial values for condition variables. ! 1012: * Note: Root pointer is long descriptor. */ ! 1013: int t = 0; ! 1014: int addr_position = 1; ! 1015: int next_size = 0; ! 1016: int descr_size = 8; ! 1017: int descr_num = 0; ! 1018: bool early_termination = false; ! 1019: ! 1020: int i; ! 1021: int old_regs_s; /* Supervisor status when this function has been called */ ! 1022: ! 1023: old_regs_s = regs.s; ! 1024: regs.s = 1; /* FIXME: Always enable supervisor (needed for SysMem) */ ! 1025: ! 1026: TRY(prb) { ! 1027: /* Use super user root pointer if enabled in TC register and access is in ! 1028: * super user mode, else use cpu root pointer. */ ! 1029: if ((tc_030&TC_ENABLE_SUPERVISOR) && super) { ! 1030: descr[0] = (srp_030>>32)&0xFFFFFFFF; ! 1031: descr[1] = srp_030&0xFFFFFFFF; ! 1032: #if MMU030_REG_DBG_MSG ! 1033: write_log("Supervisor Root Pointer: %08X%08X\n",descr[0],descr[1]); ! 1034: #endif // MMU030_REG_DBG_MSG ! 1035: } else { ! 1036: descr[0] = (crp_030>>32)&0xFFFFFFFF; ! 1037: descr[1] = crp_030&0xFFFFFFFF; ! 1038: #if MMU030_REG_DBG_MSG ! 1039: write_log("CPU Root Pointer: %08X%08X\n",descr[0],descr[1]); ! 1040: #endif ! 1041: } ! 1042: ! 1043: if (descr[0]&RP_ZERO_BITS) { ! 1044: write_log("MMU Warning: Root pointer reserved bits are non-zero!\n"); ! 1045: descr[0] &= (~RP_ZERO_BITS); ! 1046: } ! 1047: ! 1048: /* Check descriptor type of root pointer */ ! 1049: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1050: switch (descr_type) { ! 1051: case DESCR_TYPE_INVALID: ! 1052: write_log("Fatal error: Root pointer is invalid descriptor!\n"); ! 1053: mmu030.status |= MMUSR_INVALID; ! 1054: goto stop_search; ! 1055: case DESCR_TYPE_EARLY_TERM: ! 1056: write_log("Root pointer is early termination page descriptor.\n"); ! 1057: early_termination = true; ! 1058: goto handle_page_descriptor; ! 1059: case DESCR_TYPE_VALID4: ! 1060: next_size = 4; ! 1061: break; ! 1062: case DESCR_TYPE_VALID8: ! 1063: next_size = 8; ! 1064: break; ! 1065: } ! 1066: ! 1067: /* If function code lookup is enabled in TC register use function code as ! 1068: * index for top level table, limit check not required */ ! 1069: ! 1070: if (tc_030&TC_ENABLE_FCL) { ! 1071: write_log("Function code lookup enabled, FC = %i\n", fc); ! 1072: ! 1073: addr_position = (descr_size==4) ? 0 : 1; ! 1074: table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; ! 1075: table_index = fc; /* table index is function code */ ! 1076: write_log("Table FCL at %08X: index = %i, ",table_addr,table_index); ! 1077: ! 1078: /* Fetch next descriptor */ ! 1079: descr_num++; ! 1080: descr_addr[descr_num] = table_addr+(table_index*next_size); ! 1081: ! 1082: if (next_size==4) { ! 1083: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1084: #if MMU030_REG_DBG_MSG ! 1085: write_log("Next descriptor: %08X\n",descr[0]); ! 1086: #endif ! 1087: } else { ! 1088: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1089: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1090: #if MMU030_REG_DBG_MSG ! 1091: write_log("Next descriptor: %08X%08X\n",descr[0],descr[1]); ! 1092: #endif ! 1093: } ! 1094: ! 1095: descr_size = next_size; ! 1096: ! 1097: /* Check descriptor type */ ! 1098: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1099: switch (descr_type) { ! 1100: case DESCR_TYPE_INVALID: ! 1101: write_log("Invalid descriptor!\n"); ! 1102: /* stop table walk */ ! 1103: mmu030.status |= MMUSR_INVALID; ! 1104: goto stop_search; ! 1105: case DESCR_TYPE_EARLY_TERM: ! 1106: #if MMU030_REG_DBG_MSG ! 1107: write_log("Early termination page descriptor!\n"); ! 1108: #endif ! 1109: early_termination = true; ! 1110: goto handle_page_descriptor; ! 1111: case DESCR_TYPE_VALID4: ! 1112: next_size = 4; ! 1113: break; ! 1114: case DESCR_TYPE_VALID8: ! 1115: next_size = 8; ! 1116: break; ! 1117: } ! 1118: } ! 1119: ! 1120: ! 1121: /* Upper level tables */ ! 1122: do { ! 1123: if (descr_num) { /* if not root pointer */ ! 1124: /* Set the updated bit */ ! 1125: if (!level && !(descr[0]&DESCR_U) && !(mmu030.status&MMUSR_SUPER_VIOLATION)) { ! 1126: descr[0] |= DESCR_U; ! 1127: phys_put_long(descr_addr[descr_num], descr[0]); ! 1128: } ! 1129: /* Update status bits */ ! 1130: if (descr_size==8) { ! 1131: if (descr[0]&DESCR_S) ! 1132: mmu030.status |= super ? 0 : MMUSR_SUPER_VIOLATION; ! 1133: } ! 1134: if (descr[0]&DESCR_WP) { ! 1135: mmu030.status |= (descr[0]&DESCR_WP) ? MMUSR_WRITE_PROTECTED : 0; ! 1136: write_protect = true; ! 1137: } ! 1138: ! 1139: /* Check if ptest level is reached */ ! 1140: if (level && (level==descr_num)) { ! 1141: goto stop_search; ! 1142: } ! 1143: } ! 1144: ! 1145: addr_position = (descr_size==4) ? 0 : 1; ! 1146: table_addr = descr[addr_position]&DESCR_TD_ADDR_MASK; ! 1147: table_index = (addr&mmu030.translation.table[t].mask)>>mmu030.translation.table[t].shift; ! 1148: #if MMU030_REG_DBG_MSG ! 1149: write_log("Table %c at %08X: index = %i, ",table_letter[t],table_addr,table_index); ! 1150: #endif // MMU030_REG_DBG_MSG ! 1151: t++; /* Proceed to the next table */ ! 1152: ! 1153: /* Perform limit check */ ! 1154: if (descr_size==8) { ! 1155: limit = (descr[0]&DESCR_LIMIT_MASK)>>16; ! 1156: if ((descr[0]&DESCR_LOWER_MASK) && (table_index<limit)) { ! 1157: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1158: write_log("limit violation (lower limit %i)\n",limit); ! 1159: goto stop_search; ! 1160: } ! 1161: if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) { ! 1162: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1163: write_log("limit violation (upper limit %i)\n",limit); ! 1164: goto stop_search; ! 1165: } ! 1166: } ! 1167: ! 1168: /* Fetch next descriptor */ ! 1169: descr_num++; ! 1170: descr_addr[descr_num] = table_addr+(table_index*next_size); ! 1171: ! 1172: if (next_size==4) { ! 1173: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1174: #if MMU030_REG_DBG_MSG ! 1175: write_log("Next descriptor: %08X\n",descr[0]); ! 1176: #endif ! 1177: } else { ! 1178: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1179: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1180: #if MMU030_REG_DBG_MSG ! 1181: write_log("Next descriptor: %08X%08X\n",descr[0],descr[1]); ! 1182: #endif ! 1183: } ! 1184: ! 1185: descr_size = next_size; ! 1186: ! 1187: /* Check descriptor type */ ! 1188: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1189: switch (descr_type) { ! 1190: case DESCR_TYPE_INVALID: ! 1191: write_log("Invalid descriptor!\n"); ! 1192: /* stop table walk */ ! 1193: mmu030.status |= MMUSR_INVALID; ! 1194: goto stop_search; ! 1195: case DESCR_TYPE_EARLY_TERM: ! 1196: /* go to last level table handling code */ ! 1197: if (t<=mmu030.translation.last_table) { ! 1198: #if MMU030_REG_DBG_MSG ! 1199: write_log("Early termination page descriptor!\n"); ! 1200: #endif ! 1201: early_termination = true; ! 1202: } ! 1203: goto handle_page_descriptor; ! 1204: case DESCR_TYPE_VALID4: ! 1205: next_size = 4; ! 1206: break; ! 1207: case DESCR_TYPE_VALID8: ! 1208: next_size = 8; ! 1209: break; ! 1210: } ! 1211: } while (t<=mmu030.translation.last_table); ! 1212: ! 1213: ! 1214: /* Handle indirect descriptor */ ! 1215: ! 1216: /* Check if ptest level is reached */ ! 1217: if (level && (level==descr_num)) { ! 1218: goto stop_search; ! 1219: } ! 1220: ! 1221: addr_position = (descr_size==4) ? 0 : 1; ! 1222: indirect_addr = descr[addr_position]&DESCR_ID_ADDR_MASK; ! 1223: write_log("Page indirect descriptor at %08X: ",indirect_addr); ! 1224: ! 1225: /* Fetch indirect descriptor */ ! 1226: descr_num++; ! 1227: descr_addr[descr_num] = indirect_addr; ! 1228: ! 1229: if (next_size==4) { ! 1230: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1231: write_log("descr = %08X\n",descr[0]); ! 1232: } else { ! 1233: descr[0] = phys_get_long(descr_addr[descr_num]); ! 1234: descr[1] = phys_get_long(descr_addr[descr_num]+4); ! 1235: write_log("descr = %08X%08X",descr[0],descr[1]); ! 1236: } ! 1237: ! 1238: descr_size = next_size; ! 1239: ! 1240: /* Check descriptor type, only page descriptor is valid */ ! 1241: descr_type = descr[0]&DESCR_TYPE_MASK; ! 1242: if (descr_type!=DESCR_TYPE_PAGE) { ! 1243: mmu030.status |= MMUSR_INVALID; ! 1244: goto stop_search; ! 1245: } ! 1246: ! 1247: handle_page_descriptor: ! 1248: ! 1249: if (descr_num) { /* if not root pointer */ ! 1250: if (!level && !(mmu030.status&MMUSR_SUPER_VIOLATION)) { ! 1251: /* set modified bit */ ! 1252: if (!(descr[0]&DESCR_M) && write && !(mmu030.status&MMUSR_WRITE_PROTECTED)) { ! 1253: descr[0] |= DESCR_M; ! 1254: descr_modified = true; ! 1255: } ! 1256: /* set updated bit */ ! 1257: if (!(descr[0]&DESCR_U)) { ! 1258: descr[0] |= DESCR_U; ! 1259: descr_modified = true; ! 1260: } ! 1261: /* write modified descriptor if neccessary */ ! 1262: if (descr_modified) { ! 1263: phys_put_long(descr_addr[descr_num], descr[0]); ! 1264: } ! 1265: } ! 1266: ! 1267: if ((descr_size==8) && (descr[0]&DESCR_S)) { ! 1268: mmu030.status |= super ? 0 : MMUSR_SUPER_VIOLATION; ! 1269: } ! 1270: ! 1271: /* check if caching is inhibited */ ! 1272: cache_inhibit = descr[0]&DESCR_CI ? true : false; ! 1273: ! 1274: /* check write protection */ ! 1275: if (descr[0]&DESCR_WP) { ! 1276: mmu030.status |= (descr[0]&DESCR_WP) ? MMUSR_WRITE_PROTECTED : 0; ! 1277: write_protect = true; ! 1278: } ! 1279: /* TODO: check if this is handled at correct point (maybe before updating descr?) */ ! 1280: mmu030.status |= (descr[0]&DESCR_M) ? MMUSR_MODIFIED : 0; ! 1281: } ! 1282: ! 1283: /* Check limit using next index field of logical address. ! 1284: * Limit is only checked on early termination. If we are ! 1285: * still at root pointer level, only check limit, if FCL ! 1286: * is disabled. */ ! 1287: if (early_termination) { ! 1288: if (descr_num || !(tc_030&TC_ENABLE_FCL)) { ! 1289: if (descr_size==8) { ! 1290: table_index = (addr&mmu030.translation.table[t].mask)>>mmu030.translation.table[t].shift; ! 1291: limit = (descr[0]&DESCR_LIMIT_MASK)>>16; ! 1292: if ((descr[0]&DESCR_LOWER_MASK) && (table_index<limit)) { ! 1293: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1294: write_log("Limit violation (lower limit %i)\n",limit); ! 1295: goto stop_search; ! 1296: } ! 1297: if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) { ! 1298: mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID); ! 1299: write_log("Limit violation (upper limit %i)\n",limit); ! 1300: goto stop_search; ! 1301: } ! 1302: } ! 1303: } ! 1304: /* Get all unused bits of the logical address table index field. ! 1305: * they are added to the page address */ ! 1306: /* TODO: They should be added via "signed addition". How to? */ ! 1307: do { ! 1308: unused_fields_mask |= mmu030.translation.table[t].mask; ! 1309: t++; ! 1310: } while (t<=mmu030.translation.last_table); ! 1311: page_addr = addr&unused_fields_mask; ! 1312: #if MMU030_REG_DBG_MSG ! 1313: write_log("Logical address unused bits: %08X (mask = %08X)\n", ! 1314: page_addr,unused_fields_mask); ! 1315: #endif ! 1316: } ! 1317: ! 1318: /* Get page address */ ! 1319: addr_position = (descr_size==4) ? 0 : 1; ! 1320: page_addr += (descr[addr_position]&DESCR_PD_ADDR_MASK); ! 1321: #if MMU030_REG_DBG_MSG ! 1322: write_log("Page at %08X\n",page_addr); ! 1323: #endif // MMU030_REG_DBG_MSG ! 1324: ! 1325: stop_search: ! 1326: ; /* Make compiler happy */ ! 1327: } CATCH(prb) { ! 1328: /* We jump to this place, if a bus error occured during table search. ! 1329: * bBusErrorReadWrite is set in m68000.c, M68000_BusError: read = 1 */ ! 1330: if (bBusErrorReadWrite) { ! 1331: descr_num--; ! 1332: } ! 1333: mmu030.status |= (MMUSR_BUS_ERROR|MMUSR_INVALID); ! 1334: write_log("MMU: Bus error while %s descriptor!\n", ! 1335: bBusErrorReadWrite?"reading":"writing"); ! 1336: } ENDTRY ! 1337: ! 1338: regs.s = old_regs_s; ! 1339: ! 1340: /* check if we have to handle ptest */ ! 1341: if (level) { ! 1342: if (mmu030.status&MMUSR_INVALID) { ! 1343: /* these bits are undefined, if the I bit is set: */ ! 1344: mmu030.status &= ~(MMUSR_WRITE_PROTECTED|MMUSR_MODIFIED|MMUSR_SUPER_VIOLATION); ! 1345: } ! 1346: mmu030.status = (mmu030.status&~MMUSR_NUM_LEVELS_MASK) | descr_num; ! 1347: ! 1348: /* If root pointer is page descriptor (descr_num 0), return 0 */ ! 1349: return descr_num ? descr_addr[descr_num] : 0; ! 1350: } ! 1351: ! 1352: /* Find an ATC entry to replace */ ! 1353: /* Search for invalid entry */ ! 1354: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1355: if (!mmu030.atc[i].logical.valid) { ! 1356: break; ! 1357: } ! 1358: } ! 1359: /* If there are no invalid entries, replace first entry ! 1360: * with history bit not set */ ! 1361: if (i == ATC030_NUM_ENTRIES) { ! 1362: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1363: if (!mmu030.atc[i].mru) { ! 1364: break; ! 1365: } ! 1366: } ! 1367: #if MMU030_ATC_DBG_MSG ! 1368: write_log("ATC is full. Replacing entry %i\n", i); ! 1369: #endif ! 1370: } ! 1371: mmu030_atc_handle_history_bit(i); ! 1372: ! 1373: /* Create ATC entry */ ! 1374: mmu030.atc[i].logical.addr = addr & (~mmu030.translation.page.mask); /* delete page index bits */ ! 1375: mmu030.atc[i].logical.fc = fc; ! 1376: mmu030.atc[i].logical.valid = true; ! 1377: mmu030.atc[i].physical.addr = page_addr & (~mmu030.translation.page.mask); /* delete page index bits */ ! 1378: if ((mmu030.status&MMUSR_INVALID) || (mmu030.status&MMUSR_SUPER_VIOLATION)) { ! 1379: mmu030.atc[i].physical.bus_error = true; ! 1380: } else { ! 1381: mmu030.atc[i].physical.bus_error = false; ! 1382: } ! 1383: if (write && !(mmu030.status&MMUSR_SUPER_VIOLATION) && !(mmu030.status&MMUSR_LIMIT_VIOLATION)) { ! 1384: mmu030.atc[i].physical.modified = true; ! 1385: } else { ! 1386: mmu030.atc[i].physical.modified = false; ! 1387: } ! 1388: mmu030.atc[i].physical.cache_inhibit = cache_inhibit; ! 1389: mmu030.atc[i].physical.write_protect = write_protect; ! 1390: ! 1391: #if MMU030_ATC_DBG_MSG ! 1392: write_log("ATC create entry(%i): logical = %08X, physical = %08X, FC = %i\n", i, ! 1393: mmu030.atc[i].logical.addr, mmu030.atc[i].physical.addr, ! 1394: mmu030.atc[i].logical.fc); ! 1395: write_log("ATC create entry(%i): B = %i, CI = %i, WP = %i, M = %i\n", i, ! 1396: mmu030.atc[i].physical.bus_error?1:0, ! 1397: mmu030.atc[i].physical.cache_inhibit?1:0, ! 1398: mmu030.atc[i].physical.write_protect?1:0, ! 1399: mmu030.atc[i].physical.modified?1:0); ! 1400: #endif // MMU030_ATC_DBG_MSG ! 1401: ! 1402: return 0; ! 1403: } ! 1404: ! 1405: /* This function is used for PTEST level 0. */ ! 1406: void mmu030_ptest_atc_search(uaecptr logical_addr, uae_u32 fc, bool write) { ! 1407: int i; ! 1408: mmu030.status = 0; ! 1409: ! 1410: if (mmu030_match_ttr(logical_addr, fc, write)&TT_OK_MATCH) { ! 1411: mmu030.status |= MMUSR_TRANSP_ACCESS; ! 1412: return; ! 1413: } ! 1414: ! 1415: for (i = 0; i < ATC030_NUM_ENTRIES; i++) { ! 1416: if ((mmu030.atc[i].logical.fc == fc) && ! 1417: (mmu030.atc[i].logical.addr == logical_addr) && ! 1418: mmu030.atc[i].logical.valid) { ! 1419: break; ! 1420: } ! 1421: } ! 1422: ! 1423: if (i==ATC030_NUM_ENTRIES) { ! 1424: mmu030.status |= MMUSR_INVALID; ! 1425: return; ! 1426: } ! 1427: ! 1428: mmu030.status |= mmu030.atc[i].physical.bus_error ? (MMUSR_BUS_ERROR|MMUSR_INVALID) : 0; ! 1429: mmu030.status |= mmu030.atc[i].physical.write_protect ? MMUSR_WRITE_PROTECTED : 0; ! 1430: mmu030.status |= mmu030.atc[i].physical.modified ? MMUSR_MODIFIED : 0; ! 1431: if (mmu030.status&MMUSR_INVALID) { ! 1432: mmu030.status &= ~(MMUSR_WRITE_PROTECTED|MMUSR_MODIFIED); ! 1433: } ! 1434: } ! 1435: ! 1436: /* This function is used for PTEST level 1 - 7. */ ! 1437: uae_u32 mmu030_ptest_table_search(uaecptr logical_addr, uae_u32 fc, bool write, int level) { ! 1438: if (mmu030_match_ttr(logical_addr, fc, write)&TT_OK_MATCH) { ! 1439: return 0; ! 1440: } else { ! 1441: return mmu030_table_search(logical_addr, fc, write, level); ! 1442: } ! 1443: } ! 1444: ! 1445: ! 1446: /* Address Translation Cache ! 1447: * ! 1448: * The ATC uses a pseudo-least-recently-used algorithm to keep track of ! 1449: * least recently used entries. They are replaced if the cache is full. ! 1450: * An internal history-bit (MRU-bit) is used to identify these entries. ! 1451: * If an entry is accessed, its history-bit is set to 1. If after that ! 1452: * there are no more entries with zero-bits, all other history-bits are ! 1453: * set to 0. When no more invalid entries are in the ATC, the first entry ! 1454: * with a zero-bit is replaced. ! 1455: * ! 1456: * ! 1457: * Logical Portion (28 bit): ! 1458: * oooo ---- xxxx xxxx xxxx xxxx xxxx xxxx ! 1459: * logical address (most significant 24 bit) ! 1460: * ! 1461: * oooo -xxx ---- ---- ---- ---- ---- ---- ! 1462: * function code ! 1463: * ! 1464: * oooo x--- ---- ---- ---- ---- ---- ---- ! 1465: * valid ! 1466: * ! 1467: * ! 1468: * Physical Portion (28 bit): ! 1469: * oooo ---- xxxx xxxx xxxx xxxx xxxx xxxx ! 1470: * physical address ! 1471: * ! 1472: * oooo ---x ---- ---- ---- ---- ---- ---- ! 1473: * modified ! 1474: * ! 1475: * oooo --x- ---- ---- ---- ---- ---- ---- ! 1476: * write protect ! 1477: * ! 1478: * oooo -x-- ---- ---- ---- ---- ---- ---- ! 1479: * cache inhibit ! 1480: * ! 1481: * oooo x--- ---- ---- ---- ---- ---- ---- ! 1482: * bus error ! 1483: * ! 1484: */ ! 1485: ! 1486: #define ATC030_MASK 0x0FFFFFFF ! 1487: #define ATC030_ADDR_MASK 0x00FFFFFF /* after masking shift 8 (<< 8) */ ! 1488: ! 1489: #define ATC030_LOG_FC 0x07000000 ! 1490: #define ATC030_LOG_V 0x08000000 ! 1491: ! 1492: #define ATC030_PHYS_M 0x01000000 ! 1493: #define ATC030_PHYS_WP 0x02000000 ! 1494: #define ATC030_PHYS_CI 0x04000000 ! 1495: #define ATC030_PHYS_BE 0x08000000 ! 1496: ! 1497: void mmu030_page_fault(uaecptr addr, bool read) { ! 1498: write_log("MMU: page fault (logical addr = %08X)\n", addr); ! 1499: regs.mmu_fault_addr = addr; ! 1500: bBusErrorReadWrite = read; ! 1501: THROW(2); ! 1502: } ! 1503: ! 1504: void mmu030_put_long_atc(uaecptr addr, uae_u32 val, int l) { ! 1505: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1506: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1507: ! 1508: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1509: #if MMU030_ATC_DBG_MSG ! 1510: write_log("ATC match(%i): page addr = %08X, index = %08X (lput %08X)\n", ! 1511: l, physical_addr, page_index, val); ! 1512: #endif ! 1513: physical_addr += page_index; ! 1514: ! 1515: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1516: mmu030_page_fault(addr, 0); ! 1517: return; ! 1518: } ! 1519: ! 1520: phys_put_long(physical_addr, val); ! 1521: } ! 1522: ! 1523: void mmu030_put_word_atc(uaecptr addr, uae_u16 val, int l) { ! 1524: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1525: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1526: ! 1527: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1528: #if MMU030_ATC_DBG_MSG ! 1529: write_log("ATC match(%i): page addr = %08X, index = %08X (wput %04X)\n", ! 1530: l, physical_addr, page_index, val); ! 1531: #endif ! 1532: physical_addr += page_index; ! 1533: ! 1534: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1535: mmu030_page_fault(addr, 0); ! 1536: return; ! 1537: } ! 1538: ! 1539: phys_put_word(physical_addr, val); ! 1540: } ! 1541: ! 1542: void mmu030_put_byte_atc(uaecptr addr, uae_u8 val, int l) { ! 1543: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1544: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1545: ! 1546: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1547: #if MMU030_ATC_DBG_MSG ! 1548: write_log("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n", ! 1549: l, physical_addr, page_index, val); ! 1550: #endif ! 1551: physical_addr += page_index; ! 1552: ! 1553: if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { ! 1554: mmu030_page_fault(addr, 0); ! 1555: return; ! 1556: } ! 1557: ! 1558: phys_put_byte(physical_addr, val); ! 1559: } ! 1560: ! 1561: uae_u32 mmu030_get_long_atc(uaecptr addr, int l) { ! 1562: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1563: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1564: ! 1565: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1566: #if MMU030_ATC_DBG_MSG ! 1567: write_log("ATC match(%i): page addr = %08X, index = %08X (lget %08X)\n", l, ! 1568: physical_addr, page_index, phys_get_long(physical_addr+page_index)); ! 1569: #endif ! 1570: physical_addr += page_index; ! 1571: ! 1572: if (mmu030.atc[l].physical.bus_error) { ! 1573: mmu030_page_fault(addr, 1); ! 1574: return 0; ! 1575: } ! 1576: ! 1577: return phys_get_long(physical_addr); ! 1578: } ! 1579: ! 1580: uae_u16 mmu030_get_word_atc(uaecptr addr, int l) { ! 1581: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1582: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1583: ! 1584: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1585: #if MMU030_ATC_DBG_MSG ! 1586: write_log("ATC match(%i): page addr = %08X, index = %08X (wget %04X)\n", l, ! 1587: physical_addr, page_index, phys_get_word(physical_addr+page_index)); ! 1588: #endif ! 1589: physical_addr += page_index; ! 1590: ! 1591: if (mmu030.atc[l].physical.bus_error) { ! 1592: mmu030_page_fault(addr, 1); ! 1593: return 0; ! 1594: } ! 1595: ! 1596: return phys_get_word(physical_addr); ! 1597: } ! 1598: ! 1599: uae_u8 mmu030_get_byte_atc(uaecptr addr, int l) { ! 1600: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1601: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1602: ! 1603: uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; ! 1604: #if MMU030_ATC_DBG_MSG ! 1605: write_log("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n", l, ! 1606: physical_addr, page_index, phys_get_byte(physical_addr+page_index)); ! 1607: #endif ! 1608: physical_addr += page_index; ! 1609: ! 1610: if (mmu030.atc[l].physical.bus_error) { ! 1611: mmu030_page_fault(addr, 1); ! 1612: return 0; ! 1613: } ! 1614: ! 1615: return phys_get_byte(physical_addr); ! 1616: } ! 1617: ! 1618: ! 1619: /* This function checks if a certain logical address is in the ATC ! 1620: * by comparing the logical address and function code to the values ! 1621: * stored in the ATC entries. If a matching entry is found it sets ! 1622: * the history bit and returns the cache index of the entry. */ ! 1623: int mmu030_logical_is_in_atc(uaecptr addr, uae_u32 fc, bool write) { ! 1624: uaecptr physical_addr = 0; ! 1625: uaecptr logical_addr = 0; ! 1626: uae_u32 addr_mask = ~mmu030.translation.page.mask; ! 1627: uae_u32 page_index = addr & mmu030.translation.page.mask; ! 1628: ! 1629: int i, j; ! 1630: for (i=0; i<ATC030_NUM_ENTRIES; i++) { ! 1631: logical_addr = mmu030.atc[i].logical.addr; ! 1632: /* If actual address matches address in ATC */ ! 1633: if ((addr&addr_mask)==(logical_addr&addr_mask) && ! 1634: (mmu030.atc[i].logical.fc==fc) && ! 1635: mmu030.atc[i].logical.valid) { ! 1636: /* If M bit is set or access is read, return true ! 1637: * else invalidate entry */ ! 1638: if (mmu030.atc[i].physical.modified || !write) { ! 1639: /* Maintain history bit */ ! 1640: mmu030_atc_handle_history_bit(i); ! 1641: return i; ! 1642: } else { ! 1643: mmu030.atc[i].logical.valid = false; ! 1644: } ! 1645: } ! 1646: } ! 1647: return ATC030_NUM_ENTRIES; ! 1648: } ! 1649: ! 1650: void mmu030_atc_handle_history_bit(int entry_num) { ! 1651: int j; ! 1652: mmu030.atc[entry_num].mru = 1; ! 1653: for (j=0; j<ATC030_NUM_ENTRIES; j++) { ! 1654: if (!mmu030.atc[j].mru) ! 1655: break; ! 1656: } ! 1657: /* If there are no more zero-bits, reset all */ ! 1658: if (j==ATC030_NUM_ENTRIES) { ! 1659: for (j=0; j<ATC030_NUM_ENTRIES; j++) { ! 1660: mmu030.atc[j].mru = 0; ! 1661: } ! 1662: mmu030.atc[entry_num].mru = 1; ! 1663: #if MMU030_ATC_DBG_MSG ! 1664: write_log("ATC: No more history zero-bits. Reset all.\n"); ! 1665: #endif ! 1666: } ! 1667: } ! 1668: ! 1669: ! 1670: /* Memory access functions: ! 1671: * If the address matches one of the transparent translation registers ! 1672: * use it directly as physical address, else check ATC for the ! 1673: * logical address. If the logical address is not resident in the ATC ! 1674: * create a new ATC entry and then look up the physical address. ! 1675: */ ! 1676: ! 1677: void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc, int size) { ! 1678: ! 1679: // addr,super,write ! 1680: if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { ! 1681: phys_put_long(addr,val); ! 1682: return; ! 1683: } ! 1684: ! 1685: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1686: ! 1687: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1688: mmu030_put_long_atc(addr, val, atc_line_num); ! 1689: } else { ! 1690: mmu030_table_search(addr,fc,true,0); ! 1691: mmu030_put_long_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); ! 1692: } ! 1693: } ! 1694: ! 1695: void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc, int size) { ! 1696: ! 1697: // addr,super,write ! 1698: if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { ! 1699: phys_put_word(addr,val); ! 1700: return; ! 1701: } ! 1702: ! 1703: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1704: ! 1705: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1706: mmu030_put_word_atc(addr, val, atc_line_num); ! 1707: } else { ! 1708: mmu030_table_search(addr, fc, true, 0); ! 1709: mmu030_put_word_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); ! 1710: } ! 1711: } ! 1712: ! 1713: void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc, int size) { ! 1714: ! 1715: // addr,super,write ! 1716: if ((!mmu030.enabled) || (mmu030_match_ttr(addr, fc, true)&TT_OK_MATCH) || (fc==7)) { ! 1717: phys_put_byte(addr,val); ! 1718: return; ! 1719: } ! 1720: ! 1721: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); ! 1722: ! 1723: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1724: mmu030_put_byte_atc(addr, val, atc_line_num); ! 1725: } else { ! 1726: mmu030_table_search(addr, fc, true, 0); ! 1727: mmu030_put_byte_atc(addr, val, mmu030_logical_is_in_atc(addr,fc,true)); ! 1728: } ! 1729: } ! 1730: ! 1731: uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc, int size) { ! 1732: ! 1733: // addr,super,write ! 1734: if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { ! 1735: return phys_get_long(addr); ! 1736: } ! 1737: ! 1738: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 1739: ! 1740: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1741: return mmu030_get_long_atc(addr, atc_line_num); ! 1742: } else { ! 1743: mmu030_table_search(addr, fc, false, 0); ! 1744: return mmu030_get_long_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); ! 1745: } ! 1746: } ! 1747: ! 1748: uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc, int size) { ! 1749: ! 1750: // addr,super,write ! 1751: if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { ! 1752: return phys_get_word(addr); ! 1753: } ! 1754: ! 1755: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 1756: ! 1757: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1758: return mmu030_get_word_atc(addr, atc_line_num); ! 1759: } else { ! 1760: mmu030_table_search(addr, fc, false, 0); ! 1761: return mmu030_get_word_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); ! 1762: } ! 1763: } ! 1764: ! 1765: uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc, int size) { ! 1766: ! 1767: // addr,super,write ! 1768: if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { ! 1769: return phys_get_byte(addr); ! 1770: } ! 1771: ! 1772: int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); ! 1773: ! 1774: if (atc_line_num<ATC030_NUM_ENTRIES) { ! 1775: return mmu030_get_byte_atc(addr, atc_line_num); ! 1776: } else { ! 1777: mmu030_table_search(addr, fc, false, 0); ! 1778: return mmu030_get_byte_atc(addr, mmu030_logical_is_in_atc(addr,fc,false)); ! 1779: } ! 1780: } ! 1781: ! 1782: ! 1783: uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc) ! 1784: { ! 1785: uae_u16 res; ! 1786: ! 1787: res = (uae_u16)mmu030_get_byte(addr, fc, sz_word) << 8; ! 1788: SAVE_EXCEPTION; ! 1789: TRY(prb) { ! 1790: res |= mmu030_get_byte(addr + 1, fc, sz_word); ! 1791: RESTORE_EXCEPTION; ! 1792: } ! 1793: CATCH(prb) { ! 1794: RESTORE_EXCEPTION; ! 1795: // regs.mmu_fault_addr = addr; ! 1796: regs.mmu_ssw |= MMU_SSW_MA; ! 1797: THROW_AGAIN(prb); ! 1798: } ENDTRY ! 1799: return res; ! 1800: } ! 1801: ! 1802: uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) ! 1803: { ! 1804: uae_u32 res; ! 1805: ! 1806: if (likely(!(addr & 1))) { ! 1807: res = (uae_u32)mmu030_get_word(addr, fc, sz_long) << 16; ! 1808: SAVE_EXCEPTION; ! 1809: TRY(prb) { ! 1810: res |= mmu030_get_word(addr + 2, fc, sz_long); ! 1811: RESTORE_EXCEPTION; ! 1812: } ! 1813: CATCH(prb) { ! 1814: RESTORE_EXCEPTION; ! 1815: // regs.mmu_fault_addr = addr; ! 1816: regs.mmu_ssw |= MMU_SSW_MA; ! 1817: THROW_AGAIN(prb); ! 1818: } ENDTRY ! 1819: } else { ! 1820: res = (uae_u32)mmu030_get_byte(addr, fc, sz_long) << 8; ! 1821: SAVE_EXCEPTION; ! 1822: TRY(prb) { ! 1823: res = (res | mmu030_get_byte(addr + 1, fc, sz_long)) << 8; ! 1824: res = (res | mmu030_get_byte(addr + 2, fc, sz_long)) << 8; ! 1825: res |= mmu030_get_byte(addr + 3, fc, sz_long); ! 1826: RESTORE_EXCEPTION; ! 1827: } ! 1828: CATCH(prb) { ! 1829: RESTORE_EXCEPTION; ! 1830: // regs.mmu_fault_addr = addr; ! 1831: regs.mmu_ssw |= MMU_SSW_MA; ! 1832: THROW_AGAIN(prb); ! 1833: } ENDTRY ! 1834: } ! 1835: return res; ! 1836: } ! 1837: ! 1838: ! 1839: void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc) ! 1840: { ! 1841: SAVE_EXCEPTION; ! 1842: TRY(prb) { ! 1843: if (likely(!(addr & 1))) { ! 1844: mmu030_put_word(addr, val >> 16, fc, sz_long); ! 1845: mmu030_put_word(addr + 2, val, fc, sz_long); ! 1846: } else { ! 1847: mmu030_put_byte(addr, val >> 24, fc, sz_long); ! 1848: mmu030_put_byte(addr + 1, val >> 16, fc, sz_long); ! 1849: mmu030_put_byte(addr + 2, val >> 8, fc, sz_long); ! 1850: mmu030_put_byte(addr + 3, val, fc, sz_long); ! 1851: } ! 1852: RESTORE_EXCEPTION; ! 1853: } ! 1854: CATCH(prb) { ! 1855: RESTORE_EXCEPTION; ! 1856: regs.wb3_data = val; ! 1857: if (regs.mmu_fault_addr != addr) { ! 1858: // regs.mmu_fault_addr = addr; ! 1859: regs.mmu_ssw |= MMU_SSW_MA; ! 1860: } ! 1861: THROW_AGAIN(prb); ! 1862: } ENDTRY ! 1863: } ! 1864: ! 1865: void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc) ! 1866: { ! 1867: SAVE_EXCEPTION; ! 1868: TRY(prb) { ! 1869: mmu030_put_byte(addr, val >> 8, fc, sz_word); ! 1870: mmu030_put_byte(addr + 1, val, fc, sz_word); ! 1871: RESTORE_EXCEPTION; ! 1872: } ! 1873: CATCH(prb) { ! 1874: RESTORE_EXCEPTION; ! 1875: regs.wb3_data = val; ! 1876: if (regs.mmu_fault_addr != addr) { ! 1877: // regs.mmu_fault_addr = addr; ! 1878: regs.mmu_ssw |= MMU_SSW_MA; ! 1879: } ! 1880: THROW_AGAIN(prb); ! 1881: } ENDTRY ! 1882: } ! 1883: ! 1884: ! 1885: /* MMU Reset */ ! 1886: void mmu030_reset(int hardreset) ! 1887: { ! 1888: /* A CPU reset causes the E-bits of TC and TT registers to be zeroed. */ ! 1889: mmu030.enabled = false; ! 1890: tc_030 &= ~TC_ENABLE_TRANSLATION; ! 1891: tt0_030 &= ~TT_ENABLE; ! 1892: tt1_030 &= ~TT_ENABLE; ! 1893: if (hardreset) { ! 1894: srp_030 = crp_030 = 0; ! 1895: tt0_030 = tt1_030 = tc_030 = 0; ! 1896: mmusr_030 = 0; ! 1897: mmu030_flush_atc_all(); ! 1898: } ! 1899: } ! 1900: ! 1901: ! 1902: void m68k_do_rte_mmu030 (uaecptr a7) ! 1903: { ! 1904: uae_u16 ssr = get_word_mmu030 (a7 + 8 + 4); ! 1905: if (ssr & MMU_SSW_CT) { ! 1906: uaecptr src_a7 = a7 + 8 - 8; ! 1907: uaecptr dst_a7 = a7 + 8 + 52; ! 1908: put_word_mmu030 (dst_a7 + 0, get_word_mmu030 (src_a7 + 0)); ! 1909: put_long_mmu030 (dst_a7 + 2, get_long_mmu030 (src_a7 + 2)); ! 1910: // skip this word ! 1911: put_long_mmu030 (dst_a7 + 8, get_long_mmu030 (src_a7 + 8)); ! 1912: } ! 1913: } ! 1914: ! 1915: void flush_mmu030 (uaecptr addr, int n) ! 1916: { ! 1917: } ! 1918: ! 1919: void m68k_do_rts_mmu030 (void) ! 1920: { ! 1921: m68k_setpc (get_long_mmu030 (m68k_areg (regs, 7))); ! 1922: m68k_areg (regs, 7) += 4; ! 1923: } ! 1924: ! 1925: void m68k_do_bsr_mmu030 (uaecptr oldpc, uae_s32 offset) ! 1926: { ! 1927: put_long_mmu030 (m68k_areg (regs, 7) - 4, oldpc); ! 1928: m68k_areg (regs, 7) -= 4; ! 1929: m68k_incpci (offset); ! 1930: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.