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