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