From: Toni Wilen Date: Thu, 30 May 2019 18:59:53 +0000 (+0300) Subject: MMU support X-Git-Tag: 4300~197 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=4442d3099a8e2eaebfaa34ce57097334e0e0d4b7;p=francis%2Fwinuae.git MMU support --- diff --git a/utilities/stateload/asm.S b/utilities/stateload/asm.S index 9e126a0a..27088504 100644 --- a/utilities/stateload/asm.S +++ b/utilities/stateload/asm.S @@ -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 diff --git a/utilities/stateload/header.h b/utilities/stateload/header.h index f930e528..5810b0d2 100644 --- a/utilities/stateload/header.h +++ b/utilities/stateload/header.h @@ -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); + diff --git a/utilities/stateload/main.c b/utilities/stateload/main.c index 90140a96..84cd3a05 100644 --- a/utilities/stateload/main.c +++ b/utilities/stateload/main.c @@ -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 #include @@ -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)) { diff --git a/utilities/stateload/makefile b/utilities/stateload/makefile index dd639609..f6a65e73 100644 --- a/utilities/stateload/makefile +++ b/utilities/stateload/makefile @@ -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 index 00000000..0fa24745 --- /dev/null +++ b/utilities/stateload/mmu.c @@ -0,0 +1,206 @@ + +/* I made this originally for AROS m68k */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +}