/* 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>
}
}
-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;
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;
}
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) {
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;
}
}
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;
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;
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)
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(+)
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;
ULONG mraddr = 0;
if (mrtest) {
if (TypeOfMem((APTR)0x0bf00000)) {
- if (!TypeOfMem((APTR)0x0fd80000)) {
+ if (!TypeOfMem((APTR)0x0fe80000)) {
mrd->type = MAPROM_ACA12xx;
mrd->addr = 0x0ff00000;
}
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++) {
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;
}
}
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];
}
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;
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;
return 0;
}
st->usemaprom = 1;
+ st->canusemmu = 1;
for(int i = 2; i < argc; i++) {
if (!stricmp(argv[i], "debug"))
st->debug = 1;
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"))
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)) {
--- /dev/null
+
+/* 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;
+}