Bool Mem32DevIns(CMemRange *tmpmr)
{
        CMemRange *tmpmr1 = dev.mem32_head.next, *tmpmr2;

        while (tmpmr1 != &dev.mem32_head)
        {
                if (!tmpmr1->type && tmpmr->base >= tmpmr1->base && tmpmr->base + tmpmr->size <= tmpmr1->base + tmpmr1->size)
                {
                        if (tmpmr->base > tmpmr1->base)
                        {
                                tmpmr2 = ZMAlloc(sizeof(CMemRange));
                                tmpmr2->type  = MRT_UNUSED;
                                tmpmr2->flags = 0;
                                tmpmr2->base  = tmpmr1->base;
                                tmpmr2->size  = tmpmr->base - tmpmr1->base;
                                QueueInsertRev(tmpmr2, tmpmr1);
                        }
                        QueueInsertRev(tmpmr, tmpmr1);
                        tmpmr1->size = tmpmr1->base + tmpmr1->size - (tmpmr->base + tmpmr->size);
                        tmpmr1->base = tmpmr->base + tmpmr->size;
                        if (!tmpmr1->size)
                        {
                                QueueRemove(tmpmr1);
                                Free(tmpmr1);
                        }
                        return TRUE;
                }
                tmpmr1 = tmpmr1->next;
        }

        return FALSE;
}

U0 Mem32DevInit()
{
        CMemRange *tmpmr;
        CMemE820  *m20 = MEM_E820;

        QueueInit(&dev.mem32_head);
        tmpmr = ZMAlloc(sizeof(CMemRange));
        tmpmr->type  = MRT_UNUSED;
        tmpmr->flags = 0;
//Maybe !!! Change this to 0xF0000000 !!!
        tmpmr->base  = 0xE0000000;
        tmpmr->size  = 0x10000000;
        QueueInsert(tmpmr, dev.mem32_head.last);

        if (m20->type)
        {
                while (m20->type)
                {
                        tmpmr = ZMAlloc(sizeof(CMemRange));
                        tmpmr->type  = m20->type;
                        tmpmr->flags = 0;
                        tmpmr->base  = m20->base;
                        tmpmr->size  = m20->len;
                        if (!Mem32DevIns(tmpmr))
                                Free(tmpmr);
                        m20++;
                }
        }
}

U8 *Mem32DevAlloc(I64 size, I64 alignment)
{//Alloc 32-bit addr space for device. (Doesn't work.) Not used.
//For this to work the BIOS E820 map must be searched for gaps in
        //the 32-bit range and the pool initialized to the gaps.
        U8                      *base, *limit;
        CMemRange       *tmpmr, *tmpmr1;

        while (LBts(&sys_semas[SEMA_DEV_MEM], 0))
                Yield;
        tmpmr1 = dev.mem32_head.next;
        while (tmpmr1 != &dev.mem32_head)
        {
                base = (tmpmr1->base + alignment - 1) & ~(alignment - 1);
                limit = base + size - 1;
                if (!tmpmr1->type && limit < tmpmr1->base + tmpmr1->size)
                {
                        tmpmr = ZMAlloc(sizeof(CMemRange));
                        tmpmr->type  = MRT_DEV;
                        tmpmr->flags = 0;
                        tmpmr->base  = base;
                        tmpmr->size  = size;
                        if (!Mem32DevIns(tmpmr))
                        {
                                Free(tmpmr);
                                LBtr(&sys_semas[SEMA_DEV_MEM], 0);
                                return NULL;
                        }
                        LBtr(&sys_semas[SEMA_DEV_MEM], 0);
                        return tmpmr->base;
                }
                tmpmr1 = tmpmr1->next;
        }
        LBtr(&sys_semas[SEMA_DEV_MEM], 0);

        return NULL;
}

U0 Mem32DevFree(U8 *base)
{//Free 32-bit device address space.
        CMemRange *tmpmr;

        if (!base)
                return;
        while (LBts(&sys_semas[SEMA_DEV_MEM], 0))
                Yield;
        tmpmr = dev.mem32_head.next;
        while (tmpmr != &dev.mem32_head)
        {
                if (tmpmr->base == base)
                {
                        tmpmr->type = MRT_UNUSED;
                        break;
                }
                tmpmr = tmpmr->next;
        }
        LBtr(&sys_semas[SEMA_DEV_MEM], 0);
}

U8 *Mem64DevAlloc(I64 *_pages1Gig)
{//Alloc 64-bit addr space for device.
        U8 *a;
        I64 i = *_pages1Gig, *pte;

        while (LBts(&sys_semas[SEMA_DEV_MEM], 0))
                Yield;
        while (i--)
        {
                a = dev.mem64_ptr -= 1 << 30;
                do
                {
                        pte = MemPageTable(a);
                        *pte = *pte & ~0x18 | 0x11; //Uncached and present
                        InvalidatePage(dev.mem64_ptr);
                        a += mem_page_size;
                }
                while (a - dev.mem64_ptr < 1 << 30);
        }
        LBtr(&sys_semas[SEMA_DEV_MEM], 0);
        return dev.mem64_ptr;
}

U0 Mem64DevFree(U8 *base, I64 pages1Gig)
{//Free 64-bit device address space.
        if (!base)
                return;
        while (LBts(&sys_semas[SEMA_DEV_MEM], 0))
                Yield;
        if (base == dev.mem64_ptr)
                dev.mem64_ptr += pages1Gig * 1 << 30;
//else not freed
        LBtr(&sys_semas[SEMA_DEV_MEM], 0);
}

U0 UncachedAliasAlloc() //Make uncached alias for 4 lowest Gig.
{
        I64 i = 4, *pte;
        U8 *a;

        a = dev.uncached_alias = Mem64DevAlloc(&i);
        do
        {
                pte = MemPageTable(a);
                *pte = 0x197 + a - dev.uncached_alias;
                InvalidatePage(a);
                a += mem_page_size;
        }
        while (a - dev.uncached_alias < 1 << 32);
}

I64 MemBIOSTotal()
{//Returns max of either E801 or E820 mem map.
        I64                      total01 = 0x100000, total20 = 0;
        U16                     *mem01 = MEM_E801;
        CMemE820        *mem20 = MEM_E820;

        total01 += mem01[0] * 1024;             //First U16 is mem between 1MiB and 16 MiB in KiB.
        total01 += mem01[1] * 64 * 1024;//Second U16 is mem above 16MiB (until it encounters a hole) in 64KiB blocks.

        if (mem20->type)
        {
                while (mem20->type)
                {
                        if (mem20->type == MEM_E820t_USABLE)
                                total20 += mem20->len;
                        mem20++;
                }
        }
        return MaxI64(total01, total20);  
}

I64 Scale2Mem(I64 min, I64 max, I64 limit=2*1024*1024*1024)
{//Helps pick DiskCache and RAMDisk sizes.
//Can be used in BootHDIns() config scripts.
        I64 i;

        if (sys_data_bp)
                i = sys_data_bp->alloced_u8s;
        else
                i = sys_code_bp->alloced_u8s;
        if (i >= limit)
                return max;
        else
                return min + (max - min) * i / limit;
}

I64 Seg2Linear(U32 *ptr)
{//Convert 32-bit segmented farptr to linear address. Modify in place.
        *ptr = *ptr >> 16 << 4 + *ptr & 0xFFFF;
        return *ptr;
}