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 = SysMAlloc(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 = SysMAlloc(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 = SysMAlloc(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 address 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 = SysMAlloc(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 address 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 far pointer to linear address. Modify in place.
    *ptr = *ptr >> 16 << 4 + *ptr & 0xFFFF;
    return *ptr;
}