short	logical2physical(register unsigned long logicalAddress,register unsigned long* physicalAddress);
short	physical2logical(register unsigned long physicalAddress,register unsigned long* logicalAddress);
short	logical2physicalBAT(register unsigned long logicalAddress,register unsigned long* physicalAddress);

short logical2physicalBAT(register unsigned long logicalAddress,register unsigned long* physicalAddress)
{
	register unsigned long BATU,BATL;
	register unsigned long lowMask,highMask;
	
	for(unsigned long i=0;i<4;i++)
	{
		switch(i)
		{
			case 0:	BATU = _getIBAT0U();	BATL = _getIBAT0L();	break;
			case 1:	BATU = _getIBAT1U();	BATL = _getIBAT1L();	break;
			case 2:	BATU = _getIBAT2U();	BATL = _getIBAT2L();	break;
			case 3:	BATU = _getIBAT3U();	BATL = _getIBAT3L();	break;
		}
		
		lowMask = (((BATL & 0x0000003F) + 1) << 17) - 1;
		highMask = ~lowMask;
		
		// Is this BAT pair valid?
		if(BATL & 0x00000040)
		{
			if((BATU & highMask) == (logicalAddress & highMask) )	// Is it in this block?
			{
				*physicalAddress = (BATL & highMask) | (logicalAddress & lowMask);
				return 0;
			}
		}
	}
	
	return -1;
}

short logical2physical(register unsigned long logicalAddress,register unsigned long* physicalAddress)
{
	/*
		This routine calculates the physical address of some logical address.  It is pretty messy and involved.  I have followed
		the algorithm described in "PowerPC Microprocessor Developer's Guide" by Bunda, Potter, and Shadowen.  That book
		is a mess.  Don't buy it if you have the choice.  However, the algorithm seems to work, so I have probably implemented
		it correctly.
		
		Return values:	0	=	no errors occurred
					-1	=	the logical address is not in physical memory
					-2	=	the logical address is in a direct-store segment
	*/
	register unsigned long	sdr1 = _getSDR1();
	register unsigned long	primaryHash = 0;
	register unsigned long	secondaryHash;
	register unsigned long*	pte1;
	register unsigned long*	pte2;
	register unsigned long	sr;
	register unsigned long	msr;
	register char			foundOne = false;
	register unsigned long	realAddress;
	
	sr = _getSRx( (logicalAddress >> 28) & 0x0000000F);
	if(sr & 0x80000000)	// Is this a direct-store segment?
		return -2;
	
	// Calculate the primary and secondary hash values
	primaryHash = ( (logicalAddress >> 12) & 0x0000FFFF) ^ (sr & 0x0007FFFF);
	secondaryHash = ~primaryHash;
	
	// Find the location of the primary and secondary hash tables
	pte1 = (unsigned long*)(	(( (sdr1 >> 25) & 0x0000007F) << 25) |
						(( ((sdr1 >> 16) & 0x000001FF) | ((sdr1 & 0x000001FF) & ((primaryHash >> 10) & 0x000001FF)) ) << 16 ) |
						( (primaryHash & 0x000003FF) << 6 ));
	pte2 = (unsigned long*)(	(( (sdr1 >> 25) & 0x0000007F) << 25) |
						(( ((sdr1 >> 16) & 0x000001FF) | ((sdr1 & 0x000001FF) & ((secondaryHash >> 10) & 0x000001FF)) ) << 16 ) |
						( (secondaryHash & 0x000003FF) << 6 ));
	
	// We are now going to search the page table group (8 PTEs) for the correct page
	msr = _disableDR();
	for(long i=0;i<8;i++)
	{
		if( pte1[0] & 0x80000000)	// Is it valid?
		{
			if( !(pte1[0] & 0x00000040) )	// Is it a primary PTE?
			{
				if( (( pte1[0] >> 7) & 0x00FFFFFF) == (sr & 0x00FFFFFF) )	// Do the virtual segment ID's match?
				{
					if( ( pte1[0] & 0x0000003F) == ((logicalAddress >> 22) & 0x0000003F) )	// Does the API match?
					{
						// We have a match!
						foundOne = true;
						realAddress = (pte1[1] & 0xFFFFF000) | (logicalAddress & 0x00000FFF);
						goto out;
					}
				}
			}
		}
		if( pte2[0] & 0x80000000)	// Is it valid?
		{
			if( pte2[0] & 0x00000040 )	// Is it a secondary PTE?
			{
				if( (( pte2[0] >> 7) & 0x00FFFFFF) == (sr & 0x00FFFFFF) )	// Do the virtual segment ID's match?
				{
					if( ( pte2[0] & 0x0000003F) == ((logicalAddress >> 22) & 0x0000003F))	// Does the API match?
					{
						// We have a match!
						foundOne = true;
						realAddress = (pte2[1] & 0xFFFFF000) | (logicalAddress & 0x00000FFF);
						goto out;
					}
				}
			}
		}
		pte1+=2;
		pte2+=2;
	}
out:
	_setMSR(msr);
	*physicalAddress = realAddress;
	return (foundOne ? 0:-1);
}

short physical2logical(register unsigned long physicalAddr,register unsigned long* logicalAddr)
{
	/*
		This routine calculates the logical address of some physical address.  It is possible that a physical address is not used
		by the current process - this is the case if the physical address is mapped using segment registers belonging to some
		other process.  That means that it is possible that this routine will not return the logical address.  (An error is returned
		in that case.)  This routine is very inefficient due to the nature of the hash tables on the PowerPC - they are optimised
		for going from logical->physical.  For this reason, we need to do some "reverse-address resolution", in which we call
		logical2physical many times to help find the correct address.
		
		Return values:	0	=	no errors occurred
					-1	=	the physical address is not mapped into this proccess's memory space
	*/
	register unsigned long	sdr1 = _getSDR1();
	register unsigned long*	pte;
	register unsigned long	hashTableLen;
	register unsigned long	i,j;
	register unsigned long	rpn = physicalAddr & 0xFFFFF000;
	register unsigned long	sr;
	register unsigned long	lr;
	register unsigned long	msr;
	register char			foundOne;
	unsigned long			pa;
	
	hashTableLen = ( ((sdr1 & 0x000001FF)+1) << 13);
	pte = (unsigned long*)(sdr1 & 0xFFFF0000);
	foundOne = false;
	
	msr = _disableDR();
	for(i=0;i<hashTableLen;i++)
	{
		if(pte[0] & 0x80000000)	// Is this entry valid?
		{
			if(rpn == (pte[1] & 0xFFFFF000))	// Do the real page numbers match?
			{
				for(sr=0;sr<16;sr++)
				{
					if( (_getSRx(sr) & 0x00FFFFFF) == ( (pte[0] >> 7) & 0x00FFFFFF) )	// Does the VSID match an sr?
						break;
				}
				if(sr < 16)
				{
					lr = ( (sr & 0x0000000F) << 28) | ( (pte[0] & 0x0000003F) << 22) | (physicalAddr & 0x00000FFF);
					_setMSR(msr);
					for(j=0;j<1024;j++)
					{
						if(!logical2physical(lr,&pa))
						{
							if(pa == physicalAddr)
							{
								foundOne = true;
								goto out;
							}
						}
						lr += 0x00001000;
					}
					msr = _disableDR();
				}
			}
		}
		pte += 2;
	}
out:
	_setMSR(msr);
	
	*logicalAddr = lr;
	return (foundOne ? 0:-1);
}
