//
//	Hardware breakpoint support
//
//	www.catch22.net
//
#include <windows.h>

inline void SETBITS(unsigned long& dw, int lowBit, int bits, int newValue)
{
	int mask = (1 << bits) - 1; 
	dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
}

#define DBG_COND_EXECUTE	0
#define DBG_COND_WRITE		1
#define DBG_COND_READWRITE  3

//
//	Address   - address to break on.
//	Length    - must be 0, 1, 2 or 4
//  Condition - must be 0, 1 or 3
//
BOOL SetHardwareBP(PROCESS_INFORMATION *ppi, DWORD Address, DWORD Length, int Condition)
{
	CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
	
	int i;

	// get contents of every debug register
	if(!GetThreadContext(ppi->hThread, &context))
		return -1;

	// find available hardware register
	for(i = 0; i < 4; i++)
	{
		if((context.Dr7 & (1 << (i * 2))) == 0)
		{
			*(&context.Dr0 + i) = Address;

			SETBITS(context.Dr7, 16 + i*4, 2, Condition);
			SETBITS(context.Dr7, 18 + i*4, 2, Length);
			SETBITS(context.Dr7, i * 2,    1, 1);

			if(!SetThreadContext(ppi->hThread, &context))
				return -1;

			return i;
		}
	}

	return -1;
}

BOOL ClearHardwareBP(PROCESS_INFORMATION *ppi, int DbgRegister)
{
	CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
	
	// get contents of every debug register
	if(!GetThreadContext(ppi->hThread, &context))
		return FALSE;

	SETBITS(context.Dr7, DbgRegister*2, 1, 0);

	if(!SetThreadContext(ppi->hThread, &context))
		return FALSE;

	return TRUE;
}

BOOL SingleStep(PROCESS_INFORMATION *ppi)
{
	CONTEXT context = { CONTEXT_CONTROL };
	
	if(!GetThreadContext(ppi->hThread, &context))
		return FALSE;

	context.EFlags |= 0x100;	// set the "trap" flag for single step

	if(!SetThreadContext(ppi->hThread, &context))
		return FALSE;

	return TRUE;
}