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