/*
	SetCPU V1.60
	by Dave Haynie, April 13, 1990
	Released to the Public Domain

	MMU.C MODULE

	This module is responsible for some of the MMU table creation
        functions.
*/

#include "setcpu.h"


/* ====================================================================== */

/* This function maps the phantom area for a given memory location. */

void Phantom(tag,loc)
struct systag *tag;
ULONG loc;
{
   ULONG xindex = loc/ROMROUND;

   if (xindex < 60)
      tag->maintable[xindex] = PD_ADDR(0x00800000)|PD_DT_PAGE;
   else
      tag->maintable[xindex] = PD_ADDR(0x00ce0000)|PD_DT_PAGE;
}


/* This table contains a list of the blocks of memory that should be 
   cache-inhibited. */

static ULONG InTable[] = { 0x00000000,0x00200000,0x00b00000,0x00f00000,0L,0L };
   

/* This function fills the basic MMU table and other standard tag itemd from 
   an allocated image.  Some tables may need additional information in the 
   table.  The basic table consists of one table level and uses direct page 
   translation on a grain of 128K per entry.  Everything's directly mapped 
   except for the last two entries, which are for the $FC0000-$FFFFFF area. */

void FillBasicTable(tag,phantoms)
struct systag *tag;
LONG phantoms;
{
   register ULONG i, image, j;
   ULONG page,size,loc;
	
   tag->tagsize = SizeOf(struct systag);
   tag->progver = PROGRAM_VERSION;
   tag->patches = 0;
   tag->patchlist = tag->devs = NULL;
   tag->config = 1;
   tag->BerrSize = 0L;
   tag->OldBerr = tag->BerrHandler = NULL;
   tag->sysstack = NULL;
   tag->sysstksize = 0;
   tag->OldReset = tag->ResetCode = NULL;
   tag->ResetSize = 0;

  /* Here I make a table that maps everything straight through. */
   for (i = 0; i < tag->tablesize/sizeof(ULONG); i++) 
      tag->maintable[i] = PD_ADDR(i<<17)|PD_DT_PAGE;
   tag->maintable[i] = IV_ADDR(tag)|PD_DT_INVALID;

  /* Mark the inhibited pages. */
   for (i = 0; InTable[i+1]; i += 2)
      for (j = InTable[i]/PAGESIZE; j < InTable[i+1]/PAGESIZE; ++j)
         tag->maintable[j] |= PD_CI;

  /* Here I map the ROM image to the ROM location. */
   image = (ULONG)tag->romlo;
   loc = tag->romloc/PAGESIZE;
   if (image) {
      if (phantoms) Phantom(tag,image);
      tag->maintable[loc++] = PD_ADDR(image)|PD_WP|PD_DT_PAGE;
      if (phantoms) Phantom(tag,image+PAGESIZE);
      tag->maintable[loc++] = PD_ADDR(image+PAGESIZE)|PD_WP|PD_DT_PAGE;
   }
   image = (ULONG)tag->romhi;
   if (phantoms) Phantom(tag,image);
   tag->maintable[loc++] = PD_ADDR(image)|PD_WP|PD_DT_PAGE;
   if (phantoms) Phantom(tag,image+PAGESIZE);
   tag->maintable[loc]   = PD_ADDR(image+PAGESIZE)|PD_WP|PD_DT_PAGE;

  /* Here I look for Bridge Cards, which are known to have problems with
     the data cache enabled, so I always disable it, reguardless of 
     whether caching is actually enabled in the CACR. */

   if (Bridge.Addr && Bridge.Size) {
      page = Bridge.Addr/PAGESIZE;
      size = max((ULONG)Bridge.Size/PAGESIZE,1);
      while (size-- && page < tag->tablesize)
         if ((tag->maintable[page] & PD_DT_TYPE) == PD_DT_PAGE)
            tag->maintable[page++] |= PD_CI;
   } 	
}

/* This routine sets up the MMU registers (shadowed in tag fields) in the
   standard SetCPU fashion.  The CPU Root Pointer tells the MMU about the 
   table I've set up, and all that's left to do is bang the Translation
   Control Register to turn the thing on.  Note that the first half of the 
   CRP is control data, the second the address of my table.  */
   
void SetMMURegs(tag)
struct systag *tag;
{
   tag->CRP[0] = CRP_LIMIT(tag->tablesize/sizeof(ULONG)-1)|CRP_SG|CRP_DT_V4BYTE;
   tag->CRP[1] = (ULONG)(tag->maintable);

   tag->TC = TC_PS(0x0a)|TC_IS(tag->wrapup)|
             TC_TIA(0x0f-tag->wrapup)|TC_TIB(0x03)|TC_TIC(0x04)|TC_TID(0);
}

/* ====================================================================== */

/* This function frees all MMU tables. */

void FreeMMUTable(tag)
struct systag *tag;
{
   ULONG i,j, *subtab;

   for (i = 0; i < 128; ++i)
      if ((tag->maintable[i] & PD_DT_TYPE) == PD_DT_V4BYTE) {
         for (subtab = (ULONG *)PD_ADDR(tag->maintable[i]), j = 0; j < 8; ++j)
            if ((subtab[j] & PD_DT_TYPE) == PD_DT_V4BYTE)
               FreeMem((char *)PD_ADDR(subtab[j]),STKTABSIZE);
         FreeMem((char *)subtab,SUBTABSIZE);
      }
   FreeMem((char *)tag->maintable,tag->tablesize);
}

/* ====================================================================== */

/* This function makes subtables for CardROMFile I/O devices. */

void MakeExpTable(tag)
struct systag *tag;
{
   ULONG i, *SUBTable, iospace;
   struct ExpROMData *er;

   while (er = GetExpROM()) {
      er->next = tag->devs;
      tag->devs = er;

      iospace = ((ULONG)er->ROMbase)/ROMROUND;

     /* If necessary, modify the table */
      if (tag->maintable[iospace] == PD_ADDR(iospace<<17)|PD_DT_PAGE) {
         if (SUBTable = AllocAligned(SUBTABSIZE,TABROUND)) {
            for (i = 0; i < 8; ++i) 
               SUBTable[i] = PD_ADDR((iospace<<17)+(i<<14))|PD_CI|PD_DT_PAGE;
            tag->maintable[iospace] = PD_ADDR(SUBTable)|PD_DT_V4BYTE;
         }
      } else
         SUBTable = (ULONG *)PD_ADDR(tag->maintable[iospace]);

      if (SUBTable) {
         er->imagebase = (ULONG)AllocAligned(er->ROMsize,DEVROUND);
         er->tablebase = (ULONG)&SUBTable[0];

         MemCopy(er->ROMbase,er->imagebase,er->ROMsize);
         iospace = (er->ROMbase - (iospace * ROMROUND))/DEVROUND;
         for (i = 0; i < er->ROMsize/DEVROUND; ++i)
            SUBTable[iospace+i] = PD_ADDR(er->imagebase+(DEVROUND*i))|PD_CI|PD_DT_PAGE;
      }
   }
}

/* ====================================================================== */

/* This function hunts down the system stack and translates it into a 
   block of 32 bit memory if it's found to be located in Chip RAM. Note
   that this routine must only be called when the MMU is turned on.  The
   current functions used to turn the MMU on make use of the supervisor
   stack.  This code must be run in user mode to insure that the supervisor
   stack image and the actual stack are the same during the transfer of
   control. */

void MakeFastStack(tag)
struct systag *tag;
{
   struct ExecBase *eb = *((struct ExecBase **)4L);
   ULONG i, *SUBTable, *STKTable, tabndx, subndx, stkndx, tmem, base;

   base = (((ULONG)eb->SysStkLower)/STKROUND)*STKROUND;

   if (base >= 0x00200000L) return;

   tag->sysstksize = (((ULONG)eb->SysStkUpper - (ULONG)eb->SysStkLower + 1)/STKROUND)*STKROUND;
   tabndx = base/ROMROUND;
   tmem = tabndx * ROMROUND;

  /* Make the I/O level subtable (level 2).  Most entries are going to be
     a straight translation. */

   if (!(SUBTable = AllocAligned(SUBTABSIZE,TABROUND)) ||
       !(STKTable = AllocAligned(STKTABSIZE,TABROUND))) return;

   for (i = 0; i < 8; ++i) 
      SUBTable[i] = PD_ADDR(tmem+(i*DEVROUND))|PD_DT_PAGE;
   tag->maintable[tabndx] = PD_ADDR(SUBTable)|PD_DT_V4BYTE;

  /* Make the Stack level subtable (level 3).  Fill all 16 entries with a
     straight translation. */
   
   subndx = (base - tmem)/DEVROUND;
   tmem += subndx * DEVROUND;

   for (i = 0; i < 16; ++i)
      STKTable[i] = PD_ADDR((tmem)+(i*STKROUND))|PD_DT_PAGE;
   SUBTable[subndx] = PD_ADDR(STKTable)|PD_DT_V4BYTE;

  /* Now take care of the actual stack translation. */

   tag->sysstack = (char *)AllocAligned(tag->sysstksize,STKROUND);
   MemCopy(base,tag->sysstack,tag->sysstksize);

   stkndx = (base - tmem)/STKROUND;
   
   for (i = 0; i < tag->sysstksize/STKROUND && i < 16; ++i)
      STKTable[i+stkndx] = PD_ADDR(tag->sysstack+(STKROUND*i))|PD_DT_PAGE;
   FlushATC();
}

/* This function removes the fast stack translation. Like the fast stack
   creation routine, this must be called with the MMU turned on, in user
   mode, so that both stack images can be identical when the switch is
   made.  This routine doesn't clean up the MMU tables, it just deals
   with the stack image. */

void FreeFastStack(tag)
struct systag *tag;
{
   struct ExecBase *eb = *((struct ExecBase **)4L);
   ULONG *SUBTable, *STKTable, tabndx, subndx, tmem, base;

   base = (((ULONG)eb->SysStkLower)/STKROUND)*STKROUND;

  /* Find the stack's translation table. */

   tabndx = base/ROMROUND;
   tmem = tabndx * ROMROUND;
   SUBTable = (ULONG *)PD_ADDR(tag->maintable[tabndx]);

   subndx = (base - tmem)/DEVROUND;

  /* Set things back the way they were. */

   Disable();
   STKTable = (ULONG *)PD_ADDR(SUBTable[subndx]);
   MemCopy(tag->sysstack,base,tag->sysstksize);
   SUBTable[subndx] = PD_ADDR(tmem+(subndx*DEVROUND))|PD_DT_PAGE;
   FlushATC();
   Enable();

  /* Get rid of the fast image stuff. */

   FreeMem((char *)STKTable,STKTABSIZE);
   FreeMem((char *)tag->sysstack,tag->sysstksize);
}

/* ====================================================================== */

/* This routine knows the best way to reset the system, in an attempt to
   avoid MMU-based troubles. */

void CleverReset()
{
   geta4();
   CleanBoot();
}
