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