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.

DS development > Need help with button input, main and sub screen programs

#176595 - Squeakychu - Tue Aug 23, 2011 7:22 am

Hello. I'm new to DS programming, though I have had programming experience before. I hope to get along with everyone and make some cool games and programs for the DS. Anyways, I have a few problems and questions on some programs I'm trying to make with devkitpro, and I hope this is the right place to get help.

In my first program, I'm trying to display moving stars using random colors in random positions and random speed. I'm also trying to get the DS to read inputs and change the message if a button is inputted. The stars are rendered on the top screen while the button information is on the bottom. Here's the full code for the program.

Code:
#include <nds.h>
#include <stdio.h>
#define NUM_STARS 60

typedef struct
{
   int x;
   int y;
   int speed;
   int speed2;
   int xpixle;
   unsigned short color;
}Star;

Star star[NUM_STARS];

void ClearScreen(void)
{
   int i;
   for(i = 0; i < 256 * 192; i++)
      VRAM_A[i] = RGB15(0,0,0);
}
void InitStars(void)
{
   int i;
   for(i = 0; i < NUM_STARS; i++)
   {
      star[i].color = RGB15(rand() % 15 + 16, rand() % 20 + 11, rand() % 25 + 16);
      star[i].speed = rand() % 90 + 20;
      star[i].speed2 = 0;
      star[i].x = rand() % 256;
      star[i].y = rand() % 192;
      star[i].xpixle = 0;
   }
}

void EraseStar(Star* star)
{
   VRAM_A[(int)star->x + star->y * SCREEN_WIDTH] = RGB15(0,0,0);
}

void MoveStar(Star* star)
{
   star->speed2 += star->speed;

   if(star->speed2 >= 100)
   {
      while(star->speed2 >= 100)
      {
         star->xpixle++;
         star->speed2 -= 100;
      }
   star->x += star->xpixle;
   star->xpixle = 0;
   }
   
   if(star->x >= SCREEN_WIDTH)
   {
      star->color = RGB15(rand() % 15 + 16, rand() % 20 + 11, rand() % 25 + 16);
      star->x = 0;
      star->y = rand() % 192;
      star->speed = rand() % 90 + 20;
   }
}

void DrawStar(Star* star)
{
   VRAM_A[(int)star->x + star->y * SCREEN_WIDTH] = star->color;
}

int main(void)
{
   int i;

   irqInit();
   irqEnable(IRQ_VBLANK);

   videoSetMode(MODE_FB0);
   vramSetBankA(VRAM_A_LCD);

   ClearScreen();
   InitStars();
   
   consoleDemoInit();

   while(1)
   {
   scanKeys();

   if (keysHeld() & KEY_A)
      printf("Button A is pressed\n");
   else
      printf("Button A is released\n");

   if (keysHeld() & KEY_X)
      printf("Button X is pressed\n");
   else
      printf("Button X is released\n");

   if (keysHeld() & KEY_B)
      printf("Button B is pressed\n");
   else
      printf("Button B is released\n");

   if (keysHeld() & KEY_Y)
      printf("Button Y is pressed\n");
   else
      printf("Button Y is released\n");

   if (keysHeld() & KEY_L)
      printf("Button L is pressed\n");
   else
      printf("Button L is released\n");
   
   if (keysHeld() & KEY_R)
      printf("Button R is pressed\n");
   else
      printf("Button R is released\n");

   if (keysHeld() & KEY_START)
      printf("Start button is pressed\n");
   else
      printf("Start button is released\n");
      
   if (keysHeld() & KEY_SELECT)
      printf("Select button is pressed\n");
   else
      printf("Select button is released\n");

   if (keysHeld() & KEY_TOUCH)
      printf("The Touch screen is touched\n");
   else
      printf("The touch screen is not touched\n");

   swiWaitForVBlank();

   for(i = 0; i < NUM_STARS; i++)
      {
         EraseStar(&star[i]);

         MoveStar(&star[i]);

         DrawStar(&star[i]);
      }
   consoleClear();
   }
   return 0;
}


The program compiles and runs just fine, but the X, Y, and touch buttons don't seem to register, even though it works just fine on it's own. I'm curious what the issue with that is. Also, I don't want stars to move at an integer speed per frame, but ARM9 doesn't handle floating point integers well from what I hear. So I used this code in order to have greater control over my speed without using floating-point variables.

Code:
void MoveStar(Star* star)
{
   star->speed2 += star->speed;

   if(star->speed2 >= 100)
   {
      while(star->speed2 >= 100)
      {
         star->xpixle++;
         star->speed2 -= 100;
      }
   star->x += star->xpixle;
   star->xpixle = 0;
   }
   
   if(star->x >= SCREEN_WIDTH)
   {
      star->color = RGB15(31,31,31);
      star->x = 0;
      star->y = rand() % 192;
      star->speed = rand() % 90 + 20;
   }
}


I'm curious if this is the correct way of moving a star less than 1 pixel at a time, or if there's a better or less CPU-intensive method of doing what's intended in this code.

Lastly, I'm trying to get to grips with control of the Main and Sub Displays, so I'm trying to make a program that displays the moving stars on the top screen, while having a simple drawing program run on the bottom screen. I'm trying to set the drawing program to the main display, while setting the moving stars to the sub display, and as you can maybe see in this code, I'm having great difficulty trying to achieve that.

Code:
#include <nds.h>
#include <stdio.h>

#define NUM_STARS 60

typedef struct
{
   int x;
   int y;
   int speed;
   int speed2;
   int xpixle;
   unsigned short color;
}Star;

Star star[NUM_STARS];

void ClearScreen(void)
{
   int i;
   for(i = 0; i < 256 * 192; i++)
      VRAM_B[i] = RGB15(0,0,0);
}
void InitStars(void)
{
   int i;
   for(i = 0; i < NUM_STARS; i++)
   {
      star[i].color = RGB15(31,31,31);
      star[i].speed = rand() % 90 + 20;
      star[i].speed2 = 0;
      star[i].x = rand() % 256;
      star[i].y = rand() % 192;
      star[i].xpixle = 0;
   }
}

//code for drawing a line between two points
void DrawLine(int x1, int y1, int x2, int y2, unsigned short color)
{
   int yStep = SCREEN_WIDTH;
   int xStep = 1;
   int xDiff = x2 - x1;
   int yDiff = y2 - y1;

   int errorTerm = 0;
   int offset = y1 * SCREEN_WIDTH + x1;
   int i;

   if(xDiff < 0)
   {
      xDiff = -xDiff;
      xStep = -xStep;
   }

   if(yDiff < 0)
   {
      yDiff = -yDiff;
      yStep = -yStep;
   }

   if (xDiff > yDiff)
   {
      for(i = 0; i < xDiff + 1; i++)
      {
         VRAM_A[offset] = color;

         offset += xStep;

         errorTerm += yDiff;

         if (errorTerm > xDiff)
         {
            errorTerm -= xDiff;
            offset += yStep;
         }
      }
   }
   else
   {
      for(i = 0; i < yDiff + 1; i++)
      {
         VRAM_A[offset] = color;

         offset += yStep;

         errorTerm += xDiff;

         if (errorTerm > yDiff)
         {
            errorTerm -= yDiff;
            offset += xStep;
         }
      }
   }
}

void EraseStar(Star* star)
{
   VRAM_B[(int)star->x + star->y * SCREEN_WIDTH] = RGB15(0,0,0);
}

void MoveStar(Star* star)
{
   star->speed2 += star->speed;

   if(star->speed2 >= 100)
   {
      while(star->speed2 >= 100)
      {
         star->xpixle++;
         star->speed2 -= 100;
      }
   star->x += star->xpixle;
   star->xpixle = 0;
   }
   
   if(star->x >= SCREEN_WIDTH)
   {
      star->color = RGB15(31,31,31);
      star->x = 0;
      star->y = rand() % 192;
      star->speed = rand() % 90 + 20;
   }
}

void DrawStar(Star* star)
{
   VRAM_B[(int)star->x + star->y * SCREEN_WIDTH] = star->color;
}

int main(void)
{
   touchPosition touch;

   int oldX = 0;
   int oldY = 0;
   int i;
       
 // trying to set drawing(VRAM_A) to the bottom(main) screen and the moving stars(VRAM_B) to the top(sub).
   videoSetMode(MODE_FB0);
   videoSetModeSub(MODE_FB1);
   vramSetBankA(VRAM_A_LCD);
   vramSetBankB(VRAM_B_LCD);

   lcdMainOnBottom();

   ClearScreen();
   InitStars();

   while(1)
   {
      scanKeys();

      if(keysHeld() & KEY_START)
      {
         for(i = 0; i < 256 * 192; i++)
         {
            VRAM_A[i] = RGB15(0,0,0);
         }
      }


      touchRead(&touch);

      if(!(keysDown() & KEY_TOUCH) && (keysHeld() & KEY_TOUCH))
      {
         DrawLine(oldX, oldY, touch.px, touch.py, rand());
      }
      oldX = touch.px;
      oldY = touch.py;

      swiWaitForVBlank();

   for(i = 0; i < NUM_STARS; i++)
      {
         EraseStar(&star[i]);

         MoveStar(&star[i]);

         DrawStar(&star[i]);
      }
   consoleClear();
   }
   return 0;
}


With this program as it is, the drawing part works just fine, but the top screen displays nothing but white. I have no idea how to correct this issue. If anyone has suggestions, that would be just fantastic!

Anyway, these are my major concerns. Thanks for bearing with me, and I hope to have a good experience on these forums. :)

#176596 - sverx - Tue Aug 23, 2011 11:03 am

ARM9 has no support for floating point, but you can use fixed point with no problem.
The NDS SUB Engine has no frame buffer mode. You could use a BG in BMP mode. VRAM_B can't be used with SUB Engine, you can use VRAM_C. Check this example:

Code:
#include <nds.h>

int main(void) {

  vramSetBankC (VRAM_C_SUB_BG);
 
  videoSetModeSub (MODE_4_2D | DISPLAY_BG3_ACTIVE);
 
  REG_BG3CNT_SUB = BG_BMP16_256x256 | BG_BMP_BASE(0);
 
  REG_BG3PA_SUB = 1 << 8;
  REG_BG3PB_SUB = 0;
  REG_BG3PC_SUB = 0;
  REG_BG3PD_SUB = 1 << 8;
 
  lcdMainOnTop ();
 
  while(1) {
 
    scanKeys();
   
    if (keysHeld() & KEY_TOUCH) {
     
      touchPosition touch; touchRead(&touch);
     
      BG_GFX_SUB [touch.py * SCREEN_WIDTH + touch.px] = RGB5(31,31,31) | BIT (15);
    }

    swiWaitForVBlank();
  }
}



edit: with current dkA/libnds, you no longer need to add:
Code:
irqInit();
irqEnable(IRQ_VBLANK);

to your code. Remove them.

#176598 - Squeakychu - Tue Aug 23, 2011 5:03 pm

Awesome! All of my problems have been resolved. Removing irqInit() and irqEnable() fixed my input problem, and now X, Y, and touch register just fine. My stars/drawing program also works like a charm now setting it in Mode4/BG3. Thanks for the advice sverx!

#176602 - Squeakychu - Wed Aug 24, 2011 6:47 am

I'm not sure if I should create another help topic in so little time, and this is related to my original post, so I guess I should post this here. Now that my program works perfectly, I'm trying to optimize it to use as little resources as possible. I'm trying to make my program so that the stars only move a decimal amount (speed) via fixed-point arithmetic. However, I'm having a hard time coming to heads with fixed-point and the concept of shifting bits.
I know that something like '1 << 8' would equal 256, or 100000000 in binary, but I'm still lost on how to apply this to dividing numbers that don't completely go into the divisor. In the mean time, I have used this code to move X by .37 by dividing by 100 instead.

Code:
void InitStars(void)
{
   unsigned short i;
   for(i = 0; i < NUM_STARS; i++)
   {
      star[i].color = RGB15(31,31,31);
// need speed divided by fixed point
      star[i].speed = 37;
      star[i].speed2 = 0;
      star[i].x = rand() % 256;
      star[i].y = rand() % 192;
   }
}


Code:
void MoveStar(Star* star)
{
   star->speed2 += star->speed;
   if(star->speed2 >= 100)
   {
      while(star->speed2 >= 100)
      {
         star->x++;
         star->speed2 -= 100;
      }
   }
   
   if(star->x >= SCREEN_WIDTH)
   {
      star->color = RGB15(31,31,31);
      star->x = 0;
      star->y = rand() % 192;
// same as above
      star->speed = rand() 37;
   }
}


I'm tend to learn by example, but I can't find many examples of fixed-point numbers in C. Would anyone know how to divide something like 37 by 100 via fixed point arithmetic, or is there any example code that divides by fixed point numbers? Again, thanks!

#176603 - Dwedit - Wed Aug 24, 2011 8:15 am

37/100 is about 95/256. So if you want a fixed point number that gets shifted back 8 bits, you use 256 as your fraction denominator.

A popular fixed point format is 16.16, so you have a 16-bit integer part, and a 16-bit fractional part, but you can use any convention you want to separate the 32-bit numbers into an integer and fractional part. Other formats include 24.8, or 8.24. 0.32 (0 bits for integer part, 32 bits for fractional part) is also useful for pure fractions, since the ARM processor can just do a 32-bit * 32-bit long multiply to get a 64 bit answer, then discard the low word to get the integer result.

When you use fixed point numbers, you have to deal with precision loss. Many fractions that would be fine in decimal do not divide evenly into a power of 2. For example, 1/3, 1/5 and 1/10 are infinitely repeating fractions when expressed in binary. 1/10 = 0.0001100110011001100...
As a hex number with 32 bits of fraction, it's 0x19999999. Multiply that by 10 (0x0A), and you get 0xFFFFFFFA, which is not quite 1. But if you add 1 to your fraction, you get 0x100000004 when you multiply by 10. So your integer part is correct, but your fractional part is a tad too high.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#176608 - Azenris - Wed Aug 24, 2011 1:55 pm

http://www.coranac.com/tonc/text/fixed.htm
_________________
<Robert Morley>
My Homebrew Games