/*****************************************************************************
 * L2P.C - Function to convert a logical address to a physical address under *
 *         the WATCOM 32 bit DOS extender environment.                       *
 *                                                                           *
 * Copyright (c) 1999, ATI Technologies Inc.  All Rights Reserved.           *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>

#include "main.h"

extern WORD Getds (void);
extern DWORD Getlin (WORD);
extern DWORD Getpd (void);
extern DWORD Getcr0 (void);


/******************************************************************************
 *  GetPhysical - convert a logical address to a physical address             *
 *                                                                            *
 *  Inputs:                                                                   *
 *      logical_address - logical address to convert                          *
 *                                                                            *
 *  Ouputs:                                                                   *
 *      Converted physical address                                            *
 ******************************************************************************/

DWORD GetPhysical (DWORD logical_address)
{
    WORD   ds;
    DWORD  linaddr;
    DWORD  pdaddr;
    DWORD  linear;
    DWORD  laddr1;
    DWORD  laddr2;
    DWORD  index;
    DWORD  address;
    DWORD  physical;
    DWORD  cr0;
    DWORD *dptr;
    BYTE  *bptr;

    _disable ();

    // set default output
    physical = 0;

    // form linear address from segment descriptor and logical address

    ds = Getds ();
    linaddr = Getlin (ds);
    linear = linaddr + logical_address;

    // determine if paging circuitry is enabled by reading CR0 register

    cr0 = Getcr0 ();
    if ((cr0 & 0x80000000) != 0)
    {
        // Paging: linear address != physical address

        // form directory address from page directory base and linear address
        pdaddr = Getpd ();
        laddr1 = DPMI_allocatelogicalregion (pdaddr, 0xFFFF);
        if (laddr1 != 0)
        {
            index = linear >> 22;
            bptr = (BYTE *) (laddr1 + (index*4));
            dptr = (DWORD *) bptr;
            address = (*dptr) & 0xFFFFF000;

            // form table address from directory address and linear address

            laddr2 = DPMI_allocatelogicalregion (address, 0xFFFF);
            if (laddr2 != 0)
            {
                index = (linear & 0x003FF000) >> 12;
                bptr = (BYTE *) (laddr2 + (index*4));
                dptr = (DWORD *) bptr;
                address = (*dptr) & 0xFFFFF000;

                // form physical address from table address and linear address

                physical = address + (linear & 0xFFF);

                // free mapping

                DPMI_freelogicalregion (laddr2);
            } // if

            // free mapping

            DPMI_freelogicalregion (laddr1);
        } // if
    }
    else
    {
        // No paging: linear address = physical address

        physical = linear;
    } // if

    _enable();

    return (physical);
} // GetPhysical
