]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
MMU support
authorToni Wilen <twilen@winuae.net>
Thu, 30 May 2019 18:59:53 +0000 (21:59 +0300)
committerToni Wilen <twilen@winuae.net>
Thu, 30 May 2019 18:59:53 +0000 (21:59 +0300)
utilities/stateload/asm.S
utilities/stateload/header.h
utilities/stateload/main.c
utilities/stateload/makefile
utilities/stateload/mmu.c [new file with mode: 0644]

index 9e126a0afc86aaa3e34a48d1a6f9027505d238f4..27088504fca2efa275b6bf2de6e52a2c4240a98a 100644 (file)
@@ -7,12 +7,9 @@ AGA_COLORS_CHUNK = CUSTOM_CHUNK+4
 FLOPPY_CHUNK = AGA_COLORS_CHUNK+4
 AUDIO_CHUNK = FLOPPY_CHUNK+4*4
 SPRITE_CHUNK = AUDIO_CHUNK+4*4
-MAP_ROM = SPRITE_CHUNK+4
-MAP_ROM_SIZE = MAP_ROM+4
-MAP_ROM_TYPE = MAP_ROM_SIZE+4
+MMU_TABLE = SPRITE_CHUNK+4*8
 
        .text
-       .chip 68020
        .globl _runit
        .globl _killsystem
        .globl _callinflate
@@ -52,7 +49,7 @@ _killsystem:
        move.l d0,a0
 
        | debug trigger
-       move.w #0x1234,0xfc0006
+       move.w #0x1234,0xef0006
 
        | check if 68060
        move.l 0x10.w,a1
@@ -86,6 +83,10 @@ _killsystem:
        move.w d0,0x9c(a0)
        move.w d0,0x9e(a0)
        move.l d1,a0
+       
+       move.l 8(a0),d0
+       bsr.w mmu_enable
+       
        move.l 4(a0),sp | new temp super stack
        move.l 8(a0),-(sp) | uaestate
        move.l 12(a0),a0 | func
@@ -97,7 +98,7 @@ _runit:
        subq.l #8,sp
        lea 0xdff000,a6
        
-       | find last line
+| find last line
        moveq #0,d5
        lea 0x6(a6),a2
 .wait1:
@@ -126,7 +127,7 @@ _runit:
        bne.s .wait3
 
        | debug trigger
-       move.w #0x1234,0xfc0004
+       move.w #0x1234,0xef0004
 
        move.l CUSTOM_CHUNK(a4),a0
        move.w 4+0x96(a0),d0 | DMACON
@@ -171,7 +172,8 @@ _runit:
        move.l (a1)+,d0
        btst #3,d1
        bne.s .cpu68040
-       movec d0,CAAR
+       | movec d0,CAAR
+       dc.w 0x4e7b,0x0802
 .cpu68040:
        move.l (a1)+,d0
        movec d0,CACR
@@ -221,7 +223,7 @@ _runit:
        add.w #4+4+6*4,a0
 
        | debug trigger
-       move.w #0x1234,0xfc0000
+       move.w #0x1234,0xef0000
 
        lea 0xbfde00,a1
 
@@ -241,3 +243,59 @@ _runit:
        movem.l (a0),d6-d7/a0-a1
        
        rte | GO! GO! GO!
+
+mmu_enable:
+       movem.l d0-d2/a0-a2,-(sp)
+       move.l d0,a2
+
+       | debug trigger
+       move.w #0x1234,0xef0008
+
+       move.l MMU_TABLE(a2),d2
+       beq.s .mmuend
+
+       move.w (a2),d1
+       btst #3,d1
+       beq.s .no68040
+
+       | 68040/060
+       movec d2,urp
+       movec d2,srp
+       cpusha dc
+       cinva dc
+       pflusha
+       move.l #0x8000,d0
+       movec d0,tc
+       moveq #0,d0
+       movec d0,itt0
+       movec d0,itt1
+       movec d0,dtt0
+       movec d0,dtt1
+       bra.s .mmuend
+
+.no68040:
+       | 68030?
+       btst #2,d1
+       beq.s .mmuend
+
+       sub.w #12,sp
+       move.l #0x00c07760,(sp)
+       | pmove (sp),tc
+       dc.w 0xf017,0x4000
+       move.l #0x80000002,4(sp)
+       move.l d2,8(sp)
+       | pmove 4(sp),crp
+       dc.w 0xf02f,0x4c00,0x0004
+       bset #7,(sp)
+       | pmove (sp),tc
+       dc.w 0xf017,0x4000
+       clr.l (sp)
+       | pmove (sp),tt0
+       dc.w 0xf017,0x0800
+       | pmove (sp),tt1
+       dc.w 0xf017,0x0c00
+       add.w #12,sp
+
+.mmuend:
+       movem.l (sp)+,d0-d2/a0-a2
+       rts
index f930e52842e6a3e9332aa8de4edaccad8201befe..5810b0d2c7c737bf81736f9aa3bc7f9a46880c14 100644 (file)
@@ -1,7 +1,7 @@
 
 #define TEMP_STACK_SIZE 8000
 
-#define ALLOCATIONS 30
+#define ALLOCATIONS 256
 
 struct Allocation
 {
@@ -33,6 +33,8 @@ struct MemoryBank
 #define MAPROM_ACA12xx 4
 #define MAPROM_GVP 5
 #define MAPROM_BLIZZARD12x0 6
+#define MAPROM_ACA1233N 7
+#define MAPROM_MMU 255
 
 #define FLAGS_NOCACHE 1
 #define FLAGS_FORCEPAL 2
@@ -57,9 +59,11 @@ struct uaestate
        UBYTE *floppy_chunk[4];
        UBYTE *audio_chunk[4];
        UBYTE *sprite_chunk[8];
+       ULONG *MMU_Level_A;
 
        UBYTE *maprom;
        ULONG mapromsize;
+       ULONG maprom_memlimit;
        struct mapromdata mrd[2];
 
        UBYTE *extra_ram;
@@ -74,11 +78,23 @@ struct uaestate
        int num_allocations;
        struct Allocation allocations[ALLOCATIONS];
        
+       WORD mmutype;
+       UBYTE *page_ptr;
+       ULONG page_free;
+       
        UWORD romver, romrev;
        UBYTE agastate;
        UBYTE usemaprom;
        UBYTE debug;
        UBYTE testmode;
        UBYTE nowait;
+       UBYTE canusemmu;
+       UBYTE mmuused;
 };
 
+UBYTE *allocate_abs(ULONG size, ULONG addr, struct uaestate *st);
+
+BOOL map_region(struct uaestate *st, void *addr, void *physaddr, ULONG size, BOOL invalid, BOOL writeprotect, BOOL supervisor, UBYTE cachemode);
+BOOL unmap_region(struct uaestate *st, void *addr, ULONG size);
+BOOL init_mmu(struct uaestate *st);
+
index 90140a96e24b565501ef76526c51df0c7229b640..84cd3a0553414eca51f476084c3e3724f58888d6 100644 (file)
@@ -2,7 +2,7 @@
 /* Real hardware UAE state file loader */
 /* Copyright 2019 Toni Wilen */
 
-#define VER "1.1 BETA #1"
+#define VER "2.0 BETA #1"
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -413,13 +413,48 @@ static void free_allocations(struct uaestate *st)
        }
 }
 
-static UBYTE *allocate_abs(ULONG size, ULONG addr, struct uaestate *st)
+static struct Allocation *add_allocation(void *addr, ULONG size, struct uaestate *st)
+{
+       if (st->num_allocations >= ALLOCATIONS) {
+               printf("ERROR: Too many allocations!\n");
+               return NULL;
+       }
+       struct Allocation *a = &st->allocations[st->num_allocations++];
+       a->addr = addr;
+       a->size = size;
+       return a;
+}
+
+static ULONG mmu_remap(ULONG addr, ULONG size, BOOL wp, struct uaestate *st)
+{
+       if (!st->canusemmu)
+               return 0;
+       void *phys = AllocMem(size + 4095, MEMF_FAST);
+       if (!phys) {
+               printf("MMU: Error allocating remap space for %08lx-%08lx.\n", addr, addr + size - 1);
+               return 0;
+       }
+       Forbid();
+       FreeMem(phys, size + 4095);
+       ULONG alignedphys = (((ULONG)phys) + 4095) & ~4095;
+       phys = AllocAbs(size, (void*)alignedphys);
+       Permit();
+       if (!phys)
+               return 0;
+       if (!map_region(st, (void*)addr, phys, size, FALSE, wp, FALSE, 0)) {
+               FreeMem(phys, size);
+               return 0;
+       }
+       add_allocation(phys, size, st);
+       st->mmuused++;
+       return alignedphys;
+}
+
+UBYTE *allocate_abs(ULONG size, ULONG addr, struct uaestate *st)
 {
                UBYTE *b = AllocAbs(size, (APTR)addr);
                if (b) {
-                       struct Allocation *a = &st->allocations[st->num_allocations++];
-                       a->addr = b;
-                       a->size = size;
+                       add_allocation(b, size, st);
                        return b;
                }
                return NULL;
@@ -432,9 +467,7 @@ static UBYTE *extra_allocate(ULONG size, struct uaestate *st)
        for (;;) {
                b = AllocAbs(size, st->extra_mem_pointer);
                if (b) {
-                       struct Allocation *a = &st->allocations[st->num_allocations++];
-                       a->addr = b;
-                       a->size = size;
+                       add_allocation(b, size, st);
                        st->extra_mem_pointer += (size + 7) & ~7;
                        return b;
                }
@@ -451,10 +484,9 @@ static UBYTE *tempmem_allocate(ULONG size, struct uaestate *st)
        if (st->extra_mem_head) {
                b = Allocate(st->extra_mem_head, size);
                if (b) {
-                       struct Allocation *a = &st->allocations[st->num_allocations++];
-                       a->mh = st->extra_mem_head;
-                       a->addr = b;
-                       a->size = size;
+                       struct Allocation *a = add_allocation(b, size, st);
+                       if (a)
+                               a->mh = st->extra_mem_head;
                }
        }
        if (!b) {
@@ -476,9 +508,7 @@ static UBYTE *tempmem_allocate_reserved(ULONG size, WORD index, struct uaestate
                        return NULL;
                UBYTE *b = AllocAbs(size, addr);
                if (b) {
-                       struct Allocation *a = &st->allocations[st->num_allocations++];
-                       a->addr = b;
-                       a->size = size;
+                       add_allocation(b, size, st);
                        return b;
                }
        }
@@ -508,7 +538,7 @@ static void copyrom(ULONG addr, struct uaestate *st)
 
 static void set_maprom(struct uaestate *st)
 {
-       struct mapromdata *mrd = &st->mrd[0];
+       struct mapromdata *mrd = &st->mrd[1];
        if (mrd->type == MAPROM_ACA500 || mrd->type == MAPROM_ACA500P) {
                volatile UBYTE *base = (volatile UBYTE*)mrd->board;
                base[0x3000] = 0;
@@ -551,6 +581,33 @@ static void set_maprom(struct uaestate *st)
                base[0x18 / 2] = 0xffff; // maprom on
                volatile UWORD dummy = base[0];
        }
+       if (mrd->type == MAPROM_ACA1233N) {
+               volatile UWORD *base = (volatile UWORD*)mrd->board;
+               ULONG mapromaddr = mrd->addr;
+               volatile UWORD dummy = base[0];
+               base[0x00 / 2] = 0xffff; // s unlock 0
+               base[0x02 / 2] = 0xffff; // s unlock 1
+               base[0x20 / 2] = 0xffff; // c unlock 0
+               base[0x04 / 2] = 0xffff; // s unlock 2
+               base[0x22 / 2] = 0xffff; // c unlock 1
+               base[0x06 / 2] = 0xffff; // s unlock 3
+               // maprom off
+               base[0x28 / 2] = 0xffff;
+               if (mrd->config) {
+                       // maprom overlay on
+                       for(int i = 0; i < 6; i++)
+                               base[0x1c / 2] = 0xffff;
+                       base[0x1a / 2] = 0xffff;
+               }
+               copyrom(mapromaddr, st);
+               copyrom(mapromaddr + 524288, st);
+               if (mrd->config) {
+                       // maprom overlay off
+                       base[0x3a / 2] = 0xffff;
+               }
+               // maprom on
+               base[0x08 / 2] = 0xffff;
+       }
        if (mrd->type == MAPROM_GVP) {
                copyrom(mrd->addr, st);
                volatile UWORD *base = (volatile UWORD*)mrd->board;
@@ -561,6 +618,9 @@ static void set_maprom(struct uaestate *st)
                volatile UBYTE *base = (volatile UBYTE*)mrd->board;
                *base = (UBYTE)mrd->config;
        }
+       if (mrd->type == MAPROM_MMU) {
+               copyrom(mrd->addr, st);
+       }
 }
 
 static BOOL has_maprom_blizzard(struct uaestate *st)
@@ -686,7 +746,6 @@ static BOOL has_maprom_gvp(struct uaestate *st)
 
 static BOOL has_maprom_aca(struct uaestate *st)
 {
-               struct mapromdata *mrd = &st->mrd[0];
        if (OpenResource("aca.resource")) {
                struct mapromdata *mrd2 = &st->mrd[1];
                // ACA500(+)
@@ -712,15 +771,32 @@ static BOOL has_maprom_aca(struct uaestate *st)
                        mrd2->type = MAPROM_ACA500P;
                        mrd2->addr = 0xa00000;
                }
-               mrd->board = (APTR)base;
+               mrd2->board = (APTR)base;
                base[0x3000] = 0;
                Enable();
                if (st->debug)
                        printf("ACA500/ACA500plus ID=%02x\n", id);
        }
-       if (FindConfigDev(0, 0x1212, 0x16)) {
+       struct mapromdata *mrd = &st->mrd[0];
+       if (FindConfigDev(0, 0x1212, 33) || FindConfigDev(0, 0x1212, 68)) {
+               // ACA1233n 68030
+               mrd->type = MAPROM_ACA1233N;
+               mrd->addr = 0x47f00000;
+               mrd->board = (APTR)0x47e8f000;
+               mrd->config = 0;
+       } else if (FindConfigDev(0, 0x1212, 32) || FindConfigDev(0, 0x1212, 72)) {
+               // ACA1233n 68EC020
+               if ((ULONG)has_maprom_aca >= 0x200000) {
+                       mrd->type = MAPROM_ACA1233N;
+                       mrd->addr = 0x100000;
+                       mrd->board = (APTR)0xb8f000;
+                       mrd->config = 1;
+                       st->maprom_memlimit |= 1 << MB_CHIP;
+               }
+       } else if (FindConfigDev(0, 0x1212, 0x16)) {
                // ACA1221EC
                mrd->type = MAPROM_ACA1221EC;
+               mrd->addr = 0x780000;
                mrd->board = (APTR)0xe90000;
                // we can't use 0x200000 because it goes away when setting up maprom..
                mrd->memunavailable = 0x00200000;
@@ -763,7 +839,7 @@ static BOOL has_maprom_aca(struct uaestate *st)
                ULONG mraddr = 0;
                if (mrtest) {
                        if (TypeOfMem((APTR)0x0bf00000)) {
-                               if (!TypeOfMem((APTR)0x0fd80000)) {
+                               if (!TypeOfMem((APTR)0x0fe80000)) {
                                        mrd->type = MAPROM_ACA12xx;
                                        mrd->addr = 0x0ff00000;
                                }
@@ -777,13 +853,34 @@ static BOOL has_maprom_aca(struct uaestate *st)
        return st->mrd[0].type != 0;
 }
 
-static WORD has_maprom(struct uaestate *st)
+static BOOL has_maprom_mmu(struct uaestate *st)
+{
+       if (!st->canusemmu)
+               return FALSE;
+       struct mapromdata *mrd = &st->mrd[0];
+       unmap_region(st, (void*)0xf80000, 524288);
+       mrd->addr = mmu_remap(0xf80000, 524288, FALSE, st);
+       if (mrd->addr) {
+               mrd->type = MAPROM_MMU;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static BOOL has_maprom(struct uaestate *st)
 {
        if (!st->usemaprom)
                return -1;
-       if (!has_maprom_aca(st)) {
-               if (!has_maprom_gvp(st))
-                       has_maprom_blizzard(st);
+       for (;;) {
+               if (has_maprom_mmu(st))
+                       break;
+               if (has_maprom_aca(st))
+                       break;
+               if (has_maprom_gvp(st))
+                       break;
+               if (!has_maprom_blizzard(st))
+                       break;
+               return FALSE;
        }
        if (st->debug) {
                for (int i = 0; i < 2; i++) {
@@ -797,34 +894,42 @@ static WORD has_maprom(struct uaestate *st)
 
 static void load_rom(struct uaestate *st)
 {
-       UBYTE rompath[100];
+       UBYTE rompath[100], rompath2[100];
+       UBYTE *p;
        
        if (!st->mrd[0].type && !st->mrd[1].type)
                return;
        
        sprintf(rompath, "DEVS:kickstarts/kick%d%03d.%s", st->romver, st->romrev, st->agastate ? "a1200" : "a500");
+       p = rompath;
        FILE *f = fopen(rompath, "rb");
        if (!f) {
-               printf("Couldn't open ROM image '%s'\n", rompath);
-               return;
+               sprintf(rompath2, "kick%d%03d.%s", st->romver, st->romrev, st->agastate ? "a1200" : "a500");
+               f = fopen(rompath2, "rb");
+               if (!f) {
+                       printf("Couldn't open ROM image '%s'\n", rompath);
+                       return;
+               }
+               p = rompath2;
        }
        fseek(f, 0, SEEK_END);
        st->mapromsize = ftell(f);
        fseek(f, 0, SEEK_SET);
-       if (!(st->maprom = tempmem_allocate_reserved(st->mapromsize, MB_CHIP, st))) {
-               if (!(st->maprom = tempmem_allocate_reserved(st->mapromsize, MB_SLOW, st))) {
-                       st->maprom = tempmem_allocate(st->mapromsize, st);
-               }
-       }
+       if (!st->maprom && !(st->maprom_memlimit & (1 << MB_CHIP)))
+               st->maprom = tempmem_allocate_reserved(st->mapromsize, MB_CHIP, st);
+       if (!st->maprom && !(st->maprom_memlimit & (1 << MB_SLOW)))
+               st->maprom = tempmem_allocate_reserved(st->mapromsize, MB_SLOW, st);
+       if (!st->maprom)
+               st->maprom = tempmem_allocate(st->mapromsize, st);
        if (!st->maprom) {
-               printf("Couldn't allocate %luk for ROM image '%s'.\n", st->mapromsize >> 10, rompath);
+               printf("Couldn't allocate %luk for ROM image '%s'.\n", st->mapromsize >> 10, p);
                fclose(f);
                return;
        }
        if (st->debug)
                printf("MapROM temp %08lx\n", st->maprom);
        if (fread(st->maprom, 1, st->mapromsize, f) != st->mapromsize) {
-               printf("Read error while reading map rom image '%s'\n", rompath);
+               printf("Read error while reading map rom image '%s'\n", p);
                fclose(f);
                return;
        }
@@ -1050,9 +1155,17 @@ static ULONG check_ram(UBYTE *ramname, UBYTE *cname, UBYTE *chunk, WORD index, U
        }
        Permit();
        if (!found) {
-               printf("ERROR: Required RAM address space %08x-%08x unavailable.\n", addr, addr + size - 1);
-               st->errors++;
-               return 0;
+               // use MMU to create this address space if available
+               if (mmu_remap(addr, size, FALSE, st)) {
+                       msize = size;
+                       mstart = addr;
+                       mh = NULL;
+                       found = 1;
+               } else {
+                       printf("ERROR: Required RAM address space %08x-%08x unavailable.\n", addr, addr + size - 1);
+                       st->errors++;
+                       return 0;
+               }
        }
        st->mem_allocated[index] = mh;
        struct MemoryBank *mb = &st->membanks[index];
@@ -1076,6 +1189,18 @@ static ULONG check_ram(UBYTE *ramname, UBYTE *cname, UBYTE *chunk, WORD index, U
                }
                return 1;
        }
+       // if too small RAM area and not chip ram: mmu remap the missing part.
+       if (st->canusemmu) {
+               ULONG mmu_start = mstart + msize;
+               ULONG mmu_size = size - msize;
+               if (mmu_remap(mmu_start, mmu_size, FALSE, st)) {
+                       printf("- MMU remapped missing address space %08x-%08x\n", mmu_start, mmu_start + mmu_size - 1);
+                       if (mstart == 0) {
+                               printf("- WARNING: Part of Chip RAM remapped, custom chipset can't access it!!\n");
+                       }
+                       return 1;
+               }
+       }
        printf("ERROR: Not enough %s RAM available. %luk required.\n", ramname, size >> 10);
        st->errors++;
        return 0;
@@ -1463,6 +1588,7 @@ int main(int argc, char *argv[])
                printf("- debug = enable debug output.\n");
                printf("- test = test mode.\n");
                printf("- nomaprom = do not use map rom.\n");
+               printf("- nommu = do not use MMU (68030/68040/68060).\n");
                printf("- nocache = disable caches before starting (68020+)\n");
                printf("- pal/ntsc = set PAL or NTSC mode (ECS/AGA only)\n");
                return 0;
@@ -1480,6 +1606,7 @@ int main(int argc, char *argv[])
                return 0;
        }
        st->usemaprom = 1;
+       st->canusemmu = 1;
        for(int i = 2; i < argc; i++) {
                if (!stricmp(argv[i], "debug"))
                        st->debug = 1;
@@ -1489,6 +1616,8 @@ int main(int argc, char *argv[])
                        st->nowait = 1;
                if (!stricmp(argv[i], "nomaprom"))
                        st->usemaprom = 0;
+               if (!stricmp(argv[i], "nommu"))
+                       st->canusemmu = 0;
                if (!stricmp(argv[i], "nocache"))
                        st->flags |= FLAGS_NOCACHE;
                if (!stricmp(argv[i], "pal"))
@@ -1497,6 +1626,16 @@ int main(int argc, char *argv[])
                        st->flags |= FLAGS_FORCENTSC;
        }
 
+       if (!(SysBase->AttnFlags & AFF_68030))
+               st->canusemmu = 0;
+
+       if (st->canusemmu) {
+               if (!init_mmu(st)) {
+                       printf("ERROR: MMU page table allocation failed.\n");
+                       st->canusemmu = 0;
+               }
+       }
+
        if (!parse_pass_1(f, st)) {
                fseek(f, 0, SEEK_SET);
                if (!parse_pass_2(f, st)) {
index dd639609e7ccd8d00aa53505a974290a2ee0757a..f6a65e739c3f1e39c38869e61d1c57595b8ab133 100644 (file)
@@ -8,7 +8,7 @@ AS=/opt/amiga/bin/m68k-amigaos-as
 CFLAGS = -mcrt=nix13 -Os -m68000 -fomit-frame-pointer -msmall-code -DREVDATE=$(NOWDATE) -DREVTIME=$(NOWTIME)
 LINK_CFLAGS = -mcrt=nix13 -s
 
-OBJS = main.o asm.o inflate.o
+OBJS = main.o asm.o inflate.o mmu.o
 
 all: $(OBJS)
        $(CC) $(LINK_CFLAGS) -o ussload $^
@@ -16,8 +16,11 @@ all: $(OBJS)
 main.o: main.c
        $(CC) $(CFLAGS) -I. -c -o $@ main.c
 
+mmu.o: mmu.c
+       $(CC) $(CFLAGS) -I. -c -o $@ mmu.c
+
 asm.o: asm.S
-       $(AS) -o $@ asm.S
+       $(AS) -m68040  -o $@ asm.S
 
 inflate.o: inflate.S
        $(CC) $(CFLAGS) -I. -c -o $@ inflate.S
diff --git a/utilities/stateload/mmu.c b/utilities/stateload/mmu.c
new file mode 100644 (file)
index 0000000..0fa2474
--- /dev/null
@@ -0,0 +1,206 @@
+
+/* I made this originally for AROS m68k */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <exec/types.h>
+#include <exec/memory.h>
+#include <exec/execbase.h>
+#include <proto/exec.h>
+
+#include "header.h"
+
+#define MMU030 1
+#define MMU040 2
+#define MMU060 3
+
+#define CM_WRITETHROUGH 0
+#define CM_COPYBACK 1
+#define CM_SERIALIZED 2
+#define CM_NONCACHEABLE 3
+
+#define LEVELA_SIZE 7
+#define LEVELB_SIZE 7
+#define LEVELC_SIZE 6
+#define PAGE_SIZE 12 // = 1 << 12 = 4096
+
+/* Macros that hopefully make MMU magic a bit easier to understand.. */
+
+#define LEVELA_VAL(x) ((((ULONG)(x)) >> (32 - (LEVELA_SIZE                                                     ))) & ((1 << LEVELA_SIZE) - 1))
+#define LEVELB_VAL(x) ((((ULONG)(x)) >> (32 - (LEVELA_SIZE + LEVELB_SIZE                         ))) & ((1 << LEVELB_SIZE) - 1))
+#define LEVELC_VAL(x) ((((ULONG)(x)) >> (32 - (LEVELA_SIZE + LEVELB_SIZE + LEVELC_SIZE))) & ((1 << LEVELC_SIZE) - 1))
+
+#define LEVELA(root, x) (root[LEVELA_VAL(x)])
+#define LEVELB(a, x) (((ULONG*)(((ULONG)a) & ~((1 << (LEVELB_SIZE + 2)) - 1)))[LEVELB_VAL(x)])
+#define LEVELC(b, x) (((ULONG*)(((ULONG)b) & ~((1 << (LEVELC_SIZE + 2)) - 1)))[LEVELC_VAL(x)])
+
+#define INVALID_DESCRIPTOR 0xDEAD0000
+#define ISINVALID(x) ((((ULONG)x) & 3) == 0)
+
+static BOOL map_region2(struct uaestate *st, void *addr, void *physaddr, ULONG size, BOOL invalid, BOOL writeprotect, BOOL supervisor, UBYTE cachemode);
+
+
+static void map_pagetable(struct uaestate *st, void *addr, ULONG size)
+{
+       /* 68040+ MMU tables should be serialized */
+       map_region2(st, addr, NULL, size, FALSE, FALSE, FALSE, CM_SERIALIZED);
+}
+
+/* Allocate MMU descriptor page, it needs to be (1 << bits) * sizeof(ULONG) aligned */
+static ULONG alloc_descriptor(struct uaestate *st, UBYTE bits, UBYTE level)
+{
+       ULONG *desc, dout;
+       ULONG size = sizeof(ULONG) * (1 << bits);
+       ULONG ps = 1 << PAGE_SIZE;
+       UWORD i;
+
+       while (st->page_free >= size && (((ULONG)st->page_ptr) & (size - 1))) {
+               st->page_ptr += 0x100;
+               st->page_free -= 0x100;
+       }
+       while (st->page_free < size) {
+               /* allocate in aligned blocks of PAGE_SIZE */
+               UBYTE *mem, *newmem, *pagemem;
+
+               // by design fail if no FAST RAM available
+               mem = AllocMem(2 * ps, MEMF_FAST);
+               if (!mem)
+                               return 0;
+               Forbid();
+               FreeMem(mem, 2 * ps);
+               newmem = (UBYTE*)((((ULONG)mem) + ps - 1) & ~(ps - 1));
+               pagemem = allocate_abs(ps, (ULONG)newmem, st);
+               Permit();
+               if (!pagemem)
+                               return 0;
+               st->page_ptr = pagemem;
+               st->page_free = ps;
+               if (level > 0 && st->mmutype >= MMU040)
+                       map_pagetable(st, pagemem, ps);
+       }
+       desc = (ULONG*)st->page_ptr;
+       for (i = 0; i < (1 << bits); i++)
+               desc[i] = INVALID_DESCRIPTOR;
+       dout = (ULONG)desc;
+       if (st->mmutype == MMU030)
+               dout |= 2; /* Valid 4 byte descriptor */
+       else
+               dout |= 3; /* Resident descriptor */
+       st->page_ptr += size;
+       st->page_free -= size;
+       return dout;
+}      
+               
+static BOOL map_region2(struct uaestate *st, void *addr, void *physaddr, ULONG size, BOOL invalid, BOOL writeprotect, BOOL supervisor, UBYTE cachemode)
+{
+       ULONG desca, descb, descc, pagedescriptor;
+       ULONG page_size = 1 << PAGE_SIZE;
+       ULONG page_mask = page_size - 1;
+
+       if ((size & page_mask) || (((ULONG)addr) & page_mask) || (((ULONG)physaddr) & page_mask))
+                       return FALSE;
+       if (physaddr == NULL)
+                       physaddr = addr;
+
+       while (size) {
+               desca = LEVELA(st->MMU_Level_A, addr);
+               if (ISINVALID(desca))
+                               desca = LEVELA(st->MMU_Level_A, addr) = alloc_descriptor(st, LEVELB_SIZE, 1);
+               if (ISINVALID(desca))
+                               return FALSE;
+               descb = LEVELB(desca, addr);
+               if (ISINVALID(descb))
+                               descb = LEVELB(desca, addr) = alloc_descriptor(st, LEVELC_SIZE, 2);
+               if (ISINVALID(descb))
+                               return FALSE;
+               descc = LEVELC(descb, addr);
+
+               if (invalid) {
+                       pagedescriptor = INVALID_DESCRIPTOR;
+    } else {
+                       pagedescriptor = ((ULONG)physaddr) & ~page_mask;
+                       BOOL wasinvalid = ISINVALID(descc);
+                       if (st->mmutype == MMU030) {
+                               pagedescriptor |= 1; // page descriptor
+                               if (writeprotect || (!wasinvalid && (descc & 4)))
+                                       pagedescriptor |= 4; // write-protected
+                               /* 68030 can only enable or disable caching */
+                               if (cachemode >= CM_SERIALIZED || (!wasinvalid && (descc & (1 << 6))))
+                                       pagedescriptor |= 1 << 6;
+                       } else {
+                               pagedescriptor |= 3; // resident page
+                               if (writeprotect || (!wasinvalid && (descc & 4)))
+                                       pagedescriptor |= 4; // write-protected
+                               if (supervisor || (!wasinvalid && (descc & (1 << 7))))
+                                       pagedescriptor |= 1 << 7;
+                               // do not override non-cached
+                               if (wasinvalid || cachemode > ((descc >> 5) & 3))
+                                       pagedescriptor |= cachemode << 5;
+                               else
+                                       pagedescriptor |= ((descc >> 5) & 3) << 5;
+                               if (addr != 0 || size != page_size)
+                                       pagedescriptor |= 1 << 10; // global if not zero page
+                       }
+               }
+
+               LEVELC(descb, addr) = pagedescriptor;
+               size -= page_size;
+               addr += page_size;
+               physaddr += page_size;
+       }
+       return TRUE;
+}
+
+BOOL map_region(struct uaestate *st, void *addr, void *physaddr, ULONG size, BOOL invalid, BOOL writeprotect, BOOL supervisor, UBYTE cachemode)
+{
+       if (addr != physaddr)
+               printf("MMU: Remap %08lx-%08lx -> %08lx (I=%d,WP=%d,S=%d)\n", addr, addr + size - 1, physaddr, invalid, writeprotect, supervisor);
+       if (!map_region2(st, addr, physaddr, size, invalid, writeprotect, supervisor, cachemode)) {
+               printf("MMU: Remap error\n");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+BOOL unmap_region(struct uaestate *st, void *addr, ULONG size)
+{
+       printf("MMU: Unmapped %08lx-%08lx\n", addr, addr + size - 1);
+       return map_region2(st, addr, NULL, size, TRUE, FALSE, FALSE, 0);
+}
+
+BOOL init_mmu(struct uaestate *st)
+{
+       st->MMU_Level_A = (ULONG*)(alloc_descriptor(st, LEVELA_SIZE, 0) & ~3);
+       if (!st->MMU_Level_A)
+               return FALSE;
+               st->mmutype = MMU030;
+       if (SysBase->AttnFlags & AFF_68040)     
+               st->mmutype = MMU040;
+       if (st->mmutype >= MMU040)
+               map_pagetable(st, st->MMU_Level_A, 1 << PAGE_SIZE);
+               
+       // Create default 1:1 mapping
+       
+       // memory
+       Forbid();
+       struct MemHeader *mh = (struct MemHeader*)SysBase->MemList.lh_Head;
+       while (mh->mh_Node.ln_Succ) {
+               ULONG mstart = ((ULONG)mh->mh_Lower) & 0xffff0000;
+               ULONG msize = ((((ULONG)mh->mh_Upper) + 0xffff) & 0xffff0000) - mstart;
+               map_region(st, (void*)mstart, (void*)mstart, msize, FALSE, FALSE, FALSE, CM_WRITETHROUGH);
+               mh = (struct MemHeader*)mh->mh_Node.ln_Succ;
+       }
+       Permit();
+       // io
+       map_region(st, (void*)0xa00000, (void*)0xa00000, 0xc00000 - 0xa00000, FALSE, FALSE, FALSE, CM_NONCACHEABLE);
+       map_region(st, (void*)0xd80000, (void*)0xd80000, 0xe00000 - 0xd80000, FALSE, FALSE, FALSE, CM_NONCACHEABLE);
+       map_region(st, (void*)0xe80000, (void*)0xe80000, 0x080000, FALSE, FALSE, FALSE, CM_NONCACHEABLE);
+       // rom
+       map_region(st, (void*)0xe00000, (void*)0xe00000, 0x080000, FALSE, FALSE, FALSE, 0);
+       map_region(st, (void*)0xf80000, (void*)0xf80000, 0x080000, FALSE, FALSE, FALSE, 0);
+               
+       return TRUE;
+}