///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  References:
//
//  - Third Generation Intel XScale Microarchitecture
//    Developer's Manual
//    May 2007
//    Order Number 316283-002US
//
//  - ARM Architecture Reference Manual
//    July 2005
//    ARM DDI 0100l
//

namespace Microsoft.Singularity.Isal.Arm.XScale
{
    using System;
    using System.Runtime.InteropServices;
    using System.Runtime.CompilerServices;

    ///////////////////////////////////////////////////////////////////////////
    // Bit discriptors of first level descriptors.

    [CLSCompliant(false)]
    public struct L1
    {
        public const uint CoarsePageTableAddressMask   = 0xfffffc00;
        public const uint SectionAddressMask           = 0xfff00000;
        public const uint SupersectionAddressMask      = 0xff000000;
        public const uint SupersectionAddress35_32Mask = 0x00f00000;
        public const int  SupersectionAddress35_32Roll = 20;
        public const uint FinePageTableAddressMask     = 0xfffff000;

        public const uint InvalidSbz                   = 0xfffffffc;
        public const uint CoarsePageTableSbz           = 0x0000001e;
        public const uint SectionSbz                   = 0x000e8011;
        public const uint SupersectionSbz              = 0x000a81f1;
        public const uint FinePageTableSbz             = 0x0000c01c;

        public const uint TexMask                      = 0x00007000;
        public const int  TexRoll                      = 12;

        public const uint ApMask                       = 0x00000c00;
        public const int  ApRoll                       = 10;

        public const uint DomainMask                   = 0x000001e0;
        public const int  DomainRoll                   = 5;

        public const uint SBit                         = 0x00010000;
        public const uint PBit                         = 0x00000200;
        public const uint CBit                         = 0x00000008;
        public const uint BBit                         = 0x00000004;

        public const uint InvalidType                  = 0x00000000;
        public const uint CoarsePageTableType          = 0x00000001;
        public const uint SectionType                  = 0x00000002;
        public const uint SupersectionType             = 0x00040002;
        public const uint FinePageTableType            = 0x00000003;
        public const uint SupersectionTypeMask         = 0x00040003;
        public const uint TypeMask                     = 0x00000003;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Bit discriptors of second level coarse descriptors.

    [CLSCompliant(false)]
    public struct L2Coarse
    {
        public const uint LargePageAddressMask          = 0xffff0000;
        public const uint SmallPageAddressMask          = 0xfffff000;
        public const uint ExtendedSmallPageAddressMask  = 0xfffff000;

        public const uint InvalidSbz                    = 0xfffffffc;
        public const uint ExtendedSmallPageSbz          = 0x00000200;

        public const uint LargePageSBit                 = 0x00008000;
        public const uint ExtendedSmallPageSBit         = 0x00000200;

        public const uint LargePageTexMask              = 0x00007000;
        public const int  LargePageTexRoll              = 12;
        public const uint ExtendedSmallPageTexMask      = 0x000001c0;
        public const int  ExtendedSmallPageTexRoll      = 6;

        public const uint Ap3Mask                       = 0x00000c00;
        public const int  Ap3Roll                       = 10;
        public const uint Ap2Mask                       = 0x00000300;
        public const int  Ap2Roll                       = 8;
        public const uint Ap1Mask                       = 0x000000c0;
        public const int  Ap1Roll                       = 6;
        public const uint Ap0Mask                       = 0x00000030;
        public const int  Ap0Roll                       = 4;

        public const uint CBit                          = 0x00000008;
        public const uint BBit                          = 0x00000004;

        public const uint InvalidType                   = 0x00000000;
        public const uint LargePageType                 = 0x00000001;
        public const uint SmallPageType                 = 0x00000002;
        public const uint ExtendedSmallPageType         = 0x00000003;
        public const uint TypeMask                      = 0x00000003;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Access permissions

    [CLSCompliant(false)]
    public enum AccessPermission : uint
    {
        NoAccess       = 0,   // All accesses fault
        PrivilegedOnly = 1,
        UserReadOnly   = 2,   // Privileged full, User R/O
        FullAccess     = 3,
        Mask           = 3
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L1InvalidEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor, L1.InvalidType, L1.InvalidSbz);
        }

        public void Initialize()
        {
            this.descriptor = 0;
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L1CoarsePageTableEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor,
                               L1.CoarsePageTableType,
                               L1.CoarsePageTableSbz);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L1.CoarsePageTableType;
            this.Address    = address;
        }

        public bool Valid
        {
            get { return IsType(this.descriptor); }
        }

        public uint Address
        {
            get {
                return LxAddress.Get(this.descriptor,
                                     L1.CoarsePageTableAddressMask);
            }
            set {
                LxAddress.Set(ref this.descriptor,
                              L1.CoarsePageTableAddressMask,
                              value);
            }
        }

        public bool P
        {
            get {
                return LxBit.Get(this.descriptor, L1.PBit);
            }
            set {
                LxBit.Set(ref this.descriptor, L1.PBit, value);
            }
        }

        public uint Domain
        {
            get { return L1Domain.Get(this.descriptor); }
            set { L1Domain.Set(ref this.descriptor, value); }
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L1SectionEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor, L1.SectionType, L1.SectionSbz);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L1.SectionType;
            this.Address    = address;
        }

        public bool Valid
        {
            get { return IsType(this.descriptor); }
        }

        public uint Address
        {
            get {
                return LxAddress.Get(this.descriptor, L1.SectionAddressMask);
            }
            set {
                LxAddress.Set(ref this.descriptor,
                              L1.SectionAddressMask,
                              value);
            }
        }

        public bool S
        {
            get { return LxBit.Get(this.descriptor, L1.SBit); }
            set { LxBit.Set(ref this.descriptor, L1.SBit, value); }
        }

        public uint Tex
        {
            get { return L1Tex.Get(this.descriptor); }
            set { L1Tex.Set(ref this.descriptor, value); }
        }

        public AccessPermission AccessPermission
        {
            get { return L1AP.Get(this.descriptor); }
            set { L1AP.Set(ref this.descriptor, value); }
        }

        public bool P
        {
            get { return LxBit.Get(this.descriptor, L1.PBit); }
            set { LxBit.Set(ref this.descriptor, L1.PBit, value); }
        }

        public uint Domain
        {
            get { return L1Domain.Get(this.descriptor); }
            set { L1Domain.Set(ref this.descriptor, value); }
        }

        public bool C
        {
            get { return LxBit.Get(this.descriptor, L1.CBit); }
            set { LxBit.Set(ref this.descriptor, L1.CBit, value); }
        }

        public bool B
        {
            get { return LxBit.Get(this.descriptor, L1.BBit); }
            set { LxBit.Set(ref this.descriptor, L1.BBit, value); }
        }

        public bool Cacheable
        {
            get { return this.C; }
            set { this.C = value; }
        }

        public bool WriteThrough
        {
            get { return !this.B; }
            set { this.B = !value; }
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L1SupersectionEntry
    {
        private uint descriptor;
        private const ulong AddressMask = 0xfff000000;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor,
                               L1.SupersectionType,
                               L1.SupersectionSbz);
        }

        public void Initialize(ulong address)
        {
            this.descriptor = L1.SupersectionType;
            this.Address    = address;
        }

        public bool Valid
        {
            get { return IsType(this.descriptor); }
        }

        public ulong Address
        {
            get {
                uint l = this.descriptor & L1.SupersectionAddressMask;
                uint h = this.descriptor & L1.SupersectionAddress35_32Mask;
                return ((ulong)l) | (((ulong)h) << 12);
            }

            set {
                DebugStub.Assert((value & ~AddressMask) == 0);
                uint h = (uint)(value >> 12);
                uint l = (uint)(value & ((ulong)L1.SupersectionAddressMask));
                this.descriptor &= (L1.SupersectionAddressMask |
                                    L1.SupersectionAddress35_32Mask);
                this.descriptor |= (h|l);
            }
        }

        public bool S
        {
            get { return LxBit.Get(this.descriptor, L1.SBit); }
            set { LxBit.Set(ref this.descriptor, L1.SBit, value); }
        }

        public uint Tex
        {
            get { return L1Tex.Get(this.descriptor); }
            set { L1Tex.Set(ref this.descriptor, value); }
        }

        public AccessPermission AccessPermission
        {
            get { return L1AP.Get(this.descriptor); }
            set { L1AP.Set(ref this.descriptor, value); }
        }

        public bool P
        {
            get { return LxBit.Get(this.descriptor, L1.PBit); }
            set { LxBit.Set(ref this.descriptor, L1.PBit, value); }
        }

        public bool C
        {
            get { return LxBit.Get(this.descriptor, L1.CBit); }
            set { LxBit.Set(ref this.descriptor, L1.CBit, value); }
        }

        public bool B
        {
            get { return LxBit.Get(this.descriptor, L1.BBit); }
            set { LxBit.Set(ref this.descriptor, L1.BBit, value); }
        }

        public bool Cacheable
        {
            get { return this.C; }
            set { this.C = value; }
        }

        public bool WriteThrough
        {
            get { return !this.B; }
            set { this.B = !value; }
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L1FineEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor,
                               L1.FinePageTableType,
                               L1.FinePageTableSbz);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L1.FinePageTableType;
            this.Address    = address;
        }

        public bool Valid
        {
            get { return IsType(this.descriptor); }
        }

        public uint Address
        {
            get {
                return LxAddress.Get(this.descriptor, L1.SectionAddressMask);
            }
            set {
                LxAddress.Set(ref this.descriptor,
                              L1.SectionAddressMask,
                              value);
            }
        }

        public bool P
        {
            get { return LxBit.Get(this.descriptor, L1.PBit); }
            set { LxBit.Set(ref this.descriptor, L1.PBit, value); }
        }

        public uint Domain
        {
            get { return L1Domain.Get(this.descriptor); }
            set { L1Domain.Set(ref this.descriptor, value); }
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L2CoarseInvalidEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor,
                               L2Coarse.InvalidType,
                               L2Coarse.InvalidSbz);
        }

        public void Initialize()
        {
            descriptor = 0;
        }
    }

    [CLSCompliant(false)]
    public struct L2CoarseLargeEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor, L2Coarse.LargePageType, 0);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L2Coarse.LargePageType;
            this.Address    = address;
        }

        public uint Address
        {
            get {
                return LxAddress.Get(descriptor,
                                     L2Coarse.LargePageAddressMask);
            }
            set {
                LxAddress.Set(ref descriptor,
                              L2Coarse.LargePageAddressMask,
                              value);
            }
        }

        public bool S
        {
            get { return LxBit.Get(descriptor, L2Coarse.LargePageSBit); }
            set { LxBit.Set(ref descriptor, L2Coarse.LargePageSBit, value); }
        }

        public uint Tex
        {
            get {
                return LxBits.Get(descriptor,
                                  L2Coarse.LargePageTexMask,
                                  L2Coarse.LargePageTexRoll);
            }
            set {
                LxBits.Set(ref descriptor,
                           L2Coarse.LargePageTexMask,
                           L2Coarse.LargePageTexRoll,
                           value);
            }
        }

        public AccessPermission AP3
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap3Mask, L2Coarse.Ap3Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap3Mask, L2Coarse.Ap3Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP2
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap2Mask, L2Coarse.Ap2Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap2Mask, L2Coarse.Ap2Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP1
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap1Mask, L2Coarse.Ap1Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap1Mask, L2Coarse.Ap1Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP0
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll,
                           (uint)value);
            }
        }

        public bool C
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.CBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.CBit, value); }
        }

        public bool B
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.BBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.BBit, value); }
        }
    }

    ///////////////////////////////////////////////////////////////////////////

    [CLSCompliant(false)]
    public struct L2CoarseSmallEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor, L2Coarse.SmallPageType, 0);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L2Coarse.SmallPageType;
            this.Address    = address;
        }

        public uint Address
        {
            get {
                return LxAddress.Get(descriptor,
                                     L2Coarse.SmallPageAddressMask);
            }
            set {
                LxAddress.Set(ref descriptor,
                              L2Coarse.SmallPageAddressMask,
                              value);
            }
        }

        public AccessPermission AP3
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap3Mask, L2Coarse.Ap3Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap3Mask, L2Coarse.Ap3Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP2
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap2Mask, L2Coarse.Ap2Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap2Mask, L2Coarse.Ap2Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP1
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap1Mask, L2Coarse.Ap1Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap1Mask, L2Coarse.Ap1Roll,
                           (uint)value);
            }
        }

        public AccessPermission AP0
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll,
                           (uint)value);
            }
        }

        public bool C
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.CBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.CBit, value); }
        }

        public bool B
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.BBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.BBit, value); }
        }

    }

    [CLSCompliant(false)]
    public struct L2CoarseExtendedSmallEntry
    {
        private uint descriptor;

        public static bool IsType(uint descriptor)
        {
            return IsValid.Get(descriptor,
                               L2Coarse.ExtendedSmallPageType,
                               L2Coarse.ExtendedSmallPageSbz);
        }

        public void Initialize(uint address)
        {
            this.descriptor = L2Coarse.ExtendedSmallPageType;
            this.Address    = address;
        }

        public uint Address
        {
            get {
                return LxAddress.Get(descriptor,
                                     L2Coarse.ExtendedSmallPageAddressMask);
            }
            set {
                LxAddress.Set(ref descriptor,
                              L2Coarse.ExtendedSmallPageAddressMask,
                              value);
            }
        }

        public bool S
        {
            get {
                return LxBit.Get(descriptor, L2Coarse.ExtendedSmallPageSBit);
            }
            set {
                LxBit.Set(ref descriptor,
                          L2Coarse.ExtendedSmallPageSBit,
                          value);
            }
        }

        public uint Tex
        {
            get {
                return LxBits.Get(descriptor,
                                  L2Coarse.ExtendedSmallPageTexMask,
                                  L2Coarse.ExtendedSmallPageTexRoll);
            }
            set {
                LxBits.Set(ref descriptor,
                           L2Coarse.ExtendedSmallPageTexMask,
                           L2Coarse.ExtendedSmallPageTexRoll,
                           value);
            }
        }

        public AccessPermission AP
        {
            get {
                return (AccessPermission)
                    LxBits.Get(descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll);
            }
            set {
                LxBits.Set(ref descriptor, L2Coarse.Ap0Mask, L2Coarse.Ap0Roll,
                           (uint)value);
            }
        }

        public bool C
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.CBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.CBit, value); }
        }

        public bool B
        {
            get { return LxBit.Get(this.descriptor, L2Coarse.BBit); }
            set { LxBit.Set(ref this.descriptor, L2Coarse.BBit, value); }
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    //
    // Helper routines for common operations that should optimize out
    //

    internal struct IsValid
    {
        internal static bool Get(uint descriptor, uint l1EntryType, uint sbz)
        {
            return (((descriptor & L1.TypeMask) == l1EntryType) &&
                    ((descriptor & sbz) == 0));
        }
    }

    internal struct IsType
    {
        internal static bool Get(uint descriptor, uint entryType)
        {
            return (descriptor & L1.TypeMask) == entryType;
        }
    }

    internal struct LxAddress
    {
        internal static void
        Set(ref uint descriptor, uint addressMask, uint address)
        {
            DebugStub.Assert((address & ~addressMask) == 0);
            descriptor &= ~addressMask;
            descriptor |= address;
        }

        internal static uint Get(uint descriptor, uint addressMask)
        {
            return descriptor & addressMask;
        }
    }

    internal struct LxBit
    {
        internal static void
        Set(ref uint descriptor, uint bitPattern, bool value)
        {
            if (value) {
                descriptor |= bitPattern;
            }
            else {
                descriptor &= bitPattern;
            }
        }

        internal static bool Get(uint descriptor, uint bitPattern)
        {
            return (descriptor & bitPattern) == bitPattern;
        }
    }

    internal struct LxBits
    {
        internal static void
        Set(ref uint descriptor, uint mask, int roll, uint value)
        {
            DebugStub.Assert((mask >> roll) >= value);

            descriptor &= mask;
            descriptor |= value << roll;
        }

        internal static uint
        Get(uint descriptor, uint mask, int roll)
        {
            return (descriptor & mask) >> roll;
        }
    }

    internal struct L1Tex
    {
        internal static void Set(ref uint descriptor, uint tex)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.SupersectionType));

            DebugStub.Assert(((tex << L1.TexRoll) & ~L1.TexMask) == 0);

            descriptor = (descriptor & L1.TexMask) | (tex << L1.TexRoll);
        }

        internal static uint Get(uint descriptor)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.SupersectionType));

            return (descriptor & L1.TexMask) >> L1.TexRoll;
        }
    }

    internal struct L1AP
    {
        internal static void Set(ref uint descriptor, AccessPermission ap)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.SupersectionType));

            descriptor = ((descriptor & ~L1.ApMask) | ((uint)ap) << L1.ApRoll);
        }

        internal static AccessPermission Get(uint descriptor)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.SupersectionType));

            return (AccessPermission)((descriptor & L1.ApMask) >> L1.ApRoll);
        }
    }

    internal struct L1Domain
    {

        internal static void Set(ref uint descriptor, uint domain)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.CoarsePageTableType) ||
                             IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.FinePageTableType));

            DebugStub.Assert((domain & (L1.DomainMask >> L1.DomainRoll)) == 0);

            descriptor = ((descriptor & ~L1.DomainMask) |
                          (domain << L1.DomainRoll));
        }

        internal static uint Get(uint descriptor)
        {
            DebugStub.Assert(IsType.Get(descriptor, L1.CoarsePageTableType) ||
                             IsType.Get(descriptor, L1.SectionType) ||
                             IsType.Get(descriptor, L1.FinePageTableType));
            return (descriptor & L1.DomainMask) >> L1.DomainRoll;
        }
    }
}
