1 /* Emulation of MC68030 MMU
2 * This code has been written for Previous - a NeXT Computer emulator
3 *
4 * This file is distributed under the GNU General Public License, version 2
5 * or at your option any later version. Read the file gpl.txt for details.
6 *
7 *
8 * Written by Andreas Grabher
9 *
10 * Many thanks go to Thomas Huth and the Hatari community for helping
11 * to test and debug this code!
12 *
13 *
14 * Release notes:
15 * 01-09-2012: First release
16 * 29-09-2012: Improved function code handling
17 * 16-11-2012: Improved exception handling
18 *
19 *
20 * - Check if read-modify-write operations are correctly detected for
21 * handling transparent access (see TT matching functions)
22 * - If possible, test mmu030_table_search with all kinds of translations
23 * (early termination, invalid descriptors, bus errors, indirect
24 * descriptors, PTEST in different levels, etc).
25 * - Check which bits of an ATC entry or the status register should be set
26 * and which should be un-set, if an invalid translation occurs.
27 * - Handle cache inhibit bit when accessing ATC entries
28 */
29
30
31 #include "sysconfig.h"
32 #include "sysdeps.h"
33
34 #ifdef WINUAE_FOR_HATARI
35 #include "main.h"
36 #include "hatari-glue.h"
37 #include "log.h"
38 #endif
39
40 #include "options_cpu.h"
41 #include "memory.h"
42 #include "newcpu.h"
43 #include "debug.h"
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
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];
75 uae_u32 mmu030_data_buffer_out;
76 uae_u32 mmu030_disp_store[2];
77 uae_u32 mmu030_fmovem_store[2];
78 uae_u8 mmu030_cache_state;
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);
84
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
97 /* for debugging messages */
98 char table_letter[4] = {'A','B','C','D'};
99
100 static const uae_u32 mmu030_size[3] = { MMU030_SSW_SIZE_B, MMU030_SSW_SIZE_W, MMU030_SSW_SIZE_L };
101
102 uae_u64 srp_030, crp_030;
103 uae_u32 tt0_030, tt1_030, tc_030;
104 uae_u16 mmusr_030;
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;
114 uae_u8 cache_inhibit;
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 */
129 static struct {
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;
140 uae_u32 imask;
141 uae_u32 size;
142 uae_u32 size3m;
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
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
171
172 } mmu030;
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
214 /* -- ATC flushing functions -- */
215
mmu030_flush_cache(uaecptr addr)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;
227 for (int i = 0; i < MMUFASTCACHE_ENTRIES030; i++) {
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 */
mmu030_flush_atc_fc(uae_u32 fc_base,uae_u32 fc_mask)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 */
mmu030_flush_atc_page_fc(uaecptr logical_addr,uae_u32 fc_base,uae_u32 fc_mask)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 */
mmu030_flush_atc_page(uaecptr logical_addr)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 }
285
286 /* This function flushes all ATC entries */
mmu030_flush_atc_all(void)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 }
297
298 /* -- Helper function for MMU instructions -- */
mmu_op30_helper_get_fc(uae_u16 next)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
317 /* -- MMU instructions -- */
318
mmu_op30_invea(uae_u32 opcode)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
325 if (eamode == 0 || eamode == 1 || eamode == 3 || eamode == 4 || (eamode == 7 && rreg > 1))
326 return true;
327 return false;
328 }
329
mmu_op30_pmove(uaecptr pc,uae_u32 opcode,uae_u16 next,uaecptr extra)330 bool mmu_op30_pmove (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra)
331 {
332 int preg = (next >> 10) & 31;
333 int rw = (next >> 9) & 1;
334 int fd = (next >> 8) & 1;
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
346 #if MMU030_OP_DBG_MSG
347 switch (preg) {
348 case 0x10:
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());
351 break;
352 case 0x12:
353 write_log(_T("PMOVE: %s SRP %08X%08X PC=%08x\n"), rw ? _T("read") : _T("write"),
354 rw?(uae_u32)(srp_030>>32)&0xFFFFFFFF:x_get_long(extra),
355 rw?(uae_u32)srp_030&0xFFFFFFFF:x_get_long(extra+4), m68k_getpc());
356 break;
357 case 0x13:
358 write_log(_T("PMOVE: %s CRP %08X%08X PC=%08x\n"), rw ? _T("read") : _T("write"),
359 rw?(uae_u32)(crp_030>>32)&0xFFFFFFFF:x_get_long(extra),
360 rw?(uae_u32)crp_030&0xFFFFFFFF:x_get_long(extra+4), m68k_getpc());
361 break;
362 case 0x18:
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());
365 break;
366 case 0x02:
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());
369 break;
370 case 0x03:
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());
373 break;
374 default:
375 break;
376 }
377 if (!fd && !rw && !(preg==0x18)) {
378 write_log(_T("PMOVE: flush ATC\n"));
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);
389 if (mmu030_decode_tc(tc_030, true))
390 return true;
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);
400 if (mmu030_decode_rp(srp_030))
401 return true;
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);
411 if (mmu030_decode_rp(crp_030))
412 return true;
413 }
414 break;
415 case 0x18: // MMUSR
416 if (fd) {
417 // FD must be always zero when MMUSR read or write
418 return true;
419 }
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:
442 write_log (_T("Bad PMOVE at %08x\n"),m68k_getpc());
443 return true;
444 }
445
446 if (!fd && !rw && preg != 0x18) {
447 mmu030_flush_atc_all();
448 }
449 tt_enabled = (tt0_030 & TT_ENABLE) || (tt1_030 & TT_ENABLE);
450 return false;
451 }
452
mmu_op30_ptest(uaecptr pc,uae_u32 opcode,uae_u16 next,uaecptr extra)453 bool mmu_op30_ptest (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra)
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;
464
465 if (mmu_op30_invea(opcode))
466 return true;
467 if (!level && a) {
468 write_log(_T("PTEST: Bad instruction causing F-line unimplemented instruction exception!\n"));
469 return true;
470 }
471
472 #if MMU030_OP_DBG_MSG
473 write_log(_T("PTEST%c: addr = %08X, fc = %i, level = %i, PC=%08x, "),
474 rw?'R':'W', extra, fc, level, m68k_getpc());
475 if (a) {
476 write_log(_T("return descriptor to register A%i\n"), areg);
477 } else {
478 write_log(_T("do not return descriptor\n"));
479 }
480 #endif
481
482 if (!level) {
483 mmu030_ptest_atc_search(extra, fc, write);
484 } else {
485 ret = mmu030_table_search(extra, fc, write, level);
486 if (a) {
487 m68k_areg (regs, areg) = ret;
488 }
489 }
490 mmusr_030 = mmu030.status;
491
492 #if MMU030_OP_DBG_MSG
493 write_log(_T("PTEST status: %04X, B = %i, L = %i, S = %i, W = %i, I = %i, M = %i, T = %i, N = %i\n"),
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
499 return false;
500 }
501
mmu_op30_pload(uaecptr pc,uae_u32 opcode,uae_u16 next,uaecptr extra)502 static bool mmu_op30_pload (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra)
503 {
504 int rw = (next >> 9) & 1;
505 int unused = (next & (0x100 | 0x80 | 0x40 | 0x20));
506 uae_u32 fc = mmu_op30_helper_get_fc(next);
507 bool write = rw ? false : true;
508
509 if (mmu_op30_invea(opcode))
510 return true;
511 if (unused)
512 return true;
513
514 #if 0
515 write_log (_T("PLOAD%c: Create ATC entry for %08X, FC = %i\n"), write?'W':'R', extra, fc);
516 #endif
517
518 mmu030_flush_atc_page(extra);
519 mmu030_table_search(extra, fc, write, 0);
520 return false;
521 }
522
mmu_op30_pflush(uaecptr pc,uae_u32 opcode,uae_u16 next,uaecptr extra)523 bool mmu_op30_pflush (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra)
524 {
525 uae_u16 mode = (next >> 8) & 31;
526 uae_u32 fc_mask = (uae_u32)(next & 0x00E0) >> 5;
527 uae_u32 fc_base = mmu_op30_helper_get_fc(next);
528 uae_u32 fc_bits = next & 0x7f;
529
530 #if 0
531 switch (mode) {
532 case 0x1:
533 write_log(_T("PFLUSH: Flush all entries\n"));
534 break;
535 case 0x4:
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);
538 break;
539 case 0x6:
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);
543 break;
544 default:
545 break;
546 }
547 #endif
548
549 switch (mode) {
550 case 0x00: // PLOAD W
551 case 0x02: // PLOAD R
552 return mmu_op30_pload(pc, opcode, next, extra);
553 case 0x04:
554 if (fc_bits)
555 return true;
556 mmu030_flush_atc_all();
557 break;
558 case 0x10:
559 mmu030_flush_atc_fc(fc_base, fc_mask);
560 break;
561 case 0x18:
562 if (mmu_op30_invea(opcode))
563 return true;
564 mmu030_flush_atc_page_fc(extra, fc_base, fc_mask);
565 break;
566 default:
567 write_log(_T("PFLUSH %04x-%04x ERROR: bad mode! (%i)\n"), opcode, next, mode);
568 return true;
569 }
570 return false;
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
605 /* TT comparison results */
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
mmu030_decode_tt(uae_u32 TT)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
620 #if 0
621 if ((TT&TT_ENABLE) && !(TT&TT_RWM)) {
622 write_log(_T("MMU Warning: Transparent translation of read-modify-write cycle is not correctly handled!\n"));
623 }
624 #endif
625
626 #if MMU030_REG_DBG_MSG /* enable or disable debugging messages */
627 write_log(_T("\n"));
628 write_log(_T("TRANSPARENT TRANSLATION: %08X\n"), TT);
629 write_log(_T("\n"));
630
631 write_log(_T("TT: transparent translation "));
632 if (TT&TT_ENABLE) {
633 write_log(_T("enabled\n"));
634 } else {
635 write_log(_T("disabled\n"));
636 return ret;
637 }
638
639 write_log(_T("TT: caching %s\n"), (TT&TT_CI) ? _T("inhibited") : _T("enabled"));
640 write_log(_T("TT: read-modify-write "));
641 if (TT&TT_RWM) {
642 write_log(_T("enabled\n"));
643 } else {
644 write_log(_T("disabled (%s only)\n"), (TT&TT_RW) ? _T("read") : _T("write"));
645 }
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"));
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
mmu030_do_match_ttr(uae_u32 tt,TT_info comp,uaecptr addr,uae_u32 fc,bool write)665 static int mmu030_do_match_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc, bool write)
666 {
667 if (tt & TT_ENABLE) { /* transparent translation enabled */
668
669 /* Compare actual function code with function code base using mask */
670 if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) {
671
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
mmu030_do_match_lrmw_ttr(uae_u32 tt,TT_info comp,uaecptr addr,uae_u32 fc)689 static int mmu030_do_match_lrmw_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc)
690 {
691 if ((tt & TT_ENABLE) && (tt & TT_RWM)) { /* transparent translation enabled */
692
693 /* Compare actual function code with function code base using mask */
694 if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) {
695
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
705 /* This function compares the address with both transparent
706 * translation registers and returns the result */
707
mmu030_match_ttr(uaecptr addr,uae_u32 fc,bool write)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
mmu030_match_ttr_access(uaecptr addr,uae_u32 fc,bool write)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 */
mmu030_match_lrmw_ttr_access(uaecptr addr,uae_u32 fc)737 static int mmu030_match_lrmw_ttr_access(uaecptr addr, uae_u32 fc)
738 {
739 int tt0, tt1;
740
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 }
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
mmu030_do_fake_prefetch(void)800 static void mmu030_do_fake_prefetch(void)
801 {
802 if (currprefs.cpu_compatible)
803 return;
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 }
824
mmu030_decode_tc(uae_u32 TC,bool check)825 bool mmu030_decode_tc(uae_u32 TC, bool check)
826 {
827 #if MMU_IPAGECACHE030
828 mmu030.mmu030_last_logical_address = 0xffffffff;
829 #endif
830
831 if (currprefs.mmu_ec)
832 TC &= ~TC_ENABLE_TRANSLATION;
833 /* Set MMU condition */
834 if (TC & TC_ENABLE_TRANSLATION) {
835 if (!mmu030.enabled && check)
836 mmu030_do_fake_prefetch();
837 mmu030.enabled = true;
838 } else {
839 if (mmu030.enabled) {
840 mmu030_do_fake_prefetch();
841 write_log(_T("MMU disabled PC=%08x\n"), M68K_GETPC);
842 }
843 mmu030.enabled = false;
844 return false;
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;
860 mmu030.translation.page.size3m = mmu030.translation.page.size - 3;
861 mmu030.translation.init_shift = (TC & TC_IS_MASK) >> 16;
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"),
868 1<<mmu030.translation.page.size);
869 Exception(56); /* MMU Configuration Exception */
870 return true;
871 }
872 mmu030.translation.page.mask = regs.mmu_page_size - 1;
873 mmu030.translation.page.imask = ~mmu030.translation.page.mask;
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]) {
906 write_log(_T("MMU Configuration Exception: Bad value in TC register! (no first table index defined)\n"));
907 } else if ((TI_bits[0]<2) && !TI_bits[1]) {
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"));
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) {
917 write_log(_T("MMU Configuration Exception: Bad value in TC register! (bad sum)\n"));
918 Exception(56); /* MMU Configuration Exception */
919 return true;
920 }
921
922 #if MMU030_REG_DBG_MSG /* enable or disable debugging output */
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"));
934
935 for (i = 0; i <= mmu030.translation.last_table; i++) {
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);
937 }
938
939 write_log(_T("TC: Page: mask = %08X\n"), mmu030.translation.page.mask);
940 write_log(_T("\n"));
941
942 write_log(_T("TC: Last Table: %c\n"), table_letter[mmu030.translation.last_table]);
943 write_log(_T("\n"));
944 #endif
945 return false;
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
mmu030_decode_rp(uae_u64 RP)977 bool mmu030_decode_rp(uae_u64 RP) {
978
979 uae_u8 descriptor_type = (RP & RP_DESCR_MASK) >> 32;
980 if (!descriptor_type) { /* If descriptor type is invalid */
981 write_log(_T("MMU Configuration Exception: Root Pointer is invalid!\n"));
982 Exception(56); /* MMU Configuration Exception */
983 return true;
984 }
985 return false;
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
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"));
994
995 write_log(_T("RP: descriptor type = %i "), descriptor_type);
996 switch (descriptor_type) {
997 case 0:
998 write_log(_T("(invalid descriptor)\n"));
999 break;
1000 case 1:
1001 write_log(_T("(early termination page descriptor)\n"));
1002 break;
1003 case 2:
1004 write_log(_T("(valid 4 byte descriptor)\n"));
1005 break;
1006 case 3:
1007 write_log(_T("(valid 8 byte descriptor)\n"));
1008 break;
1009 }
1010
1011 write_log(_T("RP: %s limit = %i\n"), (RP&RP_LOWER_MASK) ? _T("lower") : _T("upper"), table_limit);
1012
1013 write_log(_T("RP: first table address = %08X\n"), first_addr);
1014 write_log(_T("\n"));
1015 #endif
1016 }
1017
mmu030_atc_handle_history_bit(int entry_num)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
desc_put_long(uaecptr addr,uae_u32 v)1037 static void desc_put_long(uaecptr addr, uae_u32 v)
1038 {
1039 x_phys_put_long(addr, v);
1040 }
desc_get_long(uaecptr addr)1041 static uae_u32 desc_get_long(uaecptr addr)
1042 {
1043 return x_phys_get_long(addr);
1044 }
desc_get_quad(uaecptr addr,uae_u32 * descr)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
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 */
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 ---- ---- ---- ---- | ---- ---- ---- ---- ---- ---- ---- ----
1184 * limit (only used with early termination page descriptor)
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
mmu030_table_search(uaecptr addr,uae_u32 fc,bool write,int level)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;
1241
1242 // Always use supervisor mode to access descriptors
1243 old_s = regs.s;
1244 regs.s = 1;
1245
1246 TRY(prb) {
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
1253 write_log(_T("Supervisor Root Pointer: %08X%08X\n"),descr[0],descr[1]);
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
1259 write_log(_T("CPU Root Pointer: %08X%08X\n"),descr[0],descr[1]);
1260 #endif
1261 }
1262
1263 if (descr[0]&RP_ZERO_BITS) {
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);
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:
1274 write_log(_T("Fatal error: Root pointer is invalid descriptor!\n"));
1275 mmu030.status |= MMUSR_INVALID;
1276 goto stop_search;
1277 case DESCR_TYPE_EARLY_TERM:
1278 write_log(_T("Root pointer is early termination page descriptor.\n"));
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) {
1293 write_log(_T("Function code lookup enabled, FC = %i\n"), fc);
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 */
1298 write_log(_T("Table FCL at %08X: index = %i, "),table_addr,table_index);
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) {
1305 descr[0] = desc_get_long(descr_addr[descr_num]);
1306 #if MMU030_REG_DBG_MSG
1307 write_log(_T("Next descriptor: %08X\n"),descr[0]);
1308 #endif
1309 } else {
1310 desc_get_quad(descr_addr[descr_num], descr);
1311 #if MMU030_REG_DBG_MSG
1312 write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]);
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:
1322 write_log(_T("Invalid descriptor!\n"));
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
1328 write_log(_T("Early termination page descriptor!\n"));
1329 #endif
1330 early_termination = true;
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 */
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
1353 /* Set the updated bit */
1354 if (!level && !(descr[0]&DESCR_U) && !super_violation) {
1355 descr[0] |= DESCR_U;
1356 desc_put_long(descr_addr[descr_num], descr[0]);
1357 }
1358
1359 /* Update status bits */
1360 mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0;
1361 mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0;
1362
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
1373 write_log(_T("Table %c at %08X: index = %i, "),table_letter[t],table_addr,table_index);
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);
1382 #if MMU030_REG_DBG_MSG
1383 write_log(_T("limit violation (lower limit %i)\n"),limit);
1384 #endif
1385 goto stop_search;
1386 }
1387 if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) {
1388 mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID);
1389 #if MMU030_REG_DBG_MSG
1390 write_log(_T("limit violation (upper limit %i)\n"),limit);
1391 #endif
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) {
1401 descr[0] = desc_get_long(descr_addr[descr_num]);
1402 #if MMU030_REG_DBG_MSG
1403 write_log(_T("Next descriptor: %08X\n"),descr[0]);
1404 #endif
1405 } else {
1406 desc_get_quad(descr_addr[descr_num], descr);
1407 #if MMU030_REG_DBG_MSG
1408 write_log(_T("Next descriptor: %08X%08X\n"),descr[0],descr[1]);
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:
1418 #if MMU030_REG_DBG_MSG
1419 write_log(_T("Invalid descriptor!\n"));
1420 #endif
1421 /* stop table walk */
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
1428 write_log(_T("Early termination page descriptor!\n"));
1429 #endif
1430 early_termination = true;
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;
1452 #if MMU030_REG_DBG_MSG
1453 write_log(_T("Page indirect descriptor at %08X: "),indirect_addr);
1454 #endif
1455
1456 /* Fetch indirect descriptor */
1457 descr_num++;
1458 descr_addr[descr_num] = indirect_addr;
1459
1460 if (next_size==4) {
1461 descr[0] = desc_get_long(descr_addr[descr_num]);
1462 #if MMU030_REG_DBG_MSG
1463 write_log(_T("descr = %08X\n"),descr[0]);
1464 #endif
1465 } else {
1466 desc_get_quad(descr_addr[descr_num], descr);
1467 #if MMU030_REG_DBG_MSG
1468 write_log(_T("descr = %08X%08X"),descr[0],descr[1]);
1469 #endif
1470 }
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 */
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) {
1493 /* set modified bit */
1494 if (!(descr[0]&DESCR_M) && write && !write_protected) {
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 }
1503 /* write modified descriptor if necessary */
1504 if (descr_modified) {
1505 desc_put_long(descr_addr[descr_num], descr[0]);
1506 }
1507 }
1508
1509 /* update status bits */
1510 mmu030.status |= super_violation ? MMUSR_SUPER_VIOLATION : 0;
1511 mmu030.status |= write_protected ? MMUSR_WRITE_PROTECTED : 0;
1512
1513 /* check if caching is inhibited */
1514 cache_inhibit = (descr[0]&DESCR_CI) ? CACHE_DISABLE_MMU : CACHE_ENABLE_ALL;
1515
1516 /* check for the modified bit and set it in the status register */
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);
1531 #if MMU030_REG_DBG_MSG
1532 write_log(_T("Limit violation (lower limit %i)\n"),limit);
1533 #endif
1534 goto stop_search;
1535 }
1536 if (!(descr[0]&DESCR_LOWER_MASK) && (table_index>limit)) {
1537 mmu030.status |= (MMUSR_LIMIT_VIOLATION|MMUSR_INVALID);
1538 #if MMU030_REG_DBG_MSG
1539 write_log(_T("Limit violation (upper limit %i)\n"),limit);
1540 #endif
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 */
1547 /* TODO: They should be added via "unsigned addition". How to? */
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
1554 write_log(_T("Logical address unused bits: %08X (mask = %08X)\n"),
1555 page_addr,unused_fields_mask);
1556 #endif
1557 }
1558
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
1563 write_log(_T("Page at %08X\n"),page_addr);
1564 #endif // MMU030_REG_DBG_MSG
1565
1566 stop_search:
1567 ; /* Make compiler happy */
1568 } CATCH(prb) {
1569 /* We jump to this place, if a bus error occurred during table search.
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);
1575 write_log(_T("MMU: Bus error while %s descriptor!\n"),
1576 bBusErrorReadWrite?_T("reading"):_T("writing"));
1577 } ENDTRY;
1578
1579 // Restore original supervisor state
1580 regs.s = old_s;
1581
1582 /* check if we have to handle ptest */
1583 if (level) {
1584 /* Note: wp, m and sv bits are undefined if the invalid bit is set */
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 }
1606 #if MMU030_REG_DBG_MSG
1607 write_log(_T("ATC is full. Replacing entry %i\n"), i);
1608 #endif
1609 }
1610 if (i >= ATC030_NUM_ENTRIES) {
1611 i = 0;
1612 write_log (_T("ATC entry not found!!!\n"));
1613 }
1614
1615 mmu030_atc_handle_history_bit(i);
1616
1617 /* Create ATC entry */
1618 mmu030.atc[i].logical.addr = addr & mmu030.translation.page.imask; /* delete page index bits */
1619 mmu030.atc[i].logical.fc = fc;
1620 mmu030.atc[i].logical.valid = true;
1621 mmu030.atc[i].physical.addr = page_addr & mmu030.translation.page.imask; /* delete page index bits */
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;
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;
1630
1631 mmu030_flush_cache(mmu030.atc[i].logical.addr);
1632
1633 #if MMU030_ATC_DBG_MSG
1634 write_log(_T("ATC create entry(%i): logical = %08X, physical = %08X, FC = %i\n"), i,
1635 mmu030.atc[i].logical.addr, mmu030.atc[i].physical.addr,
1636 mmu030.atc[i].logical.fc);
1637 write_log(_T("ATC create entry(%i): B = %i, CI = %i, WP = %i, M = %i\n"), i,
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. */
mmu030_ptest_atc_search(uaecptr logical_addr,uae_u32 fc,bool write)1648 static void mmu030_ptest_atc_search(uaecptr logical_addr, uae_u32 fc, bool write) {
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;
1671 /* Note: write protect and modified bits are undefined if the invalid bit is set */
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
1727 #if MMUDEBUG
dump_opcode(uae_u16 opcode)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
mmu030_page_fault(uaecptr addr,bool read,int flags,uae_u32 fc)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;
1762 fc = regs.mmu_ssw & MMU030_SSW_FC_MASK;
1763 flags = regs.mmu_ssw & ~(MMU030_SSW_FC | MMU030_SSW_RC | MMU030_SSW_FB | MMU030_SSW_RB | MMU030_SSW_RW | 7);
1764 }
1765 regs.mmu_fault_addr = addr;
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 {
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;
1778 }
1779 } else {
1780 regs.mmu_ssw = MMU030_SSW_FB | MMU030_SSW_RB;
1781 }
1782 }
1783 regs.mmu_ssw |= read ? MMU030_SSW_RW : 0;
1784 regs.mmu_ssw |= flags;
1785 regs.mmu_ssw |= fc;
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];
1789 bBusErrorReadWrite = read;
1790 mm030_stageb_address = addr;
1791
1792 #if MMUDEBUG
1793 write_log(_T("MMU: la=%08X SSW=%04x read=%d size=%d fc=%d pc=%08x ob=%08x "),
1794 addr, regs.mmu_ssw, read, (flags & MMU030_SSW_SIZE_B) ? 1 : (flags & MMU030_SSW_SIZE_W) ? 2 : 4, fc,
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("!");
1807 #endif
1808
1809 THROW(2);
1810 }
1811
mmu030_add_data_read_cache(uaecptr addr,uaecptr phys,uae_u32 fc)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 }
1822 #endif
1823 }
1824
mmu030_add_data_write_cache(uaecptr addr,uaecptr phys,uae_u32 fc)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 }
1835 #endif
1836 }
1837
mmu030_put_atc(uaecptr addr,int l,uae_u32 fc,uae_u32 size)1838 static uaecptr mmu030_put_atc(uaecptr addr, int l, uae_u32 fc, uae_u32 size) {
1839 uae_u32 page_index = addr & mmu030.translation.page.mask;
1840 uae_u32 addr_mask = mmu030.translation.page.imask;
1841 uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask;
1842
1843 #if MMU030_ATC_DBG_MSG
1844 write_log(_T("ATC match(%i): page addr = %08X, index = %08X\n"),
1845 l, physical_addr, page_index);
1846 #endif
1847
1848 if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) {
1849 mmu030_page_fault(addr, false, size, fc);
1850 return 0;
1851 }
1852
1853 mmu030_cache_state = mmu030.atc[l].physical.cache_inhibit;
1854
1855 mmu030_add_data_write_cache(addr, physical_addr, fc);
1856
1857 return physical_addr + page_index;
1858 }
1859
mmu030_get_atc(uaecptr addr,int l,uae_u32 fc,uae_u32 size)1860 static uaecptr mmu030_get_atc(uaecptr addr, int l, uae_u32 fc, uae_u32 size) {
1861 uae_u32 page_index = addr & mmu030.translation.page.mask;
1862 uae_u32 addr_mask = mmu030.translation.page.imask;
1863 uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask;
1864
1865 #if MMU030_ATC_DBG_MSG
1866 write_log(_T("ATC match(%i): page addr = %08X, index = %08X\n"), l,
1867 physical_addr, page_index);
1868 #endif
1869
1870 if (mmu030.atc[l].physical.bus_error) {
1871 mmu030_page_fault(addr, true, size, fc);
1872 return 0;
1873 }
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;
1880 }
1881
mmu030_get_i_atc(uaecptr addr,int l,uae_u32 fc,uae_u32 size)1882 static uaecptr mmu030_get_i_atc(uaecptr addr, int l, uae_u32 fc, uae_u32 size) {
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;
1886
1887 #if MMU030_ATC_DBG_MSG
1888 write_log(_T("ATC match(%i): page addr = %08X, index = %08X\n"), l,
1889 physical_addr, page_index);
1890 #endif
1891
1892 if (mmu030.atc[l].physical.bus_error) {
1893 mmu030_page_fault(addr, true, size, fc);
1894 return 0;
1895 }
1896
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;
1903 #endif
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;
1908
1909 return physical_addr + page_index;
1910 }
1911
1912 /* Generic versions of above */
mmu030_put_atc_generic(uaecptr addr,int l,uae_u32 fc,int flags)1913 static uaecptr mmu030_put_atc_generic(uaecptr addr, int l, uae_u32 fc, int flags) {
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;
1917
1918 #if MMU030_ATC_DBG_MSG
1919 write_log(_T("ATC match(%i): page addr = %08X, index = %08X\n"),
1920 l, physical_addr, page_index);
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);
1925 return 0;
1926 }
1927
1928 mmu030_add_data_write_cache(addr, physical_addr, fc);
1929
1930 return physical_addr + page_index;
1931 }
1932
mmu030_get_atc_generic(uaecptr addr,int l,uae_u32 fc,int flags,bool checkwrite)1933 static uae_u32 mmu030_get_atc_generic(uaecptr addr, int l, uae_u32 fc, int flags, bool checkwrite) {
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;
1937
1938 #if MMU030_ATC_DBG_MSG
1939 write_log(_T("ATC match(%i): page addr = %08X, index = %08X\n"), l,
1940 physical_addr, page_index);
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 }
1947
1948 mmu030_add_data_read_cache(addr, physical_addr, fc);
1949
1950 return physical_addr + page_index;
1951 }
1952
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. */
mmu030_logical_is_in_atc(uaecptr addr,uae_u32 fc,bool write)1958 static int mmu030_logical_is_in_atc(uaecptr addr, uae_u32 fc, bool write) {
1959 uaecptr logical_addr = 0;
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];
1966 for (i=0; i<ATC030_NUM_ENTRIES; i++) {
1967 logical_addr = mmu030.atc[index].logical.addr;
1968 /* If actual address matches address in ATC */
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) {
1977 /* Maintain history bit */
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;
1988 }
1989 return -1;
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
cacheablecheck(uaecptr addr)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 }
2006
mmu030_put_long(uaecptr addr,uae_u32 val,uae_u32 fc)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 }
2032
mmu030_put_word(uaecptr addr,uae_u16 val,uae_u32 fc)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);
2057 }
2058
mmu030_put_byte(uaecptr addr,uae_u8 val,uae_u32 fc)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);
2083 }
2084
2085
mmu030_get_long(uaecptr addr,uae_u32 fc)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);
2110 }
2111
mmu030_get_word(uaecptr addr,uae_u32 fc)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 }
2137
mmu030_get_byte(uaecptr addr,uae_u32 fc)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 }
2159 }
2160 cacheablecheck(addr);
2161 return x_phys_get_byte(addr);
2162 }
2163
2164
mmu030_get_ilong(uaecptr addr,uae_u32 fc)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
2176 }
2177 mmu030.mmu030_last_logical_address = 0xffffffff;
2178 #endif
2179
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);
2192 }
2193
mmu030_get_iword(uaecptr addr,uae_u32 fc)2194 uae_u16 mmu030_get_iword(uaecptr addr, uae_u32 fc) {
2195
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 }
2218 }
2219 cacheablecheck(addr);
2220 return x_phys_get_iword(addr);
2221 }
2222
2223 /* Not commonly used access function */
2224
mmu030_put_generic_lrmw(uaecptr addr,uae_u32 val,uae_u32 fc,int size,int flags)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 }
2237 }
2238
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);
2246 }
2247
mmu030_put_generic(uaecptr addr,uae_u32 val,uae_u32 fc,int size,int flags)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;
2251
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);
2274 }
2275
mmu030_get_generic_lrmw(uaecptr addr,uae_u32 fc,int size,int flags)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 }
2289
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);
2296 }
2297
mmu030_get_generic(uaecptr addr,uae_u32 fc,int size,int flags)2298 uae_u32 mmu030_get_generic(uaecptr addr, uae_u32 fc, int size, int flags)
2299 {
2300 mmu030_cache_state = CACHE_ENABLE_ALL;
2301
2302 if (flags & MMU030_SSW_RM) {
2303 return mmu030_get_generic_lrmw(addr, fc, size, flags);
2304 }
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);
2323 }
2324
uae_mmu030_check_fc(uaecptr addr,bool write,uae_u32 size)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 }
2345
2346 /* Locked RMW is rarely used */
uae_mmu030_get_lrmw_fcx(uaecptr addr,int size,int fc)2347 uae_u32 uae_mmu030_get_lrmw_fcx(uaecptr addr, int size, int fc)
2348 {
2349 if (size == sz_byte) {
2350 return mmu030_get_generic(addr, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_B);
2351 } else if (size == sz_word) {
2352 if (unlikely(is_unaligned_bus(addr, 2)))
2353 return mmu030_get_word_unaligned(addr, fc, MMU030_SSW_RM);
2354 else
2355 return mmu030_get_generic(addr, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_W);
2356 } else {
2357 if (unlikely(is_unaligned_bus(addr, 4)))
2358 return mmu030_get_long_unaligned(addr, fc, MMU030_SSW_RM);
2359 else
2360 return mmu030_get_generic(addr, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_L);
2361 }
2362 }
uae_mmu030_get_lrmw(uaecptr addr,int size)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
uae_mmu030_put_lrmw_fcx(uaecptr addr,uae_u32 val,int size,int fc)2369 void uae_mmu030_put_lrmw_fcx(uaecptr addr, uae_u32 val, int size, int fc)
2370 {
2371 if (size == sz_byte) {
2372 mmu030_put_generic(addr, val, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_B);
2373 } else if (size == sz_word) {
2374 if (unlikely(is_unaligned_bus(addr, 2)))
2375 mmu030_put_word_unaligned(addr, val, fc, MMU030_SSW_RM);
2376 else
2377 mmu030_put_generic(addr, val, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_W);
2378 } else {
2379 if (unlikely(is_unaligned_bus(addr, 4)))
2380 mmu030_put_long_unaligned(addr, val, fc, MMU030_SSW_RM);
2381 else
2382 mmu030_put_generic(addr, val, fc, size, MMU030_SSW_RM | MMU030_SSW_SIZE_L);
2383 }
2384 }
uae_mmu030_put_lrmw(uaecptr addr,uae_u32 val,int size)2385 void uae_mmu030_put_lrmw(uaecptr addr, uae_u32 val, int size)
2386 {
2387 uae_u32 fc = (regs.s ? 4 : 0) | 1;
2388 uae_mmu030_put_lrmw_fcx(addr, val, size, fc);
2389 }
2390
mmu030_get_ilong_unaligned(uaecptr addr,uae_u32 fc,int flags)2391 uae_u32 REGPARAM2 mmu030_get_ilong_unaligned(uaecptr addr, uae_u32 fc, int flags)
2392 {
2393 uae_u32 res;
2394
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
unalign_init(uaecptr addr,bool l,bool l2)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 }
unalign_set(int state)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 }
unalign_clear(void)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
mmu030_get_word_unaligned(uaecptr addr,uae_u32 fc,int flags)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
mmu030_get_long_unaligned(uaecptr addr,uae_u32 fc,int flags)2445 uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc, int flags)
2446 {
2447 if (likely(!(addr & 1))) {
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);
2452 } else {
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);
2459 }
2460 unalign_clear();
2461 return mmu030_data_buffer_out;
2462 }
2463
mmu030_put_long_unaligned(uaecptr addr,uae_u32 val,uae_u32 fc,int flags)2464 void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc, int flags)
2465 {
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);
2478 }
2479 unalign_clear();
2480 }
2481
mmu030_put_word_unaligned(uaecptr addr,uae_u16 val,uae_u32 fc,int flags)2482 void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc, int flags)
2483 {
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();
2489 }
2490
2491
2492 /* Used by debugger */
mmu030_get_addr_atc(uaecptr addr,int l,uae_u32 fc,bool write)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 }
mmu030_translate(uaecptr addr,bool super,bool data,bool write)2507 uaecptr mmu030_translate(uaecptr addr, bool super, bool data, bool write)
2508 {
2509 int fc = (super ? 4 : 0) | (data ? 1 : 2);
2510 if ((fc==7) || (mmu030_match_ttr(addr,fc,write)&TT_OK_MATCH) || (!mmu030.enabled)) {
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
2523 /* MMU Reset */
mmu030_reset(int hardreset)2524 void mmu030_reset(int hardreset)
2525 {
2526 /* A CPU reset causes the E-bits of TC and TT registers to be zeroed. */
2527 mmu030.enabled = false;
2528 #if MMU_IPAGECACHE030
2529 mmu030.mmu030_last_logical_address = 0xffffffff;
2530 #endif
2531 regs.mmu_page_size = 0;
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) {
2538 srp_030 = crp_030 = 0;
2539 tt0_030 = tt1_030 = tc_030 = 0;
2540 mmusr_030 = 0;
2541 mmu030_flush_atc_all();
2542 }
2543 mmu030_set_funcs();
2544 }
2545
mmu030_set_funcs(void)2546 void mmu030_set_funcs(void)
2547 {
2548 if (currprefs.mmu_model != 68030)
2549 return;
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;
2559 } else {
2560 x_phys_get_iword = phys_get_word;
2561 x_phys_get_ilong = phys_get_long;
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;
2568 }
2569 }
2570
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
mmu030_unaligned_read_continue(uaecptr addr,int fc,unaligned_read_func func)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
mmu030_unaligned_write_continue(uaecptr addr,int fc,unaligned_write_func func)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
m68k_do_rte_mmu030(uaecptr a7)2731 void m68k_do_rte_mmu030 (uaecptr a7)
2732 {
2733 struct mmu030_access mmu030_ad_v[MAX_MMU030_ACCESS + 1];
2734
2735 // Restore access error exception state
2736
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);
2740 uae_u16 frame = format >> 12;
2741 uae_u16 ssw = get_word_mmu030(a7 + 10);
2742 uae_u32 fault_addr = get_long_mmu030(a7 + 16);
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
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);
2764 // Misc state data
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;
2771 if (mmu030_state[1] & MMU030_STATEFLAG1_FMOVEM) {
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);
2774 }
2775
2776 // Rerun "mmu030_opcode" using restored state.
2777 mmu030_retry = true;
2778
2779 if (frame == 0xb) {
2780
2781 uae_u16 idxsize = get_word_mmu030 (a7 + 0x36);
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);
2785 }
2786 mmu030_ad_v[idxsize + 1].done = false;
2787
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
2791 mmu030_data_buffer_out_v = mmu030_data_buffer_in_v;
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;
2796 } else if (mmu030_state_1 & MMU030_STATEFLAG1_MOVEM1) {
2797 // if movem, skip next move
2798 mmu030_state_1 |= MMU030_STATEFLAG1_MOVEM2;
2799 } else {
2800 mmu030_ad_v[idxsize].done = true;
2801 if (ssw & MMU030_SSW_RW) {
2802 // Read and no DF: use value in data input buffer
2803 mmu030_ad_v[idxsize].val = mmu030_data_buffer_in_v;
2804 }
2805 }
2806 unalign_clear();
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);
2811 if (mmu030_opcode_v == -1U) {
2812 mmu030_opcode_stageb = stageb;
2813 write_log (_T("Software fixed stage B! opcode = %04x\n"), stageb);
2814 } else {
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);
2818 }
2819 }
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
2937 } else {
2938 m68k_areg (regs, 7) += 32;
2939 }
2940 }
2941
flush_mmu030(uaecptr addr,int n)2942 void flush_mmu030 (uaecptr addr, int n)
2943 {
2944 }
2945
m68k_do_rts_mmu030(void)2946 void m68k_do_rts_mmu030 (void)
2947 {
2948 m68k_setpc (get_long_mmu030_state (m68k_areg (regs, 7)));
2949 m68k_areg (regs, 7) += 4;
2950 }
2951
m68k_do_bsr_mmu030(uaecptr oldpc,uae_s32 offset)2952 void m68k_do_bsr_mmu030 (uaecptr oldpc, uae_s32 offset)
2953 {
2954 put_long_mmu030_state (m68k_areg (regs, 7) - 4, oldpc);
2955 m68k_areg (regs, 7) -= 4;
2956 m68k_incpci (offset);
2957 }
2958
get_disp_ea_020_mmu030(uae_u32 base,int idx)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 }
3032
3033 // cache
3034
m68k_do_rts_mmu030c(void)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
m68k_do_bsr_mmu030c(uaecptr oldpc,uae_s32 offset)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
get_disp_ea_020_mmu030c(uae_u32 base,int idx)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
m68k_do_rte_mmu030c(uaecptr a7)3123 void m68k_do_rte_mmu030c (uaecptr a7)
3124 {
3125 struct mmu030_access mmu030_ad_v[MAX_MMU030_ACCESS + 1];
3126
3127 // Restore access error exception state
3128
3129 uae_u16 sr = get_word_mmu030c(a7);
3130 uae_u32 pc = get_long_mmu030c(a7 + 2);
3131 uae_u16 format = get_word_mmu030c (a7 + 6);
3132 uae_u16 frame = format >> 12;
3133 uae_u16 ssw = get_word_mmu030c (a7 + 10);
3134 uae_u32 stagesbc = get_long_mmu030c(a7 + 12);
3135 uae_u32 fault_addr = get_long_mmu030c(a7 + 16);
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
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);
3151 // Internal register, misc flags
3152 uae_u32 ps = get_long_mmu030c(a7 + 0x28);
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);
3157 // Misc state data
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;
3164 if (mmu030_state[1] & MMU030_STATEFLAG1_FMOVEM) {
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);
3167 }
3168
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);
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);
3177 }
3178 mmu030_ad_v[idxsize + 1].done = false;
3179
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
3183 mmu030_data_buffer_out_v = mmu030_data_buffer_in_v;
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;
3188 } else if (mmu030_state_1 & MMU030_STATEFLAG1_MOVEM1) {
3189 // if movem, skip next move
3190 mmu030_state_1 |= MMU030_STATEFLAG1_MOVEM2;
3191 } else {
3192 mmu030_ad_v[idxsize].done = true;
3193 if (ssw & MMU030_SSW_RW) {
3194 // Read and no DF: use value in data input buffer
3195 mmu030_ad_v[idxsize].val = mmu030_data_buffer_in_v;
3196 }
3197 }
3198 unalign_clear();
3199 }
3200
3201 // Retried data access is the only memory access that can be done after this.
3202
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;
3209 regs.pipeline_stop = ((ps >> 20) & 15) == 15 ? -1 : (int)(ps >> 20) & 15;
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
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 }
3239
3240 m68k_areg (regs, 7) += 92;
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
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
3339 } else {
3340 m68k_areg (regs, 7) += 32;
3341 }
3342 }
3343