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 ? &regs.dtt0 : &regs.itt0;
77 	uae_u32 * ttr1 = datamode ? &regs.dtt1 : &regs.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