gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > Post your custom debug function here!

#10551 - poslundc - Mon Sep 08, 2003 5:28 pm

I recently wrote a little function that I use to examine the contents of memory; thought I'd share it in case anyone else finds it useful. It's not sophisticated at all but if you need to check the value of a variable or contents of an array bit-by-bit (as I often do) it's invaluable.

Code:

// use with gba.h

void DebugVar(void *data, int bytes)
{
   int n, i, j;
   u8 *p = (u8 *)data;
   u32   oldmode = REG_DISPCNT;
   u16 *vbuff = (u16*)0x6000000;
   volatile u32 *k = (volatile u32 *)0x04000130;
   
   REG_DISPCNT = 0x3 | 0x400;
   
   for (i = 0; i < 240; i += 1)
      for (j = 0; j < 16; j++)
         vbuff[i * 240 + j] = 0;

   i = 0;   
   for (n = 0; n < bytes; n++)
   {
      for (j = 7; j >= 0; j--)
      {
         if ((*p >> j) & 0x01)
            vbuff[i * 240 + (14 - (j << 1))] = 31;
         else
            vbuff[i * 240 + (14 - (j << 1))] = 31 << 5;
      }
      p++;
      i += 2;
   }
   
   while (!((~(*k)) & 0x0001))
      ;
   while (!((~(*k)) & 0x0002))
      ;
   
   REG_DISPCNT = oldmode;
}


To use it, call DebugVar() and pass to it a pointer to the data you want to show (eg. &myVariable or just myArray) and the number of bytes you want to display.

Every other line on the screen will display a series of eight dots each representing a bit of your variable: green if the bit is clear, red if it's set.

Note that it displays one byte at a time in the order it appears in memory, so a 32-bit variable will display bits in the order [7..0], [15..8], [23..16] and on the fourth line [31..24].

Press "A" then "B" when you're done viewing (I use the two-button sequence to debounce it when you call DebugVar multiple times in a row). It'll restore the previous mode, and depending on your app there's a slim chance your graphics might not even glitch! :)

If anyone else has similar debugging routines, I'd love to see them!

Dan.

#10553 - sajiimori - Mon Sep 08, 2003 5:55 pm

After I found out about VBA's debug console, that's all I use for debug output.

The "error" macro makes sure I get the debug message by filling the screen with red, then waiting until I press "start" to actually print the message.

I used macros because it's easier to use an unspecified number of arguments (don't have to mess with stdarg.h). The body of the macros should be a single expression to avoid annoying macro bugs. That's the reason for the short helper functions at the bottom.

Using macros a lot makes me wish "for", "while", etc were expressions. I mean, what good is it to distinguish statements from expressions anyway? At least I get to use "? :" in expressions.

Maybe I've just been messing with Haskell too much. :-P
Code:

/*** common.h ***/

#ifdef DEBUG
static char dprint_buffer[80];

#define dprint(...) sprintf(dprint_buffer, __VA_ARGS__), \
   dprint_asm(dprint_buffer)

static dprint_asm(char *s)
{
   asm volatile(
      "mov r0, %0;"
       "swi 0xff;"
      :
      : "r" (s)
      : "r0");
}

#define error(...) \
   REG_DISPCNT = MODE_3|BG2_ENABLE, \
   fill_screen(RED), \
   wait_for_start(), \
   dprint(__VA_ARGS__), \
   halt()

#else
#define dprint
#define error halt
#endif


/*** util.c ***/

fill_screen(u16 val)
{
   u16* p = VIDEO_MEMORY;
   while(p < VIDEO_MEMORY + 240*160)
      *p++ = val;
}

wait_for_start()
{
   while(!PRESSED(KEY_START));
}

halt()
{
   while(1);
}

#10555 - tepples - Mon Sep 08, 2003 6:31 pm

sajiimori wrote:
Using macros a lot makes me wish "for", "while", etc were expressions. I mean, what good is it to distinguish statements from expressions anyway?

ANSI C so distinguishes, but a GNU C extension lets you use for and while inside expressions. Surround the statement with ({ ... }), and then the value of the expression will be the value of the last expression in the block.

GNU C extensions are safe to use because pretty much everybody reading this board uses either DevKit Advance or Nintendo's compiler, both of which are GCC distributions.

Quote:
Maybe I've just been messing with Haskell too much.

My coding style has some functional influences from a Scheme class I took. You may see a lot of computation in my local variable declarations, which sort of looks like a Scheme let*.

Anyway, I like to use unused parts of my status-bar layer for displaying debug information.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#10557 - sajiimori - Mon Sep 08, 2003 7:01 pm

Hey, that's pretty cool! Thanks for the tip, tepples.

Edit: Sadly, anonymous {} enclosues are treated as a top-level expression (rather than a primary operator such as () ), so you can use them in if() and while(), but this test didn't compile:
Code:

main()
{
   int i;

   {
      for(i = 0; i < 10; ++i)
         0;
   } ?  // the expression is already over, so ? was not expected
      puts("++i was last") :
      puts("zero was last");
}

#10561 - tepples - Tue Sep 09, 2003 1:28 am

Did you try enclosing that for loop with ({ and }) instead of { and } ?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#10571 - Darkain - Tue Sep 09, 2003 6:32 am

Code:
void _assert_handler(const char *reason, const char *file, int line, int val0, int val1, int val2, int val3) {
   char str[64] = "\n";
   itoa(val0, str+1);
   itoa(val1, str+10);
   itoa(val2, str+19);
   itoa(val3, str+28);
   str[9] = ' ';
   str[18] = '\n';
   str[27] = ' ';
   _assert_handler(reason, file, line, str);
}

void _assert_handler(const char *reason, const char *file, int line, const char *text) {
   char str[32];

   u32 interupt = (REG_INTERUPT & 0x7FFFFFFF) | (REG_IME<<31);
   u32 interuptdata = REG_IE | (REG_IF<<16);
   REG_IME = 0;
   REG_IE = 0;

   u32 dispmode = REG_DISPCNT;
   u32 disstat = REG_DISPSTAT | (REG_VCOUNT<<16);
   u32 backgrounds1 = REG_BG0CNT | (REG_BG1CNT<<16);
   u32 backgrounds2 = REG_BG2CNT | (REG_BG3CNT<<16);
   u32 input = REG_P1 | (REG_P1CNT<<16);
   u32 input1 = REG_JOYRE;
   u32 input2 = REG_JOYTR;
   u32 input3 = REG_JSTAT;

   u32 timer0 = REG_TM0CNT | (REG_TM0D<<16);
   u32 timer1 = REG_TM1CNT | (REG_TM1D<<16);
   u32 timer2 = REG_TM2CNT | (REG_TM2D<<16);
   u32 timer3 = REG_TM3CNT | (REG_TM3D<<16);
   REG_TM0CNT = 0;
   REG_TM1CNT = 0;
   REG_TM2CNT = 0;
   REG_TM3CNT = 0;

   u32 waitstate = REG_WSCNT | (REG_PAUSE << 16);
   u32 linkmode = REG_SCCNT;
   u32 players1 = REG_SCD1 | (REG_SCD0 << 16);
   u32 players2 = REG_SCD3 | (REG_SCD2 << 16);


   gbaDebug debug(0, false);
   debug.drawString("-----  GBA Debug Screen  -----", 0, 0);
   debug.drawString("-----------  BSOD  -----------", 0, 152);

   u32 base = 8;
   u32 basey = 52;

   base += 8;
   itoa(dispmode, str);
   itoa(disstat, str+9);
   str[8] = ' ';
   debug.drawString("Display:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(backgrounds2, str);
   itoa(backgrounds1, str+9);
   str[8] = ' ';
   debug.drawString("Backgrounds:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(interupt, str);
   itoa(interuptdata, str+9);
   str[8] = ' ';
   debug.drawString("Interupts:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(input, str);
   itoa(input1, str+9);
   str[8] = ' ';
   debug.drawString("Input:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(input2, str);
   itoa(input3, str+9);
   str[8] = ' ';
   debug.drawString("Input:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(timer0, str);
   itoa(timer1, str+9);
   str[8] = ' ';
   debug.drawString("Timer 0-1:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(timer2, str);
   itoa(timer3, str+9);
   str[8] = ' ';
   debug.drawString("Timer 2-3:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(waitstate, str);
   itoa(linkmode, str+9);
   str[8] = ' ';
   debug.drawString("Wait State:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(players1, str);
   itoa(players2, str+9);
   str[8] = ' ';
   debug.drawString("Link Cable:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   itoa(line, str);
   itoa(BUILD_NUMBER, str+9);
   str[8] = ' ';
   debug.drawString("Line/Build:", 0, base);
   debug.drawString(str, basey, base);

   base += 8;
   debug.drawString("File:", 0, base);
   debug.drawString(file, basey, base);

   base += 8;
   debug.drawString("Reason:", 0, base);
   debug.drawString(reason, basey, base);

   if (text) {
      base += 8;
      debug.drawString(text, 0, base);
   }

   debug.lockScreen();
}


[Images not permitted - Click here to view it]
_________________
-=- Darkain Dragoon -=-
http://www.darkain.com
DarkStar for Nintendo DS

#10572 - sajiimori - Tue Sep 09, 2003 7:11 am

D'oh! Right you are, tepples...

#10686 - AnthC - Fri Sep 12, 2003 4:02 pm

Here's mine

#include <stdio.h>
#include <stdarg.h>

#define assert(e) {if (!(e)) {dprintf("assertion failed in %s line %s\n",__FILE_,__LINE__);}for (;;) {;}}

void dprints(const char *sz)
{
asm volatile (
"mov r2, %0
ldr r0, =0xc0ded00d
mov r1, #0
and r0, r0, r0" :
/* No output */ :
"r" (sz) :
"r0", "r1", "r2");
}

void dprintf(const char *sz,...)
{
va_list par;
va_start(par,sz);
char temp[1024];
vsprintf(temp,sz,par);
va_end(par);
dprints(temp);
}


dprintf() is basically equiv to printf but the output goes to the console in Mappy or VBA

#10692 - sajiimori - Fri Sep 12, 2003 6:50 pm

AnthC,

Nice! Try this:
Code:

#define assert(e) ({if (!(e)) \
{dprintf("assertion failed in %s line %d: "#e"\n",__FILE__,__LINE__);} \
for (;;) {;}})

Fixes a couple typos (I'm sure they aren't in your real version), prints the actual assertion that failed, and allows the macro to be used this way without compiler errors:
Code:

   if(a == 0)
      assert(x == 10);
   else
      assert(x == 5);

Not that assertions are normally used that way...

#10697 - AnthC - Fri Sep 12, 2003 10:45 pm

You sir are dead right! I stand corrected.
(I did type it in from my debug routine)
Anth

#10699 - Paul Shirley - Fri Sep 12, 2003 11:50 pm

removed