574 lines
15 KiB
C
574 lines
15 KiB
C
|
//
|
||
|
// Core functionality
|
||
|
//
|
||
|
//! \file tonc_core.h
|
||
|
//! \author J Vijn
|
||
|
//! \date 20060508 - 20080128
|
||
|
//
|
||
|
/* === NOTES ===
|
||
|
* Contents: bits, random, dma, timer
|
||
|
* 20080129,jv: added tonccpy/set routines.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#ifndef TONC_CORE_STUB
|
||
|
#define TONC_CORE_STUB
|
||
|
|
||
|
#include "tonc_memmap.h"
|
||
|
#include "tonc_memdef.h"
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// BITS and BITFIELDS
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
/*! \defgroup grpCoreBit Bit(field) macros
|
||
|
\ingroup grpCore
|
||
|
*/
|
||
|
/*! \{ */
|
||
|
|
||
|
//! \name Simple bit macros
|
||
|
//\{
|
||
|
|
||
|
//! Create value with bit \a n set
|
||
|
#define BIT(n) ( 1<<(n) )
|
||
|
|
||
|
//! Shift \a a by \a n
|
||
|
#define BIT_SHIFT(a, n) ( (a)<<(n) )
|
||
|
|
||
|
//! Create a bitmask \a len bits long
|
||
|
#define BIT_MASK(len) ( BIT(len)-1 )
|
||
|
|
||
|
//! Set the \a flag bits in \a word
|
||
|
#define BIT_SET(y, flag) ( y |= (flag) )
|
||
|
|
||
|
//! Clear the \a flag bits in \a word
|
||
|
#define BIT_CLEAR(y, flag) ( y &= ~(flag) )
|
||
|
|
||
|
//! Flip the \a flag bits in \a word
|
||
|
#define BIT_FLIP(y, flag) ( y ^= (flag) )
|
||
|
|
||
|
//! Test whether all the \a flag bits in \a word are set
|
||
|
#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) )
|
||
|
|
||
|
|
||
|
|
||
|
//! Create a bitmask of length \a len starting at bit \a shift.
|
||
|
#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) )
|
||
|
|
||
|
|
||
|
//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y.
|
||
|
#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) )
|
||
|
|
||
|
//! Prepare a bitmask for insertion or combining.
|
||
|
#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) )
|
||
|
|
||
|
//! Insert a new bitfield value \a x into \a y.
|
||
|
#define _BF_SET(y, x, shift, len) \
|
||
|
( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) )
|
||
|
|
||
|
//\}
|
||
|
|
||
|
|
||
|
/*! \name some EVIL bit-field operations, >:)
|
||
|
* These allow you to mimic bitfields with macros. Most of the
|
||
|
* bitfields in the registers have <i>foo</i>_SHIFT and
|
||
|
* <i>foo</i>_SHIFT macros indicating the mask and shift values
|
||
|
* of the bitfield named <i>foo</i> in a variable.
|
||
|
* These macros let you prepare, get and set the bitfields.
|
||
|
*/
|
||
|
//\{
|
||
|
|
||
|
//! Prepare a named bit-field for for insterion or combination.
|
||
|
#define BFN_PREP(x, name) ( ((x)<<name##_SHIFT) & name##_MASK )
|
||
|
|
||
|
//! Get the value of a named bitfield from \a y. Equivalent to (var=) y.name
|
||
|
#define BFN_GET(y, name) ( ((y) & name##_MASK)>>name##_SHIFT )
|
||
|
|
||
|
//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x.
|
||
|
#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) )
|
||
|
|
||
|
//! Compare a named bitfield to named literal \a x.
|
||
|
#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) )
|
||
|
|
||
|
|
||
|
//! Massage \a x for use in bitfield \a name with pre-shifted \a x
|
||
|
#define BFN_PREP2(x, name) ( (x) & name##_MASK )
|
||
|
|
||
|
//! Get the value of bitfield \a name from \a y, but don't down-shift
|
||
|
#define BFN_GET2(y, name) ( (y) & name##_MASK )
|
||
|
|
||
|
//! Set bitfield \a name from \a y to \a x with pre-shifted \a x
|
||
|
#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) )
|
||
|
|
||
|
//\}
|
||
|
|
||
|
INLINE u32 bf_get(u32 y, uint shift, uint len);
|
||
|
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len);
|
||
|
INLINE u32 bf_clamp(int x, uint len);
|
||
|
|
||
|
INLINE int bit_tribool(u32 x, uint plus, uint minus);
|
||
|
INLINE u32 ROR(u32 x, uint ror);
|
||
|
|
||
|
|
||
|
/*! \} */
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// DATA
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
/*! \defgroup grpData Data routines
|
||
|
\ingroup grpCore
|
||
|
*/
|
||
|
/*! \{ */
|
||
|
|
||
|
//! Get the number of elements in an array
|
||
|
#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) )
|
||
|
|
||
|
//! Align \a x to the next multiple of \a width.
|
||
|
INLINE uint align(uint x, uint width);
|
||
|
|
||
|
//! \name Copying and filling routines
|
||
|
//\{
|
||
|
|
||
|
//! Simplified copier for GRIT-exported data.
|
||
|
#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2)
|
||
|
|
||
|
|
||
|
// Base memcpy/set replacements.
|
||
|
void *tonccpy(void *dst, const void *src, uint size);
|
||
|
|
||
|
void *__toncset(void *dst, u32 fill, uint size);
|
||
|
INLINE void *toncset(void *dst, u8 src, uint count);
|
||
|
INLINE void *toncset16(void *dst, u16 src, uint count);
|
||
|
INLINE void *toncset32(void *dst, u32 src, uint count);
|
||
|
|
||
|
|
||
|
// Fast memcpy/set
|
||
|
void memset16(void *dst, u16 hw, uint hwcount);
|
||
|
void memcpy16(void *dst, const void* src, uint hwcount);
|
||
|
|
||
|
void memset32(void *dst, u32 wd, uint wcount);
|
||
|
void memcpy32(void *dst, const void* src, uint wcount);
|
||
|
|
||
|
|
||
|
//! Fastfill for halfwords, analogous to memset()
|
||
|
/*! Uses <code>memset32()</code> if \a hwcount>5
|
||
|
* \param dst Destination address.
|
||
|
* \param hw Source halfword (not address).
|
||
|
* \param hwcount Number of halfwords to fill.
|
||
|
* \note \a dst <b>must</b> be halfword aligned.
|
||
|
* \note \a r0 returns as \a dst + \a hwcount*2.
|
||
|
*/
|
||
|
void memset16(void *dst, u16 hw, uint hwcount);
|
||
|
|
||
|
//! \brief Copy for halfwords.
|
||
|
/*! Uses <code>memcpy32()</code> if \a hwn>6 and
|
||
|
\a src and \a dst are aligned equally.
|
||
|
\param dst Destination address.
|
||
|
\param src Source address.
|
||
|
\param hwcount Number of halfwords to fill.
|
||
|
\note \a dst and \a src <b>must</b> be halfword aligned.
|
||
|
\note \a r0 and \a r1 return as
|
||
|
\a dst + \a hwcount*2 and \a src + \a hwcount*2.
|
||
|
*/
|
||
|
void memcpy16(void *dst, const void* src, uint hwcount);
|
||
|
|
||
|
|
||
|
//! Fast-fill by words, analogous to memset()
|
||
|
/*! Like CpuFastSet(), only without the requirement of
|
||
|
32byte chunks and no awkward store-value-in-memory-first issue.
|
||
|
\param dst Destination address.
|
||
|
\param wd Fill word (not address).
|
||
|
\param wdcount Number of words to fill.
|
||
|
\note \a dst <b>must</b> be word aligned.
|
||
|
\note \a r0 returns as \a dst + \a wdcount*4.
|
||
|
*/
|
||
|
void memset32(void *dst, u32 wd, uint wdcount);
|
||
|
|
||
|
|
||
|
//! \brief Fast-copy by words.
|
||
|
/*! Like CpuFastFill(), only without the requirement of 32byte chunks
|
||
|
\param dst Destination address.
|
||
|
\param src Source address.
|
||
|
\param wdcount Number of words.
|
||
|
\note \a src and \a dst <b>must</b> be word aligned.
|
||
|
\note \a r0 and \a r1 return as
|
||
|
\a dst + \a wdcount*4 and \a src + \a wdcount*4.
|
||
|
*/
|
||
|
void memcpy32(void *dst, const void* src, uint wdcount);
|
||
|
|
||
|
//\}
|
||
|
|
||
|
|
||
|
/*! \name Repeated-value creators
|
||
|
These function take a hex-value and duplicate it to all fields,
|
||
|
like 0x88 -> 0x88888888.
|
||
|
*/
|
||
|
//\{
|
||
|
INLINE u16 dup8(u8 x);
|
||
|
INLINE u32 dup16(u16 x);
|
||
|
INLINE u32 quad8(u8 x);
|
||
|
INLINE u32 octup(u8 x);
|
||
|
//\}
|
||
|
|
||
|
//! \name Packing routines.
|
||
|
//\{
|
||
|
INLINE u16 bytes2hword(u8 b0, u8 b1);
|
||
|
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3);
|
||
|
INLINE u32 hword2word(u16 h0, u16 h1);
|
||
|
//\}
|
||
|
|
||
|
/*! \} */
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// DMA
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
/*! \addtogroup grpDma */
|
||
|
/*! \{ */
|
||
|
|
||
|
//! General purpose DMA transfer macro
|
||
|
/*! \param _dst Destination address.
|
||
|
\param _src Source address.
|
||
|
\param count Number of transfers.
|
||
|
\param ch DMA channel.
|
||
|
\param mode DMA mode.
|
||
|
*/
|
||
|
#define DMA_TRANSFER(_dst, _src, count, ch, mode) \
|
||
|
do { \
|
||
|
REG_DMA[ch].cnt= 0; \
|
||
|
REG_DMA[ch].src= (const void*)(_src); \
|
||
|
REG_DMA[ch].dst= (void*)(_dst); \
|
||
|
REG_DMA[ch].cnt= (count) | (mode); \
|
||
|
} while(0)
|
||
|
|
||
|
|
||
|
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode);
|
||
|
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode);
|
||
|
|
||
|
INLINE void dma3_cpy(void *dst, const void *src, uint size);
|
||
|
INLINE void dma3_fill(void *dst, volatile u32 src, uint size);
|
||
|
|
||
|
/*! \} */
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// TIMER
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
INLINE void profile_start(void);
|
||
|
INLINE uint profile_stop(void);
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// TONE GENERATOR
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS,
|
||
|
NOTE_E, NOTE_F, NOTE_FIS, NOTE_G,
|
||
|
NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B
|
||
|
} eSndNoteId;
|
||
|
|
||
|
extern const uint __snd_rates[12];
|
||
|
|
||
|
//! Gives the period of a note for the tone-gen registers.
|
||
|
/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda).
|
||
|
* \param note ID (range: [0,11>). See eSndNoteId.
|
||
|
* \param oct octave (range [-2,4)>).
|
||
|
*/
|
||
|
#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) )
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// MISC
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
/*! \defgroup grpCoreMisc Miscellaneous routines
|
||
|
* \ingroup grpCore
|
||
|
*/
|
||
|
/*! \{ */
|
||
|
|
||
|
#define STR(x) #x
|
||
|
|
||
|
//! Create text string from a literal
|
||
|
#define XSTR(x) STR(x)
|
||
|
|
||
|
|
||
|
//! \name Inline assembly
|
||
|
//\{
|
||
|
|
||
|
//! Assembly comment
|
||
|
#define ASM_CMT(str) asm volatile("@# " str)
|
||
|
|
||
|
//! No$gba breakpoint
|
||
|
#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11")
|
||
|
|
||
|
//! No-op; wait a bit.
|
||
|
#define ASM_NOP() asm volatile("\tnop")
|
||
|
|
||
|
//\}
|
||
|
|
||
|
|
||
|
//! \name Sector checking
|
||
|
//\{
|
||
|
|
||
|
u32 octant(int x, int y);
|
||
|
u32 octant_rot(int x0, int y0);
|
||
|
|
||
|
//\}
|
||
|
|
||
|
//! \name Random numbers
|
||
|
//\{
|
||
|
|
||
|
#define QRAN_SHIFT 15
|
||
|
#define QRAN_MASK ((1<<QRAN_SHIFT)-1)
|
||
|
#define QRAN_MAX QRAN_MASK
|
||
|
|
||
|
int sqran(int seed);
|
||
|
INLINE int qran(void);
|
||
|
INLINE int qran_range(int min, int max);
|
||
|
|
||
|
//\}
|
||
|
|
||
|
/*! \} */
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// GLOBALS
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
extern const u8 oam_sizes[3][4][2];
|
||
|
extern const BG_AFFINE bg_aff_default;
|
||
|
extern COLOR *vid_page;
|
||
|
|
||
|
extern int __qran_seed;
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// INLINES
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
// --- Bit and bitfields -----------------------------------------------
|
||
|
|
||
|
|
||
|
//! Get \a len long bitfield from \a y, starting at \a shift.
|
||
|
/*! \param y Value containing bitfield.
|
||
|
\param shift Bitfield Start;
|
||
|
\param len Length of bitfield.
|
||
|
\return Bitfield between bits \a shift and \a shift + \a length.
|
||
|
*/
|
||
|
INLINE u32 bf_get(u32 y, uint shift, uint len)
|
||
|
{ return (y>>shift) & ( (1<<len)-1 ); }
|
||
|
|
||
|
//! Merge \a x into an \a len long bitfield from \a y, starting at \a shift.
|
||
|
/*! \param y Value containing bitfield.
|
||
|
\param x Value to merge (will be masked to fit).
|
||
|
\param shift Bitfield Start;
|
||
|
\param len Length of bitfield.
|
||
|
\return Result of merger: (y&~M) | (x<<s & M)
|
||
|
\note Does \e not write the result back into \a y (Because pure C
|
||
|
does't have references, that's why)
|
||
|
*/
|
||
|
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len)
|
||
|
{
|
||
|
u32 mask= ((u32)(1<<len)-1);
|
||
|
return (y &~ (mask<<shift)) | (x & mask)<<shift;
|
||
|
}
|
||
|
|
||
|
//! Clamp \a to within the range allowed by \a len bits
|
||
|
INLINE u32 bf_clamp(int x, uint len)
|
||
|
{
|
||
|
u32 y=x>>len;
|
||
|
if(y)
|
||
|
x= (~y)>>(32-len);
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
|
||
|
//! Gives a tribool (-1, 0, or +1) depending on the state of some bits.
|
||
|
/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts
|
||
|
their status to give a +1, -1 or 0 result. Useful for direction flags.
|
||
|
\param flags Value with bit-flags.
|
||
|
\param plus Bit number for positive result.
|
||
|
\param minus Bit number for negative result.
|
||
|
\return <b>+1</b> if \a plus bit is set but \a minus bit isn't<br>
|
||
|
<b>-1</b> if \a minus bit is set and \a plus bit isn't<br>
|
||
|
<b>0</b> if neither or both are set.
|
||
|
*/
|
||
|
INLINE int bit_tribool(u32 flags, uint plus, uint minus)
|
||
|
{ return ((flags>>plus)&1) - ((flags>>minus)&1); }
|
||
|
|
||
|
|
||
|
//! Rotate bits right. Yes, this does lead to a ror instruction.
|
||
|
INLINE u32 ROR(u32 x, uint ror)
|
||
|
{ return (x<<(32-ror)) | (x>>ror); }
|
||
|
|
||
|
|
||
|
// --- Data -----------------------------------------------------------
|
||
|
|
||
|
INLINE uint align(uint x, uint width)
|
||
|
{ return (x+width-1)/width*width; }
|
||
|
|
||
|
|
||
|
//! VRAM-safe memset, byte version. Size in bytes.
|
||
|
INLINE void *toncset(void *dst, u8 src, uint count)
|
||
|
{ return __toncset(dst, quad8(src), count); }
|
||
|
|
||
|
//! VRAM-safe memset, halfword version. Size in hwords.
|
||
|
INLINE void *toncset16(void *dst, u16 src, uint count)
|
||
|
{ return __toncset(dst, src|src<<16, count*2); }
|
||
|
|
||
|
//! VRAM-safe memset, word version. Size in words.
|
||
|
INLINE void *toncset32(void *dst, u32 src, uint count)
|
||
|
{ return __toncset(dst, src, count*4); }
|
||
|
|
||
|
|
||
|
|
||
|
//! Duplicate a byte to form a halfword: 0x12 -> 0x1212.
|
||
|
INLINE u16 dup8(u8 x) { return x|(x<<8); }
|
||
|
|
||
|
//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234.
|
||
|
INLINE u32 dup16(u16 x) { return x|(x<<16); }
|
||
|
|
||
|
//! Quadruple a byte to form a word: 0x12 -> 0x12121212.
|
||
|
INLINE u32 quad8(u8 x) { return x*0x01010101; }
|
||
|
|
||
|
//! Octuple a nybble to form a word: 0x1 -> 0x11111111
|
||
|
INLINE u32 octup(u8 x) { return x*0x11111111; }
|
||
|
|
||
|
|
||
|
//! Pack 2 bytes into a word. Little-endian order.
|
||
|
INLINE u16 bytes2hword(u8 b0, u8 b1)
|
||
|
{ return b0 | b1<<8; }
|
||
|
|
||
|
//! Pack 4 bytes into a word. Little-endian order.
|
||
|
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3)
|
||
|
{ return b0 | b1<<8 | b2<<16 | b3<<24; }
|
||
|
|
||
|
INLINE u32 hword2word(u16 h0, u16 h1)
|
||
|
{ return h0 | h1<<16; }
|
||
|
|
||
|
|
||
|
// --- DMA ------------------------------------------------------------
|
||
|
|
||
|
/*! \addtogroup grpDma */
|
||
|
/*! \{ */
|
||
|
|
||
|
//! Generic DMA copy routine.
|
||
|
/*! \param dst Destination address.
|
||
|
* \param src Source address.
|
||
|
* \param count Number of copies to perform.
|
||
|
* \param ch DMA channel.
|
||
|
* \param mode DMA transfer mode.
|
||
|
* \note \a count is the number of copies, not the size in bytes.
|
||
|
*/
|
||
|
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode)
|
||
|
{
|
||
|
REG_DMA[ch].cnt= 0;
|
||
|
REG_DMA[ch].src= src;
|
||
|
REG_DMA[ch].dst= dst;
|
||
|
REG_DMA[ch].cnt= mode | count;
|
||
|
}
|
||
|
|
||
|
//! Generic DMA fill routine.
|
||
|
/*! \param dst Destination address.
|
||
|
* \param src Source value.
|
||
|
* \param count Number of copies to perform.
|
||
|
* \param ch DMA channel.
|
||
|
* \param mode DMA transfer mode.
|
||
|
* \note \a count is the number of copies, not the size in bytes.
|
||
|
*/
|
||
|
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode)
|
||
|
{
|
||
|
REG_DMA[ch].cnt= 0;
|
||
|
REG_DMA[ch].src= (const void*)&src;
|
||
|
REG_DMA[ch].dst= dst;
|
||
|
REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED;
|
||
|
}
|
||
|
|
||
|
//! Specific DMA copier, using channel 3, word transfers.
|
||
|
/*! \param dst Destination address.
|
||
|
* \param src Source address.
|
||
|
* \param size Number of bytes to copy
|
||
|
* \note \a size is the number of bytes
|
||
|
*/
|
||
|
INLINE void dma3_cpy(void *dst, const void *src, uint size)
|
||
|
{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); }
|
||
|
|
||
|
//! Specific DMA filler, using channel 3, word transfers.
|
||
|
/*! \param dst Destination address.
|
||
|
* \param src Source value.
|
||
|
* \param size Number of bytes to copy
|
||
|
* \note \a size is the number of bytes
|
||
|
*/
|
||
|
INLINE void dma3_fill(void *dst, volatile u32 src, uint size)
|
||
|
{ dma_fill(dst, src, size/4, 3, DMA_FILL32); }
|
||
|
|
||
|
/*! \} */
|
||
|
|
||
|
|
||
|
// --- Random ---------------------------------------------------------
|
||
|
|
||
|
//! Quick (and very dirty) pseudo-random number generator
|
||
|
/*! \return random in range [0,8000h>
|
||
|
*/
|
||
|
INLINE int qran(void)
|
||
|
{
|
||
|
__qran_seed= 1664525*__qran_seed+1013904223;
|
||
|
return (__qran_seed>>16) & QRAN_MAX;
|
||
|
}
|
||
|
|
||
|
|
||
|
//! Ranged random number
|
||
|
/*! \return random in range [\a min, \a max>
|
||
|
* \note (max-min) must be lower than 8000h
|
||
|
*/
|
||
|
INLINE int qran_range(int min, int max)
|
||
|
{ return (qran()*(max-min)>>QRAN_SHIFT)+min; }
|
||
|
|
||
|
|
||
|
// --- Timer ----------------------------------------------------------
|
||
|
|
||
|
/*! \addtogroup grpTimer */
|
||
|
/*! \{ */
|
||
|
|
||
|
//! Start a profiling run
|
||
|
/*! \note Routine uses timers 3 and 3; if you're already using these
|
||
|
* somewhere, chaos is going to ensue.
|
||
|
*/
|
||
|
INLINE void profile_start(void)
|
||
|
{
|
||
|
REG_TM2D= 0; REG_TM3D= 0;
|
||
|
REG_TM2CNT= 0; REG_TM3CNT= 0;
|
||
|
REG_TM3CNT= TM_ENABLE | TM_CASCADE;
|
||
|
REG_TM2CNT= TM_ENABLE;
|
||
|
}
|
||
|
|
||
|
//! Stop a profiling run and return the time since its start.
|
||
|
/*! \return 32bit cycle count
|
||
|
*/
|
||
|
INLINE uint profile_stop(void)
|
||
|
{
|
||
|
REG_TM2CNT= 0;
|
||
|
return (REG_TM3D<<16)|REG_TM2D;
|
||
|
}
|
||
|
|
||
|
/*! \} /addtogroup */
|
||
|
|
||
|
|
||
|
#endif // TONC_CORE
|
||
|
|