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