U0 SysBadFree(I64 *ptr)
{
    Panic("Bad Free:", ptr);
}

U0 SysBadMAlloc(I64 *ptr)
{
    Panic("Bad MAlloc:", ptr);
}

U8 *MemPagAlloc(I64 pags, CBlkPool *bp=NULL)
{/*Alloc pags from BlkPool. Don't link to task.
(Linking to a task means they will be freed when the task dies.)
It might give you more than you asked for.

Return: NULL if out of memory.
*/
    CMemBlk *res = NULL, *m;
    I64      i;

    if (!bp)
        bp = sys_code_bp;

    PUSHFD
    CLI
    while (LBts(&bp->locked_flags, BPlf_LOCKED))
        PAUSE
    if (pags < MEM_FREE_PAG_HASH_SIZE)
    {
        if (res = bp->free_pag_hash[pags])
        {
            bp->free_pag_hash[pags] = res->next;
            goto at_done;
        }
        i = Bsr(MEM_FREE_PAG_HASH_SIZE) + 1;
    }
    else
    {
        //We'll now round-up to a power of two.
        //There is some overhead on allocations and
        //we wouldn't want to round to the next
        //power of two if a power of two was requested.
        //So we use a little more than a power of two.
        pags -= MEM_EXTRA_HASH2_PAGS;
        i = Bsr(pags) + 1;
        pags = 1 << i + MEM_EXTRA_HASH2_PAGS;
        if (res = bp->free_pag_hash2[i])
        {
            bp->free_pag_hash2[i] = res->next;
            goto at_done;
        }
    }
    m = &bp->mem_free_list;
    while (TRUE)
    {
        if (!(res = m->next))
        {
            //We're probably out of luck, but lets search for a
            //freed larger size block... and, screw-it, return the whole thing.
            do
            {
                if (res = bp->free_pag_hash2[++i])
                {
                    pags = 1 << i + MEM_EXTRA_HASH2_PAGS;
                    bp->free_pag_hash2[i] = res->next;
                    goto at_done;
                }
            }
            while (i < 64 - MEM_PAG_BITS - 1);

            pags = 0;
            res = NULL; //Out of memory
            goto at_done2;
        }
        if (res->pags < pags)
            m = res;
        else
        {
            if (res->pags == pags)
            {
                m->next = res->next;
                goto at_done;
            }
            else
            {
                res->pags -= pags;
                res(U8 *) += res->pags << MEM_PAG_BITS;
                res->pags = pags;
                goto at_done;
            }
        }
    }
at_done:
    bp->used_u8s += res->pags << MEM_PAG_BITS;
at_done2:
    LBtr(&bp->locked_flags, BPlf_LOCKED);
    POPFD

    return res;
}

U0 MemPagFree(CMemBlk *m, CBlkPool *bp=NULL)
{//Return non-task pags to BlkPool.
    I64 i, pags;

    if (m)
    {
        if (!bp)
            bp = sys_code_bp;

        PUSHFD
        CLI
        while (LBts(&bp->locked_flags, BPlf_LOCKED))
            PAUSE
        pags = m->pags;
        m->mb_signature = MBS_UNUSED_SIGNATURE_VAL;
        bp->used_u8s -= pags << MEM_PAG_BITS;
        if (pags < MEM_FREE_PAG_HASH_SIZE)
        {
            m->next = bp->free_pag_hash[pags];
            bp->free_pag_hash[pags] = m;
        }
        else
        {
//We'll now round-up to a power of two.
            //There is some overhead on allocations and
            //we wouldn't want to round to the next
            //power of two if a power of two was requested.
            //So we use a little more than a power of two.
            pags -= MEM_EXTRA_HASH2_PAGS;
            i = Bsr(pags);
            m->next = bp->free_pag_hash2[i];
            bp->free_pag_hash2[i] = m;
        }
        LBtr(&bp->locked_flags, BPlf_LOCKED);
        POPFD
    }
}

CMemBlk *MemPagTaskAlloc(I64 pags, CHeapCtrl *hc)
{/*hc must be locked.  Don't preempt this routine.
Currently, this is only called from MAlloc().
Return: NULL if out of memory.
*/
    CMemBlk     *res;
    I64          threshold, count, size;
    CMemUnused  *uum, **_uum, **_ptr;

    if (res = MemPagAlloc(pags, hc->bp))
    {
        QueueInsert(res, hc->last_mem_blk);
        res->mb_signature = MBS_USED_SIGNATURE_VAL;
        hc->alloced_u8s += res->pags << MEM_PAG_BITS;

        //Tidy-up free list (Move into heap hash)
        //because if free list gets long, delay causes crash.
        threshold = MEM_HEAP_HASH_SIZE >> 4;
#assert MEM_HEAP_HASH_SIZE >> 4 >= sizeof(U8 *)
        do
        {
            count = 0;
            _uum = &hc->malloc_free_list;
            while (uum = *_uum)
            {
#assert !offset(CMemUnused.next)
                size = uum->size;
                if (size < threshold)
                {
                    *_uum = uum->next;
                    _ptr = (&hc->heap_hash)(U8 *) + size;
                    uum->next = *_ptr;
                    *_ptr = uum;
                }
                else
                {
                    count++;
                    _uum = uum;
                }
            }
            threshold <<= 1;
        }
        while (count > 8 && threshold <= MEM_HEAP_HASH_SIZE);
    }
    return res;
}

U0 MemPagTaskFree(CMemBlk *m, CHeapCtrl *hc)
{//hc must be locked
    if (m)
    {
        PUSHFD
        CLI
        if (m->mb_signature != MBS_USED_SIGNATURE_VAL)
            SysBadFree(m);
        else
        {
            QueueRemove(m);
            hc->alloced_u8s -= m->pags << MEM_PAG_BITS;
            MemPagFree(m, hc->bp);
        }
        POPFD
    }
}