1 /*
2 * cpummu.cpp - MMU emulation
3 *
4 * Copyright (c) 2001-2004 Milan Jurik of ARAnyM dev team (see AUTHORS)
5 *
6 * Inspired by UAE MMU patch
7 *
8 * This file is part of the ARAnyM project which builds a new and powerful
9 * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
10 *
11 * ARAnyM is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * ARAnyM is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with ARAnyM; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #define DEBUG 0
27 #include "sysdeps.h"
28
29 #include "cpummu.h"
30 #include "memory-uae.h"
31 #include "newcpu.h"
32 #include "debug.h"
33 #ifdef USE_JIT
34 # include "compiler/compemu.h"
35 #endif
36
37 #define DBG_MMU_VERBOSE 1
38 #define DBG_MMU_SANITY 1
39
40 #ifdef FULLMMU
41
42 mmu_atc_l1_array atc_l1[2];
43 mmu_atc_l1_array *current_atc;
44 struct mmu_atc_line atc_l2[2][ATC_L2_SIZE];
45
46 # ifdef ATC_STATS
47 static unsigned int mmu_atc_hits[ATC_L2_SIZE];
48 # endif
49
50
mmu_dump_ttr(const char * label,uae_u32 ttr)51 static void mmu_dump_ttr(const char * label, uae_u32 ttr)
52 {
53 DUNUSED(label);
54 #if DEBUG
55 uae_u32 from_addr, to_addr;
56
57 from_addr = ttr & MMU_TTR_LOGICAL_BASE;
58 to_addr = (ttr & MMU_TTR_LOGICAL_MASK) << 8;
59
60 D(bug("%s: [%08x] %08x - %08x enabled=%d supervisor=%d wp=%d cm=%02d",
61 label, ttr,
62 from_addr, to_addr,
63 ttr & MMU_TTR_BIT_ENABLED ? 1 : 0,
64 (ttr & (MMU_TTR_BIT_SFIELD_ENABLED | MMU_TTR_BIT_SFIELD_SUPER)) >> MMU_TTR_SFIELD_SHIFT,
65 ttr & MMU_TTR_BIT_WRITE_PROTECT ? 1 : 0,
66 (ttr & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT
67 ));
68 #else
69 DUNUSED(ttr);
70 #endif
71 }
72
mmu_make_transparent_region(uaecptr baseaddr,uae_u32 size,int datamode)73 void mmu_make_transparent_region(uaecptr baseaddr, uae_u32 size, int datamode)
74 {
75 uae_u32 * ttr;
76 uae_u32 * ttr0 = datamode ? ®s.dtt0 : ®s.itt0;
77 uae_u32 * ttr1 = datamode ? ®s.dtt1 : ®s.itt1;
78
79 if ((*ttr1 & MMU_TTR_BIT_ENABLED) == 0)
80 ttr = ttr1;
81 else if ((*ttr0 & MMU_TTR_BIT_ENABLED) == 0)
82 ttr = ttr0;
83 else
84 return;
85
86 *ttr = baseaddr & MMU_TTR_LOGICAL_BASE;
87 *ttr |= ((baseaddr + size - 1) & MMU_TTR_LOGICAL_BASE) >> 8;
88 *ttr |= MMU_TTR_BIT_ENABLED;
89
90 D(bug("MMU: map transparent mapping of %08x", *ttr));
91 }
92
93 /* check if an address matches a ttr */
mmu_do_match_ttr(uae_u32 ttr,uaecptr addr,int super)94 static int mmu_do_match_ttr(uae_u32 ttr, uaecptr addr, int super)
95 {
96 if (ttr & MMU_TTR_BIT_ENABLED) { /* TTR enabled */
97 uae_u8 msb, mask;
98
99 msb = ((addr ^ ttr) & MMU_TTR_LOGICAL_BASE) >> 24;
100 mask = (ttr & MMU_TTR_LOGICAL_MASK) >> 16;
101
102 if (!(msb & ~mask)) {
103
104 if ((ttr & MMU_TTR_BIT_SFIELD_ENABLED) == 0) {
105 if (((ttr & MMU_TTR_BIT_SFIELD_SUPER) == 0) != (super == 0)) {
106 return TTR_NO_MATCH;
107 }
108 }
109
110 return (ttr & MMU_TTR_BIT_WRITE_PROTECT) ? TTR_NO_WRITE : TTR_OK_MATCH;
111 }
112 }
113 return TTR_NO_MATCH;
114 }
115
mmu_match_ttr(uaecptr addr,int super,int data)116 static inline int mmu_match_ttr(uaecptr addr, int super, int data)
117 {
118 int res;
119
120 if (data) {
121 res = mmu_do_match_ttr(regs.dtt0, addr, super);
122 if (res == TTR_NO_MATCH)
123 res = mmu_do_match_ttr(regs.dtt1, addr, super);
124 } else {
125 res = mmu_do_match_ttr(regs.itt0, addr, super);
126 if (res == TTR_NO_MATCH)
127 res = mmu_do_match_ttr(regs.itt1, addr, super);
128 }
129 return res;
130 }
131
132 #if DEBUG
133 /* {{{ mmu_dump_table */
mmu_dump_table(const char * label,uaecptr root_ptr)134 static void mmu_dump_table(const char * label, uaecptr root_ptr)
135 {
136 DUNUSED(label);
137 const int ROOT_TABLE_SIZE = 128,
138 PTR_TABLE_SIZE = 128,
139 PAGE_TABLE_SIZE = regs.mmu_pagesize_8k ? 32 : 64,
140 ROOT_INDEX_SHIFT = 25,
141 PTR_INDEX_SHIFT = 18;
142 const uae_u32 ptr_addr_mask = (regs.mmu_pagesize_8k ? MMU_PTR_PAGE_ADDR_MASK_8 : MMU_PTR_PAGE_ADDR_MASK_4);
143 const uae_u32 page_addr_mask = (regs.mmu_pagesize_8k ? MMU_PAGE_ADDR_MASK_8 : MMU_PAGE_ADDR_MASK_4);
144 const uae_u32 page_ur_mask = (regs.mmu_pagesize_8k ? MMU_PAGE_UR_MASK_8 : MMU_PAGE_UR_MASK_4);
145 const uae_u32 page_size = (regs.mmu_pagesize_8k ? (1 << 13) : (1 << 12));
146 int root_idx, ptr_idx, page_idx;
147 uae_u32 root_des, ptr_des, page_des;
148 uaecptr ptr_des_addr, page_addr,
149 root_log, ptr_log, page_log;
150
151 D(bug("%s: root=%x", label, root_ptr));
152
153 for (root_idx = 0; root_idx < ROOT_TABLE_SIZE; root_idx++) {
154 root_des = phys_get_long(root_ptr + (root_idx << 2));
155
156 if ((root_des & 2) == 0)
157 continue; /* invalid */
158
159 D(bug("ROOT: %03d U=%d W=%d UDT=%02d", root_idx,
160 root_des & 8 ? 1 : 0,
161 root_des & 4 ? 1 : 0,
162 root_des & 3
163 ));
164
165 root_log = root_idx << ROOT_INDEX_SHIFT;
166
167 ptr_des_addr = root_des & MMU_ROOT_PTR_ADDR_MASK;
168
169 for (ptr_idx = 0; ptr_idx < PTR_TABLE_SIZE; ptr_idx++) {
170 struct {
171 uaecptr log, phys;
172 int start_idx, n_pages; /* number of pages covered by this entry */
173 uae_u32 match;
174 } page_info[PAGE_TABLE_SIZE];
175 int n_pages_used;
176
177 ptr_des = phys_get_long(ptr_des_addr + (ptr_idx << 2));
178 ptr_log = root_log | (ptr_idx << PTR_INDEX_SHIFT);
179
180 if ((ptr_des & 2) == 0)
181 continue; /* invalid */
182
183 page_addr = ptr_des & ptr_addr_mask;
184
185 n_pages_used = -1;
186 for (page_idx = 0; page_idx < PAGE_TABLE_SIZE; page_idx++) {
187
188 page_des = phys_get_long(page_addr + (page_idx << 2));
189 page_log = ptr_log | (page_idx * page_size);
190
191 switch (page_des & 3) {
192 case 0: /* invalid */
193 continue;
194 case 1: case 3: /* resident */
195 case 2: /* indirect */
196 if (n_pages_used == -1 ||
197 (page_info[n_pages_used].match & ~page_addr_mask) != (page_des & ~page_addr_mask) ||
198 page_info[n_pages_used].phys + (page_info[n_pages_used].n_pages * page_size) != (page_des & page_addr_mask))
199 {
200 /* use the next entry */
201 n_pages_used++;
202
203 page_info[n_pages_used].match = page_des;
204 page_info[n_pages_used].n_pages = 1;
205 page_info[n_pages_used].start_idx = page_idx;
206 page_info[n_pages_used].log = page_log;
207 page_info[n_pages_used].phys = page_des & page_addr_mask;
208 } else {
209 page_info[n_pages_used].n_pages++;
210 }
211 break;
212 }
213 }
214
215 if (n_pages_used == -1)
216 continue;
217
218 D(bug(" PTR: %03d U=%d W=%d UDT=%02d", ptr_idx,
219 ptr_des & 8 ? 1 : 0,
220 ptr_des & 4 ? 1 : 0,
221 ptr_des & 3
222 ));
223
224
225 for (page_idx = 0; page_idx <= n_pages_used; page_idx++) {
226 page_des = page_info[page_idx].match;
227
228 if ((page_des & MMU_PDT_MASK) == 2) {
229 D(bug(" PAGE: %03d-%03d log=%08x INDIRECT --> addr=%08x",
230 page_info[page_idx].start_idx,
231 page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1,
232 page_info[page_idx].log,
233 page_des & MMU_PAGE_INDIRECT_MASK
234 ));
235
236 } else {
237 D(bug(" PAGE: %03d-%03d log=%08x addr=%08x UR=%02d G=%d U1/0=%d S=%d CM=%d M=%d U=%d W=%d",
238 page_info[page_idx].start_idx,
239 page_info[page_idx].start_idx + page_info[page_idx].n_pages - 1,
240 page_info[page_idx].log,
241 page_info[page_idx].phys,
242 (page_des & page_ur_mask) >> MMU_PAGE_UR_SHIFT,
243 page_des & MMU_DES_GLOBAL ? 1 : 0,
244 (page_des & MMU_TTR_UX_MASK) >> MMU_TTR_UX_SHIFT,
245 page_des & MMU_DES_SUPER ? 1 : 0,
246 (page_des & MMU_TTR_CACHE_MASK) >> MMU_TTR_CACHE_SHIFT,
247 page_des & MMU_DES_MODIFIED ? 1 : 0,
248 page_des & MMU_DES_USED ? 1 : 0,
249 page_des & MMU_DES_WP ? 1 : 0
250 ));
251 }
252 }
253 }
254
255 }
256 }
257 /* }}} */
258 #endif
259
260 /* {{{ mmu_dump_atc */
mmu_dump_atc(void)261 void mmu_dump_atc(void)
262 {
263 int i, j;
264 for (i = 0; i < 2; i++) {
265 for (j = 0; j < ATC_L2_SIZE; j++) {
266 if (atc_l2[i][j].tag == 0x8000)
267 continue;
268 D(bug("ATC[%02d] G=%d TT=%d M=%d WP=%d VD=%d VI=%d tag=%08x --> phys=%08x",
269 j, atc_l2[i][j].global, atc_l2[i][j].tt, atc_l2[i][j].modified,
270 atc_l2[i][j].write_protect, atc_l2[i][j].valid_data, atc_l2[i][j].valid_inst,
271 atc_l2[i][j].tag, atc_l2[i][j].phys));
272 }
273 }
274 }
275 /* }}} */
276
277 /* {{{ mmu_dump_tables */
mmu_dump_tables(void)278 void mmu_dump_tables(void)
279 {
280 D(bug("URP: %08x SRP: %08x MMUSR: %x TC: %x", regs.urp, regs.srp, regs.mmusr, regs.tc));
281 mmu_dump_ttr("DTT0", regs.dtt0);
282 mmu_dump_ttr("DTT1", regs.dtt1);
283 mmu_dump_ttr("ITT0", regs.itt0);
284 mmu_dump_ttr("ITT1", regs.itt1);
285 mmu_dump_atc();
286 //mmu_dump_table("SRP", regs.srp);
287 }
288 /* }}} */
289
290 static uaecptr REGPARAM2 mmu_lookup_pagetable(uaecptr addr, int super, int write);
291
mmu_get_fc(bool super,bool data)292 static ALWAYS_INLINE int mmu_get_fc(bool super, bool data)
293 {
294 return (super ? 4 : 0) | (data ? 1 : 2);
295 }
296
mmu_bus_error(uaecptr addr,int fc,int write,int size)297 static void mmu_bus_error(uaecptr addr, int fc, int write, int size)
298 {
299 uae_u16 ssw = 0;
300
301 ssw |= fc & MMU_SSW_TM; /* Copy TM */
302 switch (size) {
303 case sz_byte:
304 ssw |= MMU_SSW_SIZE_B;
305 break;
306 case sz_word:
307 ssw |= MMU_SSW_SIZE_W;
308 break;
309 case sz_long:
310 ssw |= MMU_SSW_SIZE_L;
311 break;
312 }
313
314 regs.wb3_status = write ? 0x80 | ssw : 0;
315 if (!write)
316 ssw |= MMU_SSW_RW;
317
318 regs.mmu_fault_addr = addr;
319 regs.mmu_ssw = ssw | MMU_SSW_ATC;
320
321 D(bug("BUS ERROR: fc=%d w=%d log=%08x ssw=%04x", fc, write, addr, ssw));
322
323 breakpt();
324 THROW(2);
325 }
326
327 /*
328 * Update the atc line for a given address by doing a mmu lookup.
329 */
mmu_fill_atc_l2(uaecptr addr,int super,int data,int write,struct mmu_atc_line * l)330 static uaecptr mmu_fill_atc_l2(uaecptr addr, int super, int data, int write,
331 struct mmu_atc_line *l)
332 {
333 int res;
334 uae_u32 desc;
335
336 l->tag = ATC_TAG(addr);
337 l->hw = l->bus_fault = 0;
338
339 /* check ttr0 */
340 res = mmu_match_ttr(addr, super, data);
341 if (res != TTR_NO_MATCH) {
342 l->tt = 1;
343 if (data) {
344 l->valid_data = 1;
345 l->valid_inst = mmu_match_ttr(addr, super, 0) == res;
346 } else {
347 l->valid_inst = 1;
348 l->valid_data = mmu_match_ttr(addr, super, 1) == res;
349 }
350 l->global = 1;
351 l->modified = 1;
352 l->write_protect = (res == TTR_NO_WRITE);
353 l->phys = 0;
354
355 return 0;
356 }
357
358 l->tt = 0;
359 if (!regs.mmu_enabled) {
360 l->valid_data = l->valid_inst = 1;
361 l->global = 1;
362 l->modified = 1;
363 l->write_protect = 0;
364 l->phys = 0;
365 return 0;
366 }
367
368 SAVE_EXCEPTION;
369 TRY(prb) {
370 desc = mmu_lookup_pagetable(addr, super, write);
371 D(bug("translate: %x,%u,%u,%u -> %x", addr, super, write, data, desc));
372 RESTORE_EXCEPTION;
373 }
374 CATCH(prb) {
375 RESTORE_EXCEPTION;
376 /* bus error during table search */
377 desc = 0;
378 goto fail;
379 }
380
381 if ((desc & 1) == 0 || (!super && desc & MMU_MMUSR_S)) {
382 fail:
383 l->valid_data = l->valid_inst = 0;
384 l->global = 0;
385 } else {
386 l->valid_data = l->valid_inst = 1;
387 if (regs.mmu_pagesize_8k)
388 l->phys = (desc & ~0x1fff) - (addr & ~0x1fff);
389 else
390 l->phys = (desc & ~0xfff) - (addr & ~0xfff);
391 l->global = (desc & MMU_MMUSR_G) != 0;
392 l->modified = (desc & MMU_MMUSR_M) != 0;
393 l->write_protect = (desc & MMU_MMUSR_W) != 0;
394 }
395
396 return desc;
397 }
398
399 static ALWAYS_INLINE bool
mmu_fill_atc_l1(uaecptr addr,int super,int data,int write,struct mmu_atc_line * l1)400 mmu_fill_atc_l1(uaecptr addr, int super, int data, int write,
401 struct mmu_atc_line *l1)
402 {
403 int idx = ATC_L2_INDEX(addr);
404 int tag = ATC_TAG(addr);
405 struct mmu_atc_line *l = &atc_l2[super][idx];
406 uaecptr phys_addr;
407
408 if (l->tag != tag) {
409 restart:
410 mmu_fill_atc_l2(addr, super, data, write, l);
411 }
412 if (!(data ? l->valid_data : l->valid_inst)) {
413 D(bug("MMU: non-resident page (%x,%x,%x)!", addr, regs.pc, regs.fault_pc));
414 goto fail;
415 }
416 if (write) {
417 if (l->write_protect) {
418 D(bug("MMU: write protected (via %s) %x", l->tt ? "ttr" : "atc", addr));
419 goto fail;
420 }
421 if (!l->modified)
422 goto restart;
423 }
424 *l1 = *l;
425
426 phys_addr = addr + l1->phys;
427 if ((phys_addr & 0xfff00000) == 0x00f00000) {
428 l1->hw = 1;
429 goto fail;
430 }
431 if ((phys_addr & 0xfff00000) == 0xfff00000) {
432 l1->hw = 1;
433 l1->phys -= 0xff000000;
434 goto fail;
435 }
436
437 if (!test_ram_boundary(phys_addr, 1, super, write)) {
438 l1->bus_fault = 1;
439 goto fail;
440 }
441
442 return true;
443
444 fail:
445 l1->tag = ~l1->tag;
446 return false;
447 }
448
mmu_translate(uaecptr addr,int super,int data,int write)449 uaecptr mmu_translate(uaecptr addr, int super, int data, int write)
450 {
451 struct mmu_atc_line *l;
452
453 l = &atc_l2[super][ATC_L2_INDEX(addr)];
454 mmu_fill_atc_l2(addr, super, data, write, l);
455 if (!(data ? l->valid_data : l->valid_inst))
456 {
457 breakpt();
458 THROW(2);
459 }
460
461 return addr + l->phys;
462 }
463
464 /*
465 * Lookup the address by walking the page table and updating
466 * the page descriptors accordingly. Returns the found descriptor
467 * or produces a bus error.
468 */
mmu_lookup_pagetable(uaecptr addr,int super,int write)469 static uaecptr REGPARAM2 mmu_lookup_pagetable(uaecptr addr, int super, int write)
470 {
471 uae_u32 desc, desc_addr, wp;
472 int i;
473
474 wp = 0;
475 desc = super ? regs.srp : regs.urp;
476
477 /* fetch root table descriptor */
478 i = (addr >> 23) & 0x1fc;
479 desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i;
480 desc = phys_get_long(desc_addr);
481 if ((desc & 2) == 0) {
482 D(bug("MMU: invalid root descriptor for %x", addr));
483 return 0;
484 }
485
486 wp |= desc;
487 if ((desc & MMU_DES_USED) == 0)
488 phys_put_long(desc_addr, desc | MMU_DES_USED);
489
490 /* fetch pointer table descriptor */
491 i = (addr >> 16) & 0x1fc;
492 desc_addr = (desc & MMU_ROOT_PTR_ADDR_MASK) | i;
493 desc = phys_get_long(desc_addr);
494 if ((desc & 2) == 0) {
495 D(bug("MMU: invalid ptr descriptor for %x", addr));
496 return 0;
497 }
498 wp |= desc;
499 if ((desc & MMU_DES_USED) == 0)
500 phys_put_long(desc_addr, desc | MMU_DES_USED);
501
502 /* fetch page table descriptor */
503 if (regs.mmu_pagesize_8k) {
504 i = (addr >> 11) & 0x7c;
505 desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_8) | i;
506 } else {
507 i = (addr >> 10) & 0xfc;
508 desc_addr = (desc & MMU_PTR_PAGE_ADDR_MASK_4) | i;
509 }
510
511 desc = phys_get_long(desc_addr);
512 if ((desc & 3) == 2) {
513 /* indirect */
514 desc_addr = desc & MMU_PAGE_INDIRECT_MASK;
515 desc = phys_get_long(desc_addr);
516 }
517 if ((desc & 1) == 0) {
518 D(bug("MMU: invalid page descriptor log=%08x desc=%08x @%08x", addr, desc, desc_addr));
519 return desc;
520 }
521
522 desc |= wp & MMU_DES_WP;
523 if (write) {
524 if (desc & MMU_DES_WP) {
525 if ((desc & MMU_DES_USED) == 0) {
526 desc |= MMU_DES_USED;
527 phys_put_long(desc_addr, desc);
528 }
529 } else if ((desc & (MMU_DES_USED|MMU_DES_MODIFIED)) !=
530 (MMU_DES_USED|MMU_DES_MODIFIED)) {
531 desc |= MMU_DES_USED|MMU_DES_MODIFIED;
532 phys_put_long(desc_addr, desc);
533 }
534 } else {
535 if ((desc & MMU_DES_USED) == 0) {
536 desc |= MMU_DES_USED;
537 phys_put_long(desc_addr, desc);
538 }
539 }
540 return desc;
541 }
542
mmu_get_word_unaligned(uaecptr addr,int data)543 uae_u16 mmu_get_word_unaligned(uaecptr addr, int data)
544 {
545 uae_u16 res;
546
547 res = (uae_u16)mmu_get_byte(addr, data, sz_word) << 8;
548 SAVE_EXCEPTION;
549 TRY(prb) {
550 res |= mmu_get_byte(addr + 1, data, sz_word);
551 RESTORE_EXCEPTION;
552 }
553 CATCH(prb) {
554 RESTORE_EXCEPTION;
555 regs.mmu_fault_addr = addr;
556 regs.mmu_ssw |= MMU_SSW_MA;
557 breakpt();
558 THROW_AGAIN(prb);
559 }
560 return res;
561 }
562
mmu_get_long_unaligned(uaecptr addr,int data)563 uae_u32 mmu_get_long_unaligned(uaecptr addr, int data)
564 {
565 uae_u32 res;
566
567 if (likely(!(addr & 1))) {
568 res = (uae_u32)mmu_get_word(addr, data, sz_long) << 16;
569 SAVE_EXCEPTION;
570 TRY(prb) {
571 res |= mmu_get_word(addr + 2, data, sz_long);
572 RESTORE_EXCEPTION;
573 }
574 CATCH(prb) {
575 RESTORE_EXCEPTION;
576 regs.mmu_fault_addr = addr;
577 regs.mmu_ssw |= MMU_SSW_MA;
578 breakpt();
579 THROW_AGAIN(prb);
580 }
581 } else {
582 res = (uae_u32)mmu_get_byte(addr, data, sz_long) << 8;
583 SAVE_EXCEPTION;
584 TRY(prb) {
585 res = (res | mmu_get_byte(addr + 1, data, sz_long)) << 8;
586 res = (res | mmu_get_byte(addr + 2, data, sz_long)) << 8;
587 res |= mmu_get_byte(addr + 3, data, sz_long);
588 RESTORE_EXCEPTION;
589 }
590 CATCH(prb) {
591 RESTORE_EXCEPTION;
592 regs.mmu_fault_addr = addr;
593 regs.mmu_ssw |= MMU_SSW_MA;
594 breakpt();
595 THROW_AGAIN(prb);
596 }
597 }
598 return res;
599 }
600
mmu_get_byte_slow(uaecptr addr,int super,int data,int size,struct mmu_atc_line * cl)601 uae_u8 mmu_get_byte_slow(uaecptr addr, int super, int data,
602 int size, struct mmu_atc_line *cl)
603 {
604 uae_u32 tag = ATC_TAG(addr);
605
606 if (cl->tag == (uae_u16)~tag) {
607 redo:
608 if (cl->hw)
609 return HWget_b(cl->phys + addr);
610 mmu_bus_error(addr, mmu_get_fc(super, data), 0, size);
611 return 0;
612 }
613
614 if (!mmu_fill_atc_l1(addr, super, data, 0, cl))
615 goto redo;
616
617 return do_get_mem_byte((uae_u8 *)mmu_get_real_address(addr, cl));
618 }
619
mmu_get_word_slow(uaecptr addr,int super,int data,int size,struct mmu_atc_line * cl)620 uae_u16 mmu_get_word_slow(uaecptr addr, int super, int data,
621 int size, struct mmu_atc_line *cl)
622 {
623 uae_u32 tag = ATC_TAG(addr);
624
625 if (cl->tag == (uae_u16)~tag) {
626 redo:
627 if (cl->hw)
628 return HWget_w(cl->phys + addr);
629 mmu_bus_error(addr, mmu_get_fc(super, data), 0, size);
630 return 0;
631 }
632
633 if (!mmu_fill_atc_l1(addr, super, data, 0, cl))
634 goto redo;
635
636 return do_get_mem_word((uae_u16 *)mmu_get_real_address(addr, cl));
637 }
638
mmu_get_long_slow(uaecptr addr,int super,int data,int size,struct mmu_atc_line * cl)639 uae_u32 mmu_get_long_slow(uaecptr addr, int super, int data,
640 int size, struct mmu_atc_line *cl)
641 {
642 uae_u32 tag = ATC_TAG(addr);
643
644 if (cl->tag == (uae_u16)~tag) {
645 redo:
646 if (cl->hw)
647 return HWget_l(cl->phys + addr);
648 mmu_bus_error(addr, mmu_get_fc(super, data), 0, size);
649 return 0;
650 }
651
652 if (!mmu_fill_atc_l1(addr, super, data, 0, cl))
653 goto redo;
654
655 return do_get_mem_long((uae_u32 *)mmu_get_real_address(addr, cl));
656 }
657
658
mmu_get_quad_slow(uaecptr addr,int super,int data,struct mmu_atc_line * cl)659 uae_u64 mmu_get_quad_slow(uaecptr addr, int super, int data,
660 struct mmu_atc_line *cl)
661 {
662 uae_u64 h = mmu_get_long_slow(addr, super, data, sz_long, cl);
663 uae_u64 l = mmu_get_long_slow(addr + 4, super, data, sz_long, cl);
664 return (h << 32) | l;
665 }
666
mmu_put_long_unaligned(uaecptr addr,uae_u32 val,int data)667 REGPARAM2 void mmu_put_long_unaligned(uaecptr addr, uae_u32 val, int data)
668 {
669 SAVE_EXCEPTION;
670 TRY(prb) {
671 if (likely(!(addr & 1))) {
672 mmu_put_word(addr, val >> 16, data, sz_long);
673 mmu_put_word(addr + 2, val, data, sz_long);
674 } else {
675 mmu_put_byte(addr, val >> 24, data, sz_long);
676 mmu_put_byte(addr + 1, val >> 16, data, sz_long);
677 mmu_put_byte(addr + 2, val >> 8, data, sz_long);
678 mmu_put_byte(addr + 3, val, data, sz_long);
679 }
680 RESTORE_EXCEPTION;
681 }
682 CATCH(prb) {
683 RESTORE_EXCEPTION;
684 regs.wb3_data = val;
685 if (regs.mmu_fault_addr != addr) {
686 regs.mmu_fault_addr = addr;
687 regs.mmu_ssw |= MMU_SSW_MA;
688 }
689 breakpt();
690 THROW_AGAIN(prb);
691 }
692 }
693
mmu_put_word_unaligned(uaecptr addr,uae_u16 val,int data)694 REGPARAM2 void mmu_put_word_unaligned(uaecptr addr, uae_u16 val, int data)
695 {
696 SAVE_EXCEPTION;
697 TRY(prb) {
698 mmu_put_byte(addr, val >> 8, data, sz_word);
699 mmu_put_byte(addr + 1, val, data, sz_word);
700 RESTORE_EXCEPTION;
701 }
702 CATCH(prb) {
703 RESTORE_EXCEPTION;
704 regs.wb3_data = val;
705 if (regs.mmu_fault_addr != addr) {
706 regs.mmu_fault_addr = addr;
707 regs.mmu_ssw |= MMU_SSW_MA;
708 }
709 breakpt();
710 THROW_AGAIN(prb);
711 }
712 }
713
mmu_put_byte_slow(uaecptr addr,uae_u8 val,int super,int data,int size,struct mmu_atc_line * cl)714 REGPARAM2 void mmu_put_byte_slow(uaecptr addr, uae_u8 val, int super, int data,
715 int size, struct mmu_atc_line *cl)
716 {
717 uae_u32 tag = ATC_TAG(addr);
718
719 if (cl->tag == (uae_u16)~tag) {
720 redo:
721 if (cl->hw) {
722 HWput_b(cl->phys + addr, val);
723 return;
724 }
725 regs.wb3_data = val;
726 mmu_bus_error(addr, mmu_get_fc(super, data), 1, size);
727 return;
728 }
729
730 if (!mmu_fill_atc_l1(addr, super, data, 1, cl))
731 goto redo;
732
733 do_put_mem_byte((uae_u8 *)mmu_get_real_address(addr, cl), val);
734 }
735
mmu_put_word_slow(uaecptr addr,uae_u16 val,int super,int data,int size,struct mmu_atc_line * cl)736 REGPARAM2 void mmu_put_word_slow(uaecptr addr, uae_u16 val, int super, int data,
737 int size, struct mmu_atc_line *cl)
738 {
739 uae_u32 tag = ATC_TAG(addr);
740
741 if (cl->tag == (uae_u16)~tag) {
742 redo:
743 if (cl->hw) {
744 HWput_w(cl->phys + addr, val);
745 return;
746 }
747 regs.wb3_data = val;
748 mmu_bus_error(addr, mmu_get_fc(super, data), 1, size);
749 return;
750 }
751
752 if (!mmu_fill_atc_l1(addr, super, data, 1, cl))
753 goto redo;
754
755 do_put_mem_word((uae_u16 *)mmu_get_real_address(addr, cl), val);
756 }
757
mmu_put_long_slow(uaecptr addr,uae_u32 val,int super,int data,int size,struct mmu_atc_line * cl)758 REGPARAM2 void mmu_put_long_slow(uaecptr addr, uae_u32 val, int super, int data,
759 int size, struct mmu_atc_line *cl)
760 {
761 uae_u32 tag = ATC_TAG(addr);
762
763 if (cl->tag == (uae_u16)~tag) {
764 redo:
765 if (cl->hw) {
766 HWput_l(cl->phys + addr, val);
767 return;
768 }
769 regs.wb3_data = val;
770 mmu_bus_error(addr, mmu_get_fc(super, data), 1, size);
771 return;
772 }
773
774 if (!mmu_fill_atc_l1(addr, super, data, 1, cl))
775 goto redo;
776
777 do_put_mem_long((uae_u32 *)mmu_get_real_address(addr, cl), val);
778 }
779
mmu_put_quad_slow(uaecptr addr,uae_u64 val,int super,int data,struct mmu_atc_line * cl)780 REGPARAM2 void mmu_put_quad_slow(uaecptr addr, uae_u64 val, int super, int data,
781 struct mmu_atc_line *cl)
782 {
783 mmu_put_long_slow(addr, (uae_u32)(val >> 32), super, data, sz_long, cl);
784 mmu_put_long_slow(addr + 4, (uae_u32)(val), super, data, sz_long, cl);
785 }
786
sfc_get_long(uaecptr addr)787 uae_u32 sfc_get_long(uaecptr addr)
788 {
789 int super = (regs.sfc & 4) != 0;
790 int data = (regs.sfc & 3) != 2;
791 uae_u32 res;
792
793 if (likely(!is_unaligned(addr, 4)))
794 return mmu_get_user_long(addr, super, data, sz_long);
795
796 if (likely(!(addr & 1))) {
797 res = (uae_u32)mmu_get_user_word(addr, super, data, sz_long) << 16;
798 SAVE_EXCEPTION;
799 TRY(prb) {
800 res |= mmu_get_user_word(addr + 2, super, data, sz_long);
801 RESTORE_EXCEPTION;
802 }
803 CATCH(prb) {
804 RESTORE_EXCEPTION;
805 regs.mmu_fault_addr = addr;
806 regs.mmu_ssw |= MMU_SSW_MA;
807 breakpt();
808 THROW_AGAIN(prb);
809 }
810 } else {
811 res = (uae_u32)mmu_get_user_byte(addr, super, data, sz_long) << 8;
812 SAVE_EXCEPTION;
813 TRY(prb) {
814 res = (res | mmu_get_user_byte(addr + 1, super, data, sz_long)) << 8;
815 res = (res | mmu_get_user_byte(addr + 2, super, data, sz_long)) << 8;
816 res |= mmu_get_user_byte(addr + 3, super, data, sz_long);
817 RESTORE_EXCEPTION;
818 }
819 CATCH(prb) {
820 RESTORE_EXCEPTION;
821 regs.mmu_fault_addr = addr;
822 regs.mmu_ssw |= MMU_SSW_MA;
823 breakpt();
824 THROW_AGAIN(prb);
825 }
826 }
827 return res;
828 }
829
sfc_get_word(uaecptr addr)830 uae_u16 sfc_get_word(uaecptr addr)
831 {
832 int super = (regs.sfc & 4) != 0;
833 int data = (regs.sfc & 3) != 2;
834 uae_u16 res;
835
836 if (likely(!is_unaligned(addr, 2)))
837 return mmu_get_user_word(addr, super, data, sz_word);
838
839 res = (uae_u16)mmu_get_user_byte(addr, super, data, sz_word) << 8;
840 SAVE_EXCEPTION;
841 TRY(prb) {
842 res |= mmu_get_user_byte(addr + 1, super, data, sz_word);
843 RESTORE_EXCEPTION;
844 }
845 CATCH(prb) {
846 RESTORE_EXCEPTION;
847 regs.mmu_fault_addr = addr;
848 regs.mmu_ssw |= MMU_SSW_MA;
849 breakpt();
850 THROW_AGAIN(prb);
851 }
852 return res;
853 }
854
sfc_get_byte(uaecptr addr)855 uae_u8 sfc_get_byte(uaecptr addr)
856 {
857 int super = (regs.sfc & 4) != 0;
858 int data = (regs.sfc & 3) != 2;
859
860 return mmu_get_user_byte(addr, super, data, sz_byte);
861 }
862
dfc_put_long(uaecptr addr,uae_u32 val)863 void dfc_put_long(uaecptr addr, uae_u32 val)
864 {
865 int super = (regs.dfc & 4) != 0;
866 int data = (regs.dfc & 3) != 2;
867
868 SAVE_EXCEPTION;
869 TRY(prb) {
870 if (likely(!is_unaligned(addr, 4)))
871 mmu_put_user_long(addr, val, super, data, sz_long);
872 else if (likely(!(addr & 1))) {
873 mmu_put_user_word(addr, val >> 16, super, data, sz_long);
874 mmu_put_user_word(addr + 2, val, super, data, sz_long);
875 } else {
876 mmu_put_user_byte(addr, val >> 24, super, data, sz_long);
877 mmu_put_user_byte(addr + 1, val >> 16, super, data, sz_long);
878 mmu_put_user_byte(addr + 2, val >> 8, super, data, sz_long);
879 mmu_put_user_byte(addr + 3, val, super, data, sz_long);
880 }
881 RESTORE_EXCEPTION;
882 }
883 CATCH(prb) {
884 RESTORE_EXCEPTION;
885 regs.wb3_data = val;
886 if (regs.mmu_fault_addr != addr) {
887 regs.mmu_fault_addr = addr;
888 regs.mmu_ssw |= MMU_SSW_MA;
889 }
890 breakpt();
891 THROW_AGAIN(prb);
892 }
893 }
894
dfc_put_word(uaecptr addr,uae_u16 val)895 void dfc_put_word(uaecptr addr, uae_u16 val)
896 {
897 int super = (regs.dfc & 4) != 0;
898 int data = (regs.dfc & 3) != 2;
899
900 SAVE_EXCEPTION;
901 TRY(prb) {
902 if (likely(!is_unaligned(addr, 2)))
903 mmu_put_user_word(addr, val, super, data, sz_word);
904 else {
905 mmu_put_user_byte(addr, val >> 8, super, data, sz_word);
906 mmu_put_user_byte(addr + 1, val, super, data, sz_word);
907 }
908 RESTORE_EXCEPTION;
909 }
910 CATCH(prb) {
911 RESTORE_EXCEPTION;
912 regs.wb3_data = val;
913 if (regs.mmu_fault_addr != addr) {
914 regs.mmu_fault_addr = addr;
915 regs.mmu_ssw |= MMU_SSW_MA;
916 }
917 breakpt();
918 THROW_AGAIN(prb);
919 }
920 }
921
dfc_put_byte(uaecptr addr,uae_u8 val)922 void dfc_put_byte(uaecptr addr, uae_u8 val)
923 {
924 int super = (regs.dfc & 4) != 0;
925 int data = (regs.dfc & 3) != 2;
926
927 SAVE_EXCEPTION;
928 TRY(prb) {
929 mmu_put_user_byte(addr, val, super, data, sz_byte);
930 RESTORE_EXCEPTION;
931 }
932 CATCH(prb) {
933 RESTORE_EXCEPTION;
934 regs.wb3_data = val;
935 breakpt();
936 THROW_AGAIN(prb);
937 }
938 }
939
mmu_op(uae_u32 opcode,uae_u16 extra)940 void mmu_op(uae_u32 opcode, uae_u16 extra)
941 {
942 int super = (regs.dfc & 4) != 0;
943 DUNUSED(extra);
944 if ((opcode & 0xFE0) == 0x0500) {
945 int regno, glob;
946 //D(didflush = 0);
947 uae_u32 addr;
948 /* PFLUSH */
949 regno = opcode & 7;
950 glob = (opcode & 8) != 0;
951
952 if (opcode & 16) {
953 D(bug("pflusha(%u,%u)", glob, regs.dfc));
954 mmu_flush_atc_all(glob);
955 } else {
956 addr = m68k_areg(regs, regno);
957 D(bug("pflush(%u,%u,%x)", glob, regs.dfc, addr));
958 mmu_flush_atc(addr, super, glob);
959 }
960 flush_internals();
961 #ifdef USE_JIT
962 flush_icache(0);
963 #endif
964 } else if ((opcode & 0x0FD8) == 0x548) {
965 int write, regno;
966 uae_u32 addr;
967
968 regno = opcode & 7;
969 write = (opcode & 32) == 0;
970 addr = m68k_areg(regs, regno);
971 //bug("ptest(%u,%u,%x)", write, regs.dfc, addr);
972 D(bug("PTEST%c (A%d) %08x DFC=%d", write ? 'W' : 'R', regno, addr, regs.dfc));
973 mmu_flush_atc(addr, super, true);
974 SAVE_EXCEPTION;
975 TRY(prb) {
976 struct mmu_atc_line *l;
977 uae_u32 desc;
978 bool data = (regs.dfc & 3) != 2;
979
980 l = &atc_l2[super][ATC_L2_INDEX(addr)];
981 desc = mmu_fill_atc_l2(addr, super, data, write, l);
982 if (!(data ? l->valid_data : l->valid_inst))
983 regs.mmusr = MMU_MMUSR_B;
984 else if (l->tt)
985 regs.mmusr = MMU_MMUSR_T | MMU_MMUSR_R;
986 else {
987 regs.mmusr = desc & (~0xfff|MMU_MMUSR_G|MMU_MMUSR_Ux|MMU_MMUSR_S|
988 MMU_MMUSR_CM|MMU_MMUSR_M|MMU_MMUSR_W);
989 regs.mmusr |= MMU_MMUSR_R;
990 }
991 }
992 CATCH(prb) {
993 regs.mmusr = MMU_MMUSR_B;
994 }
995 RESTORE_EXCEPTION;
996 D(bug("PTEST result: mmusr %08x", regs.mmusr));
997 } else
998 op_illg (opcode);
999 }
1000
mmu_flush_atc(uaecptr addr,bool super,bool global)1001 void mmu_flush_atc(uaecptr addr, bool super, bool global)
1002 {
1003 struct mmu_atc_line *l;
1004 int i, j;
1005
1006 l = atc_l1[super][0][0];
1007 i = ATC_L1_INDEX(addr);
1008 for (j = 0; j < 4; j++) {
1009 if (global || !l[i].global)
1010 l[i].tag = 0x8000;
1011 l += ATC_L1_SIZE;
1012 }
1013 if (regs.mmu_pagesize_8k) {
1014 i = ATC_L1_INDEX(addr) ^ 1;
1015 for (j = 0; j < 4; j++) {
1016 if (global || !l[i].global)
1017 l[i].tag = 0x8000;
1018 l += ATC_L1_SIZE;
1019 }
1020 }
1021 l = atc_l2[super];
1022 i = ATC_L2_INDEX(addr);
1023 if (global || !l[i].global)
1024 l[i].tag = 0x8000;
1025 if (regs.mmu_pagesize_8k) {
1026 i ^= 1;
1027 if (global || !l[i].global)
1028 l[i].tag = 0x8000;
1029 }
1030 }
1031
mmu_flush_atc_all(bool global)1032 void mmu_flush_atc_all(bool global)
1033 {
1034 struct mmu_atc_line *l;
1035 unsigned int i;
1036
1037 l = atc_l1[0][0][0];
1038 for (i = 0; i < sizeof(atc_l1) / sizeof(*l); l++, i++) {
1039 if (global || !l->global)
1040 l->tag = 0x8000;
1041 }
1042
1043 l = atc_l2[0];
1044 for (i = 0; i < sizeof(atc_l2) / sizeof(*l); l++, i++) {
1045 if (global || !l->global)
1046 l->tag = 0x8000;
1047 }
1048 }
1049
mmu_reset(void)1050 void mmu_reset(void)
1051 {
1052 mmu_flush_atc_all(true);
1053
1054 regs.urp = regs.srp = 0;
1055 regs.itt0 = regs.itt1 = 0;
1056 regs.dtt0 = regs.dtt1 = 0;
1057 regs.mmusr = 0;
1058 }
1059
1060
mmu_set_tc(uae_u16 tc)1061 void mmu_set_tc(uae_u16 tc)
1062 {
1063 if (regs.tc == tc)
1064 return;
1065
1066 regs.tc = tc;
1067 regs.mmu_enabled = tc & 0x8000 ? 1 : 0;
1068 regs.mmu_pagesize_8k = tc & 0x4000 ? 1 : 0;
1069 mmu_flush_atc_all(true);
1070
1071 D(bug("MMU: enabled=%d page8k=%d\n", regs.mmu_enabled, regs.mmu_pagesize_8k));
1072 }
1073
mmu_set_super(bool super)1074 void mmu_set_super(bool super)
1075 {
1076 current_atc = &atc_l1[super];
1077 }
1078
1079 #else
1080
mmu_op(uae_u32 opcode,uae_u16)1081 void mmu_op(uae_u32 opcode, uae_u16 /*extra*/)
1082 {
1083 if ((opcode & 0xFE0) == 0x0500) {
1084 /* PFLUSH instruction */
1085 flush_internals();
1086 } else if ((opcode & 0x0FD8) == 0x548) {
1087 /* PTEST instruction */
1088 } else
1089 op_illg(opcode);
1090 }
1091
1092 #endif
1093
1094 /*
1095 vim:ts=4:sw=4:
1096 */
1097