// prp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>

int CalcNumPrp(LARGE_INTEGER physicalAddress, int sgElementSize);

void check(int testcase, int entries, int expected);

#define PAGE_SIZE (0x1000)
#define PAGE_MASK (PAGE_SIZE - 1)

#define LESS (0x100)
#define SAME (PAGE_SIZE)
#define MORE (PAGE_SIZE+0x100)

/*
Test Case Matrix

				1	2	3	4	5	6	7	8	9	10	11
aligne start	x	x	x	x							
unaligned start					x	x	x	x	x	x	x
< 1 page		x				x	x					
= 1 page			x					x				
 > 1 page				x					x	x		
> 2 pages					x						x	x
no cross +page					x	 		x		x	
cross +page	 						x	x		x		x

*/

int _tmain(int argc, _TCHAR* argv[])
{
	int entries;
	int len;
	LARGE_INTEGER startAdd;

	startAdd.QuadPart=0;

	startAdd.LowPart=0x0;
	len = PAGE_SIZE - 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(1, entries, 1);

	startAdd.LowPart=0x0;
	len = PAGE_SIZE;
	entries = CalcNumPrp(startAdd, len);
	check(2, entries, 1);

	startAdd.LowPart=0x0;
	len = PAGE_SIZE + 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(3, entries, 2);

	startAdd.LowPart=0x0;
	len = PAGE_SIZE*5;
	entries = CalcNumPrp(startAdd, len);
	check(4, entries, 5);

	startAdd.LowPart = 0x100;
	len = PAGE_SIZE - 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(5, entries, 1);

	startAdd.LowPart = 0x100;
	len = PAGE_SIZE - 0x10;
	entries = CalcNumPrp(startAdd, len);
	check(6, entries, 2);

	startAdd.LowPart = 0x100;
	len = PAGE_SIZE;
	entries = CalcNumPrp(startAdd, len);
	check(7, entries, 2);

	startAdd.LowPart = 0x100;
	len = PAGE_SIZE + 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(8, entries, 2);

	startAdd.LowPart = PAGE_SIZE - 0x10;
	len = PAGE_SIZE + 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(9, entries, 3);

	startAdd.LowPart = 0x100;
	len = PAGE_SIZE*3 + 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(10, entries, 4);

	startAdd.LowPart = PAGE_SIZE - 0x10;
	len = PAGE_SIZE*3 + 0x100;
	entries = CalcNumPrp(startAdd, len);
	check(11, entries, 5);


	system("pause");
	return 0;
}

void check(int testcase, int entries, int expected)
{
	if (entries != expected) {
		printf("Testcase %d failed: entries = %d expected = %d\n",testcase,entries,expected);
		system("pause");
	}
}
#if 1
int CalcNumPrp(LARGE_INTEGER physicalAddress, int sgElementSize)
{

	ULONG modulo, nextPage, unaligned;
	ULONG numImplicitEntries;

	/* calc number of whole pages, if any */
	numImplicitEntries = sgElementSize / PAGE_SIZE;
	/* calc the leftover size minus whole pages */
	modulo = sgElementSize % PAGE_SIZE;
	/* determine if our start is aligned or not */
	unaligned = physicalAddress.LowPart % PAGE_SIZE;

	/* 
	 * if we have a length modulo or we're not aligned then
	 * we treat this like an unaligned < page size case and
	 * look at the next page boundary address to see if
	 * we cross it or not.  If we do then we need to more 
	 * entries, one for each page if not then just one more
	 * to account for either the pre section or post section
	 */
	if (modulo || unaligned) {
		nextPage = physicalAddress.LowPart + PAGE_SIZE;
		nextPage = nextPage & ~PAGE_MASK;
		numImplicitEntries +=
			((physicalAddress.LowPart + modulo) > nextPage) ? 2 : 1;
	}

	return numImplicitEntries;
}
#else
int CalcNumPrp(LARGE_INTEGER physicalAddress, int sgElementSize)
{
	ULONG numImplicitEntries;

    if (sgElementSize <= PAGE_SIZE) {
            ULONG nextPage = 0;

            /* Must transfer at least one full page */
            numImplicitEntries = 1;
            /*
             * since we may be offset into the page, look to determine
             * address to determine if a 2nd PRP entry is needed
             */
            nextPage = physicalAddress.LowPart + PAGE_SIZE;
            nextPage = nextPage & ~PAGE_MASK;
            numImplicitEntries +=
                ((physicalAddress.LowPart + sgElementSize) > nextPage) ? 1 : 0;
        } else {
            LARGE_INTEGER startAddr, endAddr;

            startAddr.QuadPart = physicalAddress.QuadPart;
            endAddr.QuadPart = startAddr.QuadPart + sgElementSize;
            numImplicitEntries = 0;

            /* account for an entry becaue of an offset start */
            if (startAddr.QuadPart % PAGE_SIZE) {
                numImplicitEntries += 1;
                /* bump the start address to the next page boundary */
                startAddr.QuadPart += PAGE_SIZE;
                startAddr.QuadPart &= ~PAGE_MASK;
            }

            /* account for an entry becaue of tail */
            if (endAddr.QuadPart % PAGE_SIZE) {
                numImplicitEntries += 1;
                /* lop off the tail */
                endAddr.QuadPart &= ~PAGE_MASK;
            }
            /* now account for the whole pages if any */
            numImplicitEntries += (ULONG)((endAddr.QuadPart - startAddr.QuadPart) / PAGE_SIZE);
        }
	return numImplicitEntries;
}
#endif