/* alpha_mmu.c - Alpha memory management simulator

   Copyright (c) 2003-2006, Robert M Supnik

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   This module contains the routines for

        ReadB,W,L,Q     -       read aligned virtual
        ReadAccL,Q      -       read aligned virtual, special access check
        ReadPB,W,L,Q    -       read aligned physical
        WriteB,W,L,Q    -       write aligned virtual
        WriteAccL,Q     -       write aligned virtual, special access check
        WritePB,W,L,Q   -       write aligned physical

   The TLB is organized for optimum lookups and is broken up into three fields:

        tag             VA<42:13> for an 8KB page system
        pte             PTE<31:0>, <31:16> are zero; FOE, FOR, FOW stored inverted
        pfn             PFN<31:0> left shifted by page size

   The inversion of FOE, FOR, FOW means that all checked bits must be one
   for a reference to proceed.

   All Alpha implementations provide support for a 43b superpage for Unix,
   and a 32b superpage for NT:

   43b superpage                0xFFFFFC0000000000:0xFFFFFDFFFFFFFFFF
   32b superpage                0xFFFFFFFF80000000:0xFFFFFFFFBFFFFFFF
*/

#include "alpha_defs.h"

extern t_uint64 trans_i (t_uint64 va);
extern t_uint64 trans_d (t_uint64 va, uint32 acc);

extern t_uint64 *M;
extern t_uint64 p1;
extern uint32 pal_mode, dmapen;
extern uint32 cm_eacc, cm_racc, cm_wacc;
extern jmp_buf save_env;
extern UNIT cpu_unit;

/* Read virtual aligned

   Inputs:
        va      =       virtual address
   Output:
        returned data, right justified
*/

t_uint64 ReadB (t_uint64 va)
{
t_uint64 pa;

if (dmapen) pa = trans_d (va, cm_racc);                 /* mapping on? */
else pa = va;
return ReadPB (pa);
}

t_uint64 ReadW (t_uint64 va)
{
t_uint64 pa;

if (va & 1) ABORT1 (va, EXC_ALIGN);                     /* must be W aligned */
if (dmapen) pa = trans_d (va, cm_racc);                 /* mapping on? */
else pa = va;
return ReadPW (pa);
}

t_uint64 ReadL (t_uint64 va)
{
t_uint64 pa;

if (va & 3) ABORT1 (va, EXC_ALIGN);                     /* must be L aligned */
if (dmapen) pa = trans_d (va, cm_racc);                 /* mapping on? */
else pa = va;
return ReadPL (pa);
}

t_uint64 ReadQ (t_uint64 va)
{
t_uint64 pa;

if (va & 7) ABORT1 (va, EXC_ALIGN);                     /* must be Q aligned */
if (dmapen) pa = trans_d (va, cm_racc);                 /* mapping on? */
else pa = va;
return ReadPQ (pa);
}

/* Read with generalized access controls - used by PALcode */

t_uint64 ReadAccL (t_uint64 va, uint32 acc)
{
t_uint64 pa;

if (va & 3) ABORT1 (va, EXC_ALIGN);                     /* must be L aligned */
if (dmapen) pa = trans_d (va, acc);                     /* mapping on? */
else pa = va;
return ReadPL (pa);
}

t_uint64 ReadAccQ (t_uint64 va, uint32 acc)
{
t_uint64 pa;

if (va & 7) ABORT1 (va, EXC_ALIGN);                     /* must be Q aligned */
if (dmapen) pa = trans_d (va, acc);                     /* mapping on? */
else pa = va;
return ReadPQ (pa);
}

/* Read instruction */

uint32 ReadI (t_uint64 va)
{
t_uint64 pa;

if (!pal_mode) pa = trans_i (va);                       /* mapping on? */
else pa = va;
return (uint32) ReadPL (pa);
}

/* Write virtual aligned

   Inputs:
        va      =       virtual address
        val     =       data to be written, right justified in 32b or 64b
   Output:
        none
*/

void WriteB (t_uint64 va, t_uint64 dat)
{
t_uint64 pa;

if (dmapen) pa = trans_d (va, cm_wacc);                 /* mapping on? */
else pa = va;
WritePB (pa, dat);
return;
}

void WriteW (t_uint64 va, t_uint64 dat)
{
t_uint64 pa;

if (va & 1) ABORT1 (va, EXC_ALIGN);                     /* must be W aligned */
if (dmapen) pa = trans_d (va, cm_wacc);                 /* mapping on? */
else pa = va;
WritePW (pa, dat);
return;
}

void WriteL (t_uint64 va, t_uint64 dat)
{
t_uint64 pa;

if (va & 3) ABORT1 (va, EXC_ALIGN);                     /* must be L aligned */
if (dmapen) pa = trans_d (va, cm_wacc);                 /* mapping on? */
else pa = va;
WritePL (pa, dat);
return;
}

void WriteQ (t_uint64 va, t_uint64 dat)
{
t_uint64 pa;

if (va & 7) ABORT1 (va, EXC_ALIGN);                     /* must be Q aligned */
if (dmapen) pa = trans_d (va, cm_wacc);                 /* mapping on? */
else pa = va;
WritePQ (pa, dat);
return;
}

/* Write with generalized access controls - used by PALcode */

void WriteAccL (t_uint64 va, t_uint64 dat, uint32 acc)
{
t_uint64 pa;

if (va & 3) ABORT1 (va, EXC_ALIGN);                     /* must be L aligned */
if (dmapen) pa = trans_d (va, acc);                     /* mapping on? */
else pa = va;
WritePL (pa, dat);
return;
}

void WriteAccQ (t_uint64 va, t_uint64 dat, uint32 acc)
{
t_uint64 pa;

if (va & 7) ABORT1 (va, EXC_ALIGN);                     /* must be Q aligned */
if (dmapen) pa = trans_d (va, acc);                     /* mapping on? */
else pa = va;
WritePQ (pa, dat);
return;
}

/* Read and write physical aligned - access point to I/O */

INLINE t_uint64 ReadPB (t_uint64 pa)
{
t_uint64 val;

if (ADDR_IS_MEM (pa)) {
    uint32 bo = ((uint32) pa) & 07;
    return (((M[pa >> 3] >> (bo << 3))) & M8);
    }
if (ReadIO (pa, &val, L_BYTE)) return val;
return 0;
}

INLINE t_uint64 ReadPW (t_uint64 pa)
{
t_uint64 val;

if (ADDR_IS_MEM (pa)) {
    uint32 bo = ((uint32) pa) & 06;
    return (((M[pa >> 3] >> (bo << 3))) & M16);
    }
if (ReadIO (pa, &val, L_WORD)) return val;
return 0;
}

INLINE t_uint64 ReadPL (t_uint64 pa)
{
t_uint64 val;

if (ADDR_IS_MEM (pa)) {
    if (pa & 4) return (((M[pa >> 3] >> 32)) & M32);
    return ((M[pa >> 3]) & M32);
    }
if (ReadIO (pa, &val, L_LONG)) return val;
return 0;
}

INLINE t_uint64 ReadPQ (t_uint64 pa)
{
t_uint64 val;

if (ADDR_IS_MEM (pa)) return M[pa >> 3];
if (ReadIO (pa, &val, L_QUAD)) return val;
return 0;
}

INLINE void WritePB (t_uint64 pa, t_uint64 dat)
{
dat = dat & M8;
if (ADDR_IS_MEM (pa)) {
    uint32 bo = ((uint32) pa) & 07;
    M[pa >> 3] = (M[pa >> 3] & ~(((t_uint64) M8) << (bo << 3))) |
        (dat << (bo << 3));
    }
else WriteIO (pa, dat, L_BYTE);
return;
}

INLINE void WritePW (t_uint64 pa, t_uint64 dat)
{
dat = dat & M16;
if (ADDR_IS_MEM (pa)) {
    uint32 bo = ((uint32) pa) & 07;
    M[pa >> 3] = (M[pa >> 3] & ~(((t_uint64) M16) << (bo << 3))) |
        (dat << (bo << 3));
    }
else WriteIO (pa, dat, L_WORD);
return;
}

INLINE void WritePL (t_uint64 pa, t_uint64 dat)
{
dat = dat & M32;
if (ADDR_IS_MEM (pa)) {
    if (pa & 4) M[pa >> 3] = (M[pa >> 3] & M32) |
        (dat << 32);
    else M[pa >> 3] = (M[pa >> 3] & ~((t_uint64) M32)) | dat;
    }
else WriteIO (pa, dat, L_LONG);
return;
}

INLINE void WritePQ (t_uint64 pa, t_uint64 dat)
{
if (ADDR_IS_MEM (pa)) M[pa >> 3] = dat;
else WriteIO (pa, dat, L_QUAD);
return;
}

