/********************************************************************
Breakpoint.h - declarations of functions, data structures and
    constants to be used to enable/disable debug extensions,
    set data and instruction breakpoints, read trace buffer,
    Process ID and Current Program Status on a
    Pocket PC running an Intel XScale CPU.

    These functions are implemented in AccessCoproc.s

             Written 2004 by Dmitri Leman
             for an article about XScale debug extensions.

    This file was compiled for Windows CE using
    eMbedded Visual C++ 3.0 with Pocket PC 2002 SDK
    eMbedded Visual C++ 4.0 with Pocket PC 2003 SDK

********************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

/*
enum DebugControlAndStatusFlags defines flags to be passed to
SetDebugControlAndStatus to enable/disable XScale debug extensions.

We define only a subset of flags for the
Debug Control and Status Register (DCSR),
which can be used by software.
*/
enum DebugControlAndStatusFlags
{
    DEF_GlobalDebugEnabled = 0x80000000,//Enable
                                    //all debug functionality
    DEF_TraceBufferFillOnce = 2,
    DEF_TraceBufferEnabled  = 1

    //The rest of flags are not used in this program
};

/*
SetDebugControlAndStatus used to enable/disable debug extensions.
It writes (optionally) to Debug Control and Status Register (DCSR)
and returns the original value of DCSR.

Parameters:
    DWORD p_dwFlags - flags from enum DebugControlAndStatusFlags to be
                      set or reset.
    DWORD p_dwMask  - flags from enum DebugControlAndStatusFlags to be
                      modified.
Only flags specified in p_dwMask are modified, others are preserved.
Returns:
    Original value of DCSR. It may contains several flags, some of
which are defined in enum DebugControlAndStatusFlags. When debugging
is finished, this original value should be written back to DCSR.

Examples:

  //Get value of DCSR without modification:
  DWORD l_dwOriginalFlags = SetDebugControlAndStatus(0,0);

  //Enable all debug functionality:
  SetDebugControlAndStatus
    (DEF_GlobalDebugEnabled, DEF_GlobalDebugEnabled);

  //Disable all debug functionality:
  SetDebugControlAndStatus(0, DEF_GlobalDebugEnabled);

  //Restore the original value of DCSR:
  SetDebugControlAndStatus(l_dwOriginalFlags, -1);
*/
extern "C" DWORD SetDebugControlAndStatus
    (DWORD p_dwFlags, DWORD p_dwMask);

enum XScaleDataBreakpointFlags
{
    AllDisabled     = 0,
    //Break1* values cannot be ORed with each other
    Break1Disabled  = 0,
    Break1StoreOnly = 1,
    Break1LoadStore = 2,
    Break1LoadOnly  = 3,
    //Break2* values cannot be ORed with each other
    Break2Disabled  = 0,
    Break2StoreOnly = 4,
    Break2LoadStore = 8,
    Break2LoadOnly  = 0xC,
    UseMask         = 0x100//second address is used as a mask,
                           //which is together with the first
                           //address defines the first breakpoint.
};

/*
SetDataBreakPoint writes new values to DBR0, DBR1 and DBCON
to enable or disable data breakpoints.
Parameters:
    DWORD p_dwAddress1 - new value for DBR0.
        This is the address of the first data breakpoint.
    DWORD p_dwAddress2 - new value for DBR1.
        This is the address of the second data breakpoint
        or a mask to be used together with p_dwAddress1 to
        set a breakpoint on memory range.
    DWORD p_dwFlags    - new value for DBCON.
        Use combinations of values and flags from
        XScaleDataBreakpointFlags to enable,
        disable or reconfigure the data breakpoints.
*/
extern "C" void SetDataBreakPoint(
    DWORD p_dwAddress1, DWORD p_dwAddress2, DWORD p_dwFlags);

/*
SetCodeBreakPoint writes new values to IBCR0 and IBCR1
to enable or disable instruction breakpoints.
Note: instruction breakpoints appear to be useless since neither
debugger nor __try/__except appear to be able to intercept the
"pre-fetch abort", which is generated by the instruction breakpoints.
Parameters:
    p_dwAddress1 - new value for IBCR0.
        Set bit #0 to enable the breakpoint.
    p_dwAddress2 - new value for IBCR1.
        Set bit #0 to enable the breakpoint.
*/
extern "C" void SetCodeBreakPoint
    (DWORD p_dwAddress1, DWORD p_dwAddress2);

/*
ReadPID - returns value of Process ID (PID) register.
It is used to remap virtual addresses in slot 0
to the slot of the current process.
The value of the PID is equal to the address of the process slot.
The CPU uses the high 7 bits (31:25) on the PID to replace the
correspondent bits of virtual addresses when they are 0.
The same operation have to be performed when preparing addresses
for DBRx or IBCRx.
*/
extern "C" DWORD ReadPID();

/*
GetProgramStatusRegister returns value of CPSR
(Current Program Status Register).
CPSR format:
31 30 29 28 27  ...  7 6 5 4 3 2 1 0
N  Z  C  V  Q        I F T M M M M M
Where:
N Z C V Q - condition code flags
I - disable IRQ interrupts
F - disable FIQ interrupts
T - indicates Thumb execution
MMMMM - 5 bit processor mode - see enum ProcessorMode
*/
extern "C" DWORD GetProgramStatusRegister();

/*
SetCurrentProcessorMode is included for experimentation only.
It assigns a new value to the low byte of CPSR (processor mode and
interrupt disable flags).
This assignment has no effect if executed in PM_User mode.
Parameter:
    DWORD p_dwNewMode - low byte contains one of PM_* values
    from enum ProcessorMode and (optionally)
    IRQ_DisableFlag and FIQ_DisableFlag flags.
*/
extern "C" void SetCurrentProcessorMode(DWORD p_dwNewMode);

enum ProcessorMode
{
    PM_Mask       = 0x1F,//Mask to extract processor mode from CPSR
    PM_User       = 0x10,//Used for User mode
    PM_FIQ        = 0x11,
    PM_IRQ        = 0x12,
    PM_Supervisor = 0x13,
    PM_Abort      = 0x17,
    PM_Undefined  = 0x1B,
    PM_System     = 0x1F,//Used for Kernel mode

    IRQ_DisableFlag = 0x80,
    FIQ_DisableFlag = 0x40,
    //Note: on PPC all applications appear to run in Kernel mode
    //(PM_System). On SmartPhones applications are running in
    //PM_User, but trusted applications can switch to Kernel mode
    //(PM_System) using SetKMode(TRUE).
};

/*
struct TraceBuffer is used as parameter to ReadTraceBuffer
to be filled with the the content of the buffer as well
as values of CHKPT0 and CHKPT1 registers.
CHKPT0 and CHKPT1 registers are used as reference addresses
to reconstruct execution history.
*/
struct TraceBuffer
{
    BYTE  m_byBuffer[256];
    DWORD m_dwCheckPoint0;
    DWORD m_dwCheckPoint1;
};

/*
ReadTraceBuffer reads the content of the XScale trace buffer.
It should be called when the trace buffer is disabled.
Usually we call it from the exception handler (processor disables
trace buffer when exception happens).
The process of reading the trace buffer also erases it,
so after reading the buffer is ready to be enabled again.
Parameters:
    TraceBuffer * p_pTraceBuffer - pointer to struct TraceBuffer
        to be filled with the content of the buffer as well
        as values of CHKPT0 and CHKPT1 registers.
*/
extern "C" void  ReadTraceBuffer(TraceBuffer * p_pTraceBuffer);

/*
Use MAP_PTR macro to prepare an address for writing to a data or code
breakpoint register. If the address is "unmapped" (it's high 7 bits
are 0), it is ORed with the value of PID register
(address of the memory slot of the current process).
*/
#define MAP_PTR(address) (((DWORD)(address) & 0xFE000000)? \
        (DWORD)address : (DWORD)address | ReadPID())

#ifdef __cplusplus
}//extern "C"

/*
class DataBreakPoint is a convenience tool for setting local
breakpoints. To set a data breakpoint inside a function just create
an instance of DataBreakPoint on the stack passing the address of
data. When the instance goes out of scope, the destructor will
disable all breakpoints.
*/
class DataBreakPoint
{
public:
    DataBreakPoint(void * p_pAddress1, void * p_pAddress2 = NULL,
                   DWORD p_dwFlags = Break1StoreOnly)
    {
        Set(p_pAddress1, p_pAddress2, p_dwFlags);
    }
    ~DataBreakPoint()
    {
        Clear();
    }
    void Set(void * p_pAddress1, void * p_pAddress2 = NULL,
             DWORD p_dwFlags = Break1StoreOnly)
    {
        SetDataBreakPoint(MAP_PTR(p_pAddress1),
            (p_dwFlags & UseMask)? (DWORD)p_pAddress1 :
                                    MAP_PTR(p_pAddress1), p_dwFlags);
    }
    void Clear()
    {
        SetDataBreakPoint(0, 0, AllDisabled);
    }
};//struct DataBreakPoint

#endif
